From: Greg Kroah-Hartman Date: Wed, 18 Mar 2026 12:25:15 +0000 (+0100) Subject: 6.1-stable patches X-Git-Tag: v6.18.19~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4457571737a2b5f2370a06ba67d4aa658cb56bed;p=thirdparty%2Fkernel%2Fstable-queue.git 6.1-stable patches added patches: drm-bridge-ti-sn65dsi86-add-support-for-displayport-mode-with-hpd.patch --- diff --git a/queue-6.1/drm-bridge-ti-sn65dsi86-add-support-for-displayport-mode-with-hpd.patch b/queue-6.1/drm-bridge-ti-sn65dsi86-add-support-for-displayport-mode-with-hpd.patch new file mode 100644 index 0000000000..881c17136f --- /dev/null +++ b/queue-6.1/drm-bridge-ti-sn65dsi86-add-support-for-displayport-mode-with-hpd.patch @@ -0,0 +1,224 @@ +From 9133bc3f0564890218cbba6cc7e81ebc0841a6f1 Mon Sep 17 00:00:00 2001 +From: John Ripple +Date: Mon, 15 Sep 2025 11:45:43 -0600 +Subject: drm/bridge: ti-sn65dsi86: Add support for DisplayPort mode with HPD + +From: John Ripple + +commit 9133bc3f0564890218cbba6cc7e81ebc0841a6f1 upstream. + +Add support for DisplayPort to the bridge, which entails the following: +- Get and use an interrupt for HPD; +- Properly clear all status bits in the interrupt handler; + +Signed-off-by: John Ripple +Reviewed-by: Douglas Anderson +Signed-off-by: Douglas Anderson +Link: https://lore.kernel.org/r/20250915174543.2564994-1-john.ripple@keysight.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/bridge/ti-sn65dsi86.c | 112 ++++++++++++++++++++++++++++++++++ + 1 file changed, 112 insertions(+) + +--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c ++++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c +@@ -106,10 +106,21 @@ + #define SN_PWM_EN_INV_REG 0xA5 + #define SN_PWM_INV_MASK BIT(0) + #define SN_PWM_EN_MASK BIT(1) ++ ++#define SN_IRQ_EN_REG 0xE0 ++#define IRQ_EN BIT(0) ++ ++#define SN_IRQ_EVENTS_EN_REG 0xE6 ++#define HPD_INSERTION_EN BIT(1) ++#define HPD_REMOVAL_EN BIT(2) ++ + #define SN_AUX_CMD_STATUS_REG 0xF4 + #define AUX_IRQ_STATUS_AUX_RPLY_TOUT BIT(3) + #define AUX_IRQ_STATUS_AUX_SHORT BIT(5) + #define AUX_IRQ_STATUS_NAT_I2C_FAIL BIT(6) ++#define SN_IRQ_STATUS_REG 0xF5 ++#define HPD_REMOVAL_STATUS BIT(2) ++#define HPD_INSERTION_STATUS BIT(1) + + #define MIN_DSI_CLK_FREQ_MHZ 40 + +@@ -152,7 +163,9 @@ + * @ln_assign: Value to program to the LN_ASSIGN register. + * @ln_polrs: Value for the 4-bit LN_POLRS field of SN_ENH_FRAME_REG. + * @comms_enabled: If true then communication over the aux channel is enabled. ++ * @hpd_enabled: If true then HPD events are enabled. + * @comms_mutex: Protects modification of comms_enabled. ++ * @hpd_mutex: Protects modification of hpd_enabled. + * + * @gchip: If we expose our GPIOs, this is used. + * @gchip_output: A cache of whether we've set GPIOs to output. This +@@ -190,7 +203,9 @@ struct ti_sn65dsi86 { + u8 ln_assign; + u8 ln_polrs; + bool comms_enabled; ++ bool hpd_enabled; + struct mutex comms_mutex; ++ struct mutex hpd_mutex; + + #if defined(CONFIG_OF_GPIO) + struct gpio_chip gchip; +@@ -221,6 +236,23 @@ static const struct regmap_config ti_sn6 + .max_register = 0xFF, + }; + ++static int ti_sn65dsi86_read_u8(struct ti_sn65dsi86 *pdata, unsigned int reg, ++ u8 *val) ++{ ++ int ret; ++ unsigned int reg_val; ++ ++ ret = regmap_read(pdata->regmap, reg, ®_val); ++ if (ret) { ++ dev_err(pdata->dev, "fail to read raw reg %#x: %d\n", ++ reg, ret); ++ return ret; ++ } ++ *val = (u8)reg_val; ++ ++ return 0; ++} ++ + static int __maybe_unused ti_sn65dsi86_read_u16(struct ti_sn65dsi86 *pdata, + unsigned int reg, u16 *val) + { +@@ -362,6 +394,7 @@ static void ti_sn65dsi86_disable_comms(s + static int __maybe_unused ti_sn65dsi86_resume(struct device *dev) + { + struct ti_sn65dsi86 *pdata = dev_get_drvdata(dev); ++ const struct i2c_client *client = to_i2c_client(pdata->dev); + int ret; + + ret = regulator_bulk_enable(SN_REGULATOR_SUPPLY_NUM, pdata->supplies); +@@ -396,6 +429,13 @@ static int __maybe_unused ti_sn65dsi86_r + if (pdata->refclk) + ti_sn65dsi86_enable_comms(pdata); + ++ if (client->irq) { ++ ret = regmap_update_bits(pdata->regmap, SN_IRQ_EN_REG, IRQ_EN, ++ IRQ_EN); ++ if (ret) ++ dev_err(pdata->dev, "Failed to enable IRQ events: %d\n", ret); ++ } ++ + return ret; + } + +@@ -1204,6 +1244,8 @@ static void ti_sn65dsi86_debugfs_init(st + static void ti_sn_bridge_hpd_enable(struct drm_bridge *bridge) + { + struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); ++ const struct i2c_client *client = to_i2c_client(pdata->dev); ++ int ret; + + /* + * Device needs to be powered on before reading the HPD state +@@ -1212,11 +1254,35 @@ static void ti_sn_bridge_hpd_enable(stru + */ + + pm_runtime_get_sync(pdata->dev); ++ ++ mutex_lock(&pdata->hpd_mutex); ++ pdata->hpd_enabled = true; ++ mutex_unlock(&pdata->hpd_mutex); ++ ++ if (client->irq) { ++ ret = regmap_set_bits(pdata->regmap, SN_IRQ_EVENTS_EN_REG, ++ HPD_REMOVAL_EN | HPD_INSERTION_EN); ++ if (ret) ++ dev_err(pdata->dev, "Failed to enable HPD events: %d\n", ret); ++ } + } + + static void ti_sn_bridge_hpd_disable(struct drm_bridge *bridge) + { + struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); ++ const struct i2c_client *client = to_i2c_client(pdata->dev); ++ int ret; ++ ++ if (client->irq) { ++ ret = regmap_clear_bits(pdata->regmap, SN_IRQ_EVENTS_EN_REG, ++ HPD_REMOVAL_EN | HPD_INSERTION_EN); ++ if (ret) ++ dev_err(pdata->dev, "Failed to disable HPD events: %d\n", ret); ++ } ++ ++ mutex_lock(&pdata->hpd_mutex); ++ pdata->hpd_enabled = false; ++ mutex_unlock(&pdata->hpd_mutex); + + pm_runtime_put_autosuspend(pdata->dev); + } +@@ -1302,6 +1368,41 @@ static int ti_sn_bridge_parse_dsi_host(s + return 0; + } + ++static irqreturn_t ti_sn_bridge_interrupt(int irq, void *private) ++{ ++ struct ti_sn65dsi86 *pdata = private; ++ struct drm_device *dev = pdata->bridge.dev; ++ u8 status; ++ int ret; ++ bool hpd_event; ++ ++ ret = ti_sn65dsi86_read_u8(pdata, SN_IRQ_STATUS_REG, &status); ++ if (ret) { ++ dev_err(pdata->dev, "Failed to read IRQ status: %d\n", ret); ++ return IRQ_NONE; ++ } ++ ++ hpd_event = status & (HPD_REMOVAL_STATUS | HPD_INSERTION_STATUS); ++ ++ dev_dbg(pdata->dev, "(SN_IRQ_STATUS_REG = %#x)\n", status); ++ if (!status) ++ return IRQ_NONE; ++ ++ ret = regmap_write(pdata->regmap, SN_IRQ_STATUS_REG, status); ++ if (ret) { ++ dev_err(pdata->dev, "Failed to clear IRQ status: %d\n", ret); ++ return IRQ_NONE; ++ } ++ ++ /* Only send the HPD event if we are bound with a device. */ ++ mutex_lock(&pdata->hpd_mutex); ++ if (pdata->hpd_enabled && hpd_event) ++ drm_kms_helper_hotplug_event(dev); ++ mutex_unlock(&pdata->hpd_mutex); ++ ++ return IRQ_HANDLED; ++} ++ + static int ti_sn_bridge_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) + { +@@ -1933,6 +2034,7 @@ static int ti_sn65dsi86_probe(struct i2c + dev_set_drvdata(dev, pdata); + pdata->dev = dev; + ++ mutex_init(&pdata->hpd_mutex); + mutex_init(&pdata->comms_mutex); + + pdata->regmap = devm_regmap_init_i2c(client, +@@ -1963,6 +2065,16 @@ static int ti_sn65dsi86_probe(struct i2c + if (ret) + return ret; + ++ if (client->irq) { ++ ret = devm_request_threaded_irq(pdata->dev, client->irq, NULL, ++ ti_sn_bridge_interrupt, ++ IRQF_ONESHOT, ++ dev_name(pdata->dev), pdata); ++ ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to request interrupt\n"); ++ } ++ + /* + * Break ourselves up into a collection of aux devices. The only real + * motiviation here is to solve the chicken-and-egg problem of probe diff --git a/queue-6.1/series b/queue-6.1/series index 73537da6c2..000fec5eeb 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -273,3 +273,4 @@ iio-imu-inv_icm42600-fix-odr-switch-to-the-same-value.patch i3c-mipi-i3c-hci-use-etimedout-instead-of-etime-for-timeout-errors.patch i3c-mipi-i3c-hci-restart-dma-ring-correctly-after-dequeue-abort.patch i3c-mipi-i3c-hci-add-missing-tid-field-to-no-op-command-descriptor.patch +drm-bridge-ti-sn65dsi86-add-support-for-displayport-mode-with-hpd.patch