[PATCH v16 4/8] drm: bridge: Cadence: Add MHDP8501 DP/HDMI driver

Maxime Ripard mripard at kernel.org
Tue Sep 3 01:28:09 PDT 2024


On Tue, Sep 03, 2024 at 06:07:25AM GMT, Sandor Yu wrote:
> Hi Maxime,
> 
> Thanks for your comments,
> 
> > -----Original Message-----
> > From: dri-devel <dri-devel-bounces at lists.freedesktop.org> On Behalf Of
> > Maxime Ripard
> > Sent: 2024年7月2日 21:25
> > To: Sandor Yu <sandor.yu at nxp.com>
> > Cc: dmitry.baryshkov at linaro.org; andrzej.hajda at intel.com;
> > neil.armstrong at linaro.org; Laurent Pinchart
> > <laurent.pinchart at ideasonboard.com>; jonas at kwiboo.se;
> > jernej.skrabec at gmail.com; airlied at gmail.com; daniel at ffwll.ch;
> > robh+dt at kernel.org; krzysztof.kozlowski+dt at linaro.org;
> > shawnguo at kernel.org; s.hauer at pengutronix.de; festevam at gmail.com;
> > vkoul at kernel.org; dri-devel at lists.freedesktop.org;
> > devicetree at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> > linux-kernel at vger.kernel.org; linux-phy at lists.infradead.org;
> > kernel at pengutronix.de; dl-linux-imx <linux-imx at nxp.com>; Oliver Brown
> > <oliver.brown at nxp.com>; alexander.stein at ew.tq-group.com;
> > sam at ravnborg.org
> > Subject: [EXT] Re: [PATCH v16 4/8] drm: bridge: Cadence: Add MHDP8501
> > DP/HDMI driver
> > 
> > Hi,
> > 
> > There's still the scrambler issue we discussed on v15, but I have some more
> > comments.
> > 
> > On Tue, Jul 02, 2024 at 08:22:36PM GMT, Sandor Yu wrote:
> > > +enum drm_connector_status cdns_mhdp8501_detect(struct
> > > +cdns_mhdp8501_device *mhdp) {
> > > +	u8 hpd = 0xf;
> > > +
> > > +	hpd = cdns_mhdp8501_read_hpd(mhdp);
> > > +	if (hpd == 1)
> > > +		return connector_status_connected;
> > > +	else if (hpd == 0)
> > > +		return connector_status_disconnected;
> > > +
> > > +	dev_warn(mhdp->dev, "Unknown cable status, hdp=%u\n", hpd);
> > > +	return connector_status_unknown;
> > > +}
> > > +
> > > +static void hotplug_work_func(struct work_struct *work) {
> > > +	struct cdns_mhdp8501_device *mhdp = container_of(work,
> > > +						     struct cdns_mhdp8501_device,
> > > +						     hotplug_work.work);
> > > +	enum drm_connector_status status = cdns_mhdp8501_detect(mhdp);
> > > +
> > > +	drm_bridge_hpd_notify(&mhdp->bridge, status);
> > > +
> > > +	if (status == connector_status_connected) {
> > > +		/* Cable connected  */
> > > +		DRM_INFO("HDMI/DP Cable Plug In\n");
> > > +		enable_irq(mhdp->irq[IRQ_OUT]);
> > > +	} else if (status == connector_status_disconnected) {
> > > +		/* Cable Disconnected  */
> > > +		DRM_INFO("HDMI/DP Cable Plug Out\n");
> > > +		enable_irq(mhdp->irq[IRQ_IN]);
> > > +	}
> > > +}
> > 
> > You shouldn't play with the interrupt being enabled here: hotplug interrupts
> > should always enabled.
> > 
> > If you can't for some reason, the reason should be documented in your driver.
> 
> iMX8MQ have two HPD interrupters, one for plugout and the other for plugin,
> because they could not be masked, so we have to enable one and disable the other.
> I will add more comments here.

Right, but why do you need to enable and disable them? Do you get
spurious interrupts?

> > 
> > > +	/* Mailbox protect for HDMI PHY access */
> > > +	mutex_lock(&mhdp->mbox_mutex);
> > > +	ret = phy_init(mhdp->phy);
> > > +	mutex_unlock(&mhdp->mbox_mutex);
> > > +	if (ret) {
> > > +		dev_err(dev, "Failed to initialize PHY: %d\n", ret);
> > > +		goto clk_disable;
> > > +	}
> > > +
> > > +	/* Mailbox protect for HDMI PHY access */
> > > +	mutex_lock(&mhdp->mbox_mutex);
> > > +	ret = phy_set_mode(mhdp->phy, phy_mode);
> > > +	mutex_unlock(&mhdp->mbox_mutex);
> > > +	if (ret) {
> > > +		dev_err(dev, "Failed to configure PHY: %d\n", ret);
> > > +		goto clk_disable;
> > > +	}
> > 
> > Why do you need a shared mutex between the phy and HDMI controller?
> 
> Both PHY and HDMI controller could access to the HDMI firmware by mailbox,
> So add mutex to avoid race condition.

That should be handled at either the phy or mailbox level, not in your
hdmi driver.

> > 
> > > +static enum drm_mode_status
> > > +cdns_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
> > > +			       const struct drm_display_mode *mode,
> > > +			       unsigned long long tmds_rate) {
> > > +	struct cdns_mhdp8501_device *mhdp = bridge->driver_private;
> > > +	union phy_configure_opts phy_cfg;
> > > +	int ret;
> > > +
> > > +	phy_cfg.hdmi.tmds_char_rate = tmds_rate;
> > > +
> > > +	/* Mailbox protect for HDMI PHY access */
> > > +	mutex_lock(&mhdp->mbox_mutex);
> > > +	ret = phy_validate(mhdp->phy, PHY_MODE_HDMI, 0, &phy_cfg);
> > > +	mutex_unlock(&mhdp->mbox_mutex);
> > > +	if (ret < 0)
> > > +		return MODE_CLOCK_RANGE;
> > > +
> > > +	return MODE_OK;
> > > +}
> > > +
> > > +static enum drm_mode_status
> > > +cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
> > > +			    const struct drm_display_info *info,
> > > +			    const struct drm_display_mode *mode) {
> > > +	unsigned long long tmds_rate;
> > > +
> > > +	/* We don't support double-clocked and Interlaced modes */
> > > +	if (mode->flags & DRM_MODE_FLAG_DBLCLK ||
> > > +	    mode->flags & DRM_MODE_FLAG_INTERLACE)
> > > +		return MODE_BAD;
> > > +
> > > +	/* MAX support pixel clock rate 594MHz */
> > > +	if (mode->clock > 594000)
> > > +		return MODE_CLOCK_HIGH;
> > 
> > This needs to be in the tmds_char_rate_valid function
> This clock rate check is covered by function tmds_char_rate_valid()
> It could be removed if keep function tmds_char_rate_valid() be called by mode_valid.

Yeah, it's not something you should have to duplicate.

> > 
> > > +	if (mode->hdisplay > 3840)
> > > +		return MODE_BAD_HVALUE;
> > > +
> > > +	if (mode->vdisplay > 2160)
> > > +		return MODE_BAD_VVALUE;
> > > +
> > > +	tmds_rate = mode->clock * 1000ULL;
> > > +	return cdns_hdmi_tmds_char_rate_valid(bridge, mode, tmds_rate);
> > 
> > It will already be called by the core so this is redundant.
> 
> mode_valid function is use to filter the mode list in drm_helper_probe_single_connector_modes(),
> if function cdns_hdmi_tmds_char_rate_valid() is not called, unsupported modes will in mode list.

It's probably something we should deal with in the core somehow. I'm not
entirely sure how to reconcile drm_bridge_connector and the hdmi
framework there, but we should at the very least provide a mode_valid
helper for bridges.

> > 
> > > +static void cdns_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +					   struct drm_bridge_state *old_state) {
> > > +	struct cdns_mhdp8501_device *mhdp = bridge->driver_private;
> > > +	struct drm_atomic_state *state = old_state->base.state;
> > > +	struct drm_connector *connector;
> > > +	struct video_info *video = &mhdp->video_info;
> > > +	struct drm_crtc_state *crtc_state;
> > > +	struct drm_connector_state *conn_state;
> > > +	struct drm_display_mode *mode = &mhdp->mode;
> > > +	union phy_configure_opts phy_cfg;
> > > +	int ret;
> > > +
> > > +	connector = drm_atomic_get_new_connector_for_encoder(state,
> > > +							     bridge->encoder);
> > > +	if (WARN_ON(!connector))
> > > +		return;
> > > +
> > > +	mhdp->curr_conn = connector;
> > > +
> > > +	conn_state = drm_atomic_get_new_connector_state(state, connector);
> > > +	if (WARN_ON(!conn_state))
> > > +		return;
> > > +
> > > +	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
> > > +	if (WARN_ON(!crtc_state))
> > > +		return;
> > > +
> > > +	video->color_fmt = conn_state->hdmi.output_format;
> > > +	video->bpc = conn_state->hdmi.output_bpc;
> > > +
> > > +	drm_mode_copy(&mhdp->mode, &crtc_state->adjusted_mode);
> > 
> > Why do you need a copy of all these fields? You should pass the connector /
> > bridge state around and not copy these fields.
> > 
> OK, mode will be removed, and it will replace by drm_connector_state.
> 
> > > +	/* video mode check */
> > > +	if (mode->clock == 0 || mode->hdisplay == 0 || mode->vdisplay == 0)
> > > +		return;
> > 
> > This should be checked in atomic_check, but I'm pretty sure it's redundant.
> OK, It will be removed.
> > 
> > > +	dev_dbg(mhdp->dev, "Mode: %dx%dp%d\n",
> > > +		mode->hdisplay, mode->vdisplay, mode->clock);
> > > +
> > > +	drm_atomic_helper_connector_hdmi_update_infoframes(connector,
> > > +state);
> > > +
> > > +	/* Line swapping */
> > > +	cdns_mhdp_reg_write(&mhdp->base, LANES_CONFIG, 0x00400000 |
> > > +mhdp->lane_mapping);
> > > +
> > > +	mhdp->hdmi.char_rate = drm_hdmi_compute_mode_clock(mode,
> > > +							   mhdp->video_info.bpc,
> > > +							   mhdp->video_info.color_fmt);
> > 
> > The TMDS char rate is already available in the connector_state so there no
> > need to recompute it.
> > 
> > > +	phy_cfg.hdmi.tmds_char_rate = mhdp->hdmi.char_rate;
> > 
> > And you shouldn't store a copy either.
> OK, variable char_rate will be removed and will use the drm_connector_state-> hdmi.tmds_char_rate
> 
> > 
> > > +	/* Mailbox protect for HDMI PHY access */
> > > +	mutex_lock(&mhdp->mbox_mutex);
> > > +	ret = phy_configure(mhdp->phy, &phy_cfg);
> > > +	mutex_unlock(&mhdp->mbox_mutex);
> > > +	if (ret) {
> > > +		dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n",
> > > +			__func__, ret);
> > > +		return;
> > > +	}
> > > +
> > > +	cdns_hdmi_sink_config(mhdp);
> > > +
> > > +	ret = cdns_hdmi_ctrl_init(mhdp);
> > > +	if (ret < 0) {
> > > +		dev_err(mhdp->dev, "%s, ret = %d\n", __func__, ret);
> > > +		return;
> > > +	}
> > > +
> > > +	/* Config GCP */
> > > +	if (mhdp->video_info.bpc == 8)
> > > +		cdns_hdmi_disable_gcp(mhdp);
> > > +	else
> > > +		cdns_hdmi_enable_gcp(mhdp);
> > > +
> > > +	ret = cdns_hdmi_mode_config(mhdp, mode, &mhdp->video_info);
> > > +	if (ret < 0) {
> > > +		dev_err(mhdp->dev, "CDN_API_HDMITX_SetVic_blocking ret
> > = %d\n", ret);
> > > +		return;
> > > +	}
> > > +}
> > > +
> > > +static int cdns_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
> > > +					    enum hdmi_infoframe_type type) {
> > > +	return 0;
> > > +}
> > 
> > Either implement it or don't, but an empty function is dead code.
> Must have function hdmi_clear_infoframe when set DRM_BRIDGE_OP_HDMI flag in &drm_bridge->ops, 
> otherwise the drm_bridge_connector_init() will fail.
> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/gpu/drm/drm_bridge_connector.c?h=next-20240902#n419

That's a strong hint that you should implement it, not workaround it :)

Maxime
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 273 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-phy/attachments/20240903/dbff393e/attachment.sig>


More information about the linux-phy mailing list