summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com>2024-03-03 18:43:54 -0800
committerSecurityBot <android-nexus-securitybot@system.gserviceaccount.com>2024-03-03 18:43:54 -0800
commit1f419d945bf1aa87d637f694f65f9841206133e0 (patch)
tree28d40fc961f65af45936fb04b1b83d211f1aff7b
parent4b4cf587648bfd0d0b10b921248ede0c9e61fcc5 (diff)
parentfb7749b17076a6758b5eb7ac75ece297484df810 (diff)
downloaddisplay-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.c122
-rw-r--r--samsung/exynos_drm_dp.h3
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;