[PATCH RESEND 12/17] drm/spacemit: add Innosilicon DP/eDP controller bridge driver

Philipp Zabel p.zabel at pengutronix.de
Mon Jul 27 00:49:58 PDT 2026


On Sa, 2026-07-25 at 00:51 -0400, Cody Kang via B4 Relay wrote:
> From: Cody Kang <codykang.hk at gmail.com>
> 
> Add the DP/eDP controller that sits downstream of the Saturn DPU. Two
> identical instances share one compatible; the eDP-vs-DP role is board
> wiring, so it is taken from the devicetree: an eDP panel always sits
> under an aux-bus child node, an external DP connector never does.
> 
> The link is driven through the generic PHY framework, so the controller
> never touches a PLL register. The controller's HPD interrupt is gated by
> the DP pixel clock, which can be off exactly when a plug has to be
> caught, so the connector is also polled and the interrupt path re-reads
> the live level when it does fire.
> 
> Signed-off-by: Cody Kang <codykang.hk at gmail.com>
> ---
>  drivers/gpu/drm/spacemit/Kconfig            |   19 +
>  drivers/gpu/drm/spacemit/Makefile           |    3 +
>  drivers/gpu/drm/spacemit/spacemit_inno_dp.c | 2443 +++++++++++++++++++++++++++
>  drivers/gpu/drm/spacemit/spacemit_inno_dp.h |  328 ++++
>  4 files changed, 2793 insertions(+)
> 
[...]
> diff --git a/drivers/gpu/drm/spacemit/spacemit_inno_dp.c b/drivers/gpu/drm/spacemit/spacemit_inno_dp.c
> new file mode 100644
> index 000000000000..e88731d9b18b
> --- /dev/null
> +++ b/drivers/gpu/drm/spacemit/spacemit_inno_dp.c
> @@ -0,0 +1,2443 @@
[...]
> +static int inno_dp_probe(struct platform_device *pdev)
> +{
[...]
> +	dp->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);

Since this is only asserted in probe and deasserted in remove, you
could use devm_reset_control_get_optional_exclusive_deasserted() [1].

[1] https://docs.kernel.org/driver-api/reset.html#c.devm_reset_control_get_optional_exclusive_deasserted

> +	if (IS_ERR(dp->reset)) {
> +		ret = dev_err_probe(dev, PTR_ERR(dp->reset),
> +				    "failed to get reset\n");
> +		return ret;
> +	}
> +
> +	dp->pxclk = devm_clk_get(dev, "pxclk");
> +	if (IS_ERR(dp->pxclk)) {
> +		ret = dev_err_probe(dev, PTR_ERR(dp->pxclk),
> +				    "failed to get pxclk\n");
> +		return ret;
> +	}
> +
> +	if (dp->edp_mode) {
> +		dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +		/*
> +		 * OP_DETECT is required: without it
> +		 * drm_bridge_connector_detect() hard-codes CONNECTED for any
> +		 * eDP connector and fbdev wedges at bind.
> +		 */
> +		dp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> +				 DRM_BRIDGE_OP_HPD;
> +	} else {
> +		dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> +		/*
> +		 * No OP_HPD: the HPD interrupt is gated by the DP pixel clock.
> +		 * Leaving it out marks the connector POLL_CONNECT |
> +		 * POLL_DISCONNECT instead.
> +		 */
> +		dp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> +				 DRM_BRIDGE_OP_MODES;
> +	}
> +
> +	if (dp->reset) {

This check is not necessary, reset_control_deassert() handles NULL
pointers.

> +		ret = reset_control_deassert(dp->reset);

If the _deasserted() variant is used above, this can be removed and the
reset control doesn't need to be stored in dp.

> +		if (ret) {
> +			dev_err(dev, "failed to deassert reset: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
[...]
> +	return 0;
> +
> +err_clk:
> +	clk_disable_unprepare(dp->pxclk);
> +err_reset:
> +	if (dp->reset)
> +		reset_control_assert(dp->reset);

Same as above. Remove the unnecessary check or manual assertion
entirely.

> +
> +	return ret;
> +}
> +
> +static void inno_dp_remove(struct platform_device *pdev)
> +{
> +	struct spacemit_dp_dev *dp = platform_get_drvdata(pdev);
> +
> +	component_del(&pdev->dev, &spacemit_dp_ops);
> +
> +	if (dp) {
> +		clk_disable_unprepare(dp->pxclk);
> +		if (dp->reset)
> +			reset_control_assert(dp->reset);

Same as above.

> +	}
> +}

regards
Philipp



More information about the linux-riscv mailing list