Hi, On Mon, Sep 8, 2025 at 1:37 PM John Ripple <john.rip...@keysight.com> wrote: > > 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 <john.rip...@keysight.com> > --- > V1 -> V2: Cleaned up coding style and addressed review comments > > drivers/gpu/drm/bridge/ti-sn65dsi86.c | 93 +++++++++++++++++++++++++++ > 1 file changed, 93 insertions(+) > > diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c > b/drivers/gpu/drm/bridge/ti-sn65dsi86.c > index ae0d08e5e960..ad0393212279 100644 > --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c > +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c > @@ -106,10 +106,24 @@ > #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 IRQ_HPD_EN BIT(0) > +#define HPD_INSERTION_EN BIT(1) > +#define HPD_REMOVAL_EN BIT(2) > +#define HPD_REPLUG_EN BIT(3) > +#define PLL_UNLOCK_EN BIT(5) > + > #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 > > @@ -221,6 +235,19 @@ static const struct regmap_config > ti_sn65dsi86_regmap_config = { > .max_register = 0xFF, > }; > > +static int ti_sn65dsi86_read(struct ti_sn65dsi86 *pdata, unsigned int reg, > + unsigned int *val)
This is reading a byte, right? So "val" should be an "u8 *". Yeah, that means you need a local variable to adjust for the generic regmap call, but it makes a cleaner and more obvious API to the users in this file. > +{ > + int ret; > + > + ret = regmap_read(pdata->regmap, reg, val); > + if (ret) > + dev_err(pdata->dev, "fail to read raw reg %x: %d\n", > + reg, ret); nit: use %#x so that the 0x is included. > @@ -1219,12 +1246,28 @@ static void ti_sn_bridge_hpd_enable(struct drm_bridge > *bridge) > */ > > pm_runtime_get_sync(pdata->dev); > + > + /* Enable HPD and PLL events. */ > + regmap_write(pdata->regmap, SN_IRQ_EVENTS_EN_REG, > + PLL_UNLOCK_EN | > + HPD_REPLUG_EN | > + HPD_REMOVAL_EN | > + HPD_INSERTION_EN | > + IRQ_HPD_EN); * Shouldn't this be `regmap_update_bits()` to just update the bits related to HPD? * why enable "PLL_UNLOCK_EN" when you don't handle it? * I also don't think your IRQ handler handles "replug" and "irq_hpd", right? So you shouldn't enable those either? Also: should the above only be if the IRQ is enabled? AKA obtain a pointer to the i2c_client and check `client->irq`? > + > + regmap_update_bits(pdata->regmap, SN_IRQ_EN_REG, IRQ_EN, > + IRQ_EN); I guess this is OK to be here if you want, but I would maybe consider putting it in `ti_sn65dsi86_resume()` if `client->irq` is set. Then if we ever enable more interrupts it'll already be in the correct place. > @@ -1309,6 +1352,32 @@ static int ti_sn_bridge_parse_dsi_host(struct > ti_sn65dsi86 *pdata) > 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; I'm unsure if accessing "dev" here without any sort of locking is safe... It feels like, in theory, "detach" could be called and race with the IRQ handler? Maybe you need a spinlock to be sure? > + u32 status = 0; > + bool hpd_event = false; > + int ret = 0; Don't initialize variables unless they're needed. In this case it's obvious that both `hpd_event` and `ret` don't need to be initialized. Maybe you could argue that `status` should be initialized since it's passed by reference, but even that's iffy... > + > + ret = ti_sn65dsi86_read(pdata, SN_IRQ_STATUS_REG, &status); > + if (ret) > + return ret; The return for this function is not an error code but an `irqreturn_t`. You need to account for that. > + hpd_event = status & (HPD_REMOVAL_STATUS | HPD_INSERTION_STATUS); > + if (status) { > + drm_dbg(dev, "(SN_IRQ_STATUS_REG = %#x)\n", status); > + ret = regmap_write(pdata->regmap, SN_IRQ_STATUS_REG, status); > + if (ret) > + return ret; Same--you can't just return an error code. > + } > + > + /* Only send the HPD event if we are bound with a device. */ > + if (dev && hpd_event) > + drm_kms_helper_hotplug_event(dev); > + > + return IRQ_HANDLED; If "status" gives back 0 (which would be weird), you probably want to return IRQ_NONE, right? > +} > + > static int ti_sn_bridge_probe(struct auxiliary_device *adev, > const struct auxiliary_device_id *id) > { > @@ -1971,6 +2040,30 @@ static int ti_sn65dsi86_probe(struct i2c_client > *client) > if (strncmp(id_buf, "68ISD ", ARRAY_SIZE(id_buf))) > return dev_err_probe(dev, -EOPNOTSUPP, "unsupported device > id\n"); > > + if (client->irq) { > + enum drm_connector_status status; > + > + ret = devm_request_threaded_irq(pdata->dev, client->irq, NULL, > + ti_sn_bridge_interrupt, > + IRQF_TRIGGER_RISING | > + IRQF_TRIGGER_FALLING | > + IRQF_ONESHOT, > + "ti_sn65dsi86", pdata); > + if (ret) { > + return dev_err_probe(dev, ret, > + "failed to request interrupt\n"); > + } > + > + /* > + * Cleaning status register at probe is needed because if the > irq is > + * already high, the rising/falling condition will never > occurs nit: s/occurs/occur > + */ > + ret = ti_sn65dsi86_read(pdata, SN_IRQ_STATUS_REG, &status); > + ret |= regmap_write(pdata->regmap, SN_IRQ_STATUS_REG, status); > + if (ret) > + pr_warn("Failed to clear IRQ initial state: %d\n", > ret); Do you even need to read? Can't you just write all possible bits and that should be safe?