ret = clk_prepare_enable(dp->grf_clk); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to prepare_enable grf clock\n"); return ret;
}
ret = regmap_write(dp->grf, reg, val); if (ret) {
DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
clk_disable_unprepare(dp->grf_clk); return ret;
}
clk_disable_unprepare(dp->grf_clk);
return 0;
}
staticint cdn_dp_clk_enable(struct cdn_dp_device *dp)
{ int ret; unsignedlong rate;
ret = clk_prepare_enable(dp->pclk); if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "cannot enable dp pclk %d\n", ret); goto err_pclk;
}
ret = clk_prepare_enable(dp->core_clk); if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "cannot enable core_clk %d\n", ret); goto err_core_clk;
}
ret = pm_runtime_get_sync(dp->dev); if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "cannot get pm runtime %d\n", ret); goto err_pm_runtime_get;
}
if (dp->active_port < 0 || dp->active_port >= dp->ports) {
DRM_DEV_ERROR(dp->dev, "active_port is wrong!\n"); returnfalse;
}
port = dp->port[dp->active_port];
/* * Attempt to read sink count, retry in case the sink may not be ready. * * Sinks are *supposed* to come up within 1ms from an off state, but * some docks need more time to power up.
*/ while (time_before(jiffies, timeout)) { if (!extcon_get_state(port->extcon, EXTCON_DISP_DP)) returnfalse;
if (!cdn_dp_get_sink_count(dp, &sink_count)) return sink_count ? true : false;
ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
dram_data, hdr->dram_size); if (ret) return ret;
ret = cdn_dp_set_firmware_active(dp, true); if (ret) {
DRM_DEV_ERROR(dp->dev, "active ucpu failed: %d\n", ret); return ret;
}
return cdn_dp_event_config(dp);
}
staticint cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
{ int ret;
if (!cdn_dp_check_sink_connection(dp)) return -ENODEV;
ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
DP_RECEIVER_CAP_SIZE); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret;
}
return 0;
}
staticint cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port)
{ union extcon_property_value property; int ret;
if (!port->phy_enabled) {
ret = phy_power_on(port->phy); if (ret) {
DRM_DEV_ERROR(dp->dev, "phy power on failed: %d\n",
ret); goto err_phy;
}
port->phy_enabled = true;
}
ret = cdn_dp_grf_write(dp, GRF_SOC_CON26,
DPTX_HPD_SEL_MASK | DPTX_HPD_SEL); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to write HPD_SEL %d\n", ret); goto err_power_on;
}
ret = cdn_dp_get_hpd_status(dp); if (ret <= 0) { if (!ret)
DRM_DEV_ERROR(dp->dev, "hpd does not exist\n"); goto err_power_on;
}
ret = extcon_get_property(port->extcon, EXTCON_DISP_DP,
EXTCON_PROP_USB_TYPEC_POLARITY, &property); if (ret) {
DRM_DEV_ERROR(dp->dev, "get property failed\n"); goto err_power_on;
}
staticint cdn_dp_enable(struct cdn_dp_device *dp)
{ int ret, i, lanes; struct cdn_dp_port *port;
port = cdn_dp_connected_port(dp); if (!port) {
DRM_DEV_ERROR(dp->dev, "Can't enable without connection\n"); return -ENODEV;
}
if (dp->active) return 0;
ret = cdn_dp_clk_enable(dp); if (ret) return ret;
ret = cdn_dp_firmware_init(dp); if (ret) {
DRM_DEV_ERROR(dp->dev, "firmware init failed: %d", ret); goto err_clk_disable;
}
/* only enable the port that connected with downstream device */ for (i = port->id; i < dp->ports; i++) {
port = dp->port[i];
lanes = cdn_dp_get_port_lanes(port); if (lanes) {
ret = cdn_dp_enable_phy(dp, port); if (ret) continue;
ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, &dp->encoder.encoder); if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "Could not get vop id, %d", ret); return;
}
DRM_DEV_DEBUG_KMS(dp->dev, "vop %s output to cdn-dp\n",
(ret) ? "LIT" : "BIG"); if (ret)
val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16); else
val = DP_SEL_VOP_LIT << 16;
ret = cdn_dp_grf_write(dp, GRF_SOC_CON9, val); if (ret) return;
mutex_lock(&dp->lock);
ret = cdn_dp_enable(dp); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to enable bridge %d\n",
ret); goto out;
} if (!cdn_dp_check_link_status(dp)) {
ret = cdn_dp_train_link(dp); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed link train %d\n", ret); goto out;
}
}
ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); goto out;
}
ret = cdn_dp_config_video(dp); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to config video %d\n", ret); goto out;
}
ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); goto out;
}
if (dp->active) {
ret = cdn_dp_disable(dp); if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to disable bridge %d\n",
ret);
}
}
mutex_unlock(&dp->lock);
/* * In the following 2 cases, we need to run the event_work to re-enable * the DP: * 1. If there is not just one port device is connected, and remove one * device from a port, the DP will be disabled here, at this case, * run the event_work to re-open DP for the other port. * 2. If re-training or re-config failed, the DP will be disabled here. * run the event_work to re-connect it.
*/ if (!dp->connected && cdn_dp_connected_port(dp))
schedule_work(&dp->event_work);
}
ret = cdn_dp_request_firmware(dp); if (ret) goto out;
dp->connected = true;
/* Not connected, notify userspace to disable the block */ if (!cdn_dp_connected_port(dp)) {
DRM_DEV_INFO(dp->dev, "Not connected; disabling cdn\n");
dp->connected = false;
/* Connected but not enabled, enable the block */
} elseif (!dp->active) {
DRM_DEV_INFO(dp->dev, "Connected, not enabled; enabling cdn\n");
ret = cdn_dp_enable(dp); if (ret) {
DRM_DEV_ERROR(dp->dev, "Enabling dp failed: %d\n", ret);
dp->connected = false;
}
/* Enabled and connected to a dongle without a sink, notify userspace */
} elseif (!cdn_dp_check_sink_connection(dp)) {
DRM_DEV_INFO(dp->dev, "Connected without sink; assert hpd\n");
dp->connected = false;
/* Enabled and connected with a sink, re-train if requested */
} elseif (!cdn_dp_check_link_status(dp)) { unsignedint rate = dp->max_rate; unsignedint lanes = dp->max_lanes; struct drm_display_mode *mode = &dp->mode;
DRM_DEV_INFO(dp->dev, "Connected with sink; re-train link\n");
ret = cdn_dp_train_link(dp); if (ret) {
dp->connected = false;
DRM_DEV_ERROR(dp->dev, "Training link failed: %d\n", ret); goto out;
}
/* If training result is changed, update the video config */ if (mode->clock &&
(rate != dp->max_rate || lanes != dp->max_lanes)) {
ret = cdn_dp_config_video(dp); if (ret) {
dp->connected = false;
DRM_DEV_ERROR(dp->dev, "Failed to configure video: %d\n", ret);
}
}
}
/* * It would be nice to be able to just do the work inline right here. * However, we need to make a bunch of calls that might sleep in order * to turn on the block/phy, so use a worker instead.
*/
schedule_work(&dp->event_work);
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.