staticint analogix_dp_detect_hpd(struct analogix_dp_device *dp)
{ int timeout_loop = 0;
while (timeout_loop < DP_TIMEOUT_LOOP_COUNT) { if (analogix_dp_get_plug_in_status(dp) == 0) return 0;
timeout_loop++;
usleep_range(1000, 1100);
}
/* * Some edp screen do not have hpd signal, so we can't just * return failed when hpd plug in detect failed, DT property * "force-hpd" would indicate whether driver need this.
*/ if (!dp->force_hpd) return -ETIMEDOUT;
/* * The eDP TRM indicate that if HPD_STATUS(RO) is 0, AUX CH * will not work, so we need to give a force hpd action to * set HPD_STATUS manually.
*/
dev_dbg(dp->dev, "failed to get hpd plug status, try to force hpd\n");
analogix_dp_force_hpd(dp);
if (analogix_dp_get_plug_in_status(dp) != 0) {
dev_err(dp->dev, "failed to get hpd plug in status\n"); return -EINVAL;
}
dev_dbg(dp->dev, "success to get plug in status after force hpd\n");
return 0;
}
staticbool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
{ unsignedchar psr_version; int ret;
ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version); if (ret != 1) {
dev_err(dp->dev, "failed to get PSR version, disable it\n"); returnfalse;
}
dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version); return psr_version & DP_PSR_IS_SUPPORTED;
}
staticint analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
{ unsignedchar psr_en; int ret;
/* Disable psr function */
ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en); if (ret != 1) {
dev_err(dp->dev, "failed to get psr config\n"); goto end;
}
psr_en &= ~DP_PSR_ENABLE;
ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); if (ret != 1) {
dev_err(dp->dev, "failed to disable panel psr\n"); goto end;
}
/* Main-Link transmitter remains active during PSR active states */
psr_en = DP_PSR_CRC_VERIFICATION;
ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); if (ret != 1) {
dev_err(dp->dev, "failed to set panel psr\n"); goto end;
}
/* Enable psr function */
psr_en = DP_PSR_ENABLE | DP_PSR_CRC_VERIFICATION;
ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); if (ret != 1) {
dev_err(dp->dev, "failed to set panel psr\n"); goto end;
}
analogix_dp_enable_psr_crc(dp);
dp->psr_supported = true;
return 0;
end:
dev_err(dp->dev, "enable psr fail, force to disable psr\n");
ret = drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data); if (ret != 1) return ret;
if (enable)
ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
DP_LANE_COUNT_ENHANCED_FRAME_EN |
DPCD_LANE_COUNT_SET(data)); else
ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
DPCD_LANE_COUNT_SET(data));
for (lane = 0; lane < lane_count; lane++)
dp->link_train.cr_loop[lane] = 0;
/* Set link rate and count as you want to establish*/
analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
retval = analogix_dp_wait_pll_locked(dp); if (retval) {
DRM_DEV_ERROR(dp->dev, "Wait for pll lock failed %d\n", retval); return retval;
} /* * MACRO_RST must be applied after the PLL_LOCK to avoid * the DP inter pair skew issue for at least 10 us
*/
analogix_dp_reset_macro(dp);
analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
/* Setup RX configuration */
buf[0] = dp->link_train.link_rate;
buf[1] = dp->link_train.lane_count;
retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2); if (retval < 0) return retval; /* set enhanced mode if available */
retval = analogix_dp_set_enhanced_mode(dp); if (retval < 0) {
dev_err(dp->dev, "failed to set enhance mode\n"); return retval;
}
/* Set TX voltage-swing and pre-emphasis to minimum */ for (lane = 0; lane < lane_count; lane++)
dp->link_train.training_lane[lane] =
DP_TRAIN_VOLTAGE_SWING_LEVEL_0 |
DP_TRAIN_PRE_EMPH_LEVEL_0;
analogix_dp_set_lane_link_training(dp);
/* Set training pattern 1 */
analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
/* Set RX training pattern */
retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
DP_LINK_SCRAMBLING_DISABLE |
DP_TRAINING_PATTERN_1); if (retval < 0) return retval;
for (lane = 0; lane < lane_count; lane++)
buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 |
DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) { /* set training pattern 2 for EQ */
analogix_dp_set_training_pattern(dp, TRAINING_PTN2);
if (!analogix_dp_channel_eq_ok(link_status, link_align, lane_count)) { /* traing pattern Set to Normal */
retval = analogix_dp_training_pattern_dis(dp); if (retval < 0) return retval;
dev_dbg(dp->dev, "Link Training success!\n");
analogix_dp_get_link_bandwidth(dp, ®);
dp->link_train.link_rate = reg;
dev_dbg(dp->dev, "final bandwidth = %.2x\n",
dp->link_train.link_rate);
analogix_dp_get_lane_count(dp, ®);
dp->link_train.lane_count = reg;
dev_dbg(dp->dev, "final lane count = %.2x\n",
dp->link_train.lane_count);
dp->link_train.lt_state = FINISHED;
return 0;
}
/* not all locked */
dp->link_train.eq_loop++;
if (dp->link_train.eq_loop > MAX_EQ_LOOP) {
dev_err(dp->dev, "EQ Max loop\n");
analogix_dp_reduce_link_rate(dp); return -EIO;
}
/* * For DP rev.1.1, Maximum link rate of Main Link lanes * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps * For DP rev.1.2, Maximum link rate of Main Link lanes * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps, 0x14 = 5.4Gbps
*/
drm_dp_dpcd_readb(&dp->aux, DP_MAX_LINK_RATE, &data);
*bandwidth = data;
}
if ((dp->link_train.link_rate != DP_LINK_BW_1_62) &&
(dp->link_train.link_rate != DP_LINK_BW_2_7) &&
(dp->link_train.link_rate != DP_LINK_BW_5_4)) {
dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n",
dp->link_train.link_rate);
dp->link_train.link_rate = DP_LINK_BW_1_62;
}
if (dp->link_train.lane_count == 0) {
dev_err(dp->dev, "Rx Max Lane count is abnormal :%x !\n",
dp->link_train.lane_count);
dp->link_train.lane_count = (u8)LANE_COUNT1;
}
/* Setup TX lane count & rate */ if (dp->link_train.lane_count > max_lanes)
dp->link_train.lane_count = max_lanes; if (dp->link_train.link_rate > max_rate)
dp->link_train.link_rate = max_rate;
/* All DP analog module power up */
analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
dp->link_train.lt_state = START;
/* Process here */ while (!retval && !training_finished) { switch (dp->link_train.lt_state) { case START:
retval = analogix_dp_link_start(dp); if (retval)
dev_err(dp->dev, "LT link start failed!\n"); break; case CLOCK_RECOVERY:
retval = analogix_dp_process_clock_recovery(dp); if (retval)
dev_err(dp->dev, "LT CR failed!\n"); break; case EQUALIZER_TRAINING:
retval = analogix_dp_process_equalizer_training(dp); if (retval)
dev_err(dp->dev, "LT EQ failed!\n"); break; case FINISHED:
training_finished = 1; break; case FAILED: return -EREMOTEIO;
}
} if (retval)
dev_err(dp->dev, "eDP link training failed (%d)\n", retval);
return retval;
}
staticint analogix_dp_fast_link_train(struct analogix_dp_device *dp)
{ int ret;
u8 link_align, link_status[2];
analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
ret = analogix_dp_wait_pll_locked(dp); if (ret) {
DRM_DEV_ERROR(dp->dev, "Wait for pll lock failed %d\n", ret); return ret;
}
/* * MACRO_RST must be applied after the PLL_LOCK to avoid * the DP inter pair skew issue for at least 10 us
*/
analogix_dp_reset_macro(dp);
analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
analogix_dp_set_lane_link_training(dp);
/* source Set training pattern 1 */
analogix_dp_set_training_pattern(dp, TRAINING_PTN1); /* From DP spec, pattern must be on-screen for a minimum 500us */
usleep_range(500, 600);
analogix_dp_set_training_pattern(dp, TRAINING_PTN2); /* From DP spec, pattern must be on-screen for a minimum 500us */
usleep_range(500, 600);
/* * Useful for debugging issues with fast link training, disable for more * speed
*/ if (verify_fast_training) {
ret = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED,
&link_align); if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "Read align status failed %d\n",
ret); return ret;
}
ret = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status,
2); if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "Read link status failed %d\n",
ret); return ret;
}
ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_STATUS, &sink); if (ret != 1)
DRM_DEV_ERROR(dp->dev, "Failed to read psr status %d\n", ret); elseif (sink == DP_PSR_SINK_ACTIVE_RFB) return 0;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!"); return -EINVAL;
}
if (!dp->plat_data->skip_connector) {
connector = &dp->connector;
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(dp->drm_dev, connector,
&analogix_dp_connector_funcs,
DRM_MODE_CONNECTOR_eDP); if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n"); return ret;
}
/* * NOTE: the connector registration is implemented in analogix * platform driver, that to say connector would be exist after * plat_data->attch return, that's why we record the connector * point after plat attached.
*/ if (dp->plat_data->attach) {
ret = dp->plat_data->attach(dp->plat_data, bridge, connector); if (ret) {
DRM_ERROR("Failed at platform attach func\n"); return ret;
}
}
crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return;
old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); /* Don't touch the panel if we're coming back from PSR */ if (old_crtc_state && old_crtc_state->self_refresh_active) return;
drm_panel_prepare(dp->plat_data->panel);
}
staticint analogix_dp_set_bridge(struct analogix_dp_device *dp)
{ int ret;
pm_runtime_get_sync(dp->dev);
ret = analogix_dp_init_analog_func(dp); if (ret) return ret;
/* * According to DP spec v1.3 chap 3.5.1.2 Link Training, * We should first make sure the HPD signal is asserted high by device * when we want to establish a link with it.
*/
ret = analogix_dp_detect_hpd(dp); if (ret) {
DRM_ERROR("failed to get hpd single ret = %d\n", ret); goto out_dp_init;
}
ret = analogix_dp_commit(dp); if (ret) {
DRM_ERROR("dp commit error, ret = %d\n", ret); goto out_dp_init;
}
crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return;
old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); /* Not a full enable, just disable PSR and continue */ if (old_crtc_state && old_crtc_state->self_refresh_active) {
ret = analogix_dp_disable_psr(dp); if (ret)
DRM_ERROR("Failed to disable psr %d\n", ret); return;
}
if (dp->dpms_mode == DRM_MODE_DPMS_ON) return;
while (timeout_loop < MAX_PLL_LOCK_LOOP) { if (analogix_dp_set_bridge(dp) == 0) {
dp->dpms_mode = DRM_MODE_DPMS_ON; return;
}
dev_err(dp->dev, "failed to set bridge, retry: %d\n",
timeout_loop);
timeout_loop++;
usleep_range(10, 11);
}
dev_err(dp->dev, "too many times retry set bridge, give it up\n");
}
/* When moving from PSR to fully disabled, exit PSR first. */ if (old_crtc_state && old_crtc_state->self_refresh_active) {
ret = analogix_dp_disable_psr(dp); if (ret)
DRM_ERROR("Failed to disable psr (%d)\n", ret);
}
}
/* Input vide bpc and color_formats */ switch (display_info->bpc) { case 12:
video->color_depth = COLOR_12; break; case 10:
video->color_depth = COLOR_10; break; case 8:
video->color_depth = COLOR_8; break; case 6:
video->color_depth = COLOR_6; break; default:
video->color_depth = COLOR_8; break;
} if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
video->color_space = COLOR_YCBCR444; elseif (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
video->color_space = COLOR_YCBCR422; else
video->color_space = COLOR_RGB;
/* * NOTE: those property parsing code is used for providing backward * compatibility for samsung platform. * Due to we used the "of_property_read_u32" interfaces, when this * property isn't present, the "video_info" can keep the original * values and wouldn't be modified.
*/
of_property_read_u32(dp_node, "samsung,color-space",
&video->color_space);
of_property_read_u32(dp_node, "samsung,dynamic-range",
&video->dynamic_range);
of_property_read_u32(dp_node, "samsung,ycbcr-coeff",
&video->ycbcr_coeff);
of_property_read_u32(dp_node, "samsung,color-depth",
&video->color_depth); if (of_property_read_bool(dp_node, "hsync-active-high"))
video->h_sync_polarity = true; if (of_property_read_bool(dp_node, "vsync-active-high"))
video->v_sync_polarity = true; if (of_property_read_bool(dp_node, "interlaced"))
video->interlaced = true;
}
/* * platform dp driver need containor_of the plat_data to get * the driver private data, so we need to store the point of * plat_data, not the context of plat_data.
*/
dp->plat_data = plat_data;
ret = analogix_dp_dt_parse_pdata(dp); if (ret) return ERR_PTR(ret);
dp->phy = devm_phy_get(dp->dev, "dp"); if (IS_ERR(dp->phy)) {
dev_err(dp->dev, "no DP phy configured\n");
ret = PTR_ERR(dp->phy); if (ret) { /* * phy itself is not enabled, so we can move forward * assigning NULL to phy pointer.
*/ if (ret == -ENOSYS || ret == -ENODEV)
dp->phy = NULL; else return ERR_PTR(ret);
}
}
dp->clock = devm_clk_get(&pdev->dev, "dp"); if (IS_ERR(dp->clock)) {
dev_err(&pdev->dev, "failed to get clock\n"); return ERR_CAST(dp->clock);
}
dp->reg_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dp->reg_base)) return ERR_CAST(dp->reg_base);
/* Try two different names */
dp->hpd_gpiod = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); if (!dp->hpd_gpiod)
dp->hpd_gpiod = devm_gpiod_get_optional(dev, "samsung,hpd",
GPIOD_IN); if (IS_ERR(dp->hpd_gpiod)) {
dev_err(dev, "error getting HDP GPIO: %ld\n",
PTR_ERR(dp->hpd_gpiod)); return ERR_CAST(dp->hpd_gpiod);
}
if (dp->hpd_gpiod) { /* * Set up the hotplug GPIO from the device tree as an interrupt. * Simply specifying a different interrupt in the device tree * doesn't work since we handle hotplug rather differently when * using a GPIO. We also need the actual GPIO specifier so * that we can get the current state of the GPIO.
*/
dp->irq = gpiod_to_irq(dp->hpd_gpiod);
irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN;
} else {
dp->irq = platform_get_irq(pdev, 0);
irq_flags = IRQF_NO_AUTOEN;
}
if (dp->irq == -ENXIO) {
dev_err(&pdev->dev, "failed to get irq\n"); return ERR_PTR(-ENODEV);
}
ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
analogix_dp_hardirq,
analogix_dp_irq_thread,
irq_flags, "analogix-dp", dp); if (ret) {
dev_err(&pdev->dev, "failed to request irq\n"); return ERR_PTR(ret);
}
pm_runtime_use_autosuspend(dp->dev);
pm_runtime_set_autosuspend_delay(dp->dev, 100);
ret = devm_pm_runtime_enable(dp->dev); if (ret) return ERR_PTR(ret);
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.