Hello,
This patch series aims at adding support for runtime bus-format negotiation between all elements of the 'encoder -> bridges -> connector/display' section of the pipeline.
In order to support that, we need drm bridges to fully take part in the atomic state validation process, which requires addition of a drm_bridge_state + a new drm_bridge_funcs.atomic_check() hook (patches 10 - 12). Once those basic building blocks are in place, we can add new helpers to facilitate bus-format negotiation between element of the encoder chain (patches 13 - 14).
Patches 1 to 9 are preparatory patches for patches 10 - 14. Patch 1 is needed to allow inclusion of drm_atomic.h from drm_bridge.h, which we need to make drm_bridge/drm_bridge_state inherit from drm_private_obj/drm_private_obj_state. Patches 2 to 9 are about transitioning the bridge chain to a double-linked list, which is needed to allow bridge elements to negotiate with the next and prev element in the pipeline. Most of those patches can be applied independently of the rest of the series, especially patches 2 - 7 which I think fix some misuses of the drm_bridge API.
To finish, patches 15 - 17 demonstrate how bus-format negotiation can be done, and patches 18 - 19 were needed for the use case I used to test whole solution.
As you've noticed, this is an RFC, so any kind of feedback is welcome (apart from checkpatch reports :P).
Thanks,
Boris
Boris Brezillon (19): drm: Stop including drm_bridge.h from drm_crtc.h drm: Support custom encoder/bridge enable/disable sequences officially drm/vc4: Get rid of the dsi->bridge field drm/exynos: Get rid of exynos_dsi->out_bridge drm/exynos: Don't reset bridge->next drm/bridge: Create drm_bridge_chain_xx() wrappers drm/msm: Use drm_attach_bridge() to attach a bridge to an encoder drm/bridge: Introduce drm_bridge_chain_get_{first,last,next}_bridge() drm/bridge: Make the bridge chain a double-linked list drm/bridge: Add a drm_bridge_state object drm/bridge: Patch atomic hooks to take a drm_bridge_state drm/bridge: Add an ->atomic_check() hook drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper drm/bridge: Add the necessary bits to support bus format negotiation drm/imx: pd: Use bus format/flags provided by the bridge when available drm/bridge: lvds-encoder: Add a way to support custom ->atomic_check() implem drm/bridge: lvds-encoder: Implement bus format negotiation for sn75lvds83 drm/panel: simple: Add support for Toshiba LTA089AC29000 panel ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition
.../display/panel/toshiba,lt089ac29000.txt | 5 +- arch/arm/boot/dts/imx51-zii-rdu1.dts | 24 +- drivers/gpu/drm/arc/arcpgu_hdmi.c | 1 + drivers/gpu/drm/bridge/analogix-anx78xx.c | 1 + .../drm/bridge/analogix/analogix_dp_core.c | 13 +- drivers/gpu/drm/bridge/dumb-vga-dac.c | 1 + drivers/gpu/drm/bridge/lvds-encoder.c | 75 ++ .../bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 1 + drivers/gpu/drm/bridge/nxp-ptn3460.c | 1 + drivers/gpu/drm/bridge/panel.c | 1 + drivers/gpu/drm/bridge/parade-ps8622.c | 1 + drivers/gpu/drm/bridge/sii902x.c | 1 + drivers/gpu/drm/bridge/sii9234.c | 1 + drivers/gpu/drm/bridge/sil-sii8620.c | 1 + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1 + drivers/gpu/drm/bridge/tc358764.c | 1 + drivers/gpu/drm/bridge/tc358767.c | 1 + drivers/gpu/drm/bridge/ti-sn65dsi86.c | 1 + drivers/gpu/drm/bridge/ti-tfp410.c | 1 + drivers/gpu/drm/drm_atomic.c | 40 + drivers/gpu/drm/drm_atomic_helper.c | 46 +- drivers/gpu/drm/drm_bridge.c | 794 ++++++++++++++---- drivers/gpu/drm/drm_crtc_helper.c | 46 +- drivers/gpu/drm/drm_encoder.c | 16 +- drivers/gpu/drm/drm_probe_helper.c | 3 +- drivers/gpu/drm/drm_simple_kms_helper.c | 1 + drivers/gpu/drm/exynos/exynos_dp.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 18 +- drivers/gpu/drm/exynos/exynos_drm_mic.c | 1 + drivers/gpu/drm/exynos/exynos_hdmi.c | 1 + drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 1 + drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 1 + drivers/gpu/drm/i2c/tda998x_drv.c | 1 + drivers/gpu/drm/imx/imx-ldb.c | 1 + drivers/gpu/drm/imx/parallel-display.c | 29 +- drivers/gpu/drm/ingenic/ingenic-drm.c | 1 + drivers/gpu/drm/mediatek/mtk_dpi.c | 1 + drivers/gpu/drm/mediatek/mtk_dsi.c | 1 + drivers/gpu/drm/mediatek/mtk_hdmi.c | 7 +- drivers/gpu/drm/msm/dsi/dsi.h | 1 + drivers/gpu/drm/msm/edp/edp.c | 4 +- drivers/gpu/drm/msm/edp/edp.h | 1 + drivers/gpu/drm/msm/edp/edp_bridge.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi.c | 4 +- drivers/gpu/drm/msm/hdmi/hdmi.h | 2 + drivers/gpu/drm/omapdrm/dss/output.c | 1 + drivers/gpu/drm/omapdrm/omap_drv.c | 8 +- drivers/gpu/drm/omapdrm/omap_encoder.c | 4 +- drivers/gpu/drm/panel/panel-simple.c | 36 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 10 +- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 1 + drivers/gpu/drm/rockchip/rockchip_lvds.c | 1 + drivers/gpu/drm/rockchip/rockchip_rgb.c | 1 + drivers/gpu/drm/sti/sti_dvo.c | 1 + drivers/gpu/drm/sti/sti_hda.c | 1 + drivers/gpu/drm/sti/sti_hdmi.c | 1 + drivers/gpu/drm/sun4i/sun4i_lvds.c | 1 + drivers/gpu/drm/sun4i/sun4i_rgb.c | 1 + drivers/gpu/drm/sun4i/sun4i_tcon.c | 1 + drivers/gpu/drm/tilcdc/tilcdc_external.c | 1 + drivers/gpu/drm/vc4/vc4_dsi.c | 38 +- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 252 +++++- include/drm/drm_crtc.h | 1 - include/drm/drm_encoder.h | 19 +- 65 files changed, 1242 insertions(+), 297 deletions(-)
We are about to add a drm_bridge_state that inherits from drm_private_state which is defined in drm_atomic.h. Problem is, drm_atomic.h includes drm_crtc.h which in turn includes drm_bridge.h, leading to "drm_private_state has incomplete type" error.
Let's force all users of the drm_bridge API to explicitly include drm_bridge.h.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/arc/arcpgu_hdmi.c | 1 + drivers/gpu/drm/bridge/analogix-anx78xx.c | 1 + drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 1 + drivers/gpu/drm/bridge/dumb-vga-dac.c | 1 + drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 1 + drivers/gpu/drm/bridge/nxp-ptn3460.c | 1 + drivers/gpu/drm/bridge/panel.c | 1 + drivers/gpu/drm/bridge/parade-ps8622.c | 1 + drivers/gpu/drm/bridge/sii902x.c | 1 + drivers/gpu/drm/bridge/sii9234.c | 1 + drivers/gpu/drm/bridge/sil-sii8620.c | 1 + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1 + drivers/gpu/drm/bridge/tc358764.c | 1 + drivers/gpu/drm/bridge/tc358767.c | 1 + drivers/gpu/drm/bridge/ti-sn65dsi86.c | 1 + drivers/gpu/drm/bridge/ti-tfp410.c | 1 + drivers/gpu/drm/drm_atomic_helper.c | 1 + drivers/gpu/drm/drm_crtc_helper.c | 1 + drivers/gpu/drm/drm_encoder.c | 1 + drivers/gpu/drm/drm_probe_helper.c | 1 + drivers/gpu/drm/drm_simple_kms_helper.c | 1 + drivers/gpu/drm/exynos/exynos_dp.c | 1 + drivers/gpu/drm/exynos/exynos_drm_dsi.c | 1 + drivers/gpu/drm/exynos/exynos_drm_mic.c | 1 + drivers/gpu/drm/exynos/exynos_hdmi.c | 1 + drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 1 + drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 1 + drivers/gpu/drm/i2c/tda998x_drv.c | 1 + drivers/gpu/drm/imx/imx-ldb.c | 1 + drivers/gpu/drm/imx/parallel-display.c | 1 + drivers/gpu/drm/ingenic/ingenic-drm.c | 1 + drivers/gpu/drm/mediatek/mtk_dpi.c | 1 + drivers/gpu/drm/mediatek/mtk_dsi.c | 1 + drivers/gpu/drm/mediatek/mtk_hdmi.c | 1 + drivers/gpu/drm/msm/dsi/dsi.h | 1 + drivers/gpu/drm/msm/edp/edp.h | 1 + drivers/gpu/drm/msm/hdmi/hdmi.h | 2 ++ drivers/gpu/drm/omapdrm/dss/output.c | 1 + drivers/gpu/drm/omapdrm/omap_drv.c | 1 + drivers/gpu/drm/omapdrm/omap_encoder.c | 1 + drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 1 + drivers/gpu/drm/rockchip/rockchip_lvds.c | 1 + drivers/gpu/drm/rockchip/rockchip_rgb.c | 1 + drivers/gpu/drm/sti/sti_dvo.c | 1 + drivers/gpu/drm/sti/sti_hda.c | 1 + drivers/gpu/drm/sti/sti_hdmi.c | 1 + drivers/gpu/drm/sun4i/sun4i_lvds.c | 1 + drivers/gpu/drm/sun4i/sun4i_rgb.c | 1 + drivers/gpu/drm/sun4i/sun4i_tcon.c | 1 + drivers/gpu/drm/tilcdc/tilcdc_external.c | 1 + drivers/gpu/drm/vc4/vc4_dsi.c | 1 + include/drm/drm_crtc.h | 1 - 52 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c index 98aac743cc26..8fd7094beece 100644 --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c @@ -5,6 +5,7 @@ * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) */
+#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_encoder.h> #include <drm/drm_device.h> diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index 3c7cc5af735c..e3f4fd2a5ad4 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -19,6 +19,7 @@ #include <linux/types.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_dp_helper.h> #include <drm/drm_edid.h> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index f2f7f69d6cc3..b096a7a75c14 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -21,6 +21,7 @@ #include <drm/bridge/analogix_dp.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_device.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index 8ef6539ae78a..a974077913bf 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -12,6 +12,7 @@ #include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 6e81e5db57f2..e8a49f6146c6 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -25,6 +25,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index d4a1cc5052c3..57ff01339559 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/of.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index b12ae3a4c5f1..6cffeb4a42f2 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -5,6 +5,7 @@ */
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_connector.h> #include <drm/drm_encoder.h> #include <drm/drm_modeset_helper_vtables.h> diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 93c68e2e9484..b7a72dfdcac3 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -17,6 +17,7 @@ #include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 38f75ac580df..b70e8c5cf2e1 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -20,6 +20,7 @@ #include <linux/clk.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c index 25d4ad8c7ad6..ad00d841ed9e 100644 --- a/drivers/gpu/drm/bridge/sii9234.c +++ b/drivers/gpu/drm/bridge/sii9234.c @@ -13,6 +13,7 @@ * Dharam Kumar dharam.kr@samsung.com */ #include <drm/bridge/mhl.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h>
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index bd3165ee5354..14643923a721 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -9,6 +9,7 @@ #include <asm/unaligned.h>
#include <drm/bridge/mhl.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_encoder.h> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 83b94b66e464..b4901b174a90 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -26,6 +26,7 @@
#include <drm/bridge/dw_hdmi.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_encoder_slave.h> #include <drm/drm_of.h> diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 170f162ffa55..db298f550a5a 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -16,6 +16,7 @@ #include <video/mipi_display.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_fb_helper.h> #include <drm/drm_mipi_dsi.h> diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 42f03a985ac0..02c172151165 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -26,6 +26,7 @@ #include <linux/slab.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 0a580957c8cf..43abf01ebd4c 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -17,6 +17,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_dp_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 61cc2354ef1b..aa3198dc9903 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index aa16ea17ff9b..4706439fb490 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -30,6 +30,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_bridge.h> #include <drm/drm_damage_helper.h> #include <drm/drm_device.h> #include <drm/drm_plane_helper.h> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 6dd49a60deac..fa3694836c22 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -36,6 +36,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 7fb47b7b8b44..80d88a55302e 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -22,6 +22,7 @@
#include <linux/export.h>
+#include <drm/drm_bridge.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> #include <drm/drm_encoder.h> diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index ef2c468205a2..351cbc40f0f8 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -32,6 +32,7 @@ #include <linux/export.h> #include <linux/moduleparam.h>
+#include <drm/drm_bridge.h> #include <drm/drm_client.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index b11910f14c46..046055719245 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -8,6 +8,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_plane_helper.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 3a0f0ba8c63a..1e6aa24bf45e 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -19,6 +19,7 @@
#include <drm/bridge/analogix_dp.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 6926cee91b36..72726f2c7a9f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -24,6 +24,7 @@ #include <video/videomode.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_fb_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index b78e8c5ba553..f41d75923557 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -21,6 +21,7 @@ #include <video/of_videomode.h> #include <video/videomode.h>
+#include <drm/drm_bridge.h> #include <drm/drm_encoder.h> #include <drm/drm_print.h>
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index bc1565f1822a..2e3795c2c794 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -34,6 +34,7 @@ #include <media/cec-notifier.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index 279d83eaffc0..11ff06c81dd8 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -9,6 +9,7 @@ #include <linux/of_graph.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index 5bf8138941de..bdcf9c6ae9e9 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_device.h> #include <drm/drm_encoder_slave.h> #include <drm/drm_mipi_dsi.h> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 61e042918a7f..b578189b908c 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -15,6 +15,7 @@
#include <drm/drmP.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index db461b6a257f..96e9f031b713 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -20,6 +20,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_fb_helper.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 2e51b2fade75..35f20edab9fd 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -13,6 +13,7 @@ #include <video/of_display_timing.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_fb_helper.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index ce1fae3a78a9..1daa1378fc36 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -13,6 +13,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index be6d95c5ff25..01fa8b8d763d 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -17,6 +17,7 @@ #include <video/videomode.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_of.h>
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 224afb666881..a413f5ff442d 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -16,6 +16,7 @@ #include <video/videomode.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index ce91b61364eb..c79b1f855d89 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -23,6 +23,7 @@ #include <sound/hdmi-codec.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 0da8a4e428ad..eff1a4c61258 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -9,6 +9,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h>
+#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/msm/edp/edp.h b/drivers/gpu/drm/msm/edp/edp.h index f2c17858a703..eb34243dad53 100644 --- a/drivers/gpu/drm/msm/edp/edp.h +++ b/drivers/gpu/drm/msm/edp/edp.h @@ -10,6 +10,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/platform_device.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_dp_helper.h>
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 982865866a29..c681a9e22484 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -7,6 +7,8 @@ #ifndef __HDMI_CONNECTOR_H__ #define __HDMI_CONNECTOR_H__
+#include <drm/drm_bridge.h> + #include <linux/i2c.h> #include <linux/clk.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index de0f882f0f7b..de500bb05bdc 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -11,6 +11,7 @@ #include <linux/of.h> #include <linux/of_graph.h>
+#include <drm/drm_bridge.h> #include <drm/drm_panel.h>
#include "dss.h" diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 9f652d2e7af1..224ec6fdc800 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -11,6 +11,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_drv.h> #include <drm/drm_fb_helper.h> #include <drm/drm_file.h> diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 6fe14111cd95..24bbe9f2a32e 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -6,6 +6,7 @@
#include <linux/list.h>
+#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_edid.h> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 0f00bdfe2366..3a1139b725c0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -9,6 +9,7 @@
#include <linux/export.h>
+#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index 64aefa856896..8a4c9af0ba73 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -16,6 +16,7 @@ #include <linux/regmap.h> #include <linux/reset.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h>
#include <drm/drm_dp_helper.h> #include <drm/drm_of.h> diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c index 89e0bb0fe0ab..db1be1f3925c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c @@ -9,6 +9,7 @@ #include <linux/of_graph.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_dp_helper.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index e55870190bf5..0a4f00253f39 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -12,6 +12,7 @@ #include <linux/platform_device.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_device.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 94e404f13234..9d3fd6370a29 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -12,6 +12,7 @@ #include <linux/seq_file.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_debugfs.h> #include <drm/drm_device.h> #include <drm/drm_file.h> diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 9862c322f0c4..84318d0832a0 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -14,6 +14,7 @@ #include <linux/reset.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_debugfs.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c index 7fbf425acb55..25ab2ef6d545 100644 --- a/drivers/gpu/drm/sun4i/sun4i_lvds.c +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c @@ -7,6 +7,7 @@ #include <linux/clk.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index aac56983f208..e74b9eddca01 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -9,6 +9,7 @@ #include <linux/clk.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 690aeb822704..eb187da56aba 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -16,6 +16,7 @@ #include <linux/reset.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_encoder.h> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index 43d756b7810e..4fc10838de80 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -8,6 +8,7 @@ #include <linux/of_graph.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_of.h>
#include "tilcdc_drv.h" diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index c78fa8144776..3f63943e5472 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -31,6 +31,7 @@ #include <linux/pm_runtime.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 7d14c11bdc0a..7e2963cad543 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -41,7 +41,6 @@ #include <drm/drm_connector.h> #include <drm/drm_device.h> #include <drm/drm_property.h> -#include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_plane.h> #include <drm/drm_blend.h>
Hi Boris.
Good to see that you kept the alphabetical order in the include files. One nit below. With this fixed: Reviewed-by: Sam Ravnborg sam@ravnborg.org
Sam
Please follow the same order of include blocks as we see in other files:
#include <linux/*.h>
#include <drm/*.h>
#include "*.h"
And with an empty line between the blocks, and sorted withint the blocks.
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:32PM +0200, Boris Brezillon wrote:
Overall this looks good to me. Before I ack the patch, how have you tested this ? Have you compiled all the DRM/KMS drivers ?
If you wanted to go one step further you could also remove the forward declaration of struct drm_bridge from drm_ctrc.h, as it's not needed there.
On Tue, 20 Aug 2019 21:53:15 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
git grep to find all drm_bridge users that were not including drm_bridge.h directly and then I compiled all impacted drivers. Also pushed to a tree monitored by kbuild bots to make sure I didn't forget one of them.
I'll add a patch doing that.
Thanks,
Boris
The VC4 and Exynos DSI encoder drivers need a custom enable/disable sequence and manually set encoder->bridge to NULL to prevent the core from calling the bridge hooks.
Let's provide a more official way to support custom bridge/encoder enable/disable sequences.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_atomic_helper.c | 13 +++++++--- drivers/gpu/drm/drm_crtc_helper.c | 38 ++++++++++++++++++----------- include/drm/drm_encoder.h | 9 +++++++ 3 files changed, 42 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 4706439fb490..15ea61877122 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1030,7 +1030,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) * Each encoder has at most one connector (since we always steal * it away), so we won't call disable hooks twice. */ - drm_atomic_bridge_disable(encoder->bridge, old_state); + if (!encoder->custom_bridge_enable_disable_seq) + drm_atomic_bridge_disable(encoder->bridge, old_state);
/* Right function depends upon target state. */ if (funcs) { @@ -1044,7 +1045,9 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); }
- drm_atomic_bridge_post_disable(encoder->bridge, old_state); + if (!encoder->custom_bridge_enable_disable_seq) + drm_atomic_bridge_post_disable(encoder->bridge, + old_state); }
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { @@ -1342,7 +1345,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, * Each encoder has at most one connector (since we always steal * it away), so we won't call enable hooks twice. */ - drm_atomic_bridge_pre_enable(encoder->bridge, old_state); + if (!encoder->custom_bridge_enable_disable_seq) + drm_atomic_bridge_pre_enable(encoder->bridge, old_state);
if (funcs) { if (funcs->atomic_enable) @@ -1353,7 +1357,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
- drm_atomic_bridge_enable(encoder->bridge, old_state); + if (!encoder->custom_bridge_enable_disable_seq) + drm_atomic_bridge_enable(encoder->bridge, old_state); }
drm_atomic_helper_commit_writebacks(dev, old_state); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index fa3694836c22..87dae37fec12 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -160,14 +160,16 @@ drm_encoder_disable(struct drm_encoder *encoder) if (!encoder_funcs) return;
- drm_bridge_disable(encoder->bridge); + if (!encoder->custom_bridge_enable_disable_seq) + drm_bridge_disable(encoder->bridge);
if (encoder_funcs->disable) (*encoder_funcs->disable)(encoder); else if (encoder_funcs->dpms) (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
- drm_bridge_post_disable(encoder->bridge); + if (!encoder->custom_bridge_enable_disable_seq) + drm_bridge_post_disable(encoder->bridge); }
static void __drm_helper_disable_unused_functions(struct drm_device *dev) @@ -365,13 +367,15 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (!encoder_funcs) continue;
- drm_bridge_disable(encoder->bridge); + if (!encoder->custom_bridge_enable_disable_seq) + drm_bridge_disable(encoder->bridge);
/* Disable the encoders as the first thing we do. */ if (encoder_funcs->prepare) encoder_funcs->prepare(encoder);
- drm_bridge_post_disable(encoder->bridge); + if (!encoder->custom_bridge_enable_disable_seq) + drm_bridge_post_disable(encoder->bridge); }
drm_crtc_prepare_encoders(dev); @@ -414,12 +418,14 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (!encoder_funcs) continue;
- drm_bridge_pre_enable(encoder->bridge); + if (!encoder->custom_bridge_enable_disable_seq) + drm_bridge_pre_enable(encoder->bridge);
if (encoder_funcs->commit) encoder_funcs->commit(encoder);
- drm_bridge_enable(encoder->bridge); + if (!encoder->custom_bridge_enable_disable_seq) + drm_bridge_enable(encoder->bridge); }
/* Calculate and store various constants which @@ -825,18 +831,22 @@ static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) if (!encoder_funcs) return;
- if (mode == DRM_MODE_DPMS_ON) - drm_bridge_pre_enable(bridge); - else - drm_bridge_disable(bridge); + if (!encoder->custom_bridge_enable_disable_seq) { + if (mode == DRM_MODE_DPMS_ON) + drm_bridge_pre_enable(bridge); + else + drm_bridge_disable(bridge); + }
if (encoder_funcs->dpms) encoder_funcs->dpms(encoder, mode);
- if (mode == DRM_MODE_DPMS_ON) - drm_bridge_enable(bridge); - else - drm_bridge_post_disable(bridge); + if (!encoder->custom_bridge_enable_disable_seq) { + if (mode == DRM_MODE_DPMS_ON) + drm_bridge_enable(bridge); + else + drm_bridge_post_disable(bridge); + } }
static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 70cfca03d812..d986ff1197ce 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -175,6 +175,15 @@ struct drm_encoder { struct drm_bridge *bridge; const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private; + + /** + * @custom_bridge_enable_disable_seq: Set to true if the encoder needs + * a custom bridge/encoder enable/disable sequence. In that case the + * driver is responsible for calling the + * drm[_atomic]_bridge_{pre_enable,enable,disable,post_disable}() + * functions as part of its encoder enable/disable handling. + */ + uint32_t custom_bridge_enable_disable_seq : 1; };
#define obj_to_encoder(x) container_of(x, struct drm_encoder, base)
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:33PM +0200, Boris Brezillon wrote:
So, while I'm not opposed to this, I think it's a bit of a hack, and I'd like to share my vision of how I'd like to see this being implemented in the more distant future (and thus possibly on top of this change).
It has been established for quite some time now that exposing drm_encoder to userspace was likely a mistake, and that it should have stayed a kernel-only object. With the generalised usage of drm_bridge, I would go one step further: drm_encoder shouldn't matter for DRM/KMS drivers at all.
drm_bridge has been introduced to model chained encoders, where the second (and subsequent) encoders couldn't easily be supported in a standard and reusable way with just drm_encoder. A bridge (with the exception of the panel bridge) is just an encoder. It shouldn't matter whether encoders are internal to the display controller, separate IPs in the SoC or external components, they could all be modelled as bridges. We do have bridges for DSI encoder IPs, and DSI (and other) bridges potentially need similar custom enable/disable sequences. I would thus ideally like to see the VC4 and Exynos DSI encoders being implemented as bridges, with support for custom enable/disable sequences in bridges, and drop support for custom enable/disable sequences in drm_encoder.
Does this make sense to you ? While I would love to see this being implemented right away, it may be too much work as a prerequisite for this bus format negotiation series, so I don't want to reject this patch. I would however like to already make sure we agree on the way forward for the next steps.
On Tue, Aug 20, 2019 at 10:05:05PM +0300, Laurent Pinchart wrote:
I think it's also counter to how the atomic helpers are meant to be used. I mean you're adding a pile new hooks here all for essentially not having to type a few lines of helper code. If you look around at big drivers (i915, amdgpu, nouveau) _none_ of them use the modesets_enables/disables helpers. Exactly for reasons like this where you need a custom sequence.
So proper recommendation is to just type your own, you'll be happier.
Yeah the other bit is that bridges are supposed to be some kind of standard. I do wonder (I haven't looked at the series) whether the problem here is that encoders don't have their own full set of pre/post enable hooks like bridges (essentially that's what you're adding here), or whether the idea behind pre/post enable/disable hooks is not good enough.
Anyway, this here is imo not the right thing to do. If setting encoder->bridge to NULL doesn't work for you then just type your own enable/disable implementations. There's explicitly no requirement to use all parts of atomic helpers, au contraire, it's explicitly discouraged.
Cheers, Daniel
Hi Daniel,
On Wed, Aug 21, 2019 at 10:21:07AM +0200, Daniel Vetter wrote:
It's a mostly-good idea :-) DSI is a bit more troublesome as it often requires enable/disable sequences that are a bit more entangled between the source and the sink. That's why the omapdrm driver originally controlled enabling from sink to source, it would locate the bridge (or the omapdrm internal equivalent of bridge to be more accurate) at the end of the chain, and enable it. The bridge would then be responsible for forwarding the enable call to its source, broken in several fine-grained calls for DSI. That allowed a DSI sink to implement completely custom enable/disable sequences, and enable the source accordingly (usually by first enabling clocks, then enabling data transmission but without actual display data, and finally enabling transfer of display data). The cost was a more complex enable/disable implementation for each bridge.
I think the pre/post enable/disable operations we have now are mostly good, but for some links they need to be manually controlled. Of course I can't rule out a new display bus in the future that will shatter this down to pieces by requiring more complex enable/disable sequences.
On Wed, 21 Aug 2019 10:21:07 +0200 Daniel Vetter daniel@ffwll.ch wrote:
It's just that we lack the pre/post enable hooks at the encoder level. But I'm addressing this limitation slightly differently in the v2 I'm working on.
On Tue, 20 Aug 2019 22:05:05 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
I agree, it definitely looks like a hack, but I find it less hack-ish than letting drivers re-initialize the encoder->bridge field to NULL just after calling bridge_attach() :P.
I couldn't agree more.
Looks like we're on the same page.
Does this make sense to you ?
Definitely.
Actually, I think I have something [1] that implements what you're proposing here (which is not surprising since we discussed it on IRC ;)). I'll send a v2 so you can comment on the new approach.
[1]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-2
Now that we have an official way to request custom encoder/bridge enable/disable sequences we can get rid of the extra ->bridge field and use the encoder one.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/vc4/vc4_dsi.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 3f63943e5472..b670ca473786 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -498,7 +498,6 @@ struct vc4_dsi {
struct mipi_dsi_host dsi_host; struct drm_encoder *encoder; - struct drm_bridge *bridge;
void __iomem *regs;
@@ -753,9 +752,9 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) struct vc4_dsi *dsi = vc4_encoder->dsi; struct device *dev = &dsi->pdev->dev;
- drm_bridge_disable(dsi->bridge); + drm_bridge_disable(encoder->bridge); vc4_dsi_ulps(dsi, true); - drm_bridge_post_disable(dsi->bridge); + drm_bridge_post_disable(encoder->bridge);
clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->escape_clock); @@ -1055,7 +1054,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
vc4_dsi_ulps(dsi, false);
- drm_bridge_pre_enable(dsi->bridge); + drm_bridge_pre_enable(encoder->bridge);
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { DSI_PORT_WRITE(DISP0_CTRL, @@ -1072,7 +1071,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) DSI_DISP0_ENABLE); }
- drm_bridge_enable(dsi->bridge); + drm_bridge_enable(encoder->bridge);
if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&dsi->pdev->dev); @@ -1445,6 +1444,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dsi *dsi = dev_get_drvdata(dev); struct vc4_dsi_encoder *vc4_dsi_encoder; + struct drm_bridge *bridge = NULL; struct drm_panel *panel; const struct of_device_id *match; dma_cap_mask_t dma_mask; @@ -1561,7 +1561,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) }
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, - &panel, &dsi->bridge); + &panel, &bridge); if (ret) { /* If the bridge or panel pointed by dev->of_node is not * enabled, just return 0 here so that we don't prevent the DRM @@ -1576,10 +1576,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) }
if (panel) { - dsi->bridge = devm_drm_panel_bridge_add(dev, panel, - DRM_MODE_CONNECTOR_DSI); - if (IS_ERR(dsi->bridge)) - return PTR_ERR(dsi->bridge); + bridge = devm_drm_panel_bridge_add(dev, panel, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); }
/* The esc clock rate is supposed to always be 100Mhz. */ @@ -1600,17 +1600,18 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) DRM_MODE_ENCODER_DSI, NULL); drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
- ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL); - if (ret) { - dev_err(dev, "bridge attach failed: %d\n", ret); - return ret; - } /* Disable the atomic helper calls into the bridge. We * manually call the bridge pre_enable / enable / etc. calls * from our driver, since we need to sequence them within the * encoder's enable/disable paths. */ - dsi->encoder->bridge = NULL; + dsi->encoder->custom_bridge_enable_disable_seq = 1; + + ret = drm_bridge_attach(dsi->encoder, bridge, NULL); + if (ret) { + dev_err(dev, "bridge attach failed: %d\n", ret); + return ret; + }
if (dsi->port == 0) vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset); @@ -1629,7 +1630,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dsi *dsi = dev_get_drvdata(dev);
- if (dsi->bridge) + if (dsi->encoder->bridge) pm_runtime_disable(dev);
vc4_dsi_encoder_destroy(dsi->encoder);
Boris Brezillon boris.brezillon@collabora.com writes:
Patch 2, 3 are:
Reviewed-by: Eric Anholt eric@anholt.net
Now that we have an official way to request custom encoder/bridge enable/disable sequences we can get rid of the extra ->out_bridge field and use the encoder one.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 72726f2c7a9f..8593c87bdf96 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -255,7 +255,6 @@ struct exynos_dsi { struct mipi_dsi_host dsi_host; struct drm_connector connector; struct drm_panel *panel; - struct drm_bridge *out_bridge; struct device *dev;
void __iomem *reg_base; @@ -1390,7 +1389,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) if (ret < 0) goto err_put_sync; } else { - drm_bridge_pre_enable(dsi->out_bridge); + drm_bridge_pre_enable(encoder->bridge); }
exynos_dsi_set_display_mode(dsi); @@ -1401,7 +1400,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) if (ret < 0) goto err_display_disable; } else { - drm_bridge_enable(dsi->out_bridge); + drm_bridge_enable(encoder->bridge); }
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE; @@ -1426,10 +1425,10 @@ static void exynos_dsi_disable(struct drm_encoder *encoder) dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
drm_panel_disable(dsi->panel); - drm_bridge_disable(dsi->out_bridge); + drm_bridge_disable(encoder->bridge); exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel); - drm_bridge_post_disable(dsi->out_bridge); + drm_bridge_post_disable(encoder->bridge); dsi->state &= ~DSIM_STATE_ENABLED; pm_runtime_put_sync(dsi->dev); } @@ -1521,8 +1520,6 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, out_bridge = of_drm_find_bridge(device->dev.of_node); if (out_bridge) { drm_bridge_attach(encoder, out_bridge, NULL); - dsi->out_bridge = out_bridge; - encoder->bridge = NULL; } else { int ret = exynos_dsi_create_connector(encoder);
@@ -1584,10 +1581,6 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, dsi->panel = NULL; dsi->connector.status = connector_status_disconnected; mutex_unlock(&drm->mode_config.mutex); - } else { - if (dsi->out_bridge->funcs->detach) - dsi->out_bridge->funcs->detach(dsi->out_bridge); - dsi->out_bridge = NULL; }
if (drm->mode_config.poll_enabled) @@ -1686,7 +1679,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); - + encoder->custom_bridge_enable_disable_seq = 1; drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
bridge->next is only points to the new bridge if drm_bridge_attach() succeeds. No need to reset it manually here.
Note that this change is part of the attempt to make the bridge chain a double-linked list. In order to do that we must patch all drivers manipulating the bridge->next field.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/exynos/exynos_dp.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 1e6aa24bf45e..4785885c0f4f 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -110,7 +110,6 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data, if (ret) { DRM_DEV_ERROR(dp->dev, "Failed to attach bridge to drm\n"); - bridge->next = NULL; return ret; } }
On Thu, 8 Aug 2019 17:11:36 +0200 Boris Brezillon boris.brezillon@collabora.com wrote:
bridge->next is only points to the new bridge if drm_bridge_attach()
^/is//
Hi Boris,
On Thu, Aug 08, 2019 at 05:11:36PM +0200, Boris Brezillon wrote:
This looks good to me as a cleanup, and I think you can already push it without waiting for the whole series to be ready.
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
DRM bridges should not be operated independently. Let's rename the drm_bridge_xxx() helpers that take the first bridge of the chain and iterate over the whole chain into drm_bridge_chain_xx(). We also pass it the encoder containing the bridge chain instead of the dereferencing encoder->bridge.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_atomic_helper.c | 19 +- drivers/gpu/drm/drm_bridge.c | 365 +++++++++++++----------- drivers/gpu/drm/drm_crtc_helper.c | 27 +- drivers/gpu/drm/drm_probe_helper.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 8 +- drivers/gpu/drm/mediatek/mtk_hdmi.c | 4 +- drivers/gpu/drm/vc4/vc4_dsi.c | 8 +- include/drm/drm_bridge.h | 64 +++-- 8 files changed, 273 insertions(+), 224 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 15ea61877122..0deb54099570 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -445,8 +445,9 @@ mode_fixup(struct drm_atomic_state *state) encoder = new_conn_state->best_encoder; funcs = encoder->helper_private;
- ret = drm_bridge_mode_fixup(encoder->bridge, &new_crtc_state->mode, - &new_crtc_state->adjusted_mode); + ret = drm_bridge_chain_mode_fixup(encoder, + &new_crtc_state->mode, + &new_crtc_state->adjusted_mode); if (!ret) { DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); return -EINVAL; @@ -511,7 +512,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector, return ret; }
- ret = drm_bridge_mode_valid(encoder->bridge, mode); + ret = drm_bridge_chain_mode_valid(encoder, mode); if (ret != MODE_OK) { DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); return ret; @@ -1031,7 +1032,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) * it away), so we won't call disable hooks twice. */ if (!encoder->custom_bridge_enable_disable_seq) - drm_atomic_bridge_disable(encoder->bridge, old_state); + drm_atomic_bridge_chain_disable(encoder, old_state);
/* Right function depends upon target state. */ if (funcs) { @@ -1046,8 +1047,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) }
if (!encoder->custom_bridge_enable_disable_seq) - drm_atomic_bridge_post_disable(encoder->bridge, - old_state); + drm_atomic_bridge_chain_post_disable(encoder, + old_state); }
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { @@ -1228,7 +1229,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->mode_set(encoder, mode, adjusted_mode); }
- drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); + drm_bridge_chain_mode_set(encoder, mode, adjusted_mode); } }
@@ -1346,7 +1347,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, * it away), so we won't call enable hooks twice. */ if (!encoder->custom_bridge_enable_disable_seq) - drm_atomic_bridge_pre_enable(encoder->bridge, old_state); + drm_atomic_bridge_chain_pre_enable(encoder, old_state);
if (funcs) { if (funcs->atomic_enable) @@ -1358,7 +1359,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, }
if (!encoder->custom_bridge_enable_disable_seq) - drm_atomic_bridge_enable(encoder->bridge, old_state); + drm_atomic_bridge_chain_enable(encoder, old_state); }
drm_atomic_helper_commit_writebacks(dev, old_state); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index cba537c99e43..2bf9a19e11bf 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -171,24 +171,9 @@ void drm_bridge_detach(struct drm_bridge *bridge) * For detailed specification of the bridge callbacks see &drm_bridge_funcs. */
-/** - * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the - * encoder chain - * @bridge: bridge control structure - * @mode: desired mode to be set for the bridge - * @adjusted_mode: updated mode that works for this bridge - * - * Calls &drm_bridge_funcs.mode_fixup for all the bridges in the - * encoder chain, starting from the first bridge to the last. - * - * Note: the bridge passed should be the one closest to the encoder - * - * RETURNS: - * true on success, false on failure - */ -bool drm_bridge_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static bool drm_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { bool ret = true;
@@ -202,25 +187,10 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
return ret; } -EXPORT_SYMBOL(drm_bridge_mode_fixup);
-/** - * drm_bridge_mode_valid - validate the mode against all bridges in the - * encoder chain. - * @bridge: bridge control structure - * @mode: desired mode to be validated - * - * Calls &drm_bridge_funcs.mode_valid for all the bridges in the encoder - * chain, starting from the first bridge to the last. If at least one bridge - * does not accept the mode the function returns the error code. - * - * Note: the bridge passed should be the one closest to the encoder. - * - * RETURNS: - * MODE_OK on success, drm_mode_status Enum error code on failure - */ -enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_mode *mode) +static enum drm_mode_status +drm_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) { enum drm_mode_status ret = MODE_OK;
@@ -235,19 +205,8 @@ enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge,
return drm_bridge_mode_valid(bridge->next, mode); } -EXPORT_SYMBOL(drm_bridge_mode_valid);
-/** - * drm_bridge_disable - disables all bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called before - * calling the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_disable(struct drm_bridge *bridge) +static void drm_bridge_disable(struct drm_bridge *bridge) { if (!bridge) return; @@ -257,19 +216,8 @@ void drm_bridge_disable(struct drm_bridge *bridge) if (bridge->funcs->disable) bridge->funcs->disable(bridge); } -EXPORT_SYMBOL(drm_bridge_disable);
-/** - * drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.post_disable op for all the bridges in the - * encoder chain, starting from the first bridge to the last. These are called - * after completing the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_post_disable(struct drm_bridge *bridge) +static void drm_bridge_post_disable(struct drm_bridge *bridge) { if (!bridge) return; @@ -279,23 +227,10 @@ void drm_bridge_post_disable(struct drm_bridge *bridge)
drm_bridge_post_disable(bridge->next); } -EXPORT_SYMBOL(drm_bridge_post_disable);
-/** - * drm_bridge_mode_set - set proposed mode for all bridges in the - * encoder chain - * @bridge: bridge control structure - * @mode: desired mode to be set for the bridge - * @adjusted_mode: updated mode that works for this bridge - * - * Calls &drm_bridge_funcs.mode_set op for all the bridges in the - * encoder chain, starting from the first bridge to the last. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode) +static void drm_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) { if (!bridge) return; @@ -305,20 +240,8 @@ void drm_bridge_mode_set(struct drm_bridge *bridge,
drm_bridge_mode_set(bridge->next, mode, adjusted_mode); } -EXPORT_SYMBOL(drm_bridge_mode_set);
-/** - * drm_bridge_pre_enable - prepares for enabling all - * bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called - * before calling the encoder's commit op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_pre_enable(struct drm_bridge *bridge) +static void drm_bridge_pre_enable(struct drm_bridge *bridge) { if (!bridge) return; @@ -328,19 +251,8 @@ void drm_bridge_pre_enable(struct drm_bridge *bridge) if (bridge->funcs->pre_enable) bridge->funcs->pre_enable(bridge); } -EXPORT_SYMBOL(drm_bridge_pre_enable);
-/** - * drm_bridge_enable - enables all bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder - * chain, starting from the first bridge to the last. These are called - * after completing the encoder's commit op. - * - * Note that the bridge passed should be the one closest to the encoder - */ -void drm_bridge_enable(struct drm_bridge *bridge) +static void drm_bridge_enable(struct drm_bridge *bridge) { if (!bridge) return; @@ -350,22 +262,127 @@ void drm_bridge_enable(struct drm_bridge *bridge)
drm_bridge_enable(bridge->next); } -EXPORT_SYMBOL(drm_bridge_enable);
/** - * drm_atomic_bridge_disable - disables all bridges in the encoder chain - * @bridge: bridge control structure - * @state: atomic state being committed + * drm_bridge_chain_mode_fixup - fixup proposed mode for all bridges in the + * encoder chain + * @encoder: encoder object + * @mode: desired mode to be set for the bridge + * @adjusted_mode: updated mode that works for this bridge * - * Calls &drm_bridge_funcs.atomic_disable (falls back on - * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain, - * starting from the last bridge to the first. These are called before calling - * &drm_encoder_helper_funcs.atomic_disable + * Calls &drm_bridge_funcs.mode_fixup for all the bridges in the + * encoder chain, starting from the first bridge to the last. * - * Note: the bridge passed should be the one closest to the encoder + * RETURNS: + * true on success, false on failure */ -void drm_atomic_bridge_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +bool drm_bridge_chain_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return drm_bridge_mode_fixup(encoder->bridge, mode, adjusted_mode); +} +EXPORT_SYMBOL(drm_bridge_chain_mode_fixup); + +/** + * drm_bridge_chain_mode_valid - validate the mode against all bridges in the + * encoder chain. + * @encoder: encoder object + * @mode: desired mode to be validated + * + * Calls &drm_bridge_funcs.mode_valid for all the bridges in the encoder + * chain, starting from the first bridge to the last. If at least one bridge + * does not accept the mode the function returns the error code. + * + * RETURNS: + * MODE_OK on success, drm_mode_status Enum error code on failure + */ +enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + return drm_bridge_mode_valid(encoder->bridge, mode); +} +EXPORT_SYMBOL(drm_bridge_chain_mode_valid); + +/** + * drm_bridge_chain_disable - disables all bridges in the encoder chain + * @encoder: encoder object + * + * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder + * chain, starting from the last bridge to the first. These are called before + * calling the encoder's prepare op. + */ +void drm_bridge_chain_disable(struct drm_encoder *encoder) +{ + drm_bridge_disable(encoder->bridge); +} +EXPORT_SYMBOL(drm_bridge_chain_disable); + +/** + * drm_bridge_chain_post_disable - cleans up after disabling all bridges in the + * encoder chain + * @encoder: encoder object + * + * Calls &drm_bridge_funcs.post_disable op for all the bridges in the + * encoder chain, starting from the first bridge to the last. These are called + * after completing the encoder's prepare op. + */ +void drm_bridge_chain_post_disable(struct drm_encoder *encoder) +{ + drm_bridge_post_disable(encoder->bridge); +} +EXPORT_SYMBOL(drm_bridge_chain_post_disable); + +/** + * drm_bridge_chain_mode_set - set proposed mode for all bridges in the + * encoder chain + * @encoder: encoder object + * @mode: desired mode to be set for the encoder chain + * @adjusted_mode: updated mode that works for this encoder chain + * + * Calls &drm_bridge_funcs.mode_set op for all the bridges in the + * encoder chain, starting from the first bridge to the last. + */ +void drm_bridge_chain_mode_set(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); +} +EXPORT_SYMBOL(drm_bridge_chain_mode_set); + +/** + * drm_bridge_chain_pre_enable - prepares for enabling all bridges in the + * encoder chain + * @encoder: encoder object + * + * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder + * chain, starting from the last bridge to the first. These are called + * before calling the encoder's commit op. + */ +void drm_bridge_chain_pre_enable(struct drm_encoder *encoder) +{ + drm_bridge_pre_enable(encoder->bridge); +} +EXPORT_SYMBOL(drm_bridge_chain_pre_enable); + +/** + * drm_bridge_chain_enable - enables all bridges in the encoder chain + * @encoder: encoder object + * + * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder + * chain, starting from the first bridge to the last. These are called + * after completing the encoder's commit op. + */ +void drm_bridge_chain_enable(struct drm_encoder *encoder) +{ + drm_bridge_enable(encoder->bridge); +} +EXPORT_SYMBOL(drm_bridge_chain_enable); + +static void drm_atomic_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return; @@ -377,23 +394,9 @@ void drm_atomic_bridge_disable(struct drm_bridge *bridge, else if (bridge->funcs->disable) bridge->funcs->disable(bridge); } -EXPORT_SYMBOL(drm_atomic_bridge_disable);
-/** - * drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the - * encoder chain - * @bridge: bridge control structure - * @state: atomic state being committed - * - * Calls &drm_bridge_funcs.atomic_post_disable (falls back on - * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain, - * starting from the first bridge to the last. These are called after completing - * &drm_encoder_helper_funcs.atomic_disable - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return; @@ -405,23 +408,9 @@ void drm_atomic_bridge_post_disable(struct drm_bridge *bridge,
drm_atomic_bridge_post_disable(bridge->next, state); } -EXPORT_SYMBOL(drm_atomic_bridge_post_disable);
-/** - * drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the - * encoder chain - * @bridge: bridge control structure - * @state: atomic state being committed - * - * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on - * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain, - * starting from the last bridge to the first. These are called before calling - * &drm_encoder_helper_funcs.atomic_enable - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return; @@ -433,22 +422,9 @@ void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, else if (bridge->funcs->pre_enable) bridge->funcs->pre_enable(bridge); } -EXPORT_SYMBOL(drm_atomic_bridge_pre_enable);
-/** - * drm_atomic_bridge_enable - enables all bridges in the encoder chain - * @bridge: bridge control structure - * @state: atomic state being committed - * - * Calls &drm_bridge_funcs.atomic_enable (falls back on - * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain, - * starting from the first bridge to the last. These are called after completing - * &drm_encoder_helper_funcs.atomic_enable - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_atomic_bridge_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void drm_atomic_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return; @@ -460,7 +436,76 @@ void drm_atomic_bridge_enable(struct drm_bridge *bridge,
drm_atomic_bridge_enable(bridge->next, state); } -EXPORT_SYMBOL(drm_atomic_bridge_enable); + +/** + * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain + * @encoder: encoder object + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_disable (falls back on + * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_disable + */ +void drm_atomic_bridge_chain_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + drm_atomic_bridge_disable(encoder->bridge, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); + +/** + * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges + * in the encoder chain + * @encoder: encoder object + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_post_disable (falls back on + * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain, + * starting from the first bridge to the last. These are called after completing + * &drm_encoder_helper_funcs.atomic_disable + */ +void drm_atomic_bridge_chain_post_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + drm_atomic_bridge_post_disable(encoder->bridge, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); + +/** + * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in + * the encoder chain + * @encoder: encoder object + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on + * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_enable + */ +void drm_atomic_bridge_chain_pre_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + drm_atomic_bridge_pre_enable(encoder->bridge, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); + +/** + * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain + * @encoder: encoder object + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_enable (falls back on + * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain, + * starting from the first bridge to the last. These are called after completing + * &drm_encoder_helper_funcs.atomic_enable + */ +void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + drm_atomic_bridge_enable(encoder->bridge, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
#ifdef CONFIG_OF /** diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 87dae37fec12..f8a361d396df 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -161,7 +161,7 @@ drm_encoder_disable(struct drm_encoder *encoder) return;
if (!encoder->custom_bridge_enable_disable_seq) - drm_bridge_disable(encoder->bridge); + drm_bridge_chain_disable(encoder);
if (encoder_funcs->disable) (*encoder_funcs->disable)(encoder); @@ -169,7 +169,7 @@ drm_encoder_disable(struct drm_encoder *encoder) (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
if (!encoder->custom_bridge_enable_disable_seq) - drm_bridge_post_disable(encoder->bridge); + drm_bridge_chain_post_disable(encoder); }
static void __drm_helper_disable_unused_functions(struct drm_device *dev) @@ -329,8 +329,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (!encoder_funcs) continue;
- ret = drm_bridge_mode_fixup(encoder->bridge, - mode, adjusted_mode); + ret = drm_bridge_chain_mode_fixup(encoder, mode, + adjusted_mode); if (!ret) { DRM_DEBUG_KMS("Bridge fixup failed\n"); goto done; @@ -368,14 +368,14 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, continue;
if (!encoder->custom_bridge_enable_disable_seq) - drm_bridge_disable(encoder->bridge); + drm_bridge_chain_disable(encoder);
/* Disable the encoders as the first thing we do. */ if (encoder_funcs->prepare) encoder_funcs->prepare(encoder);
if (!encoder->custom_bridge_enable_disable_seq) - drm_bridge_post_disable(encoder->bridge); + drm_bridge_chain_post_disable(encoder); }
drm_crtc_prepare_encoders(dev); @@ -403,7 +403,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (encoder_funcs->mode_set) encoder_funcs->mode_set(encoder, mode, adjusted_mode);
- drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); + drm_bridge_chain_mode_set(encoder, mode, adjusted_mode); }
/* Now enable the clocks, plane, pipe, and connectors that we set up. */ @@ -419,13 +419,13 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, continue;
if (!encoder->custom_bridge_enable_disable_seq) - drm_bridge_pre_enable(encoder->bridge); + drm_bridge_chain_pre_enable(encoder);
if (encoder_funcs->commit) encoder_funcs->commit(encoder);
if (!encoder->custom_bridge_enable_disable_seq) - drm_bridge_enable(encoder->bridge); + drm_bridge_chain_enable(encoder); }
/* Calculate and store various constants which @@ -824,7 +824,6 @@ static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) /* Helper which handles bridge ordering around encoder dpms */ static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) { - struct drm_bridge *bridge = encoder->bridge; const struct drm_encoder_helper_funcs *encoder_funcs;
encoder_funcs = encoder->helper_private; @@ -833,9 +832,9 @@ static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode)
if (!encoder->custom_bridge_enable_disable_seq) { if (mode == DRM_MODE_DPMS_ON) - drm_bridge_pre_enable(bridge); + drm_bridge_chain_pre_enable(encoder); else - drm_bridge_disable(bridge); + drm_bridge_chain_disable(encoder); }
if (encoder_funcs->dpms) @@ -843,9 +842,9 @@ static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode)
if (!encoder->custom_bridge_enable_disable_seq) { if (mode == DRM_MODE_DPMS_ON) - drm_bridge_enable(bridge); + drm_bridge_chain_enable(encoder); else - drm_bridge_post_disable(bridge); + drm_bridge_chain_post_disable(encoder); } }
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 351cbc40f0f8..461199bc28c0 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -113,7 +113,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, continue; }
- ret = drm_bridge_mode_valid(encoder->bridge, mode); + ret = drm_bridge_chain_mode_valid(encoder, mode); if (ret != MODE_OK) { /* There is also no point in continuing for crtc check * here. */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 8593c87bdf96..9a96bc48b81f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1389,7 +1389,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) if (ret < 0) goto err_put_sync; } else { - drm_bridge_pre_enable(encoder->bridge); + drm_bridge_chain_pre_enable(encoder); }
exynos_dsi_set_display_mode(dsi); @@ -1400,7 +1400,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) if (ret < 0) goto err_display_disable; } else { - drm_bridge_enable(encoder->bridge); + drm_bridge_chain_enable(encoder); }
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE; @@ -1425,10 +1425,10 @@ static void exynos_dsi_disable(struct drm_encoder *encoder) dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
drm_panel_disable(dsi->panel); - drm_bridge_disable(encoder->bridge); + drm_bridge_chain_disable(encoder); exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel); - drm_bridge_post_disable(encoder->bridge); + drm_bridge_chain_post_disable(encoder); dsi->state &= ~DSIM_STATE_ENABLED; pm_runtime_put_sync(dsi->dev); } diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index c79b1f855d89..6ec3d2584539 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1247,8 +1247,8 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, struct drm_display_mode adjusted_mode;
drm_mode_copy(&adjusted_mode, mode); - if (!drm_bridge_mode_fixup(hdmi->bridge.next, mode, - &adjusted_mode)) + if (!drm_bridge_chain_mode_fixup(hdmi->bridge.encoder, mode, + &adjusted_mode)) return MODE_BAD; }
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index b670ca473786..d7cd720c4efa 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -752,9 +752,9 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) struct vc4_dsi *dsi = vc4_encoder->dsi; struct device *dev = &dsi->pdev->dev;
- drm_bridge_disable(encoder->bridge); + drm_bridge_chain_disable(encoder); vc4_dsi_ulps(dsi, true); - drm_bridge_post_disable(encoder->bridge); + drm_bridge_chain_post_disable(encoder);
clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->escape_clock); @@ -1054,7 +1054,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
vc4_dsi_ulps(dsi, false);
- drm_bridge_pre_enable(encoder->bridge); + drm_bridge_chain_pre_enable(encoder);
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { DSI_PORT_WRITE(DISP0_CTRL, @@ -1071,7 +1071,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) DSI_DISP0_ENABLE); }
- drm_bridge_enable(encoder->bridge); + drm_bridge_chain_enable(encoder);
if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&dsi->pdev->dev); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 7616f6562fe4..3057e2153f62 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -254,9 +254,10 @@ struct drm_bridge_funcs { * there is one) when this callback is called. * * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from &drm_bridge_pre_enable. It - * would be prudent to also provide an implementation of @pre_enable if - * you are expecting driver calls into &drm_bridge_pre_enable. + * atomic commit. It will not be invoked from + * &drm_bridge_chain_pre_enable. It would be prudent to also provide an + * implementation of @pre_enable if you are expecting driver calls into + * &drm_bridge_chain_pre_enable. * * The @atomic_pre_enable callback is optional. */ @@ -279,9 +280,9 @@ struct drm_bridge_funcs { * chain if there is one. * * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from &drm_bridge_enable. It - * would be prudent to also provide an implementation of @enable if - * you are expecting driver calls into &drm_bridge_enable. + * atomic commit. It will not be invoked from &drm_bridge_chain_enable. + * It would be prudent to also provide an implementation of @enable if + * you are expecting driver calls into &drm_bridge_chain_enable. * * The enable callback is optional. */ @@ -301,9 +302,10 @@ struct drm_bridge_funcs { * signals) feeding it is still running when this callback is called. * * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from &drm_bridge_disable. It - * would be prudent to also provide an implementation of @disable if - * you are expecting driver calls into &drm_bridge_disable. + * atomic commit. It will not be invoked from + * &drm_bridge_chain_disable. It would be prudent to also provide an + * implementation of @disable if you are expecting driver calls into + * &drm_bridge_chain_disable. * * The disable callback is optional. */ @@ -325,10 +327,11 @@ struct drm_bridge_funcs { * called. * * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from &drm_bridge_post_disable. + * atomic commit. It will not be invoked from + * &drm_bridge_chain_post_disable. * It would be prudent to also provide an implementation of * @post_disable if you are expecting driver calls into - * &drm_bridge_post_disable. + * &drm_bridge_chain_post_disable. * * The post_disable callback is optional. */ @@ -406,27 +409,28 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, struct drm_bridge *previous);
-bool drm_bridge_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_mode *mode); -void drm_bridge_disable(struct drm_bridge *bridge); -void drm_bridge_post_disable(struct drm_bridge *bridge); -void drm_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode); -void drm_bridge_pre_enable(struct drm_bridge *bridge); -void drm_bridge_enable(struct drm_bridge *bridge); +bool drm_bridge_chain_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode); +void drm_bridge_chain_disable(struct drm_encoder *encoder); +void drm_bridge_chain_post_disable(struct drm_encoder *encoder); +void drm_bridge_chain_mode_set(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode); +void drm_bridge_chain_pre_enable(struct drm_encoder *encoder); +void drm_bridge_chain_enable(struct drm_encoder *encoder);
-void drm_atomic_bridge_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state); -void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, +void drm_atomic_bridge_chain_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_post_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_pre_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, struct drm_atomic_state *state); -void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state); -void drm_atomic_bridge_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state);
#ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:37PM +0200, Boris Brezillon wrote:
I'm not sure about this. I do agree that the helpers operate on a chain, but I think they're use for calling them on any bridge, especially in conjunction with your work. The way I see it is that when a bridge in the chain needs a custom enable/disable sequence (flagged by some kind of flag in the bridge structure), the helpers will not automatically propagate the calls down the chain, and let the bridge call the pre/post enable/disable operations on the downstream bridge. This means that those bridges with special needs will have to call the helpers on the next bridge down the chain, and thus require keeping the ability to do so.
We could of course propose two sets of helpers, one taking a bridge pointer, and another one taking an encoder pointer, but I think it's a bit overkill. Especially if you consider my comments earlier in this series where I propose moving the custom sequence feature to bridges instead of encoders, I don't think this patch will be needed.
On Wed, 21 Aug 2019 17:45:04 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Also changed my mind on this one after the discussion we had. I kept the rename part but dropper the s/bridge/encoder/ change. This way, bridges that need to control the enable/disable sequence can use those helpers on a sub-chain (the chain starting at the bridge element just after them).
Agreed. As said above, I kept the rename part of this patch because I think it better reflects the fact that those helpers are acting on a bridge chain, and not a single bridge element.
This is part of our attempt to make the bridge chain a double-linked list based on the generic list helpers. In order to do that, we must patch all drivers manipulating the encoder->bridge field directly.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/msm/edp/edp.c | 4 +++- drivers/gpu/drm/msm/hdmi/hdmi.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c index 0f312ac5b624..ad4e963ccd9b 100644 --- a/drivers/gpu/drm/msm/edp/edp.c +++ b/drivers/gpu/drm/msm/edp/edp.c @@ -178,7 +178,9 @@ int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev, goto fail; }
- encoder->bridge = edp->bridge; + ret = drm_bridge_attach(encoder, edp->bridge, NULL); + if (ret) + goto fail;
priv->bridges[priv->num_bridges++] = edp->bridge; priv->connectors[priv->num_connectors++] = edp->connector; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 0e4217be3f00..55b9a8c8312b 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -327,7 +327,9 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi, goto fail; }
- encoder->bridge = hdmi->bridge; + ret = drm_bridge_attach(encoder, hdmi->bridge, NULL); + if (ret) + goto fail;
priv->bridges[priv->num_bridges++] = hdmi->bridge; priv->connectors[priv->num_connectors++] = hdmi->connector;
On Thu, Aug 08, 2019 at 05:11:38PM +0200, Boris Brezillon wrote:
Reviewed-by: Sam Ravnborg sam@ravnborg.org
Sean, this patch looks like a nice cleanup we can apply outside the series. It would be good that drivers do not poke direct in the encoder data that this patch fixes.
Sam
On Mon, Aug 19, 2019 at 07:19:39PM +0200, Sam Ravnborg wrote:
Agreed, and
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
And use them in drivers accessing the encoder->bridge or bridge->next fields directly. This is part of our attempt to make the bridge chain a double-linked list based on the generic list helpers.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_bridge.c | 52 ++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_hdmi.c | 2 +- drivers/gpu/drm/msm/edp/edp_bridge.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 7 ++-- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 +- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 10 +++-- drivers/gpu/drm/vc4/vc4_dsi.c | 4 +- include/drm/drm_bridge.h | 6 +++ 8 files changed, 75 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 2bf9a19e11bf..b96bbba05ba9 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -160,6 +160,58 @@ void drm_bridge_detach(struct drm_bridge *bridge) bridge->dev = NULL; }
+/** + * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain + * @encoder: encoder object + * + * RETURNS: + * the first bridge in the encoder chain, or NULL if there's no bridge + * attached to this encoder. + */ +struct drm_bridge * +drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) +{ + return encoder->bridge; +} +EXPORT_SYMBOL(drm_bridge_chain_get_first_bridge); + +/** + * drm_bridge_chain_get_last_bridge() - Get the last bridge in the chain + * @encoder: encoder object + * + * RETURNS: + * the last bridge in the encoder chain, or NULL if there's no bridge + * attached to this encoder. + */ +struct drm_bridge * +drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder) +{ + struct drm_bridge *bridge = encoder->bridge; + + while(bridge && bridge->next) + bridge = bridge->next; + + return bridge; +} +EXPORT_SYMBOL(drm_bridge_chain_get_last_bridge); + +/** + * drm_bridge_chain_get_next_bridge() - Get the next bridge in the chain + * @bridge: bridge object + * + * RETURNS: + * the next bridge in the chain, or NULL if there's @bridge is the last. + */ +struct drm_bridge * +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) +{ + if (!bridge || !bridge->encoder) + return NULL; + + return bridge->next; +} +EXPORT_SYMBOL(drm_bridge_chain_get_next_bridge); + /** * DOC: bridge callbacks * diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 6ec3d2584539..17e6d79303e0 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1243,7 +1243,7 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, mode->hdisplay, mode->vdisplay, mode->vrefresh, !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
- if (hdmi->bridge.next) { + if (drm_bridge_chain_get_next_bridge(&hdmi->bridge)) { struct drm_display_mode adjusted_mode;
drm_mode_copy(&adjusted_mode, mode); diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c index 2950bba4aca9..6d2d405580d2 100644 --- a/drivers/gpu/drm/msm/edp/edp_bridge.c +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c @@ -56,7 +56,7 @@ static void edp_bridge_mode_set(struct drm_bridge *bridge,
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if ((connector->encoder != NULL) && - (connector->encoder->bridge == bridge)) { + (drm_bridge_chain_get_first_bridge(connector->encoder) == bridge)) { msm_edp_ctrl_timing_cfg(edp->ctrl, adjusted_mode, &connector->display_info); break; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 224ec6fdc800..41fb98a9141b 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -215,11 +215,10 @@ static int omap_display_id(struct omap_dss_device *output) node = display->dev->of_node; omapdss_device_put(display); } else if (output->bridge) { - struct drm_bridge *bridge = output->bridge; - - while (bridge->next) - bridge = bridge->next; + struct drm_encoder *encoder = output->bridge->encoder; + struct drm_bridge *bridge;
+ bridge = drm_bridge_chain_get_last_bridge(encoder); node = bridge->of_node; } else if (output->panel) { node = output->panel->dev->of_node; diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 24bbe9f2a32e..727a6bea3807 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -126,7 +126,8 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, for (dssdev = output; dssdev; dssdev = dssdev->next) omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
- for (bridge = output->bridge; bridge; bridge = bridge->next) { + for (bridge = drm_bridge_chain_get_first_bridge(encoder); + bridge; bridge = drm_bridge_chain_get_next_bridge(bridge)) { if (!bridge->timings) continue;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 2da46e3dc4ae..f2ae4c410244 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -14,6 +14,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_device.h> #include <drm/drm_fb_cma_helper.h> @@ -680,9 +681,10 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index]; const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct drm_bridge *bridge;
- rcar_lvds_clk_enable(encoder->base.bridge, - mode->clock * 1000); + bridge = drm_bridge_chain_get_first_bridge(&encoder->base); + rcar_lvds_clk_enable(bridge, mode->clock * 1000); }
rcar_du_crtc_start(rcrtc); @@ -702,12 +704,14 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { struct rcar_du_encoder *encoder = rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index]; + struct drm_bridge *bridge;
/* * Disable the LVDS clock output, see * rcar_du_crtc_atomic_enable(). */ - rcar_lvds_clk_disable(encoder->base.bridge); + bridge = drm_bridge_chain_get_first_bridge(&encoder->base); + rcar_lvds_clk_disable(bridge); }
spin_lock_irq(&crtc->dev->event_lock); diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index d7cd720c4efa..a1698417927d 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1629,8 +1629,10 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dsi *dsi = dev_get_drvdata(dev); + struct drm_bridge *bridge;
- if (dsi->encoder->bridge) + bridge = drm_bridge_chain_get_first_bridge(dsi->encoder); + if (bridge) pm_runtime_disable(dev);
vc4_dsi_encoder_destroy(dsi->encoder); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3057e2153f62..d429bd1c00f9 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -409,6 +409,12 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, struct drm_bridge *previous);
+struct drm_bridge * +drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder); +struct drm_bridge * +drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder); +struct drm_bridge * +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge); bool drm_bridge_chain_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
So each element in the chain can easily access its predecessor. This will be needed for the work we are doing on bus format negociation between elements of the bridge chain.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_bridge.c | 278 +++++++++++++--------------------- drivers/gpu/drm/drm_encoder.c | 15 +- include/drm/drm_bridge.h | 4 +- include/drm/drm_encoder.h | 4 +- 4 files changed, 116 insertions(+), 185 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index b96bbba05ba9..ed2410f49581 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -55,7 +55,7 @@ * just provide additional hooks to get the desired output at the end of the * encoder chain. * - * Bridges can also be chained up using the &drm_bridge.next pointer. + * Bridges can also be chained up using the &drm_bridge.chain_node field. * * Both legacy CRTC helpers and the new atomic modeset helpers support bridges. */ @@ -114,6 +114,7 @@ EXPORT_SYMBOL(drm_bridge_remove); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, struct drm_bridge *previous) { + LIST_HEAD(tmp_list); int ret;
if (!encoder || !bridge) @@ -127,6 +128,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
bridge->dev = encoder->dev; bridge->encoder = encoder; + list_add_tail(&bridge->chain_node, &tmp_list);
if (bridge->funcs->attach) { ret = bridge->funcs->attach(bridge); @@ -138,9 +140,9 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, }
if (previous) - previous->next = bridge; + list_splice(&tmp_list, &previous->chain_node); else - encoder->bridge = bridge; + list_splice(&tmp_list, &encoder->bridge_chain);
return 0; } @@ -157,6 +159,7 @@ void drm_bridge_detach(struct drm_bridge *bridge) if (bridge->funcs->detach) bridge->funcs->detach(bridge);
+ list_del(&bridge->chain_node); bridge->dev = NULL; }
@@ -171,7 +174,8 @@ void drm_bridge_detach(struct drm_bridge *bridge) struct drm_bridge * drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) { - return encoder->bridge; + return list_first_entry_or_null(&encoder->bridge_chain, + struct drm_bridge, chain_node); } EXPORT_SYMBOL(drm_bridge_chain_get_first_bridge);
@@ -186,12 +190,11 @@ EXPORT_SYMBOL(drm_bridge_chain_get_first_bridge); struct drm_bridge * drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder) { - struct drm_bridge *bridge = encoder->bridge; + if (list_empty(&encoder->bridge_chain)) + return NULL;
- while(bridge && bridge->next) - bridge = bridge->next; - - return bridge; + return list_last_entry(&encoder->bridge_chain, struct drm_bridge, + chain_node); } EXPORT_SYMBOL(drm_bridge_chain_get_last_bridge);
@@ -205,10 +208,11 @@ EXPORT_SYMBOL(drm_bridge_chain_get_last_bridge); struct drm_bridge * drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) { - if (!bridge || !bridge->encoder) - return NULL; + if (!bridge || !bridge->encoder || + list_is_last(&bridge->encoder->bridge_chain, &bridge->chain_node)) + return NULL;
- return bridge->next; + return list_next_entry(bridge, chain_node); } EXPORT_SYMBOL(drm_bridge_chain_get_next_bridge);
@@ -223,98 +227,6 @@ EXPORT_SYMBOL(drm_bridge_chain_get_next_bridge); * For detailed specification of the bridge callbacks see &drm_bridge_funcs. */
-static bool drm_bridge_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - bool ret = true; - - if (!bridge) - return true; - - if (bridge->funcs->mode_fixup) - ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode); - - ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode); - - return ret; -} - -static enum drm_mode_status -drm_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_mode *mode) -{ - enum drm_mode_status ret = MODE_OK; - - if (!bridge) - return ret; - - if (bridge->funcs->mode_valid) - ret = bridge->funcs->mode_valid(bridge, mode); - - if (ret != MODE_OK) - return ret; - - return drm_bridge_mode_valid(bridge->next, mode); -} - -static void drm_bridge_disable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - drm_bridge_disable(bridge->next); - - if (bridge->funcs->disable) - bridge->funcs->disable(bridge); -} - -static void drm_bridge_post_disable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - if (bridge->funcs->post_disable) - bridge->funcs->post_disable(bridge); - - drm_bridge_post_disable(bridge->next); -} - -static void drm_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode) -{ - if (!bridge) - return; - - if (bridge->funcs->mode_set) - bridge->funcs->mode_set(bridge, mode, adjusted_mode); - - drm_bridge_mode_set(bridge->next, mode, adjusted_mode); -} - -static void drm_bridge_pre_enable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - drm_bridge_pre_enable(bridge->next); - - if (bridge->funcs->pre_enable) - bridge->funcs->pre_enable(bridge); -} - -static void drm_bridge_enable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - if (bridge->funcs->enable) - bridge->funcs->enable(bridge); - - drm_bridge_enable(bridge->next); -} - /** * drm_bridge_chain_mode_fixup - fixup proposed mode for all bridges in the * encoder chain @@ -332,7 +244,17 @@ bool drm_bridge_chain_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - return drm_bridge_mode_fixup(encoder->bridge, mode, adjusted_mode); + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (!bridge->funcs->mode_fixup) + continue; + + if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode)) + return false; + } + + return true; } EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
@@ -353,7 +275,20 @@ enum drm_mode_status drm_bridge_chain_mode_valid(struct drm_encoder *encoder, const struct drm_display_mode *mode) { - return drm_bridge_mode_valid(encoder->bridge, mode); + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + enum drm_mode_status ret; + + if (!bridge->funcs->mode_valid) + continue; + + ret = bridge->funcs->mode_valid(bridge, mode); + if (ret != MODE_OK) + return ret; + } + + return MODE_OK; } EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
@@ -367,7 +302,13 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid); */ void drm_bridge_chain_disable(struct drm_encoder *encoder) { - drm_bridge_disable(encoder->bridge); + struct drm_bridge *bridge; + + list_for_each_entry_reverse(bridge, &encoder->bridge_chain, + chain_node) { + if (bridge->funcs->disable) + bridge->funcs->disable(bridge); + } } EXPORT_SYMBOL(drm_bridge_chain_disable);
@@ -382,7 +323,12 @@ EXPORT_SYMBOL(drm_bridge_chain_disable); */ void drm_bridge_chain_post_disable(struct drm_encoder *encoder) { - drm_bridge_post_disable(encoder->bridge); + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->post_disable) + bridge->funcs->post_disable(bridge); + } } EXPORT_SYMBOL(drm_bridge_chain_post_disable);
@@ -400,7 +346,12 @@ void drm_bridge_chain_mode_set(struct drm_encoder *encoder, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { - drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->mode_set) + bridge->funcs->mode_set(bridge, mode, adjusted_mode); + } } EXPORT_SYMBOL(drm_bridge_chain_mode_set);
@@ -415,7 +366,13 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set); */ void drm_bridge_chain_pre_enable(struct drm_encoder *encoder) { - drm_bridge_pre_enable(encoder->bridge); + struct drm_bridge *bridge; + + list_for_each_entry_reverse(bridge, &encoder->bridge_chain, + chain_node) { + if (bridge->funcs->pre_enable) + bridge->funcs->pre_enable(bridge); + } } EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
@@ -429,66 +386,15 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable); */ void drm_bridge_chain_enable(struct drm_encoder *encoder) { - drm_bridge_enable(encoder->bridge); + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->enable) + bridge->funcs->enable(bridge); + } } EXPORT_SYMBOL(drm_bridge_chain_enable);
-static void drm_atomic_bridge_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) -{ - if (!bridge) - return; - - drm_atomic_bridge_disable(bridge->next, state); - - if (bridge->funcs->atomic_disable) - bridge->funcs->atomic_disable(bridge, state); - else if (bridge->funcs->disable) - bridge->funcs->disable(bridge); -} - -static void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) -{ - if (!bridge) - return; - - if (bridge->funcs->atomic_post_disable) - bridge->funcs->atomic_post_disable(bridge, state); - else if (bridge->funcs->post_disable) - bridge->funcs->post_disable(bridge); - - drm_atomic_bridge_post_disable(bridge->next, state); -} - -static void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) -{ - if (!bridge) - return; - - drm_atomic_bridge_pre_enable(bridge->next, state); - - if (bridge->funcs->atomic_pre_enable) - bridge->funcs->atomic_pre_enable(bridge, state); - else if (bridge->funcs->pre_enable) - bridge->funcs->pre_enable(bridge); -} - -static void drm_atomic_bridge_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) -{ - if (!bridge) - return; - - if (bridge->funcs->atomic_enable) - bridge->funcs->atomic_enable(bridge, state); - else if (bridge->funcs->enable) - bridge->funcs->enable(bridge); - - drm_atomic_bridge_enable(bridge->next, state); -} - /** * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain * @encoder: encoder object @@ -502,7 +408,15 @@ static void drm_atomic_bridge_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { - drm_atomic_bridge_disable(encoder->bridge, state); + struct drm_bridge *bridge; + + list_for_each_entry_reverse(bridge, &encoder->bridge_chain, + chain_node) { + if (bridge->funcs->atomic_disable) + bridge->funcs->atomic_disable(bridge, state); + else if (bridge->funcs->disable) + bridge->funcs->disable(bridge); + } } EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
@@ -520,7 +434,14 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); void drm_atomic_bridge_chain_post_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { - drm_atomic_bridge_post_disable(encoder->bridge, state); + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->atomic_post_disable) + bridge->funcs->atomic_post_disable(bridge, state); + else if (bridge->funcs->post_disable) + bridge->funcs->post_disable(bridge); + } } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
@@ -538,7 +459,15 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); void drm_atomic_bridge_chain_pre_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { - drm_atomic_bridge_pre_enable(encoder->bridge, state); + struct drm_bridge *bridge; + + list_for_each_entry_reverse(bridge, &encoder->bridge_chain, + chain_node) { + if (bridge->funcs->atomic_pre_enable) + bridge->funcs->atomic_pre_enable(bridge, state); + else if (bridge->funcs->pre_enable) + bridge->funcs->pre_enable(bridge); + } } EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
@@ -555,7 +484,14 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { - drm_atomic_bridge_enable(encoder->bridge, state); + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->atomic_enable) + bridge->funcs->atomic_enable(bridge, state); + else if (bridge->funcs->enable) + bridge->funcs->enable(bridge); + } } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 80d88a55302e..e555281f43d4 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -140,6 +140,7 @@ int drm_encoder_init(struct drm_device *dev, goto out_put; }
+ INIT_LIST_HEAD(&encoder->bridge_chain); list_add_tail(&encoder->head, &dev->mode_config.encoder_list); encoder->index = dev->mode_config.num_encoder++;
@@ -160,22 +161,16 @@ EXPORT_SYMBOL(drm_encoder_init); void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; + struct drm_bridge *bridge, *next;
/* Note that the encoder_list is considered to be static; should we * remove the drm_encoder at runtime we would have to decrement all * the indices on the drm_encoder after us in the encoder_list. */
- if (encoder->bridge) { - struct drm_bridge *bridge = encoder->bridge; - struct drm_bridge *next; - - while (bridge) { - next = bridge->next; - drm_bridge_detach(bridge); - bridge = next; - } - } + list_for_each_entry_safe(bridge, next, &encoder->bridge_chain, + chain_node) + drm_bridge_detach(bridge);
drm_mode_object_unregister(dev, &encoder->base); kfree(encoder->name); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index d429bd1c00f9..f40181274ed7 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -383,8 +383,8 @@ struct drm_bridge { struct drm_device *dev; /** @encoder: encoder to which this bridge is connected */ struct drm_encoder *encoder; - /** @next: the next bridge in the encoder chain */ - struct drm_bridge *next; + /** @chain_node: used to form a bridge chain */ + struct list_head chain_node; #ifdef CONFIG_OF /** @of_node: device node pointer to the bridge */ struct device_node *of_node; diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index d986ff1197ce..0899f7f34e3a 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -89,7 +89,7 @@ struct drm_encoder_funcs { * @head: list management * @base: base KMS object * @name: human readable name, can be overwritten by the driver - * @bridge: bridge associated to the encoder + * @bridge_chain: bridges associated to the encoder * @funcs: control functions * @helper_private: mid-layer private data * @@ -172,7 +172,7 @@ struct drm_encoder { * &drm_connector_state.crtc. */ struct drm_crtc *crtc; - struct drm_bridge *bridge; + struct list_head bridge_chain; const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private;
One of the last remaining object to not have its atomic state.
This is being motivated by our attempt to support runtime bus-format negotiation between elements of the bridge chain. This patch just paves the road for such a feature by adding a new drm_bridge_state object inheriting from drm_private_obj so we can re-use some of the state initialization/tracking logic.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_atomic.c | 40 ++++++++ drivers/gpu/drm/drm_atomic_helper.c | 18 ++++ drivers/gpu/drm/drm_bridge.c | 141 +++++++++++++++++++++++++++- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 99 +++++++++++++++++++ 5 files changed, 296 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 419381abbdd1..ed6fd3e31b56 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -30,6 +30,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_bridge.h> #include <drm/drm_debugfs.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> @@ -1010,6 +1011,45 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, connector->funcs->atomic_print_state(p, state); }
+/** + * drm_atomic_add_encoder_bridges - add bridges attached to an encoder + * @state: atomic state + * @encoder: DRM encoder + * + * This function adds all bridges attached to @encoder. This is needed to add + * bridge states to @state and make them available when + * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are + * called + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, + struct drm_encoder *encoder) +{ + struct drm_bridge_state *bridge_state; + struct drm_bridge *bridge; + + if (!encoder) + return 0; + + DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n", + encoder->base.id, encoder->name, state); + + for (bridge = drm_bridge_chain_get_first_bridge(encoder); bridge; + bridge = drm_bridge_chain_get_next_bridge(bridge)) { + bridge_state = drm_atomic_get_bridge_state(state, bridge); + if (IS_ERR(bridge_state)) + return PTR_ERR(bridge_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_encoder_bridges); + /** * drm_atomic_add_affected_connectors - add connectors for crtc * @state: atomic state diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 0deb54099570..e76ec9648b6f 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -736,6 +736,24 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return ret; }
+ /* + * Iterate over all connectors again, and add all affected bridges to + * the state. + */ + for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { + struct drm_encoder *encoder; + + encoder = old_connector_state->best_encoder; + ret = drm_atomic_add_encoder_bridges(state, encoder); + if (ret) + return ret; + + encoder = new_connector_state->best_encoder; + ret = drm_atomic_add_encoder_bridges(state, encoder); + if (ret) + return ret; + } + ret = mode_valid(state); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index ed2410f49581..0280da6be1e6 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -89,6 +89,38 @@ void drm_bridge_remove(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_remove);
+static struct drm_private_state * +drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj) +{ + struct drm_bridge *bridge = drm_priv_to_bridge(obj); + struct drm_bridge_state *state; + + if (bridge->funcs->atomic_duplicate_state) + state = bridge->funcs->atomic_duplicate_state(bridge); + else + state = drm_atomic_helper_duplicate_bridge_state(bridge); + + return &state->base; +} + +static void +drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj, + struct drm_private_state *s) +{ + struct drm_bridge_state *state = drm_priv_to_bridge_state(s); + struct drm_bridge *bridge = drm_priv_to_bridge(obj); + + if (bridge->funcs->atomic_destroy_state) + bridge->funcs->atomic_destroy_state(bridge, state); + else + drm_atomic_helper_destroy_bridge_state(bridge, state); +} + +static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { + .atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state, + .atomic_destroy_state = drm_bridge_atomic_destroy_priv_state, +}; + /** * drm_bridge_attach - attach the bridge to an encoder's chain * @@ -114,6 +146,7 @@ EXPORT_SYMBOL(drm_bridge_remove); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, struct drm_bridge *previous) { + struct drm_bridge_state *state; LIST_HEAD(tmp_list); int ret;
@@ -132,19 +165,39 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
if (bridge->funcs->attach) { ret = bridge->funcs->attach(bridge); - if (ret < 0) { - bridge->dev = NULL; - bridge->encoder = NULL; - return ret; - } + if (ret < 0) + goto err_reset_bridge; }
+ if (bridge->funcs->atomic_duplicate_state) + state = bridge->funcs->atomic_duplicate_state(bridge); + else + state = drm_atomic_helper_duplicate_bridge_state(bridge); + + if (!state) { + ret = -ENOMEM; + goto err_detach_bridge; + } + + drm_atomic_private_obj_init(bridge->dev, &bridge->base, + &state->base, + &drm_bridge_priv_state_funcs); + if (previous) list_splice(&tmp_list, &previous->chain_node); else list_splice(&tmp_list, &encoder->bridge_chain);
return 0; + +err_detach_bridge: + if (bridge->funcs->detach) + bridge->funcs->detach(bridge); + +err_reset_bridge: + bridge->dev = NULL; + bridge->encoder = NULL; + return ret; } EXPORT_SYMBOL(drm_bridge_attach);
@@ -156,6 +209,8 @@ void drm_bridge_detach(struct drm_bridge *bridge) if (WARN_ON(!bridge->dev)) return;
+ drm_atomic_private_obj_fini(&bridge->base); + if (bridge->funcs->detach) bridge->funcs->detach(bridge);
@@ -495,6 +550,82 @@ void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
+/** + * drm_atomic_helper_init_bridge_state() - Initializes a bridge state + * @bridge this state is referring to + * @state: bridge state to initialize + * + * For now it's just a memset(0) plus a state->bridge assignment. Might + * be extended in the future. + */ +void drm_atomic_helper_init_bridge_state(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + memset(state, 0, sizeof(*state)); + state->bridge = bridge; +} +EXPORT_SYMBOL(drm_atomic_helper_init_bridge_state); + +/** + * drm_atomic_helper_copy_bridge_state() - Copy the content of a bridge state + * @bridge: bridge the old and new state are referring to + * @old: previous bridge state to copy from + * @new: new bridge state to copy to + * + * Should be used by custom &drm_bridge_funcs.atomic_duplicate() implementation + * to copy the previous state into the new object. + */ +void drm_atomic_helper_copy_bridge_state(struct drm_bridge *bridge, + const struct drm_bridge_state *old, + struct drm_bridge_state *new) +{ + *new = *old; +} +EXPORT_SYMBOL(drm_atomic_helper_copy_bridge_state); + +/** + * drm_atomic_helper_duplicate_bridge_state() - Default duplicate state helper + * @bridge: bridge containing the state to duplicate + * + * Default implementation of &drm_bridge_funcs.atomic_duplicate(). + * + * RETURNS: + * a valid state object or NULL if the allocation fails. + */ +struct drm_bridge_state * +drm_atomic_helper_duplicate_bridge_state(struct drm_bridge *bridge) +{ + struct drm_bridge_state *old = NULL, *new; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + if (bridge->base.state) { + old = drm_priv_to_bridge_state(bridge->base.state); + drm_atomic_helper_copy_bridge_state(bridge, old, new); + } else { + drm_atomic_helper_init_bridge_state(bridge, new); + } + + return new; +} +EXPORT_SYMBOL(drm_atomic_helper_duplicate_bridge_state); + +/** + * drm_atomic_helper_destroy_bridge_state() - Default destroy state helper + * @bridge: the bridge this state refers to + * @state: state object to destroy + * + * Just a simple kfree() for now. + */ +void drm_atomic_helper_destroy_bridge_state(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_destroy_bridge_state); + #ifdef CONFIG_OF /** * of_drm_find_bridge - find the bridge corresponding to the device node in diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 927e1205d7aa..5ee25fd51e80 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -660,6 +660,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state, return plane->state; }
+int __must_check +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, + struct drm_encoder *encoder); int __must_check drm_atomic_add_affected_connectors(struct drm_atomic_state *state, struct drm_crtc *crtc); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index f40181274ed7..8ca25d266d0c 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -25,6 +25,7 @@
#include <linux/list.h> #include <linux/ctype.h> +#include <drm/drm_atomic.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h>
@@ -32,6 +33,23 @@ struct drm_bridge; struct drm_bridge_timings; struct drm_panel;
+/** + * struct drm_bridge_state - Atomic bridge state object + * @base: inherit from &drm_private_state + * @bridge: the bridge this state refers to + */ +struct drm_bridge_state { + struct drm_private_state base; + + struct drm_bridge *bridge; +}; + +static inline struct drm_bridge_state * +drm_priv_to_bridge_state(struct drm_private_state *priv) +{ + return container_of(priv, struct drm_bridge_state, base); +} + /** * struct drm_bridge_funcs - drm_bridge control functions */ @@ -337,6 +355,30 @@ struct drm_bridge_funcs { */ void (*atomic_post_disable)(struct drm_bridge *bridge, struct drm_atomic_state *state); + + /** + * @atomic_duplicate_state: + * + * Duplicate the current bridge state object. + * + * Note that this function can be called when current bridge state is + * NULL. In this case, implementations should either implement HW + * readback or initialize the state with sensible default values. + * + * RETURNS: + * A valid drm_bridge_state object or NULL if the allocation fails. + */ + struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge); + + /** + * @atomic_duplicate_state: + * + * Destroy a bridge state object previously allocated by + * &drm_bridge_funcs.atomic_duplicate_state() or + * &drm_bridge_funcs.atomic_get_initial_state(). + */ + void (*atomic_destroy_state)(struct drm_bridge *bridge, + struct drm_bridge_state *state); };
/** @@ -379,6 +421,8 @@ struct drm_bridge_timings { * struct drm_bridge - central DRM bridge control structure */ struct drm_bridge { + /** @base: inherit from &drm_private_object */ + struct drm_private_obj base; /** @dev: DRM device this bridge belongs to */ struct drm_device *dev; /** @encoder: encoder to which this bridge is connected */ @@ -403,6 +447,12 @@ struct drm_bridge { void *driver_private; };
+static inline struct drm_bridge * +drm_priv_to_bridge(struct drm_private_obj *priv) +{ + return container_of(priv, struct drm_bridge, base); +} + void drm_bridge_add(struct drm_bridge *bridge); void drm_bridge_remove(struct drm_bridge *bridge); struct drm_bridge *of_drm_find_bridge(struct device_node *np); @@ -438,6 +488,55 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_encoder *encoder, void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, struct drm_atomic_state *state);
+void drm_atomic_helper_init_bridge_state(struct drm_bridge *bridge, + struct drm_bridge_state *state); +void drm_atomic_helper_copy_bridge_state(struct drm_bridge *bridge, + const struct drm_bridge_state *old, + struct drm_bridge_state *new); +struct drm_bridge_state * +drm_atomic_helper_duplicate_bridge_state(struct drm_bridge *bridge); +void drm_atomic_helper_destroy_bridge_state(struct drm_bridge *bridge, + struct drm_bridge_state *state); + +static inline struct drm_bridge_state * +drm_atomic_get_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_private_obj_state(state, &bridge->base); + if (!obj_state) + return NULL; + + return drm_priv_to_bridge_state(obj_state); +} + +static inline struct drm_bridge_state * +drm_atomic_get_old_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base); + if (!obj_state) + return NULL; + + return drm_priv_to_bridge_state(obj_state); +} + +static inline struct drm_bridge_state * +drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base); + if (!obj_state) + return NULL; + + return drm_priv_to_bridge_state(obj_state); +} + #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, u32 connector_type);
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:41PM +0200, Boris Brezillon wrote:
Would a for_each_bridge() macro make sense ? I'd implement it on top of list_for_each_entry() to make it simple.
s/Initializes/Initialize/
Did you mean
* @bridge: the bridge this state is referring to ?
Other objects don't export a state init function helper, but state reset and state duplicate helpers. Is there a reason to depart from that model ? Same for the copy helper below.
Related to the question above, shouldn't we have a reset state operation instead ?
On Wed, 21 Aug 2019 23:14:56 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
I can add this helper. Note that for_each_bridge() will iterate over the whole bridge chain, and we might need a for_each_bridge_from() at some point if we want to iterate over a sub-chain.
[...]
Yes.
I thought those names better reflect what the helpers do. __drm_atomic_helper_crtc_duplicate_state() for instance, it actually does not duplicate the state, but just copies current state info into the new state object which has been allocated by the caller. Same for the reset helpers, they actually do not reset anything but just initialize the state to a default. I also try to avoid prefixing functions with __ when I can (names can be clarified/extended to avoid conflicts most of the time). Will switch back to reset/dup names if you prefer.
I had a version with a reset hook and a helper that was falling back to ->duplicate_state() when not specified, but I decided to drop it to simplify things (I think most drivers will just use the default helper, and those that actually want to implement HW readback can easily do it from their ->duplicate_state() hook by testing bridge->state value). I can re-introduce it if you like.
Hi Boris,
On Thu, Aug 22, 2019 at 11:00:56AM +0200, Boris Brezillon wrote:
I think consistency would help. Regarding the reset operation, unless I'm mistaken, it got its name from the fact that it should create a software state that reflects the current hardware state, in order to support fastboot (taking over a running display controller started by the boot loader without causing any glitch). That being said, I'm not sure reset was the best name even for that :-)
It would help with consistency, but I'll leave that to you.
Instead of a drm_atomic_state. The only driver implementing those hooks is patched too.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- .../drm/bridge/analogix/analogix_dp_core.c | 12 ++-- drivers/gpu/drm/drm_bridge.c | 57 +++++++++++++++---- include/drm/drm_bridge.h | 8 +-- 3 files changed, 57 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index b096a7a75c14..02b9ee0e4f7a 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1290,8 +1290,9 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp, }
static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *bstate) { + struct drm_atomic_state *state = bstate->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; @@ -1367,8 +1368,9 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp) }
static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *bstate) { + struct drm_atomic_state *state = bstate->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; @@ -1441,8 +1443,9 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) }
static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *bstate) { + struct drm_atomic_state *state = bstate->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state = NULL; @@ -1465,8 +1468,9 @@ static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
static void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *bstate) { + struct drm_atomic_state *state = bstate->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 0280da6be1e6..0528ca941855 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -467,10 +467,18 @@ void drm_atomic_bridge_chain_disable(struct drm_encoder *encoder,
list_for_each_entry_reverse(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_disable) - bridge->funcs->atomic_disable(bridge, state); - else if (bridge->funcs->disable) + if (bridge->funcs->atomic_disable) { + struct drm_bridge_state *bridge_state; + + bridge_state = drm_atomic_get_old_bridge_state(state, + bridge); + if (WARN_ON(!bridge_state)) + return; + + bridge->funcs->atomic_disable(bridge, bridge_state); + } else if (bridge->funcs->disable) { bridge->funcs->disable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); @@ -492,10 +500,19 @@ void drm_atomic_bridge_chain_post_disable(struct drm_encoder *encoder, struct drm_bridge *bridge;
list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_post_disable) - bridge->funcs->atomic_post_disable(bridge, state); - else if (bridge->funcs->post_disable) + if (bridge->funcs->atomic_post_disable) { + struct drm_bridge_state *bridge_state; + + bridge_state = drm_atomic_get_old_bridge_state(state, + bridge); + if (WARN_ON(!bridge_state)) + return; + + bridge->funcs->atomic_post_disable(bridge, + bridge_state); + } else if (bridge->funcs->post_disable) { bridge->funcs->post_disable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); @@ -518,10 +535,18 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_encoder *encoder,
list_for_each_entry_reverse(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_pre_enable) - bridge->funcs->atomic_pre_enable(bridge, state); - else if (bridge->funcs->pre_enable) + if (bridge->funcs->atomic_pre_enable) { + struct drm_bridge_state *bridge_state; + + bridge_state = drm_atomic_get_new_bridge_state(state, + bridge); + if (WARN_ON(!bridge_state)) + return; + + bridge->funcs->atomic_pre_enable(bridge, bridge_state); + } else if (bridge->funcs->pre_enable) { bridge->funcs->pre_enable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); @@ -542,10 +567,18 @@ void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, struct drm_bridge *bridge;
list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_enable) - bridge->funcs->atomic_enable(bridge, state); - else if (bridge->funcs->enable) + if (bridge->funcs->atomic_enable) { + struct drm_bridge_state *bridge_state; + + bridge_state = drm_atomic_get_new_bridge_state(state, + bridge); + if (WARN_ON(!bridge_state)) + return; + + bridge->funcs->atomic_enable(bridge, bridge_state); + } else if (bridge->funcs->enable) { bridge->funcs->enable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 8ca25d266d0c..9383b4e4b853 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -280,7 +280,7 @@ struct drm_bridge_funcs { * The @atomic_pre_enable callback is optional. */ void (*atomic_pre_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *state); + struct drm_bridge_state *state);
/** * @atomic_enable: @@ -305,7 +305,7 @@ struct drm_bridge_funcs { * The enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *state); + struct drm_bridge_state *state); /** * @atomic_disable: * @@ -328,7 +328,7 @@ struct drm_bridge_funcs { * The disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *state); + struct drm_bridge_state *state);
/** * @atomic_post_disable: @@ -354,7 +354,7 @@ struct drm_bridge_funcs { * The post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *state); + struct drm_bridge_state *state);
/** * @atomic_duplicate_state:
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:42PM +0200, Boris Brezillon wrote:
Shouldn't you get the old state here ? The new state in commit-related operations is supposed to be obtained from the object itself, and the old state is passed to the function. See how the CRTC and encoder .atomic_enable() are called in drm_atomic_helper_commit_modeset_enables (drm_atomic_helper.c) for instance.
You should update the documentation of the bridge atomic operations to makes this explicit. The documentation of the bridge helpers (drm_atomic_bridge_enable() & co.) is also misleading, they get passed the old state, while the documentation states "atomic state being committed". I think you should rename all those state parameters to old_state to make this clearer.
Last but not least, the implementation in the analogix bridge driver seems to expect the new state, so I think it's buggy.
On Thu, 22 Aug 2019 03:02:17 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
I based that decision on the only driver implementing those hooks. I can pass the old_state instead.
So that bridge drivers have a way to check/reject an atomic operation. The drm_atomic_bridge_chain_check() (which is just a wrapper around the ->atomic_check() hook) is called in place of drm_bridge_chain_mode_fixup() (when ->atomic_check() is not implemented, the core falls back to ->mode_fixup(), so the behavior should stay the same for existing bridge drivers).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_atomic_helper.c | 11 +++--- drivers/gpu/drm/drm_bridge.c | 59 +++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 23 +++++++++++ 3 files changed, 87 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e76ec9648b6f..3fadde38ead7 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -445,12 +445,11 @@ mode_fixup(struct drm_atomic_state *state) encoder = new_conn_state->best_encoder; funcs = encoder->helper_private;
- ret = drm_bridge_chain_mode_fixup(encoder, - &new_crtc_state->mode, - &new_crtc_state->adjusted_mode); - if (!ret) { - DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); - return -EINVAL; + ret = drm_atomic_bridge_chain_check(encoder, new_crtc_state, + new_conn_state); + if (ret) { + DRM_DEBUG_ATOMIC("Bridge atomic check failed\n"); + return ret; }
if (funcs && funcs->atomic_check) { diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 0528ca941855..dcad661daa74 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -583,6 +583,65 @@ void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
+static int drm_atomic_bridge_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + if (bridge->funcs->atomic_check) { + struct drm_bridge_state *bridge_state; + int ret; + + bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + bridge); + if (WARN_ON(!bridge_state)) + return -EINVAL; + + ret = bridge->funcs->atomic_check(bridge, bridge_state, + crtc_state, conn_state); + if (ret) + return ret; + } else if (bridge->funcs->mode_fixup) { + if (!bridge->funcs->mode_fixup(bridge, &crtc_state->mode, + &crtc_state->adjusted_mode)) + return -EINVAL; + } + + return 0; +} + +/** + * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain + * @encoder: encoder object + * @crtc_state: new CRTC state + * @conn_state: new connector state + * + * Calls &drm_bridge_funcs.atomic_check() (falls back on + * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_check() + * + * RETURNS: + * 0 on success, a negative error code on failure + */ +int drm_atomic_bridge_chain_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_bridge *bridge; + + list_for_each_entry_reverse(bridge, &encoder->bridge_chain, + chain_node) { + int ret; + + ret = drm_atomic_bridge_check(bridge, crtc_state, conn_state); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_check); + /** * drm_atomic_helper_init_bridge_state() - Initializes a bridge state * @bridge this state is referring to diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 9383b4e4b853..5d8fe3709bde 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -379,6 +379,26 @@ struct drm_bridge_funcs { */ void (*atomic_destroy_state)(struct drm_bridge *bridge, struct drm_bridge_state *state); + + /** + * @atomic_check: + * + * This method is responsible for checking bridge state correctness. + * It can also check the state of the surrounding components in chain + * to make sure the whole pipeline can work properly. + * + * &drm_bridge_funcs.atomic_check() hooks are called in reverse + * order (from the last to the first bridge). + * + * This method is optional. + * + * RETURNS: + * zero if the check passed, a negative error code otherwise. + */ + int (*atomic_check)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); };
/** @@ -479,6 +499,9 @@ void drm_bridge_chain_mode_set(struct drm_encoder *encoder, void drm_bridge_chain_pre_enable(struct drm_encoder *encoder); void drm_bridge_chain_enable(struct drm_encoder *encoder);
+int drm_atomic_bridge_chain_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); void drm_atomic_bridge_chain_disable(struct drm_encoder *encoder, struct drm_atomic_state *state); void drm_atomic_bridge_chain_post_disable(struct drm_encoder *encoder,
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:43PM +0200, Boris Brezillon wrote:
As this is meant to replace .mode_fixup() you should document that here and in the .mode_fixup() operation.
There's also the additional question of what parameters can be modified. Is a bridge .atomic_check() allowed to modify the CRTC or connector state ? Of so, what are the restrictions on how those can be modified ? For instance .mode_fixup() can change the pixel clock but isn't allowed to modify the h/v active values. Those restrictions were not clearly documented for .mode_fixup() and have led to grey areas. I don't want to repeat the same mistake for bridges, we need to be very explicit here.
In the list of open questions, what if a bridge changes the pixel clock to a value it supports (as .mode_fixup() does), and then the previous bridge in the chain (the next one in traversal order for the drm_atomic_bridge_chain_check() function) also changes the pixel clock, to a value that the current bridge doesn't support ? I think we really need to think about the semantic of this operation.
As the goal of your series is to implement configuration of bus formats between bridges, shouldn't a bridge .atomic_check() also receive the state of the next bridge in the chain ?
On Thu, 22 Aug 2019 03:12:07 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Any recommendation? I didn't face this problem myself, so I'd need inputs from those who did to take a decision.
Don't we have the exact same problem right now with the ->mode_fixup() hook when bridges are chained? Not sure why it suddenly becomes a priority to clarify that, but if we quickly settle on this new semantic, I'm fine integrating those changes in my patchset.
I thought we had enough arguments passed to the atomic_check() hook and decided to let the driver retrieve the next state. I can pass the next bridge state if you prefer.
Will be useful for bridge drivers that want to do bus format negotiation with their neighbours.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_bridge.c | 19 +++++++++++++++++++ include/drm/drm_bridge.h | 2 ++ 2 files changed, 21 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index dcad661daa74..9efb27087e70 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -271,6 +271,25 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_chain_get_next_bridge);
+/** + * drm_bridge_chain_get_prev_bridge() - Get the previous bridge in the chain + * @bridge: bridge object + * + * RETURNS: + * the previous bridge in the chain, or NULL if there's @bridge is the + * last. + */ +struct drm_bridge * +drm_bridge_chain_get_prev_bridge(struct drm_bridge *bridge) +{ + if (!bridge || !bridge->encoder || + list_is_first(&bridge->encoder->bridge_chain, &bridge->chain_node)) + return NULL; + + return list_prev_entry(bridge, chain_node); +} +EXPORT_SYMBOL(drm_bridge_chain_get_prev_bridge); + /** * DOC: bridge callbacks * diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 5d8fe3709bde..2f69adb7b0f3 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -485,6 +485,8 @@ struct drm_bridge * drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder); struct drm_bridge * drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge); +struct drm_bridge * +drm_bridge_chain_get_prev_bridge(struct drm_bridge *bridge); bool drm_bridge_chain_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:44PM +0200, Boris Brezillon wrote:
Did you mean "if the @bridge is the first" ?
Do we really need to protect against !bridge and !bridge->encoder, can that happen ? Especially !bridge, are there use cases for potentially calling this function (and the other list traversal helpers in your previous patches) with a NULL bridge ?
On Thu, 22 Aug 2019 03:17:32 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Yes, sorry about that, I meant "if @bridge is the first", indeed.
Maybe not.
I think this checks was done when bridge iteration was based on recursivity, which is no longer the case.
This takes the form of various helpers, plus the addition of 2 new structs: * drm_bus_caps: describe the bus capabilities of a bridge/encoder. For bridges we have one for the input port and one for the output port. Encoders just have one output port. * drm_bus_cfg: added to the drm_bridge_state. It's supposed to store bus configuration information. For now we just have format and flags but more fields might be added in the future. Again, each drm_bridge_state contains 2 drm_bus_cfg elements: one for the output port and one for the input port
Helpers can be used from bridge drivers' ->atomic_check() implementation to negotiate the bus format to use. Negotiation happens in reserve order, starting from the last element of the bridge chain (the one directly connected to the display) to the first element of the chain (the one connected to the encoder).
Negotiation usually takes place in 2 steps: * make sure the input end of the next element in the chain picked a format that's supported by the output end of our bridge. That's done with drm_atomic_bridge_choose_output_bus_cfg(). * choose a format for the input end of our bridge based on what's supported by the previous bridge in the chain. This is achieved by calling drm_atomic_bridge_choose_input_bus_cfg()
Note that those helpers are a bit lax when it comes to unitialized bus format validation. That's intentional. If we don't do that we'll basically break things if we start adding support for bus format negotiation to some elements of the pipeline leaving others on the side, and that's likely to happen for all external bridges since we can't necessarily tell where they are used.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_bridge.c | 187 +++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 46 +++++++++ include/drm/drm_encoder.h | 6 ++ 3 files changed, 239 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 9efb27087e70..ef072df42422 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -602,6 +602,193 @@ void drm_atomic_bridge_chain_enable(struct drm_encoder *encoder, } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
+int drm_find_best_bus_format(const struct drm_bus_caps *a, + const struct drm_bus_caps *b, + const struct drm_display_mode *mode, + u32 *selected_bus_fmt) +{ + unsigned int i, j; + + /* + * Some drm_bridge/drm_encoder don't care about the input/output bus + * format, let's set the format to zero in that case (this is only + * valid if both side of the link don't care). + */ + if (!a->num_supported_fmts && !b->num_supported_fmts) { + *selected_bus_fmt = 0; + return 0; + } else if (b->num_supported_fmts > 1 && b->supported_fmts) { + *selected_bus_fmt = b->supported_fmts[0]; + return 0; + } else if (a->num_supported_fmts > 1 && a->supported_fmts) { + *selected_bus_fmt = a->supported_fmts[0]; + return 0; + } else if (!a->num_supported_fmts || !a->supported_fmts || + !b->num_supported_fmts || !b->supported_fmts) { + return -EINVAL; + } + + /* + * TODO: + * Dummy algo picking the first match. We probably want something + * smarter where the narrowest format (in term of bus width) that + * does not incur data loss is picked, and if all possible formats + * are lossy, pick the one that's less lossy among all the choices + * we have. In order to do that we'd need to convert MEDIA_BUS_FMT_ + * modes into something like drm_format_info. + */ + for (i = 0; i < a->num_supported_fmts; i++) { + for (j = 0; j < b->num_supported_fmts; j++) { + if (a->supported_fmts[i] == b->supported_fmts[j]) { + *selected_bus_fmt = a->supported_fmts[i]; + return 0; + } + } + } + + return -EINVAL; +} +EXPORT_SYMBOL(drm_find_best_bus_format); + +/** + * drm_atomic_bridge_choose_input_bus_cfg() - Choose bus config for the input + * end + * @bridge_state: bridge state + * @crtc_state: CRTC state + * @conn_state: connector state + * + * Choose a bus format for the input side of a bridge based on what the + * previous bridge in the chain and this bridge support. Can be called from + * bridge drivers' &drm_bridge_funcs.atomic_check() implementation. + * + * RETURNS: + * 0 if a matching format was found, a negative error code otherwise + */ +int +drm_atomic_bridge_choose_input_bus_cfg(struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_bridge *self = bridge_state->bridge; + struct drm_bus_caps *prev_output_bus_caps; + struct drm_bridge *prev; + int ret; + + prev = drm_bridge_chain_get_prev_bridge(self); + if (!prev) + prev_output_bus_caps = &self->encoder->output_bus_caps; + else + prev_output_bus_caps = &prev->output_bus_caps; + + ret = drm_find_best_bus_format(prev_output_bus_caps, + &self->input_bus_caps, &crtc_state->mode, + &bridge_state->input_bus_cfg.fmt); + if (ret) + return ret; + + /* + * TODO: + * Should we fill/check the ->flag field too? + */ + return 0; +} +EXPORT_SYMBOL(drm_atomic_bridge_choose_input_bus_cfg); + +/** + * drm_atomic_bridge_choose_output_bus_cfg() - Choose bus config for the output + * end + * @bridge_state: bridge state + * @crtc_state: CRTC state + * @conn_state: connector state + * + * Choose a bus format for the output side of a bridge based on what the next + * bridge in the chain and this bridge support. Can be called from bridge + * drivers' &drm_bridge_funcs.atomic_check() implementation. + * + * RETURNS: + * 0 if a matching format was found, a negative error code otherwise + */ +int +drm_atomic_bridge_choose_output_bus_cfg(struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct drm_bridge *self = bridge_state->bridge; + struct drm_bridge_state *next_bridge_state; + struct drm_bridge *next; + u32 fmt; + + next = drm_bridge_chain_get_next_bridge(self); + if (!next) { + struct drm_connector *conn = conn_state->connector; + struct drm_display_info *di = &conn->display_info; + + /* + * FIXME: + * We currently pick the first supported format + * unconditionally. It seems to fit all current use cases but + * might be too limited for panels/displays that can configure + * the bus format dynamically. + */ + if (di->num_bus_formats && di->bus_formats) + bridge_state->output_bus_cfg.fmt = di->bus_formats[0]; + else + bridge_state->output_bus_cfg.fmt = 0; + + bridge_state->output_bus_cfg.flags = di->bus_flags; + return 0; + } + + /* + * We expect output_bus_caps to contain at least one format. Note + * that don't care about bus format negotiation can simply not + * call this helper. + */ + if (!self->output_bus_caps.num_supported_fmts ||! + !self->output_bus_caps.supported_fmts) + return -EINVAL; + + next_bridge_state = drm_atomic_get_new_bridge_state(state, next); + + /* + * The next bridge is expected to have called + * &drm_atomic_bridge_choose_input_bus_cfg() as part of its + * &drm_bridge_funcs.atomic_check() implementation, but this hook is + * optional, and even if it's implemented, calling + * &drm_atomic_bridge_choose_input_bus_cfg() is not mandated. + * If fmt is zero, that means the next element in the chain doesn't + * care about bus format negotiation (probably because there's only + * one possible setting). In that case, we still have to select one + * bus format for the output port of our bridge, and this is only + * possible if the bridge supports only one format. + */ + fmt = next_bridge_state->input_bus_cfg.fmt; + if (fmt) { + unsigned int i; + + for (i = 0; i < self->output_bus_caps.num_supported_fmts; i++) { + if (self->output_bus_caps.supported_fmts[i] == fmt) + break; + } + + if (i == self->output_bus_caps.num_supported_fmts) + return -ENOTSUPP; + } else if (self->output_bus_caps.num_supported_fmts == 1) { + fmt = self->output_bus_caps.supported_fmts[0]; + } else { + return -EINVAL; + } + + /* + * TODO: + * Should we fill/check the ->flag field too? + */ + bridge_state->output_bus_cfg.fmt = fmt; + return 0; +} +EXPORT_SYMBOL(drm_atomic_bridge_choose_output_bus_cfg); + static int drm_atomic_bridge_check(struct drm_bridge *bridge, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2f69adb7b0f3..c578800a05e0 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -33,15 +33,45 @@ struct drm_bridge; struct drm_bridge_timings; struct drm_panel;
+/** + * struct drm_bus_cfg - bus configuration + * @fmt: format used on this bus + * @flags: DRM_BUS_ flags used on this bus + * + * Encodes the bus format and bus flags used by one end of the bridge or + * by the encoder output. + */ +struct drm_bus_cfg { + u32 fmt; + u32 flags; +}; + +/** + * struct drm_bus_caps - bus capabilities + * @supported_fmts: array of MEDIA_BUS_FMT_ formats + * @num_supported_fmts: size of the supported_fmts array + * + * Used by the core to negotiate the bus format at runtime. + */ +struct drm_bus_caps { + const u32 *supported_fmts; + unsigned int num_supported_fmts; +}; + /** * struct drm_bridge_state - Atomic bridge state object * @base: inherit from &drm_private_state * @bridge: the bridge this state refers to + * @input_bus_info: input bus information + * @output_bus_info: output bus information */ struct drm_bridge_state { struct drm_private_state base;
struct drm_bridge *bridge; + + struct drm_bus_cfg input_bus_cfg; + struct drm_bus_cfg output_bus_cfg; };
static inline struct drm_bridge_state * @@ -465,6 +495,9 @@ struct drm_bridge { const struct drm_bridge_funcs *funcs; /** @driver_private: pointer to the bridge driver's internal context */ void *driver_private; + + struct drm_bus_caps input_bus_caps; + struct drm_bus_caps output_bus_caps; };
static inline struct drm_bridge * @@ -479,6 +512,14 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, struct drm_bridge *previous);
+int +drm_atomic_bridge_choose_input_bus_cfg(struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); +int +drm_atomic_bridge_choose_output_bus_cfg(struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); struct drm_bridge * drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder); struct drm_bridge * @@ -562,6 +603,11 @@ drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, return drm_priv_to_bridge_state(obj_state); }
+int drm_find_best_bus_format(const struct drm_bus_caps *a, + const struct drm_bus_caps *b, + const struct drm_display_mode *mode, + u32 *selected_bus_fmt); + #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, u32 connector_type); diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 0899f7f34e3a..e891921b3aed 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -25,6 +25,7 @@
#include <linux/list.h> #include <linux/ctype.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_mode.h> #include <drm/drm_mode_object.h> @@ -184,6 +185,11 @@ struct drm_encoder { * functions as part of its encoder enable/disable handling. */ uint32_t custom_bridge_enable_disable_seq : 1; + + /** + * @output_bus_caps: Supported output bus caps + */ + struct drm_bus_caps output_bus_caps; };
#define obj_to_encoder(x) container_of(x, struct drm_encoder, base)
On 08/08/2019 17:11, Boris Brezillon wrote:
Here, `!a->num_supported_fmts &&` is missing otherwise this code will select b->supported_fmts[0] whatever the supported formats of a.
Here, `!b->num_supported_fmts &&` is missing otherwise this code will select a->supported_fmts[0] whatever the supported formats of b.
[...]
With that fixed, this works with basic negociation of the single MEDIA_BUS_FMT_YUV8_1X24 exposed by the meson hdmi encoder, otherwise it takes the first default MEDIA_BUS_FMT_RGB888_1X24 format exposed by dw-hdmi.
Neil
On Wed, 14 Aug 2019 09:51:07 +0200 Neil Armstrong narmstrong@baylibre.com wrote:
Thanks for the report. Will be fixed in v2.
Boris
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:45PM +0200, Boris Brezillon wrote:
s/reserve/reverse/
I think most bridges that support multiple input and output formats will not necessarily be able to transcode, meaning that for a particular output format a particular corresponding input format will need to be picked (or possibly one of a subset of input formats). I'm thus not sure how useful this helper will be in practice.
I also fear that this negotiation mechanism is too simple and will lock is with support for very simple systems only :-S Let's assume a system with a display device (D), two bridges (B0 and B1) and a connector (C). Let's further assume that the bus format required by the connector is fixed. With this patch series, B1 will pick an output format identical to the format on the connector, and will then pick an input format among the ones supported by both B0 and B1. Let's assume there are multiple options, Fa and Fb, and drm_find_best_bus_format() will pick one of them, say Fa. Then B0 will configure its output with that format, and will proceed with finding an input format supported by D. It could be that the output format Fa for B0 requires an input format that is not supported by D, while Fb would have produced a working pipeline.
I don't think this example is really far-fetched, and while we don't need to support it immediately, we need an API that makes it possible to support it, as changing the API later would require reworking all the drivers that use it.
What is the bridge driver supposed to do in this case ?
I'm not sure to parse the second sentence correctly.
Extra ! at the end of the line ?
Theoritically we could also just pick the first one (or actually any of the supported formats), couldn't we ?
I would make this an array, to prepare for bridges that will have more than one input or one output.
On Thu, 22 Aug 2019 03:55:56 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Yes, I assumed all pipeline elements would be able to convert any of their supported input formats to any of the output ones, which might not be the case in practice. I could add a matrix to expose the supported conversions and add a pass of 'format eviction' at bridge attach time, but I'm not sure that's enough.
Pick a default value (or a something fixed by a FW property), as it used to do before this patchset. There's no way we can convert all bridges at once, so we need to support mixed chains formed of bridges that support bus format negotiation and bridges that don't.
Oops.
"Note that drivers that don't care about ...". I'm just trying to explain why EINVAL is returned when ->output_bus_caps is not populated.
Hm, not really. I mean, if the bridge supports several output formats, there's no way we can tell which one is the right one for this specific setup. The idea being that drivers are responsible for picking an appropriate output bus-format based on something else (DT prop?) when drm_atomic_bridge_choose_output_bus_cfg() returns an error.
Hm, not sure how you'd represent that. I guess you want to support bridges that can activate several inputs+outputs in parallel. Do we even support that right now? Also, how do we link the input to the output in that case? By their position in the array? And we'd need an extra field in bus_caps to point to the corresponding bridge input/output port.
I'm glad we can discuss all those problems, but I'd like to focus on what's needed right now, not what could potentially be needed in the future. If we can design things to be more future-proof that's great, but in my experience, trying to solve problems that do not exist yet often leads to complex designs which prove to be wrong when we try to apply them to real situations when they finally occur.
On 22/08/2019 09:48, Boris Brezillon wrote:
I had a thought when implementing bus format negotiation for dw-hdmi.
Depending on the output bus-format, we could have different sets of possible input drm_bus_caps.
For example, if output is MEDIA_BUS_FMT_RGB888_1X24, the input bus formats could only be MEDIA_BUS_FMT_RGB888_1X24 or MEDIA_BUS_FMT_YUV8_1X24. Same for MEDIA_BUS_FMT_RGB101010_1X30, MEDIA_BUS_FMT_RGB121212_1X36, MEDIA_BUS_FMT_RGB161616_1X48... And the special MEDIA_BUS_FMT_UYYVYY8_0_5X24, where input must be output.
How could we handle this ? I mean, could we have a fixed output drm_bus_caps and be able to dynamically change the input drm_bus_caps ?
Adding matrix in struct drm_bridge seems over-engineered, no ? Since it should take in account the actual drm_display_mode.
Maybe by passing a dynamic input drm_bus_caps to drm_atomic_bridge_choose_input_bus_cfg() and take the default struct drm_bridge one if not provided ? This would really simplify the bridge atomic_check implementation by reusing the drm_atomic_bridge_choose_*() functions.
Neil
On Thu, 22 Aug 2019 11:29:40 +0200 Neil Armstrong narmstrong@baylibre.com wrote:
Sounds exactly like what Laurent was describing.
How could we handle this ? I mean, could we have a fixed output drm_bus_caps and be able to dynamically change the input drm_bus_caps ?
If all output ends of bridges have fixed formats (meaning that something sets it to a fixed value, like a DT prop), there's no need for negotiation at all, since the output of the previous element in the chain will force input format of the current element.
Adding matrix in struct drm_bridge seems over-engineered, no ?
Will have to try it to give an answer to that question :).
Since it should take in account the actual drm_display_mode.
Taking the display_mode into account is yet another thing, though it might impact the way we want to handle bus-format negotiation. The way I see it, we have 2 issues to solve:
1/ make sure the atomic modeset does not fail if there's at least one bus-format combination that works (this is what the supported-transcoding matrix is for) 2/ make sure that we pick the best option among all the possibilities. That requires defining what 'best' means. In some cases you might want to reduce power-consumption/bandwidth in others you might want to preserve image-quality (relative to the selected mode of course)
I decided to ignore problem #2 for now, but that's definitely something we should work on at some point. Problem #1 is a problem we should solve now. If we find a solution that solves both it's even better :-).
Maybe by passing a dynamic input drm_bus_caps to drm_atomic_bridge_choose_input_bus_cfg() and take the default struct drm_bridge one if not provided ?
IIUC, you propose to restrict the set of input formats based on the selected output format from inside the driver ->atomic_check() implementation. That's basically like open-coding the supported-transcoding matrix: you'll have to have a list of supported input formats for each output format (or set of output formats), right?
Note that we could implement the supported-transcoding thing with a hook instead of a matrix, but, no matter the solution we pick, it will always be something that can answer this question "is bridge X able to convert input format A into output format B?".
The nice thing about having the transcoding matrix calculated at attach time is that you reject combinations that are not possible early instead of having to do this work at each atomic_check(). Also, if you're not rejecting impossible combinations early, I think you still have the problem Laurent was talking about when you have more than 2 elements in the chain.
This would really simplify the bridge atomic_check implementation by reusing the drm_atomic_bridge_choose_*() functions.
On 22/08/2019 12:09, Boris Brezillon wrote:
Another thought, I was thinking about a matrix with an optional callback checking is this particular matrix line is possible, an example :
struct drm_bridge_bus_fmt_assoc dw_hdmi_bus_fmt_matrix[] = { { MEDIA_BUS_FMT_RGB888_1X24, {MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24, 0}, {dw_hdmi_bus_fmt_no_yuv420_check, NULL}, }, { MEDIA_BUS_FMT_YUV8_1X24, {MEDIA_BUS_FMT_YUV8_1X24, 0}, {dw_hdmi_bus_fmt_no_yuv420_check, dw_hdmi_bus_fmt_yuv444_check, NULL}, }, { MEDIA_BUS_FMT_RGB101010_1X30, {MEDIA_BUS_FMT_RGB101010_1X30, MEDIA_BUS_FMT_YUV10_1X30, 0}, {dw_hdmi_bus_fmt_no_yuv420_check, dw_hdmi_bus_fmt_rgb_10bit_check, NULL}, }, { MEDIA_BUS_FMT_UYYVYY8_0_5X24, {MEDIA_BUS_FMT_UYYVYY8_0_5X24, 0}, dw_hdmi_bus_fmt_yuv420_check, }, { MEDIA_BUS_FMT_UYYVYY10_0_5X30, {MEDIA_BUS_FMT_UYYVYY8_0_5X24, 0}, {dw_hdmi_bus_fmt_yuv420_check, dw_hdmi_bus_fmt_yuv420_10bit_check, NULL}, }, };
Or with some flags combinations to check on the mode and some on the edid ?
Neil
On Thu, 22 Aug 2019 12:22:02 +0200 Neil Armstrong narmstrong@baylibre.com wrote:
Okay, so you need to check/reject bus-formats at runtime (the mode is not known at attach time). Do you have an idea of what would be checked in those hooks?
On 22/08/2019 12:34, Boris Brezillon wrote:
The output bus format is dependent on the mode (drm_mode_is_420_only() and drm_mode_is_420_also()) and the display info(info->hdmi.scdc.supported, info->bpc and info->color_formats).
dw_hdmi_bus_fmt_yuv420_check would check : drm_mode_is_420_only(info, mode) || (!info->hdmi.scdc.supported && drm_mode_is_420_also(info, mode)) and if encoder supports the corresponding input mode
dw_hdmi_bus_fmt_no_yuv420_check would be reverse.
dw_hdmi_bus_fmt_yuv444_check would check : info->color_formats & DRM_COLOR_FORMAT_YCRCB444 and if encoder supports the corresponding input mode
dw_hdmi_bus_fmt_rgb_10bit_check would check: info->bpc >= 10 and if encoder supports the corresponding input mode
10/12/16 bit selection is best-effort (at least how intel implemented it), this means we would put the 16/12/10bits bus-formats first and 8bit last.
Same for YUV vs RGB, we should select YUV first if encoder supports it, otherwise RGB.
Neil
Now that bridges can expose the bus format/flags they expect, we can use those instead of the relying on the display_info provided by the connector (which is only valid if the encoder is directly connected to bridge element driving the panel/display).
We also explicitly expose the bus formats supported by our encoder by filling encoder->output_bus_caps with proper info.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/imx/parallel-display.c | 28 +++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 35f20edab9fd..57fee48d6bb6 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -115,8 +115,18 @@ static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder, struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); struct drm_display_info *di = &conn_state->connector->display_info; struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); + struct drm_bridge_state *bridge_state = NULL; + struct drm_bridge *bridge;
- if (!imxpd->bus_format && di->num_bus_formats) { + bridge = drm_bridge_chain_get_first_bridge(encoder); + if (bridge) + bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + bridge); + + if (bridge_state && bridge_state->input_bus_cfg.fmt) { + imx_crtc_state->bus_format = bridge_state->input_bus_cfg.fmt; + imx_crtc_state->bus_flags = bridge_state->input_bus_cfg.flags; + } else if (!imxpd->bus_format && di->num_bus_formats) { imx_crtc_state->bus_flags = di->bus_flags; imx_crtc_state->bus_format = di->bus_formats[0]; } else { @@ -152,10 +162,18 @@ static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { .atomic_check = imx_pd_encoder_atomic_check, };
+static const u32 imx_pd_bus_fmts[] = { + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_RGB565_1X16, + MEDIA_BUS_FMT_RGB666_1X18, + MEDIA_BUS_FMT_RGB666_1X24_CPADHI, +}; + static int imx_pd_register(struct drm_device *drm, struct imx_parallel_display *imxpd) { struct drm_encoder *encoder = &imxpd->encoder; + struct drm_bus_caps *bus_caps = &encoder->output_bus_caps; int ret;
ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node); @@ -173,6 +191,14 @@ static int imx_pd_register(struct drm_device *drm, drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
+ if (imxpd->bus_format) { + bus_caps->supported_fmts = &imxpd->bus_format; + bus_caps->num_supported_fmts = 1; + } else { + bus_caps->supported_fmts = imx_pd_bus_fmts; + bus_caps->num_supported_fmts = ARRAY_SIZE(imx_pd_bus_fmts); + } + if (!imxpd->bridge) { drm_connector_helper_add(&imxpd->connector, &imx_pd_connector_helper_funcs);
Some LVDS drivers might want to negotiate the bus format with the previous element in the encoder chain. Let's define a chip-specific ops structure that contains an ->atomic_check() hook and plug that to the drm_bridge->atomic_check() infra.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/bridge/lvds-encoder.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c index 2ab2c234f26c..da7013c5faf1 100644 --- a/drivers/gpu/drm/bridge/lvds-encoder.c +++ b/drivers/gpu/drm/bridge/lvds-encoder.c @@ -6,16 +6,25 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/platform_device.h>
#include <drm/drm_bridge.h> #include <drm/drm_panel.h>
+struct lvds_encoder_ops { + int (*atomic_check)(struct drm_bridge *bridge, + struct drm_bridge_state *bride_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); +}; + struct lvds_encoder { struct drm_bridge bridge; struct drm_bridge *panel_bridge; struct gpio_desc *powerdown_gpio; + const struct lvds_encoder_ops *ops; };
static int lvds_encoder_attach(struct drm_bridge *bridge) @@ -48,10 +57,27 @@ static void lvds_encoder_disable(struct drm_bridge *bridge) gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1); }
+static int lvds_encoder_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct lvds_encoder *lvds_encoder = container_of(bridge, + struct lvds_encoder, + bridge); + + if (!lvds_encoder->ops && !lvds_encoder->ops->atomic_check) + return 0; + + return lvds_encoder->ops->atomic_check(bridge, bridge_state, + crtc_state, conn_state); +} + static struct drm_bridge_funcs funcs = { .attach = lvds_encoder_attach, .enable = lvds_encoder_enable, .disable = lvds_encoder_disable, + .atomic_check = lvds_encoder_atomic_check, };
static int lvds_encoder_probe(struct platform_device *pdev) @@ -67,6 +93,7 @@ static int lvds_encoder_probe(struct platform_device *pdev) if (!lvds_encoder) return -ENOMEM;
+ lvds_encoder->ops = of_device_get_match_data(&pdev->dev); lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(lvds_encoder->powerdown_gpio)) {
The SN75LVDS83 bridge support several input formats except the input format is directly related to the expected output format. Let's express that constraint by setting the bridge_state->output_bus_cfg.fmt field to the appropriate value.
The previous element in the chain might be able to use this information to pick the appropriate output bus format.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/bridge/lvds-encoder.c | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c index da7013c5faf1..8d399d1828b0 100644 --- a/drivers/gpu/drm/bridge/lvds-encoder.c +++ b/drivers/gpu/drm/bridge/lvds-encoder.c @@ -159,9 +159,57 @@ static int lvds_encoder_remove(struct platform_device *pdev) return 0; }
+static int sn75lvds83_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + int ret; + + ret = drm_atomic_bridge_choose_output_bus_cfg(bridge_state, crtc_state, + conn_state); + if (ret) + return ret; + + /* + * The output bus format has a direct impact on the expected input bus + * format. + */ + switch (bridge_state->output_bus_cfg.fmt) { + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + /* + * JEIDA and SPWG variants theoretically require different pin + * mapping, but MEDIA_BUS_FMT_ definitions do not allow + * fined-grained pin placement definition, and this is + * something we expect to be taken care of at board design + * time, so let's ignore this for now. + * If it becomes a problem, we can always add a way to override + * the bus format with a FW property. + */ + bridge_state->input_bus_cfg.fmt = MEDIA_BUS_FMT_RGB888_1X24; + break; + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + bridge_state->input_bus_cfg.fmt = MEDIA_BUS_FMT_RGB666_1X18; + break; + default: + bridge_state->input_bus_cfg.fmt = 0; + break; + } + + /* Propagate the bus_flags. */ + bridge_state->input_bus_cfg.flags = bridge_state->output_bus_cfg.flags; + return 0; +} + +static const struct lvds_encoder_ops sn75lvds83_ops = { + .atomic_check = sn75lvds83_atomic_check, +}; + static const struct of_device_id lvds_encoder_match[] = { { .compatible = "lvds-encoder" }, { .compatible = "thine,thc63lvdm83d" }, + { .compatible = "ti,sn75lvds83", .data = &sn75lvds83_ops }, {}, }; MODULE_DEVICE_TABLE(of, lvds_encoder_match);
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:48PM +0200, Boris Brezillon wrote:
This driver is supposed to only support LVDS encoders that require no special configuration, and thus not contain device-specific code. I'm not sure I like departing from this :-S
Would there be a way to make this more generic ? Do you think the code below could be generalized, or is it really device-specific ?
These seems a bit fragile. The SN75LVDS83 has a 28-bit input data bus, and maps those bits to 3x8 RGB data + 1x4 control signals. It serialises the data and outputs them on 4 LVDS pairs. When a panel uses MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA or MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, the 4 LVDS pairs are used, and MEDIA_BUS_FMT_RGB888_1X24 is required. When outputting MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, the fourth LVDS pair is ignored, but the 3x8 RGB input bus can still receive MEDIA_BUS_FMT_RGB888_1X24, the 2 LSBs of each component will just be discarded. The source can thus ignore those 3x2 LSBs, but it still has to map the 3x6 MSBs to the high order bits of the 3x8 signals. *However*, if the hardware designs wires the 3x6 MSBs of the SN75LVDS83 to the 3x6 LSBs of the source, then the source will have to produce MEDIA_BUS_FMT_RGB666_1X18. Hardcoding the mapping as done here thus seems to be system-specific. I think you need to check how signals are connected (through DT properties, bus-width and data-shift come to mind).
On Thu, 22 Aug 2019 03:32:10 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
The list of supported formats is likely to differ, so you'd at least need to attach that to the compat. Also not sure the RGB -> LVDS format conversion always follow the same logic, hence the decision to let bridges implement ->atomic_check() if they want/need to.
Should I create a new driver for this bridge?
True, I just wanted to keep it simple until the need for the other case arises. Should we make the bus-width/data-shift mandatory for that bridge, or can we have a default behavior (like the one proposed here) when the props are missing?
Add a new entry for the Toshiba LTA089AC29000 panel.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- .../display/panel/toshiba,lt089ac29000.txt | 5 ++- drivers/gpu/drm/panel/panel-simple.c | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt b/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt index 89826116628c..26ebfa098966 100644 --- a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt +++ b/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt @@ -1,7 +1,10 @@ Toshiba 8.9" WXGA (1280x768) TFT LCD panel
Required properties: -- compatible: should be "toshiba,lt089ac29000" +- compatible: should be one of the following + "toshiba,lt089ac29000" + "toshiba,lta089ac29000" + - power-supply: as specified in the base binding
This binding is compatible with the simple-panel binding, which is specified diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index bff7578f84dd..4c1deed44ce2 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2857,6 +2857,39 @@ static const struct panel_desc toshiba_lt089ac29000 = { .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, };
+static const struct drm_display_mode toshiba_lta089ac29000_mode = { + .clock = 79500, + .hdisplay = 1280, + .hsync_start = 1280 + 192, + .hsync_end = 1280 + 192 + 128, + .htotal = 1280 + 192 + 128 + 64, + .vdisplay = 768, + .vsync_start = 768 + 20, + .vsync_end = 768 + 20 + 7, + .vtotal = 768 + 20 + 7 + 3, + .vrefresh = 60, +}; + +static const struct panel_desc toshiba_lta089ac29000 = { + .modes = &toshiba_lta089ac29000_mode, + .num_modes = 1, + .size = { + .width = 194, + .height = 116, + }, + /* + * FIXME: + * The panel supports 2 bus formats: jeida-24 and jeida-18. The + * mode is selected through the 8b6b_SEL pin. If anyone ever needs + * support for jeida-18 we should probably parse the 'data-mapping' + * property. + * In the unlikely event where 8b6b_SEL is connected to a GPIO, we'd + * need a new infra to allow bus format negotiation at the panel level. + */ + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, +}; + static const struct drm_display_mode tpk_f07a_0102_mode = { .clock = 33260, .hdisplay = 800, @@ -3305,6 +3338,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "toshiba,lt089ac29000", .data = &toshiba_lt089ac29000, + }, { + .compatible = "toshiba,lta089ac29000", + .data = &toshiba_lta089ac29000, }, { .compatible = "tpk,f07a-0102", .data = &tpk_f07a_0102,
Hi Boris,
Thank you for the patch.
On Thu, Aug 08, 2019 at 05:11:49PM +0200, Boris Brezillon wrote:
While at it, any change to convert this binding to .yaml ? For panels it's fairly easy.
I would also leave the GPIO case for later, but it would be nice to already implement reading the data-mapping property, in order to validate the DT (you should add the data-mapping property to the bindings by the way). Bonus points if it can be done in a way that is also usable by other panels.
On Thu, 22 Aug 2019 03:36:32 +0300 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
I'll add a patch doing that.
I'll do that.
Bonus points if it can be done in a way that is also usable by other panels.
Sure, I'll make that code generic.
The current definition does not represent the exact display pipeline we have on the board: the LVDS panel is actually connected through a parallel -> LVDS bridge. Let's fix that so the driver can select the proper bus format on the CRTC end.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- arch/arm/boot/dts/imx51-zii-rdu1.dts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/imx51-zii-rdu1.dts b/arch/arm/boot/dts/imx51-zii-rdu1.dts index 3596060f52e7..db006a4d4250 100644 --- a/arch/arm/boot/dts/imx51-zii-rdu1.dts +++ b/arch/arm/boot/dts/imx51-zii-rdu1.dts @@ -95,11 +95,31 @@ reg = <1>;
display_out: endpoint { - remote-endpoint = <&panel_in>; + remote-endpoint = <&lvds_encoder_in>; }; }; };
+ lvds-encoder { + compatible = "ti,sn75lvds83", "lvds-encoder"; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds_encoder_in: endpoint { + remote-endpoint = <&display_out>; + }; + }; + + port@1 { + reg = <1>; + lvds_encoder_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + panel { /* no compatible here, bootloader will patch in correct one */ pinctrl-names = "default"; @@ -110,7 +130,7 @@
port { panel_in: endpoint { - remote-endpoint = <&display_out>; + remote-endpoint = <&lvds_encoder_out>; }; }; };
Hi Boris,
On 08/08/2019 17:11, Boris Brezillon wrote:
It was one of the big subject of the dw-hdmi support of 10/12/16bit and YUV420 output, and the bridge states was plannes, and I'm happy you did it !
I'll ASAP to try implementing the dw-hdmi YUV420 and YUV444 output support with this patchset, but overall with my limited knowledge, it looks globally sane !
Neil
dri-devel@lists.freedesktop.org