FROMLIST: drm/bridge: analogix: protect power when get_modes or detect
authorMark Yao <mark.yao@rock-chips.com>
Wed, 12 Oct 2016 10:03:08 +0000 (18:03 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Thu, 13 Oct 2016 02:13:26 +0000 (10:13 +0800)
The drm callback ->detect and ->get_modes seems is not power safe,
they may be called when device is power off, do register access on
detect or get_modes will cause system die.

Here is the path call ->detect before analogix_dp power on
[<ffffff800843babc>] analogix_dp_detect+0x44/0xdc
[<ffffff80083fd840>] drm_helper_probe_single_connector_modes_merge_bits+0xe8/0x41c
[<ffffff80083fdb84>] drm_helper_probe_single_connector_modes+0x10/0x18
[<ffffff8008418d24>] drm_mode_getconnector+0xf4/0x304
[<ffffff800840cff0>] drm_ioctl+0x23c/0x390
[<ffffff80081a8adc>] do_vfs_ioctl+0x4b8/0x58c
[<ffffff80081a8c10>] SyS_ioctl+0x60/0x88

Change-Id: Ica3fda1f22f903ee9ba2f0caed40cdae9bdfa32b
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
(am from https://patchwork.kernel.org/patch/9374135)

drivers/gpu/drm/bridge/analogix/analogix_dp_core.c

index 01afc8a0da63b7c205a1a6220a456899417fd550..5216d0496b0d0c04ba84f2c85dd19a7fcaedad59 100644 (file)
@@ -929,6 +929,8 @@ int analogix_dp_get_modes(struct drm_connector *connector)
        struct edid *edid = (struct edid *)dp->edid;
        int num_modes = 0;
 
        struct edid *edid = (struct edid *)dp->edid;
        int num_modes = 0;
 
+       pm_runtime_get_sync(dp->dev);
+
        if (analogix_dp_handle_edid(dp) == 0) {
                drm_mode_connector_update_edid_property(&dp->connector, edid);
                num_modes += drm_add_edid_modes(&dp->connector, edid);
        if (analogix_dp_handle_edid(dp) == 0) {
                drm_mode_connector_update_edid_property(&dp->connector, edid);
                num_modes += drm_add_edid_modes(&dp->connector, edid);
@@ -940,6 +942,8 @@ int analogix_dp_get_modes(struct drm_connector *connector)
        if (dp->plat_data->get_modes)
                num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
 
        if (dp->plat_data->get_modes)
                num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
 
+       pm_runtime_put(dp->dev);
+
        return num_modes;
 }
 
        return num_modes;
 }
 
@@ -960,11 +964,16 @@ enum drm_connector_status
 analogix_dp_detect(struct drm_connector *connector, bool force)
 {
        struct analogix_dp_device *dp = to_dp(connector);
 analogix_dp_detect(struct drm_connector *connector, bool force)
 {
        struct analogix_dp_device *dp = to_dp(connector);
+       enum drm_connector_status status = connector_status_connected;
+
+       pm_runtime_get_sync(dp->dev);
 
        if (analogix_dp_detect_hpd(dp))
 
        if (analogix_dp_detect_hpd(dp))
-               return connector_status_disconnected;
+               status = connector_status_disconnected;
+
+       pm_runtime_put(dp->dev);
 
 
-       return connector_status_connected;
+       return status;
 }
 
 static void analogix_dp_connector_destroy(struct drm_connector *connector)
 }
 
 static void analogix_dp_connector_destroy(struct drm_connector *connector)