diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/ite-it6505.c')
-rw-r--r-- | drivers/gpu/drm/bridge/ite-it6505.c | 168 |
1 files changed, 100 insertions, 68 deletions
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 2767b70fa2cb..21a9b8422bda 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -412,6 +412,7 @@ struct it6505 { * Mutex protects extcon and interrupt functions from interfering * each other. */ + struct mutex irq_lock; struct mutex extcon_lock; struct mutex mode_lock; /* used to bridge_detect */ struct mutex aux_lock; /* used to aux data transfers */ @@ -421,6 +422,7 @@ struct it6505 { struct notifier_block event_nb; struct extcon_dev *extcon; struct work_struct extcon_wq; + int extcon_state; enum drm_connector_status connector_status; enum link_train_status link_state; struct work_struct link_works; @@ -439,7 +441,7 @@ struct it6505 { enum hdcp_state hdcp_status; struct delayed_work hdcp_work; struct work_struct hdcp_wait_ksv_list; - struct completion wait_edid_complete; + struct completion extcon_completion; u8 auto_train_retry; bool hdcp_desired; bool is_repeater; @@ -724,28 +726,6 @@ static void it6505_calc_video_info(struct it6505 *it6505) DRM_MODE_ARG(&it6505->video_info)); } -static int it6505_drm_dp_link_probe(struct drm_dp_aux *aux, - struct it6505_drm_dp_link *link) -{ - u8 values[3]; - int err; - - memset(link, 0, sizeof(*link)); - - err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); - if (err < 0) - return err; - - link->revision = values[0]; - link->rate = drm_dp_bw_code_to_link_rate(values[1]); - link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; - - if (values[2] & DP_ENHANCED_FRAME_CAP) - link->capabilities = DP_ENHANCED_FRAME_CAP; - - return 0; -} - static int it6505_drm_dp_link_set_power(struct drm_dp_aux *aux, struct it6505_drm_dp_link *link, u8 mode) @@ -1455,11 +1435,19 @@ static void it6505_parse_link_capabilities(struct it6505 *it6505) int bcaps; if (it6505->dpcd[0] == 0) { - it6505_aux_on(it6505); - it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd, - ARRAY_SIZE(it6505->dpcd)); + dev_err(dev, "DPCD is not initialized"); + return; } + memset(link, 0, sizeof(*link)); + + link->revision = it6505->dpcd[0]; + link->rate = drm_dp_bw_code_to_link_rate(it6505->dpcd[1]); + link->num_lanes = it6505->dpcd[2] & DP_MAX_LANE_COUNT_MASK; + + if (it6505->dpcd[2] & DP_ENHANCED_FRAME_CAP) + link->capabilities = DP_ENHANCED_FRAME_CAP; + DRM_DEV_DEBUG_DRIVER(dev, "DPCD Rev.: %d.%d", link->revision >> 4, link->revision & 0x0F); @@ -2322,19 +2310,32 @@ static int it6505_process_hpd_irq(struct it6505 *it6505) static void it6505_irq_hpd(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; + int dp_sink_count; it6505->hpd_state = it6505_get_sink_hpd_status(it6505); DRM_DEV_DEBUG_DRIVER(dev, "hpd change interrupt, change to %s", it6505->hpd_state ? "high" : "low"); - if (it6505->bridge.dev) - drm_helper_hpd_irq_event(it6505->bridge.dev); - DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d", - it6505->sink_count); - if (it6505->hpd_state) { - wait_for_completion_timeout(&it6505->wait_edid_complete, - msecs_to_jiffies(6000)); + wait_for_completion_timeout(&it6505->extcon_completion, + msecs_to_jiffies(1000)); + it6505_aux_on(it6505); + if (it6505->dpcd[0] == 0) { + it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd, + ARRAY_SIZE(it6505->dpcd)); + it6505_variable_config(it6505); + it6505_parse_link_capabilities(it6505); + } + it6505->auto_train_retry = AUTO_TRAIN_RETRY; + + it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link, + DP_SET_POWER_D0); + dp_sink_count = it6505_dpcd_read(it6505, DP_SINK_COUNT); + it6505->sink_count = DP_GET_SINK_COUNT(dp_sink_count); + + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d", + it6505->sink_count); + it6505_lane_termination_on(it6505); it6505_lane_power_on(it6505); @@ -2362,6 +2363,9 @@ static void it6505_irq_hpd(struct it6505 *it6505) it6505_lane_off(it6505); it6505_link_reset_step_train(it6505); } + + if (it6505->bridge.dev) + drm_helper_hpd_irq_event(it6505->bridge.dev); } static void it6505_irq_hpd_irq(struct it6505 *it6505) @@ -2490,8 +2494,7 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) }; int int_status[3], i; - msleep(100); - mutex_lock(&it6505->extcon_lock); + mutex_lock(&it6505->irq_lock); if (it6505->enable_drv_hold || !it6505->powered) goto unlock; @@ -2521,7 +2524,7 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) } unlock: - mutex_unlock(&it6505->extcon_lock); + mutex_unlock(&it6505->irq_lock); return IRQ_HANDLED; } @@ -2624,26 +2627,14 @@ static enum drm_connector_status it6505_detect(struct it6505 *it6505) goto unlock; if (it6505->enable_drv_hold) { - status = it6505_get_sink_hpd_status(it6505) ? - connector_status_connected : - connector_status_disconnected; + status = it6505->hpd_state ? connector_status_connected : + connector_status_disconnected; goto unlock; } - if (it6505_get_sink_hpd_status(it6505)) { - it6505_aux_on(it6505); - it6505_drm_dp_link_probe(&it6505->aux, &it6505->link); + if (it6505->hpd_state) { it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link, DP_SET_POWER_D0); - it6505->auto_train_retry = AUTO_TRAIN_RETRY; - - if (it6505->dpcd[0] == 0) { - it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd, - ARRAY_SIZE(it6505->dpcd)); - it6505_variable_config(it6505); - it6505_parse_link_capabilities(it6505); - } - dp_sink_count = it6505_dpcd_read(it6505, DP_SINK_COUNT); it6505->sink_count = DP_GET_SINK_COUNT(dp_sink_count); DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d branch:%d", @@ -2685,31 +2676,44 @@ static void it6505_extcon_work(struct work_struct *work) { struct it6505 *it6505 = container_of(work, struct it6505, extcon_wq); struct device *dev = &it6505->client->dev; - int state = extcon_get_state(it6505->extcon, EXTCON_DISP_DP); - unsigned int pwroffretry = 0; + int state, ret; if (it6505->enable_drv_hold) return; mutex_lock(&it6505->extcon_lock); + state = extcon_get_state(it6505->extcon, EXTCON_DISP_DP); DRM_DEV_DEBUG_DRIVER(dev, "EXTCON_DISP_DP = 0x%02x", state); - if (state > 0) { + + if (state == it6505->extcon_state || unlikely(state < 0)) + goto unlock; + it6505->extcon_state = state; + if (state) { DRM_DEV_DEBUG_DRIVER(dev, "start to power on"); msleep(100); - it6505_poweron(it6505); + ret = pm_runtime_get_sync(dev); + + /* + * On system resume, extcon_work can be triggered before + * pm_runtime_force_resume re-enables runtime power management. + * Handling the error here to make sure the bridge is powered on. + */ + if (ret < 0) + it6505_poweron(it6505); + + complete_all(&it6505->extcon_completion); } else { DRM_DEV_DEBUG_DRIVER(dev, "start to power off"); - while (it6505_poweroff(it6505) && pwroffretry++ < 5) { - DRM_DEV_DEBUG_DRIVER(dev, "power off fail %d times", - pwroffretry); - } + pm_runtime_put_sync(dev); + reinit_completion(&it6505->extcon_completion); drm_helper_hpd_irq_event(it6505->bridge.dev); memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); DRM_DEV_DEBUG_DRIVER(dev, "power off it6505 success!"); } +unlock: mutex_unlock(&it6505->extcon_lock); } @@ -2860,10 +2864,7 @@ static int it6505_bridge_attach(struct drm_bridge *bridge, } /* Register aux channel */ - it6505->aux.name = "DP-AUX"; - it6505->aux.dev = dev; it6505->aux.drm_dev = bridge->dev; - it6505->aux.transfer = it6505_aux_transfer; ret = drm_dp_aux_register(&it6505->aux); @@ -2980,6 +2981,28 @@ static void it6505_bridge_atomic_disable(struct drm_bridge *bridge, } } +static void it6505_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "start"); + + pm_runtime_get_sync(dev); +} + +static void it6505_bridge_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "start"); + + pm_runtime_put_sync(dev); +} + static enum drm_connector_status it6505_bridge_detect(struct drm_bridge *bridge) { @@ -3014,6 +3037,8 @@ static const struct drm_bridge_funcs it6505_bridge_funcs = { .mode_valid = it6505_bridge_mode_valid, .atomic_enable = it6505_bridge_atomic_enable, .atomic_disable = it6505_bridge_atomic_disable, + .atomic_pre_enable = it6505_bridge_atomic_pre_enable, + .atomic_post_disable = it6505_bridge_atomic_post_disable, .detect = it6505_bridge_detect, .get_edid = it6505_bridge_get_edid, }; @@ -3032,8 +3057,10 @@ static __maybe_unused int it6505_bridge_suspend(struct device *dev) return it6505_poweroff(it6505); } -static SIMPLE_DEV_PM_OPS(it6505_bridge_pm_ops, it6505_bridge_suspend, - it6505_bridge_resume); +static const struct dev_pm_ops it6505_bridge_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(it6505_bridge_suspend, it6505_bridge_resume, NULL) +}; static int it6505_init_pdata(struct it6505 *it6505) { @@ -3250,6 +3277,7 @@ static int it6505_i2c_probe(struct i2c_client *client, if (!it6505) return -ENOMEM; + mutex_init(&it6505->irq_lock); mutex_init(&it6505->extcon_lock); mutex_init(&it6505->mode_lock); mutex_init(&it6505->aux_lock); @@ -3305,7 +3333,7 @@ static int it6505_i2c_probe(struct i2c_client *client, INIT_WORK(&it6505->link_works, it6505_link_training_work); INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list); INIT_DELAYED_WORK(&it6505->hdcp_work, it6505_hdcp_work); - init_completion(&it6505->wait_edid_complete); + init_completion(&it6505->extcon_completion); memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); it6505->powered = false; it6505->enable_drv_hold = DEFAULT_DRV_HOLD; @@ -3315,6 +3343,12 @@ static int it6505_i2c_probe(struct i2c_client *client, DRM_DEV_DEBUG_DRIVER(dev, "it6505 device name: %s", dev_name(dev)); debugfs_init(it6505); + pm_runtime_enable(dev); + + it6505->aux.name = "DP-AUX"; + it6505->aux.dev = dev; + it6505->aux.transfer = it6505_aux_transfer; + drm_dp_aux_init(&it6505->aux); it6505->bridge.funcs = &it6505_bridge_funcs; it6505->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; @@ -3325,7 +3359,7 @@ static int it6505_i2c_probe(struct i2c_client *client, return 0; } -static int it6505_i2c_remove(struct i2c_client *client) +static void it6505_i2c_remove(struct i2c_client *client) { struct it6505 *it6505 = i2c_get_clientdata(client); @@ -3333,8 +3367,6 @@ static int it6505_i2c_remove(struct i2c_client *client) drm_dp_aux_unregister(&it6505->aux); it6505_debugfs_remove(it6505); it6505_poweroff(it6505); - - return 0; } static const struct i2c_device_id it6505_id[] = { |