diff options
author | PixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com> | 2024-03-03 18:43:54 -0800 |
---|---|---|
committer | SecurityBot <android-nexus-securitybot@system.gserviceaccount.com> | 2024-03-03 18:43:54 -0800 |
commit | 1f419d945bf1aa87d637f694f65f9841206133e0 (patch) | |
tree | 28d40fc961f65af45936fb04b1b83d211f1aff7b | |
parent | 4b4cf587648bfd0d0b10b921248ede0c9e61fcc5 (diff) | |
parent | fb7749b17076a6758b5eb7ac75ece297484df810 (diff) | |
download | display-1f419d945bf1aa87d637f694f65f9841206133e0.tar.gz |
Merge android14-gs-pixel-5.15-24Q2 into android14-gs-pixel-5.15
SBMerger: 610748217
Change-Id: I14cb3e222e9db20c3fc86d4c8da3942be148c2e6
Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
-rw-r--r-- | samsung/exynos_drm_dp.c | 122 | ||||
-rw-r--r-- | samsung/exynos_drm_dp.h | 3 |
2 files changed, 90 insertions, 35 deletions
diff --git a/samsung/exynos_drm_dp.c b/samsung/exynos_drm_dp.c index e11250a..47d2a56 100644 --- a/samsung/exynos_drm_dp.c +++ b/samsung/exynos_drm_dp.c @@ -963,6 +963,25 @@ static int dp_link_up(struct dp_device *dp) dp->sink.revision, dp->sink.link_rate / 100, dp->sink.num_lanes, dp->sink.enhanced_frame, dp->sink.ssc, dp->sink.fec, dp->sink.dsc); + /* Sanity-check the sink's max link rate */ + if (dp->sink.link_rate != drm_dp_bw_code_to_link_rate(DP_LINK_BW_1_62) && + dp->sink.link_rate != drm_dp_bw_code_to_link_rate(DP_LINK_BW_2_7) && + dp->sink.link_rate != drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4) && + dp->sink.link_rate != drm_dp_bw_code_to_link_rate(DP_LINK_BW_8_1)) { + dp_err(dp, "DP Sink: invalid max link rate\n"); + mutex_unlock(&dp->training_lock); + dp->stats.dpcd_read_failures++; + return -EINVAL; + } + + /* Sanity-check the sink's max lane count */ + if (dp->sink.num_lanes != 1 && dp->sink.num_lanes != 2 && dp->sink.num_lanes != 4) { + dp_err(dp, "DP Sink: invalid max lane count\n"); + mutex_unlock(&dp->training_lock); + dp->stats.dpcd_read_failures++; + return -EINVAL; + } + /* Power DP Sink device Up */ dp_sink_power_up(dp, true); @@ -989,24 +1008,33 @@ static int dp_link_up(struct dp_device *dp) dp_info(dp, "DP Branch Device: DFP count = %d, sink count = %d\n", dp->dfp_count, dp->sink_count); + + /* Sanity-check the sink count */ + if (dp->sink_count > dp->dfp_count) { + dp_err(dp, "DP Branch Device: invalid sink count\n"); + mutex_unlock(&dp->training_lock); + dp->stats.sink_count_invalid_failures++; + return -EINVAL; + } } else { dp->dfp_count = 0; dp_info(dp, "DP Sink: sink count = %d\n", dp->sink_count); - } - if (dp->sink_count == 0) { - if (dp->dfp_count > 0) { - dp_info(dp, "DP Link: training defer: DP Branch Device, sink count = 0\n"); - mutex_unlock(&dp->training_lock); - return 0; - } else { - dp_err(dp, "DP Sink: invalid sink count = 0\n"); + /* Sanity-check the sink count */ + if (dp->sink_count != 1) { + dp_err(dp, "DP Sink: invalid sink count\n"); mutex_unlock(&dp->training_lock); dp->stats.sink_count_invalid_failures++; return -EINVAL; } } + if (dp->sink_count == 0) { + dp_info(dp, "DP Link: training defer: DP Branch Device, sink count = 0\n"); + mutex_unlock(&dp->training_lock); + return 0; + } + /* Pick link parameters */ dp->link.link_rate = dp_get_max_link_rate(dp); dp->link.num_lanes = dp_get_max_num_lanes(dp); @@ -1743,7 +1771,7 @@ static int dp_downstream_port_event_handler(struct dp_device *dp, int new_sink_c } /* Works */ -static void dp_work_hpd(struct work_struct *work) +static void dp_work_hpd(enum hotplug_state state) { struct dp_device *dp = get_dp_drvdata(); struct drm_connector *connector = &dp->connector; @@ -1754,7 +1782,7 @@ static void dp_work_hpd(struct work_struct *work) mutex_lock(&dp->hpd_lock); - if (dp_get_hpd_state(dp) == EXYNOS_HPD_PLUG) { + if (state == EXYNOS_HPD_PLUG) { if (mutex_trylock(&private->dp_tui_lock) == 0) { /* TUI is active, bail out */ dp_info(dp, "unable to handle HPD_PLUG, TUI is active\n"); @@ -1762,12 +1790,13 @@ static void dp_work_hpd(struct work_struct *work) return; } + /* block suspend and increment power usage count */ + pm_stay_awake(dp->dev); pm_runtime_get_sync(dp->dev); dp_debug(dp, "pm_rtm_get_sync usage_cnt(%d)\n", atomic_read(&dp->dev->power.usage_count)); - dp_enable_dposc(dp); - pm_stay_awake(dp->dev); + dp_enable_dposc(dp); dp->dp_hotplug_error_code = 0; /* PHY power on */ @@ -1793,10 +1822,9 @@ static void dp_work_hpd(struct work_struct *work) dp_update_link_status(dp, LINK_TRAINING_SUCCESS); dp_on_by_hpd_plug(dp); } - } else if (dp_get_hpd_state(dp) == EXYNOS_HPD_UNPLUG) { - if ((!pm_runtime_get_if_in_use(dp->dev)) || - (dp->state == DP_STATE_INIT)) { - dp_info(dp, "%s: DP is not ON\n", __func__); + } else if (state == EXYNOS_HPD_UNPLUG) { + if (!pm_runtime_get_if_in_use(dp->dev)) { + dp_info(dp, "%s: DP is already powered off\n", __func__); mutex_unlock(&dp->hpd_lock); return; } @@ -1810,16 +1838,16 @@ static void dp_work_hpd(struct work_struct *work) dp_hw_deinit(&dp->hw_config); dp_disable_dposc(dp); + dp->state = DP_STATE_INIT; + dp_info(dp, "%s: DP State changed to INIT\n", __func__); + + /* decrement power usage count and unblock suspend */ pm_runtime_put(dp->dev); - /* put runtime power obtained during HPD_PLUG */ - pm_runtime_put_sync(dp->dev); + pm_runtime_put_sync(dp->dev); /* obtained during HPD_PLUG */ dp_debug(dp, "pm_rtm_put_sync usage_cnt(%d)\n", atomic_read(&dp->dev->power.usage_count)); pm_relax(dp->dev); - dp->state = DP_STATE_INIT; - dp_info(dp, "%s: DP State changed to INIT\n", __func__); - mutex_unlock(&private->dp_tui_lock); } @@ -1834,12 +1862,7 @@ HPD_FAIL: hdcp_dplink_connect_state(DP_DISCONNECT); dp_hw_deinit(&dp->hw_config); dp_disable_dposc(dp); - pm_relax(dp->dev); - dp_init_info(dp); - pm_runtime_put_sync(dp->dev); - dp_debug(dp, "pm_rtm_put_sync usage_cnt(%d)\n", - atomic_read(&dp->dev->power.usage_count)); /* in error case, add delay to avoid very short interval reconnection */ msleep(300); @@ -1853,10 +1876,26 @@ HPD_FAIL: dp->dp_hotplug_error_code); drm_kms_helper_hotplug_event(dp->connector.dev); + /* decrement power usage count and unblock suspend */ + pm_runtime_put_sync(dp->dev); + dp_debug(dp, "pm_rtm_put_sync usage_cnt(%d)\n", + atomic_read(&dp->dev->power.usage_count)); + pm_relax(dp->dev); + mutex_unlock(&private->dp_tui_lock); mutex_unlock(&dp->hpd_lock); } +static void dp_work_hpd_plug(struct work_struct *work) +{ + dp_work_hpd(EXYNOS_HPD_PLUG); +} + +static void dp_work_hpd_unplug(struct work_struct *work) +{ + dp_work_hpd(EXYNOS_HPD_UNPLUG); +} + static u8 sysfs_triggered_irq = 0; static void dp_work_hpd_irq(struct work_struct *work) @@ -1866,13 +1905,14 @@ static void dp_work_hpd_irq(struct work_struct *work) u8 irq = 0, irq2 = 0, irq3 = 0; u8 link_status[DP_LINK_STATUS_SIZE]; + mutex_lock(&dp->hpd_lock); + if (!pm_runtime_get_if_in_use(dp->dev)) { dp_debug(dp, "[HPD IRQ] IRQ work skipped as power is off\n"); + mutex_unlock(&dp->hpd_lock); return; } - mutex_lock(&dp->hpd_lock); - if (sysfs_triggered_irq != 0) { irq = sysfs_triggered_irq; sysfs_triggered_irq = 0; @@ -1928,6 +1968,12 @@ static void dp_work_hpd_irq(struct work_struct *work) } if (dp->dfp_count > 0) { + /* Sanity-check the sink count */ + if (sink_count > dp->dfp_count) { + dp_err(dp, "invalid sink count, adjusting to 0\n"); + sink_count = 0; + } + if ((link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED) || (dp->sink_count != sink_count)) { dp_info(dp, "[HPD IRQ] DP downstream port status change\n"); @@ -1961,8 +2007,8 @@ process_irq: dp_info(dp, "[HPD IRQ] unknown IRQ (0x%X)\n", irq); release_irq_resource: - mutex_unlock(&dp->hpd_lock); pm_runtime_put(dp->dev); + mutex_unlock(&dp->hpd_lock); } /* Type-C Handshaking Functions */ @@ -1973,9 +2019,14 @@ static void dp_hpd_changed(struct dp_device *dp, enum hotplug_state state) return; } - if ((state == EXYNOS_HPD_PLUG) || (state == EXYNOS_HPD_UNPLUG)) { + if (state == EXYNOS_HPD_PLUG) { + dp_set_hpd_state(dp, state); + if (!queue_work(dp->dp_wq, &dp->hpd_plug_work)) + dp_warn(dp, "DP HPD PLUG work was already queued"); + } else if (state == EXYNOS_HPD_UNPLUG) { dp_set_hpd_state(dp, state); - queue_work(dp->dp_wq, &dp->hpd_work); + if (!queue_work(dp->dp_wq, &dp->hpd_unplug_work)) + dp_warn(dp, "DP HPD UNPLUG work was already queued"); } else dp_err(dp, "DP HPD changed to abnormal state(%d)\n", state); } @@ -2049,7 +2100,8 @@ static int usb_typec_dp_notification_locked(struct dp_device *dp, enum hotplug_s } else if (hpd == EXYNOS_HPD_IRQ) { if (dp_get_hpd_state(dp) == EXYNOS_HPD_PLUG) { dp_info(dp, "%s: Service IRQ from sink\n", __func__); - queue_work(dp->dp_wq, &dp->hpd_irq_work); + if (!queue_work(dp->dp_wq, &dp->hpd_irq_work)) + dp_warn(dp, "DP HPD IRQ work was already queued"); } } else { dp_info(dp, "%s: USB Type-C is HPD UNPLUG status, or not in display ALT mode\n", @@ -2968,7 +3020,8 @@ static int dp_probe(struct platform_device *pdev) goto err; } - INIT_WORK(&dp->hpd_work, dp_work_hpd); + INIT_WORK(&dp->hpd_plug_work, dp_work_hpd_plug); + INIT_WORK(&dp->hpd_unplug_work, dp_work_hpd_unplug); INIT_WORK(&dp->hpd_irq_work, dp_work_hpd_irq); pm_runtime_enable(dev); @@ -3024,5 +3077,6 @@ struct platform_driver dp_driver } }; MODULE_AUTHOR("YongWook Shin <yongwook.shin@samsung.com>"); -MODULE_DESCRIPTION("Samusung DisplayPort driver"); +MODULE_AUTHOR("Petri Gynther <pgynther@google.com>"); +MODULE_DESCRIPTION("Samsung SoC DisplayPort driver"); MODULE_LICENSE("GPL"); diff --git a/samsung/exynos_drm_dp.h b/samsung/exynos_drm_dp.h index 102bd32..df4dbe0 100644 --- a/samsung/exynos_drm_dp.h +++ b/samsung/exynos_drm_dp.h @@ -168,7 +168,8 @@ struct dp_device { struct dp_resources res; struct workqueue_struct *dp_wq; - struct work_struct hpd_work; + struct work_struct hpd_plug_work; + struct work_struct hpd_unplug_work; struct work_struct hpd_irq_work; struct mutex cmd_lock; |