static int rockchip_dp_removed(struct hdmi *hdmi_drv)
{
struct dp_dev *dp_dev = hdmi_drv->property->priv;
+ int ret;
- cdn_dp_encoder_disable(dp_dev->dp);
+ ret = cdn_dp_encoder_disable(dp_dev->dp);
+ if (ret)
+ dev_warn(hdmi_drv->dev, "dp has been removed twice:%d\n", ret);
return HDMI_ERROR_SUCCESS;
}
static int rockchip_dp_enable(struct hdmi *hdmi_drv)
{
- hdmi_submit_work(hdmi_drv, HDMI_HPD_CHANGE, 10, 0);
return 0;
}
static int rockchip_dp_control_output(struct hdmi *hdmi_drv, int enable)
{
struct dp_dev *dp_dev = hdmi_drv->property->priv;
+ int ret;
- if (enable == HDMI_AV_UNMUTE)
- cdn_dp_encoder_enable(dp_dev->dp);
- else if (enable & HDMI_VIDEO_MUTE)
- cdn_dp_encoder_disable(dp_dev->dp);
+ if (enable == HDMI_AV_UNMUTE) {
+ if (!dp_dev->early_suspended) {
+ ret = cdn_dp_encoder_enable(dp_dev->dp);
+ if (ret) {
+ dev_err(hdmi_drv->dev,
+ "dp enable video and audio output error:%d\n", ret);
+ return HDMI_ERROR_FALSE;
+ }
+ } else
+ dev_warn(hdmi_drv->dev,
+ "don't output video and audio after dp has been suspended!\n");
+ } else if (enable & HDMI_VIDEO_MUTE)
+ dev_dbg(hdmi_drv->dev, "dp disable video and audio output !\n");
return 0;
}
struct hdmi_video_timing *timing = NULL;
struct dp_dev *dp_dev = hdmi_drv->property->priv;
struct dp_disp_info *disp_info = &dp_dev->disp_info;
+ int ret;
+
+ if (dp_dev->early_suspended) {
+ dev_warn(hdmi_drv->dev,
+ "don't config video after dp has been suspended!\n");
+ return 0;
+ }
timing = (struct hdmi_video_timing *)hdmi_vic2timing(vpara->vic);
if (!timing) {
disp_info->vsync_polarity = 1;
disp_info->hsync_polarity = 1;
- cdn_dp_encoder_mode_set(dp_dev->dp, disp_info);
+ ret = cdn_dp_encoder_mode_set(dp_dev->dp, disp_info);
+ if (ret) {
+ dev_err(hdmi_drv->dev, "dp config video mode error:%d\n", ret);
+ return HDMI_ERROR_FALSE;
+ }
+
return 0;
}
static int rockchip_dp_read_edid(struct hdmi *hdmi_drv, int block, u8 *buf)
{
- int ret;
+ int ret = 0;
struct dp_dev *dp_dev = hdmi_drv->property->priv;
if (dp_dev->lanes == 4)
ret = cdn_dp_get_edid(dp_dev->dp, buf, block);
if (ret)
- return ret;
- return 0;
+ dev_err(hdmi_drv->dev, "dp config video mode error:%d\n", ret);
+
+ return ret;
}
static int rockchip_dp_insert(struct hdmi *hdmi_drv)
if (lanes)
dp_dev->lanes = lanes;
- if (dp_dev->hdmi->enable)
- hdmi_submit_work(dp_dev->hdmi, HDMI_HPD_CHANGE, 20, 0);
+
+ if (dp_dev->hdmi->enable) {
+ if (dp_dev->early_suspended)
+ dev_warn(dp_dev->hdmi->dev,
+ "hpd triggered after early suspend, so don't send hpd change event !\n");
+ else
+ hdmi_submit_work(dp_dev->hdmi, HDMI_HPD_CHANGE, 10, 0);
+ }
}
static void rockchip_dp_early_suspend(struct dp_dev *dp_dev)
{
hdmi_submit_work(dp_dev->hdmi, HDMI_SUSPEND_CTL, 0, 1);
+ cdn_dp_suspend(dp_dev->dp);
}
static void rockchip_dp_early_resume(struct dp_dev *dp_dev)
{
+ cdn_dp_resume(dp_dev->dp);
hdmi_submit_work(dp_dev->hdmi, HDMI_RESUME_CTL, 0, 0);
}
case FB_BLANK_UNBLANK:
break;
default:
- if (!dp_dev->hdmi->sleep)
+ if (!dp_dev->hdmi->sleep) {
rockchip_dp_early_suspend(dp_dev);
+ dp_dev->early_suspended = true;
+ }
break;
}
} else if (action == FB_EVENT_BLANK) {
switch (blank_mode) {
case FB_BLANK_UNBLANK:
- if (dp_dev->hdmi->sleep)
+ if (dp_dev->hdmi->sleep) {
+ dp_dev->early_suspended = false;
rockchip_dp_early_resume(dp_dev);
+ }
break;
default:
break;
rk_dp_ops);
dp_dev->hdmi->dev = dev;
dp_dev->hdmi->enable = 1;
+ dp_dev->early_suspended = 0;
dp_dev->hdmi->sleep = 0;
dp_dev->hdmi->colormode = HDMI_COLOR_RGB_0_255;
dp_dev->dp = dp;
MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
-static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+static int cdn_dp_grf_write(struct cdn_dp_device *dp,
+ unsigned int reg, unsigned int val)
{
int ret;
+
+ ret = clk_prepare_enable(dp->grf_clk);
+ if (ret) {
+ dev_err(dp->dev, "Failed to prepare_enable grf clock\n");
+ return ret;
+ }
+
+ ret = regmap_write(dp->grf, reg, val);
+ if (ret) {
+ dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+ return ret;
+ }
+
+ clk_disable_unprepare(dp->grf_clk);
+
+ return 0;
+}
+
+static int cdn_dp_set_fw_rate(struct cdn_dp_device *dp)
+{
u32 rate;
+ if (!dp->fw_clk_enabled) {
+ rate = clk_get_rate(dp->core_clk);
+ if (rate < 0) {
+ dev_err(dp->dev, "get clk rate failed: %d\n", rate);
+ return rate;
+ }
+ cdn_dp_set_fw_clk(dp, rate);
+ cdn_dp_clock_reset(dp);
+ dp->fw_clk_enabled = true;
+ }
+
+ return 0;
+}
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+ int ret;
+
ret = clk_prepare_enable(dp->pclk);
if (ret < 0) {
dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
- goto err_pclk;
+ goto runtime_get_pm;
}
ret = clk_prepare_enable(dp->core_clk);
goto err_core_clk;
}
- rate = clk_get_rate(dp->core_clk);
- if (rate < 0) {
- dev_err(dp->dev, "get clk rate failed: %d\n", rate);
- goto err_set_rate;
+ ret = pm_runtime_get_sync(dp->dev);
+ if (ret < 0) {
+ dev_err(dp->dev, "cannot get pm runtime %d\n", ret);
+ return ret;
}
- cdn_dp_set_fw_clk(dp, rate);
+ ret = cdn_dp_set_fw_rate(dp);
+ if (ret < 0) {
+ dev_err(dp->dev, "cannot get pm runtime %d\n", ret);
+ goto err_set_rate;
+ }
return 0;
clk_disable_unprepare(dp->core_clk);
err_core_clk:
clk_disable_unprepare(dp->pclk);
-err_pclk:
+runtime_get_pm:
+ pm_runtime_put_sync(dp->dev);
return ret;
}
+static void cdn_dp_clk_disable(struct cdn_dp_device *dp)
+{
+ pm_runtime_put_sync(dp->dev);
+ clk_disable_unprepare(dp->pclk);
+ clk_disable_unprepare(dp->core_clk);
+}
+
int cdn_dp_get_edid(void *dp, u8 *buf, int block)
{
int ret;
struct cdn_dp_device *dp_dev = dp;
+ mutex_lock(&dp_dev->lock);
ret = cdn_dp_get_edid_block(dp_dev, buf, block, EDID_BLOCK_SIZE);
+ mutex_unlock(&dp_dev->lock);
+
return ret;
}
int cdn_dp_connector_detect(void *dp)
{
struct cdn_dp_device *dp_dev = dp;
+ bool ret = false;
+ mutex_lock(&dp_dev->lock);
if (dp_dev->hpd_status == connector_status_connected)
- return true;
- return false;
+ ret = true;
+ mutex_unlock(&dp_dev->lock);
+
+ return ret;
}
-void cdn_dp_encoder_disable(void *dp)
+int cdn_dp_encoder_disable(void *dp)
{
struct cdn_dp_device *dp_dev = dp;
+ int ret = 0;
- dp_dev->dpms_mode = DRM_MODE_DPMS_OFF;
+ mutex_lock(&dp_dev->lock);
+ if (dp_dev->hpd_status == connector_status_disconnected) {
+ dp_dev->dpms_mode = DRM_MODE_DPMS_OFF;
+ mutex_unlock(&dp_dev->lock);
+ return ret;
+ }
+
+ if (dp_dev->dpms_mode == DRM_MODE_DPMS_ON) {
+ dp_dev->dpms_mode = DRM_MODE_DPMS_OFF;
+ } else{
+ dev_warn(dp_dev->dev, "wrong dpms status,dp encoder has already been disabled\n");
+ ret = -1;
+ }
+ mutex_unlock(&dp_dev->lock);
+
+ return ret;
}
static void cdn_dp_commit(struct cdn_dp_device *dp)
dp->dpms_mode = DRM_MODE_DPMS_ON;
}
-void cdn_dp_encoder_mode_set(void *dp, struct dp_disp_info *disp_info)
+int cdn_dp_encoder_mode_set(void *dp, struct dp_disp_info *disp_info)
{
int ret, val;
struct cdn_dp_device *dp_dev = dp;
struct drm_display_mode disp_mode;
struct fb_videomode *mode = disp_info->mode;
+ mutex_lock(&dp_dev->lock);
disp_mode.clock = mode->pixclock / 1000;
disp_mode.hdisplay = mode->xres;
disp_mode.hsync_start = disp_mode.hdisplay + mode->right_margin;
else
val = DP_SEL_VOP_LIT << 16;
- ret = regmap_write(dp_dev->grf, DP_VOP_SEL, val);
- if (ret != 0)
+ ret = cdn_dp_grf_write(dp, GRF_SOC_CON9, val);
+ if (ret != 0) {
dev_err(dp_dev->dev, "Could not write to GRF: %d\n", ret);
-
+ mutex_unlock(&dp_dev->lock);
+ return ret;
+ }
memcpy(&dp_dev->mode, &disp_mode, sizeof(disp_mode));
+
+ mutex_unlock(&dp_dev->lock);
+
+ return 0;
}
-void cdn_dp_encoder_enable(void *dp)
+int cdn_dp_encoder_enable(void *dp)
{
struct cdn_dp_device *dp_dev = dp;
+ int ret = 0;
+
+ mutex_lock(&dp_dev->lock);
- if (dp_dev->dpms_mode != DRM_MODE_DPMS_ON)
+ if (dp_dev->dpms_mode == DRM_MODE_DPMS_OFF) {
cdn_dp_commit(dp_dev);
+ } else {
+ dev_warn(dp_dev->dev, "wrong dpms status,dp encoder has already been enabled\n");
+ ret = -1;
+ }
+ mutex_unlock(&dp_dev->lock);
+
+ return ret;
}
static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
struct device_node *np = dev->of_node;
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
- int ret;
dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(dp->grf)) {
return PTR_ERR(dp->spdif_clk);
}
+ dp->grf_clk = devm_clk_get(dev, "grf");
+ if (IS_ERR(dp->grf_clk)) {
+ dev_err(dev, "cannot get grf clk\n");
+ return PTR_ERR(dp->grf_clk);
+ }
+
dp->spdif_rst = devm_reset_control_get(dev, "spdif");
if (IS_ERR(dp->spdif_rst)) {
dev_err(dev, "no spdif reset control found\n");
return PTR_ERR(dp->spdif_rst);
}
+ dp->dptx_rst = devm_reset_control_get(dev, "dptx");
+ if (IS_ERR(dp->dptx_rst)) {
+ dev_err(dev, "no uphy reset control found\n");
+ return PTR_ERR(dp->dptx_rst);
+ }
+
+ dp->apb_rst = devm_reset_control_get(dev, "apb");
+ if (IS_ERR(dp->apb_rst)) {
+ dev_err(dev, "no apb reset control found\n");
+ return PTR_ERR(dp->apb_rst);
+ }
+
dp->dpms_mode = DRM_MODE_DPMS_OFF;
+ dp->fw_clk_enabled = false;
- ret = cdn_dp_clk_enable(dp);
- if (ret < 0)
- return ret;
+ pm_runtime_enable(dev);
- cdn_dp_clock_reset(dp);
+ reset_control_assert(dp->dptx_rst);
+ udelay(15);
+ reset_control_deassert(dp->dptx_rst);
+ reset_control_assert(dp->apb_rst);
+ udelay(15);
+ reset_control_deassert(dp->apb_rst);
+ mutex_init(&dp->lock);
+ wake_lock_init(&dp->wake_lock, WAKE_LOCK_SUSPEND, "cdn_dp_fb");
return 0;
}
static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
struct extcon_dev *edev)
{
- bool dfp, dptx;
- u8 lanes;
+ union extcon_property_value property;
+ u8 lanes = 0;
+ int dptx;
- dfp = extcon_get_state(edev, EXTCON_USB_HOST);
- dptx = extcon_get_state(edev, EXTCON_DISP_DP);
+ if (dp->suspend)
+ return 0;
- if (dfp && dptx)
- lanes = 2;
- else if (dptx)
- lanes = 4;
- else
- lanes = 0;
+ dptx = extcon_get_state(edev, EXTCON_DISP_DP);
+ if (dptx > 0) {
+ extcon_get_property(edev, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS, &property);
+ if (property.intval)
+ lanes = 2;
+ else
+ lanes = 4;
+ }
return lanes;
}
-static int cdn_dp_pd_event(struct notifier_block *nb,
- unsigned long event, void *priv)
+static int cdn_dp_get_dpcd(struct cdn_dp_device *dp, struct cdn_dp_port *port)
{
- struct cdn_dp_port *port;
+ u8 sink_count;
+ int i, ret;
+ int retry = 5;
- port = container_of(nb, struct cdn_dp_port, event_nb);
- schedule_delayed_work(&port->event_wq, 0);
- return 0;
+ /*
+ * Native read with retry for link status and receiver capability reads
+ * for cases where the sink may still not be ready.
+ *
+ * Sinks are *supposed* to come up within 1ms from an off state, but
+ * some DOCKs need about 5 seconds to power up, so read the dpcd every
+ * 100ms, if can not get a good dpcd in 10 seconds, give up.
+ */
+ for (i = 0; i < 100; i++) {
+ ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT,
+ &sink_count, 1);
+ if (!ret) {
+ dev_dbg(dp->dev, "get dpcd success!\n");
+
+ sink_count = DP_GET_SINK_COUNT(sink_count);
+ if (!sink_count) {
+ if (retry-- <= 0) {
+ dev_err(dp->dev, "sink cout is 0, no sink device!\n");
+ return -ENODEV;
+ }
+ mdelay(50);
+ continue;
+ }
+
+ ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
+ DP_RECEIVER_CAP_SIZE);
+ if (ret)
+ continue;
+
+ return ret;
+ } else if (!extcon_get_state(port->extcon, EXTCON_DISP_DP)) {
+ break;
+ }
+
+ msleep(100);
+ }
+
+ dev_err(dp->dev, "get dpcd failed!\n");
+
+ return -ETIMEDOUT;
}
-static void cdn_dp_pd_event_wq(struct work_struct *work)
+static void cdn_dp_enter_standy(struct cdn_dp_device *dp,
+ struct cdn_dp_port *port)
{
- struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
- event_wq.work);
- union extcon_property_value property;
- struct cdn_dp_device *dp = port->dp;
- u8 new_cap_lanes, i;
- int ret;
+ int i, ret;
- new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
- if (new_cap_lanes == port->cap_lanes) {
- dev_warn(dp->dev, "lanes count does not change: %d\n",
- new_cap_lanes);
+ ret = phy_power_off(port->phy);
+ if (ret) {
+ dev_err(dp->dev, "phy power off failed: %d", ret);
return;
}
- if (!new_cap_lanes) {
- ret = phy_power_off(port->phy);
- if (ret) {
- port->cap_lanes = 0;
- dev_err(dp->dev, "phy power off failed: %d", ret);
+ port->phy_status = false;
+ port->cap_lanes = 0;
+ for (i = 0; i < dp->ports; i++)
+ if (dp->port[i]->phy_status)
return;
- }
- port->phy_status = false;
- port->cap_lanes = new_cap_lanes;
- for (i = 0; i < dp->ports; i++) {
- if (dp->port[i]->phy_status)
- return;
- }
- dp->hpd_status = connector_status_disconnected;
+ memset(dp->dpcd, 0, DP_RECEIVER_CAP_SIZE);
+ if (dp->fw_loaded)
cdn_dp_set_firmware_active(dp, false);
- hpd_change(dp->dev, new_cap_lanes);
- return;
- }
+ cdn_dp_clk_disable(dp);
+ dp->hpd_status = connector_status_disconnected;
- /* if other phy is running, do not touch the hpd_status, and return */
- for (i = 0; i < dp->ports; i++) {
- if (dp->port[i]->phy_status) {
- dev_warn(dp->dev, "busy, phy[%d] is running",
- dp->port[i]->id);
- port->cap_lanes = 0;
- return;
- }
- }
+ hpd_change(dp->dev, 0);
+}
+
+static int cdn_dp_start_work(struct cdn_dp_device *dp,
+ struct cdn_dp_port *port,
+ u8 cap_lanes)
+{
+ union extcon_property_value property;
+ int ret;
if (!dp->fw_loaded) {
ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
- if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
- unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
-
- /*
- * If can not find the file, retry to load the firmware
- * in 1 second, if still failed after 1 minute, give up.
- */
- schedule_delayed_work(&port->event_wq, time);
- dev_warn(dp->dev, "retry to request firmware in %dS\n",
- dp->fw_wait);
- dp->fw_wait *= 2;
- port->cap_lanes = 0;
- return;
- } else if (ret) {
- port->cap_lanes = 0;
- dev_err(dp->dev, "failed to request firmware: %d\n",
- ret);
- return;
+ if (ret) {
+ if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
+ unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
+
+ /*
+ * Keep trying to load the firmware for up to 1 minute,
+ * if can not find the file.
+ */
+ schedule_delayed_work(&port->event_wq, time);
+ dp->fw_wait *= 2;
+ } else {
+ dev_err(dp->dev, "failed to request firmware: %d\n",
+ ret);
+ }
+
+ return ret;
}
}
+ ret = cdn_dp_clk_enable(dp);
+ if (ret < 0) {
+ dev_err(dp->dev, "failed to enable clock for dp: %d\n", ret);
+ return ret;
+ }
if (dp->fw_loaded)
cdn_dp_set_firmware_active(dp, true);
+ ret = cdn_dp_grf_write(dp, GRF_SOC_CON26,
+ (port->id << UPHY_SEL_BIT) | UPHY_SEL_MASK);
+ if (ret)
+ goto err_phy;
+
ret = phy_power_on(port->phy);
if (ret) {
- if (!dp->fw_loaded)
- release_firmware(dp->fw);
dev_err(dp->dev, "phy power on failed: %d\n", ret);
- port->cap_lanes = 0;
- return;
+ goto err_phy;
}
+
port->phy_status = true;
if (!dp->fw_loaded) {
ret = cdn_dp_firmware_init(dp);
- release_firmware(dp->fw);
if (ret) {
dev_err(dp->dev, "firmware init failed: %d", ret);
goto err_firmware;
}
}
- /* read hpd status failed, or the hpd does not exist. */
+ ret = cdn_dp_grf_write(dp, GRF_SOC_CON26,
+ DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
+ if (ret)
+ goto err_grf;
+
ret = cdn_dp_get_hpd_status(dp);
if (ret <= 0) {
- dev_err(dp->dev, "get hpd failed: %d", ret);
- goto err_firmware;
+ if (!ret)
+ dev_err(dp->dev, "hpd does not exist\n");
+ goto err_hpd;
}
ret = extcon_get_property(port->extcon, EXTCON_DISP_DP,
EXTCON_PROP_USB_TYPEC_POLARITY, &property);
if (ret) {
dev_err(dp->dev, "get property failed\n");
- goto err_firmware;
+ goto err_hpd;
}
- ret = cdn_dp_set_host_cap(dp, new_cap_lanes, property.intval);
+ ret = cdn_dp_set_host_cap(dp, cap_lanes, property.intval);
if (ret) {
dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
- goto err_firmware;
+ goto err_hpd;
}
- /*
- * Native read with retry for link status and receiver capability reads
- * for cases where the sink may still be asleep.
- *
- * Sinks are *supposed* to come up within 1ms from an off state, but
- * we're also supposed to retry 3 times per the spec.
- */
- for (i = 1; i < 100; i++) {
- ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
- DP_RECEIVER_CAP_SIZE);
- if (!ret) {
- dev_dbg(dp->dev, "get dpcd success!\n");
- port->cap_lanes = new_cap_lanes;
- dp->hpd_status = connector_status_connected;
- hpd_change(dp->dev, new_cap_lanes);
- return;
- } else if (!extcon_get_state(port->extcon, EXTCON_DISP_DP)) {
- break;
- }
- msleep(100);
- }
+ ret = cdn_dp_get_dpcd(dp, port);
+ if (ret)
+ goto err_hpd;
+
+ return 0;
- dev_err(dp->dev, "get dpcd failed: %d", ret);
+err_hpd:
+ cdn_dp_grf_write(dp, GRF_SOC_CON26,
+ DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
+
+err_grf:
+ cdn_dp_set_firmware_active(dp, false);
err_firmware:
- port->cap_lanes = 0;
- ret = phy_power_off(port->phy);
- if (ret)
+ if (phy_power_off(port->phy))
dev_err(dp->dev, "phy power off failed: %d", ret);
else
port->phy_status = false;
+
+err_phy:
if (dp->fw_loaded)
cdn_dp_set_firmware_active(dp, false);
+ cdn_dp_clk_disable(dp);
+ return ret;
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+ unsigned long event, void *priv)
+{
+ struct cdn_dp_port *port;
+
+ port = container_of(nb, struct cdn_dp_port, event_nb);
+ schedule_delayed_work(&port->event_wq, 0);
+ return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+ struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
+ event_wq.work);
+ struct cdn_dp_device *dp = port->dp;
+ u8 new_cap_lanes, sink_count, i;
+ int ret;
+
+ mutex_lock(&dp->lock);
+ wake_lock_timeout(&dp->wake_lock, msecs_to_jiffies(1000));
+
+ new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
+
+ if (new_cap_lanes == port->cap_lanes) {
+ if (!new_cap_lanes) {
+ dev_err(dp->dev, "dp lanes is 0, and same with last time\n");
+ goto out;
+ }
+
+ /*
+ * If HPD interrupt is triggered, and cable states is still
+ * attached, that means something on the Type-C Dock/Dongle
+ * changed, check the sink count by DPCD. If sink count became
+ * 0, this port phy can be powered off; if the sink count does
+ * not change, it means the sink device status has update,
+ * re-training to make it work again.
+ */
+ ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &sink_count, 1);
+ if (ret || sink_count) {
+ if (dp->dpms_mode == DRM_MODE_DPMS_ON) {
+ dev_warn(dp->dev,
+ "hpd interrupt is triggered when dp is already connected successfully\n");
+ ret = cdn_dp_training_start(dp);
+ if (!ret)
+ cdn_dp_get_training_status(dp);
+ }
+ goto out;
+ }
+ new_cap_lanes = 0;
+ }
+
+ if (dp->hpd_status == connector_status_connected && new_cap_lanes) {
+ dev_err(dp->dev, "error, dp connector has already been connected\n");
+ goto out;
+ }
+
+ if (!new_cap_lanes) {
+ dev_info(dp->dev, "dp lanes is 0, enter standby\n");
+ cdn_dp_enter_standy(dp, port);
+ goto out;
+ }
+
+ /* if other phy is running, do not do anything, just return */
+ for (i = 0; i < dp->ports; i++) {
+ if (dp->port[i]->phy_status) {
+ dev_warn(dp->dev, "busy, phy[%d] is running",
+ dp->port[i]->id);
+ goto out;
+ }
+ }
+
+ ret = cdn_dp_start_work(dp, port, new_cap_lanes);
+ if (ret) {
+ dev_err(dp->dev, "dp failed to connect ,error = %d\n", ret);
+ goto out;
+ }
+ port->cap_lanes = new_cap_lanes;
+ dp->hpd_status = connector_status_connected;
+ wake_unlock(&dp->wake_lock);
+ mutex_unlock(&dp->lock);
+ hpd_change(dp->dev, new_cap_lanes);
+
+ return;
+out:
+ wake_unlock(&dp->wake_lock);
+ mutex_unlock(&dp->lock);
}
static int cdn_dp_bind(struct cdn_dp_device *dp)
}
if (extcon_get_state(port->extcon, EXTCON_DISP_DP))
+ schedule_delayed_work(&port->event_wq,
+ msecs_to_jiffies(2000));
+ }
+
+ return 0;
+}
+
+int cdn_dp_suspend(void *dp_dev)
+{
+ struct cdn_dp_device *dp = dp_dev;
+ struct cdn_dp_port *port;
+ int i;
+
+ for (i = 0; i < dp->ports; i++) {
+ port = dp->port[i];
+ if (port->phy_status) {
+ cdn_dp_dpcd_write(dp, DP_SET_POWER, DP_SET_POWER_D3);
+ cdn_dp_enter_standy(dp, port);
+ }
+ }
+
+ /*
+ * if dp has been suspended, need to download firmware
+ * and set fw clk again.
+ */
+ dp->fw_clk_enabled = false;
+ dp->fw_loaded = false;
+ dp->suspend = true;
+ return 0;
+}
+
+int cdn_dp_resume(void *dp_dev)
+{
+ struct cdn_dp_device *dp = dp_dev;
+ struct cdn_dp_port *port;
+ int i;
+ if (dp->suspend) {
+ dp->suspend = false;
+ reset_control_assert(dp->dptx_rst);
+ udelay(15);
+ reset_control_deassert(dp->dptx_rst);
+ reset_control_assert(dp->apb_rst);
+ udelay(15);
+ reset_control_deassert(dp->apb_rst);
+
+ for (i = 0; i < dp->ports; i++) {
+ port = dp->port[i];
schedule_delayed_work(&port->event_wq, 0);
+ flush_delayed_work(&port->event_wq);
+ }
}
return 0;
return ret;
}
-static int cdn_dp_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver cdn_dp_driver = {
.probe = cdn_dp_probe,
- .remove = cdn_dp_remove,
.driver = {
.name = "cdn-dp-fb",
.owner = THIS_MODULE,