/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ staticint
i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
uint8_t write_byte, uint8_t *read_byte)
{ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; int ret;
ret = (*algo_data->aux_ch)(adapter, mode,
write_byte, read_byte); return ret;
}
if (is_edp(encoder) && intel_dp->panel_fixed_mode) { if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) return MODE_PANEL; if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) return MODE_PANEL;
}
/* only refuse the mode on non eDP since we have seen some weird eDP panels
which are outside spec tolerances but somehow work by magic */ if (!is_edp(encoder) &&
(cdv_intel_dp_link_required(mode->clock, dev_priv->edp.bpp)
> cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))) return MODE_CLOCK_HIGH;
if (is_edp(encoder)) { if (cdv_intel_dp_link_required(mode->clock, 24)
> cdv_intel_dp_max_data_rate(max_link_clock, max_lanes)) return MODE_CLOCK_HIGH;
} if (mode->clock < 10000) return MODE_CLOCK_LOW;
return MODE_OK;
}
static uint32_t
pack_aux(uint8_t *src, int src_bytes)
{ int i;
uint32_t v = 0;
if (src_bytes > 4)
src_bytes = 4; for (i = 0; i < src_bytes; i++)
v |= ((uint32_t) src[i]) << ((3-i) * 8); return v;
}
staticvoid
unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
{ int i; if (dst_bytes > 4)
dst_bytes = 4; for (i = 0; i < dst_bytes; i++)
dst[i] = src >> ((3-i) * 8);
}
/* The clock divider is based off the hrawclk, *andwouldliketorunat2MHz.So,takethe *hrawclkvalueanddivideby2andusethat *OnCDVplatformituses200MHzashrawclk. *
*/
aux_clock_divider = 200 / 2;
precharge = 4; if (is_edp(encoder))
precharge = 10;
if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) {
DRM_ERROR("dp_aux_ch not started status 0x%08x\n",
REG_READ(ch_ctl)); return -EBUSY;
}
/* Must try at least 3 times according to DP spec */ for (try = 0; try < 5; try++) { /* Load the send data into the aux channel data registers */ for (i = 0; i < send_bytes; i += 4)
REG_WRITE(ch_data + i,
pack_aux(send + i, send_bytes - i));
/* Send the command and wait for it to complete */
REG_WRITE(ch_ctl,
DP_AUX_CH_CTL_SEND_BUSY |
DP_AUX_CH_CTL_TIME_OUT_400us |
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR); for (;;) {
status = REG_READ(ch_ctl); if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) break;
udelay(100);
}
/* Clear done status and any errors */
REG_WRITE(ch_ctl,
status |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR); if (status & DP_AUX_CH_CTL_DONE) break;
}
if ((status & DP_AUX_CH_CTL_DONE) == 0) {
DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); return -EBUSY;
}
/* Check for timeout or receive error. *Timeoutsoccurwhenthesinkisnotconnected
*/ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); return -EIO;
}
/* Timeouts occur when the device isn't connected, so they're
* "normal" -- don't fill the kernel log with these */ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); return -ETIMEDOUT;
}
/* Unload any bytes sent back from the other side */
recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); if (recv_bytes > recv_size)
recv_bytes = recv_size;
for (i = 0; i < recv_bytes; i += 4)
unpack_aux(REG_READ(ch_data + i),
recv + i, recv_bytes - i);
return recv_bytes;
}
/* Write data to the aux channel in native mode */ staticint
cdv_intel_dp_aux_native_write(struct gma_encoder *encoder,
uint16_t address, uint8_t *send, int send_bytes)
{ int ret;
uint8_t msg[20]; int msg_bytes;
uint8_t ack;
/* Write a single byte to the aux channel in native mode */ staticint
cdv_intel_dp_aux_native_write_1(struct gma_encoder *encoder,
uint16_t address, uint8_t byte)
{ return cdv_intel_dp_aux_native_write(encoder, address, &byte, 1);
}
/* read bytes from a native aux channel */ staticint
cdv_intel_dp_aux_native_read(struct gma_encoder *encoder,
uint16_t address, uint8_t *recv, int recv_bytes)
{
uint8_t msg[4]; int msg_bytes;
uint8_t reply[20]; int reply_bytes;
uint8_t ack; int ret;
if (is_edp(encoder))
cdv_intel_edp_panel_vdd_on(encoder);
ret = i2c_dp_aux_add_bus(&intel_dp->adapter); if (is_edp(encoder))
cdv_intel_edp_panel_vdd_off(encoder);
/* If the sink supports it, try to set the power state appropriately */ staticvoid cdv_intel_dp_sink_dpms(struct gma_encoder *encoder, int mode)
{ struct cdv_intel_dp *intel_dp = encoder->dev_priv; int ret, i;
/* Should have a valid DPCD by this point */ if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) return;
if (mode != DRM_MODE_DPMS_ON) {
ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER,
DP_SET_POWER_D3); if (ret != 1)
DRM_DEBUG_DRIVER("failed to write sink power state\n");
} else { /* *Whenturningon,weneedtoretryfor1mstogivethesink *timetowakeup.
*/ for (i = 0; i < 3; i++) {
ret = cdv_intel_dp_aux_native_write_1(encoder,
DP_SET_POWER,
DP_SET_POWER_D0); if (ret == 1) break;
udelay(1000);
}
}
}
if (edp) {
cdv_intel_edp_backlight_off(intel_encoder);
cdv_intel_edp_panel_off(intel_encoder);
cdv_intel_edp_panel_vdd_on(intel_encoder);
} /* Wake up the sink first */
cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON);
cdv_intel_dp_link_down(intel_encoder); if (edp)
cdv_intel_edp_panel_vdd_off(intel_encoder);
}
if (edp)
cdv_intel_edp_panel_on(intel_encoder);
cdv_intel_dp_start_link_train(intel_encoder);
cdv_intel_dp_complete_link_train(intel_encoder); if (edp)
cdv_intel_edp_backlight_on(intel_encoder);
}
static uint8_t
cdv_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane)
{ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); int s = ((lane & 1) ?
DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
uint8_t l = cdv_intel_dp_link_status(link_status, i);
static uint8_t
cdv_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane)
{ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); int s = ((lane & 1) ?
DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
uint8_t l = cdv_intel_dp_link_status(link_status, i);
staticvoid
cdv_intel_get_adjust_train(struct gma_encoder *encoder)
{ struct cdv_intel_dp *intel_dp = encoder->dev_priv;
uint8_t v = 0;
uint8_t p = 0; int lane;
for (lane = 0; lane < intel_dp->lane_count; lane++) {
uint8_t this_v = cdv_intel_get_adjust_request_voltage(intel_dp->link_status, lane);
uint8_t this_p = cdv_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane);
if (this_v > v)
v = this_v; if (this_p > p)
p = this_p;
}
if (v >= CDV_DP_VOLTAGE_MAX)
v = CDV_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
if (p == DP_TRAIN_PRE_EMPHASIS_MASK)
p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
for (lane = 0; lane < 4; lane++)
intel_dp->train_set[lane] = v | p;
}
static uint8_t
cdv_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane)
{ int i = DP_LANE0_1_STATUS + (lane >> 1); int s = (lane & 1) * 4;
uint8_t l = cdv_intel_dp_link_status(link_status, i);
return (l >> s) & 0xf;
}
/* Check for clock recovery is done on all channels */ staticbool
cdv_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
{ int lane;
uint8_t lane_status;
for (lane = 0; lane < lane_count; lane++) {
lane_status = cdv_intel_get_lane_status(link_status, lane); if ((lane_status & DP_LANE_CR_DONE) == 0) returnfalse;
} returntrue;
}
/* Check to see if channel eq is done on all channels */ #define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\
DP_LANE_CHANNEL_EQ_DONE|\
DP_LANE_SYMBOL_LOCKED) staticbool
cdv_intel_channel_eq_ok(struct gma_encoder *encoder)
{ struct cdv_intel_dp *intel_dp = encoder->dev_priv;
uint8_t lane_align;
uint8_t lane_status; int lane;
lane_align = cdv_intel_dp_link_status(intel_dp->link_status,
DP_LANE_ALIGN_STATUS_UPDATED); if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) returnfalse; for (lane = 0; lane < intel_dp->lane_count; lane++) {
lane_status = cdv_intel_get_lane_status(intel_dp->link_status, lane); if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) returnfalse;
} returntrue;
}
/* Enable corresponding port and start training pattern 1 */ staticvoid
cdv_intel_dp_start_link_train(struct gma_encoder *encoder)
{ struct drm_device *dev = encoder->base.dev; struct cdv_intel_dp *intel_dp = encoder->dev_priv; int i;
uint8_t voltage; bool clock_recovery = false; int tries;
u32 reg;
uint32_t DP = intel_dp->DP;
DP |= DP_PORT_EN;
DP &= ~DP_LINK_TRAIN_MASK;
reg = DP;
reg |= DP_LINK_TRAIN_PAT_1; /* Enable output, wait for it to become active */
REG_WRITE(intel_dp->output_reg, reg);
REG_READ(intel_dp->output_reg);
gma_wait_for_vblank(dev);
DRM_DEBUG_KMS("Link config\n"); /* Write the link configuration data */
cdv_intel_dp_aux_native_write(encoder, DP_LINK_BW_SET,
intel_dp->link_configuration, 2);
for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n",
intel_dp->train_set[0],
intel_dp->link_configuration[0],
intel_dp->link_configuration[1]);
if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) {
DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n");
}
cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); /* Set training pattern 1 */
udelay(200); if (!cdv_intel_dp_get_link_status(encoder)) break;
DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n",
intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2],
intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]);
if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
DRM_DEBUG_KMS("PT1 train is done\n");
clock_recovery = true; break;
}
/* Check to see if we've tried the max voltage */ for (i = 0; i < intel_dp->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; if (i == intel_dp->lane_count) break;
/* Check to see if we've tried the same voltage 5 times */ if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
++tries; if (tries == 5) break;
} else
tries = 0;
voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
/* Compute new intel_dp->train_set as requested by target */
cdv_intel_get_adjust_train(encoder);
}
if (!clock_recovery) {
DRM_DEBUG_KMS("failure in DP pattern 1 training, train set %x\n", intel_dp->train_set[0]);
}
DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n",
intel_dp->train_set[0],
intel_dp->link_configuration[0],
intel_dp->link_configuration[1]); /* channel eq pattern */
if (!cdv_intel_dp_set_link_train(encoder, reg,
DP_TRAINING_PATTERN_2)) {
DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n");
} /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
if (cr_tries > 5) {
DRM_ERROR("failed to train DP, aborting\n");
cdv_intel_dp_link_down(encoder); break;
}
udelay(1000); if (!cdv_intel_dp_get_link_status(encoder)) break;
DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n",
intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2],
intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]);
/* Make sure clock is still ok */ if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
cdv_intel_dp_start_link_train(encoder);
cr_tries++; continue;
}
if (cdv_intel_channel_eq_ok(encoder)) {
DRM_DEBUG_KMS("PT2 train is done\n"); break;
}
/* Try 5 times, then try clock recovery if that fails */ if (tries > 5) {
cdv_intel_dp_link_down(encoder);
cdv_intel_dp_start_link_train(encoder);
tries = 0;
cr_tries++; continue;
}
/* Compute new intel_dp->train_set as requested by target */
cdv_intel_get_adjust_train(encoder);
++tries;
if (edp)
cdv_intel_edp_panel_vdd_on(encoder);
status = cdv_dp_detect(encoder); if (status != connector_status_connected) { if (edp)
cdv_intel_edp_panel_vdd_off(encoder); return status;
}
if (intel_dp->force_audio) {
intel_dp->has_audio = intel_dp->force_audio > 0;
} else {
edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) {
intel_dp->has_audio = drm_detect_monitor_audio(edid);
kfree(edid);
}
} if (edp)
cdv_intel_edp_panel_vdd_off(encoder);
return connector_status_connected;
}
staticint cdv_intel_dp_get_modes(struct drm_connector *connector)
{ struct gma_encoder *intel_encoder = gma_attached_encoder(connector); struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct edid *edid = NULL; int ret = 0; int edp = is_edp(intel_encoder);
edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) {
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
}
/* check the VBT to see whether the eDP is on DP-D port */ staticbool cdv_intel_dpc_is_edp(struct drm_device *dev)
{ struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct child_device_config *p_child; int i;
if (!dev_priv->child_dev_num) returnfalse;
for (i = 0; i < dev_priv->child_dev_num; i++) {
p_child = dev_priv->child_dev + i;
/* Set up the DDC bus. */ switch (output_reg) { case DP_B:
name = "DPDDC-B";
gma_encoder->ddi_select = (DP_MASK | DDI0_SELECT); break; case DP_C:
name = "DPDDC-C";
gma_encoder->ddi_select = (DP_MASK | DDI1_SELECT); break;
}
DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n",
intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay,
intel_dp->panel_power_cycle_delay);
DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
cdv_intel_edp_panel_vdd_on(gma_encoder);
ret = cdv_intel_dp_aux_native_read(gma_encoder, DP_DPCD_REV,
intel_dp->dpcd, sizeof(intel_dp->dpcd));
cdv_intel_edp_panel_vdd_off(gma_encoder); if (ret <= 0) { /* if this fails, presume the device is a ghost */
DRM_INFO("failed to retrieve link info, disabling eDP\n");
drm_encoder_cleanup(encoder);
cdv_intel_dp_destroy(connector); goto err_connector;
} else {
DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n",
intel_dp->dpcd[0], intel_dp->dpcd[1],
intel_dp->dpcd[2], intel_dp->dpcd[3]);
} /* The CDV reference driver moves pnale backlight setup into the displays that haveabacklight:thisisagoodideaandoneweshouldprobablyadopt,however
we need to migrate all the drivers before we can do that */ /*cdv_intel_panel_setup_backlight(dev); */
} return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.