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 adding a drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook. Once those basic building blocks are in place, we can add new hooks to allow bus format negotiation (those are called just before ->atomic_check()). The bus format selection is done at runtime by testing all possible combinations across the whole bridge chain until one is reported to work.
Major changes since v2: * Get rid of the dummy bridge embedded in drm_encoder and let encoder drivers provide their own bridge element * Clarify APIs and improve the doc * Propagate bus flags by default
Major changes since the RFC:
* Add a dummy bridge to the drm_encoder object so that vc4 and exynos DSI drivers can implement the pre_enable/post_disable hooks instead of manually setting encoder->bridge to NULL to control the enable/disable sequence. This change is also a first step towards drm_bridge/drm_encoder unification. New encoder drivers should stop implementing drm_encoder_helper_funcs and switch to drm_bridge_funcs. Existing drivers can be converted progressively (already have a branch where I started converting some of them [1]) * rework the bus format negotiation to give more control to bridge drivers in the selection process (driver can select at runtime which input bus format they support for a specific output bus format based on any information available in the connector, crtc and bridge state.
A more detailed changelog is provided in each patch.
This patch series is also available here [2].
Thanks,
Boris
[1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
*** BLURB HERE ***
Boris Brezillon (21): drm/vc4: Declare the DSI encoder as a bridge element drm/exynos: Don't reset bridge->next drm/exynos: Declare the DSI encoder as a bridge element drm/bridge: Rename bridge helpers targeting a bridge chain drm/bridge: Introduce drm_bridge_chain_get_next_bridge() drm: Stop accessing encoder->bridge directly drm/bridge: Make the bridge chain a double-linked list drm/bridge: Add the drm_for_each_bridge_in_chain() helper drm/bridge: Add a drm_bridge_state object drm/bridge: Clarify the atomic enable/disable hooks semantics 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: Implement basic bus format negotiation dt-bindings: display: bridge: lvds-transmitter: Add new props drm/bridge: panel: Propage bus format/flags drm/panel: simple: Add support for Toshiba LTA089AC29000 panel dt-bindings: display: panel: Add the LTA089AC29000 variant ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition
.../display/bridge/lvds-transmitter.txt | 13 + .../display/panel/toshiba,lt089ac29000.txt | 5 +- arch/arm/boot/dts/imx51-zii-rdu1.dts | 24 +- .../drm/bridge/analogix/analogix_dp_core.c | 12 +- drivers/gpu/drm/bridge/lvds-encoder.c | 72 ++ drivers/gpu/drm/bridge/panel.c | 1 + drivers/gpu/drm/drm_atomic.c | 39 + drivers/gpu/drm/drm_atomic_helper.c | 54 +- drivers/gpu/drm/drm_bridge.c | 800 +++++++++++++++--- drivers/gpu/drm/drm_encoder.c | 15 +- drivers/gpu/drm/drm_probe_helper.c | 4 +- drivers/gpu/drm/exynos/exynos_dp.c | 1 - drivers/gpu/drm/exynos/exynos_drm_dsi.c | 90 +- drivers/gpu/drm/imx/parallel-display.c | 174 +++- drivers/gpu/drm/mediatek/mtk_hdmi.c | 8 +- drivers/gpu/drm/msm/edp/edp_bridge.c | 10 +- drivers/gpu/drm/omapdrm/omap_drv.c | 4 +- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 +- drivers/gpu/drm/panel/panel-simple.c | 36 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 11 +- drivers/gpu/drm/vc4/vc4_dsi.c | 90 +- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 396 ++++++++- include/drm/drm_encoder.h | 9 +- 24 files changed, 1588 insertions(+), 286 deletions(-)
Encoder drivers will progressively transition to the drm_bridge interface in place of the drm_encoder one.
Let's start with the VC4 driver, and use the ->pre_{enable,disable}() hooks to get rid of the hack resetting encoder->bridge.next (which was needed to control the encoder/bridge enable/disable sequence).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * Embed a drm_bridge object in vc4_dsi since drm_encoder no longer has a dummy bridge
Changes in v2: * New patch (replaces "drm/vc4: Get rid of the dsi->bridge field") --- drivers/gpu/drm/vc4/vc4_dsi.c | 88 +++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index c9ba83ed49b9..49f8a313e759 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -498,7 +498,11 @@ struct vc4_dsi {
struct mipi_dsi_host dsi_host; struct drm_encoder *encoder; - struct drm_bridge *bridge; + + /* Embed a bridge object so we can implement bridge funcs instead of + * encoder ones. + */ + struct drm_bridge bridge;
void __iomem *regs;
@@ -543,6 +547,11 @@ struct vc4_dsi { struct debugfs_regset32 regset; };
+static inline struct vc4_dsi *bridge_to_vc4_dsi(struct drm_bridge *bridge) +{ + return container_of(bridge, struct vc4_dsi, bridge); +} + #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host)
static inline void @@ -747,16 +756,11 @@ dsi_esc_timing(u32 ns) return DIV_ROUND_UP(ns, ESC_TIME_NS); }
-static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) +static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge) { - struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); - struct vc4_dsi *dsi = vc4_encoder->dsi; + struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct device *dev = &dsi->pdev->dev;
- drm_bridge_disable(dsi->bridge); - vc4_dsi_ulps(dsi, true); - drm_bridge_post_disable(dsi->bridge); - clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->escape_clock); clk_disable_unprepare(dsi->pixel_clock); @@ -764,6 +768,13 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) pm_runtime_put(dev); }
+static void vc4_dsi_bridge_disable(struct drm_bridge *bridge) +{ + struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); + + vc4_dsi_ulps(dsi, true); +} + /* Extends the mode's blank intervals to handle BCM2835's integer-only * DSI PLL divider. * @@ -777,12 +788,11 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) * higher-than-expected clock rate to the panel, but that's what the * firmware does too. */ -static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); - struct vc4_dsi *dsi = vc4_encoder->dsi; + struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); unsigned long parent_rate = clk_get_rate(phy_parent); unsigned long pixel_clock_hz = mode->clock * 1000; @@ -816,11 +826,11 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, return true; }
-static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) +static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge) { + struct drm_encoder *encoder = bridge->encoder; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; - struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); - struct vc4_dsi *dsi = vc4_encoder->dsi; + struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct device *dev = &dsi->pdev->dev; bool debug_dump_regs = false; unsigned long hs_clock; @@ -1054,8 +1064,12 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) }
vc4_dsi_ulps(dsi, false); +}
- drm_bridge_pre_enable(dsi->bridge); +static void vc4_dsi_bridge_enable(struct drm_bridge *bridge) +{ + struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); + bool debug_dump_regs = false;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { DSI_PORT_WRITE(DISP0_CTRL, @@ -1072,8 +1086,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) DSI_DISP0_ENABLE); }
- drm_bridge_enable(dsi->bridge); - if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&dsi->pdev->dev); dev_info(&dsi->pdev->dev, "DSI regs after:\n"); @@ -1290,10 +1302,12 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = { .transfer = vc4_dsi_host_transfer, };
-static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = { - .disable = vc4_dsi_encoder_disable, - .enable = vc4_dsi_encoder_enable, - .mode_fixup = vc4_dsi_encoder_mode_fixup, +static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = { + .pre_enable = vc4_dsi_bridge_pre_enable, + .enable = vc4_dsi_bridge_enable, + .disable = vc4_dsi_bridge_disable, + .post_disable = vc4_dsi_bridge_post_disable, + .mode_fixup = vc4_dsi_bridge_mode_fixup, };
static const struct of_device_id vc4_dsi_dt_match[] = { @@ -1445,6 +1459,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 *next_bridge; struct drm_panel *panel; const struct of_device_id *match; dma_cap_mask_t dma_mask; @@ -1561,7 +1576,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, &next_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 +1591,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) }
if (panel) { - dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel, + next_bridge = devm_drm_panel_bridge_add_typed(dev, panel, DRM_MODE_CONNECTOR_DSI); - if (IS_ERR(dsi->bridge)) - return PTR_ERR(dsi->bridge); + if (IS_ERR(next_bridge)) + return PTR_ERR(next_bridge); }
/* The esc clock rate is supposed to always be 100Mhz. */ @@ -1598,19 +1613,20 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs, 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); + /* Declare ourself as the first bridge element. */ + dsi->bridge.funcs = &vc4_dsi_bridge_funcs; + ret = drm_bridge_attach(dsi->encoder, &dsi->bridge, NULL); + if (ret) { + dev_err(dev, "bridge attach failed: %d\n", ret); + return ret; + } + + ret = drm_bridge_attach(dsi->encoder, next_bridge, &dsi->bridge); 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;
if (dsi->port == 0) vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset); @@ -1629,7 +1645,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->bridge.next) pm_runtime_disable(dev);
vc4_dsi_encoder_destroy(dsi->encoder);
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:52PM +0200, Boris Brezillon wrote:
Encoder drivers will progressively transition to the drm_bridge interface in place of the drm_encoder one.
Let's start with the VC4 driver, and use the ->pre_{enable,disable}() hooks to get rid of the hack resetting encoder->bridge.next (which was needed to control the encoder/bridge enable/disable sequence).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Embed a drm_bridge object in vc4_dsi since drm_encoder no longer has a dummy bridge
Changes in v2:
- New patch (replaces "drm/vc4: Get rid of the dsi->bridge field")
drivers/gpu/drm/vc4/vc4_dsi.c | 88 +++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index c9ba83ed49b9..49f8a313e759 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -498,7 +498,11 @@ struct vc4_dsi {
struct mipi_dsi_host dsi_host; struct drm_encoder *encoder;
- struct drm_bridge *bridge;
/* Embed a bridge object so we can implement bridge funcs instead of
* encoder ones.
*/
struct drm_bridge bridge;
void __iomem *regs;
@@ -543,6 +547,11 @@ struct vc4_dsi { struct debugfs_regset32 regset; };
+static inline struct vc4_dsi *bridge_to_vc4_dsi(struct drm_bridge *bridge) +{
- return container_of(bridge, struct vc4_dsi, bridge);
+}
#define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host)
static inline void @@ -747,16 +756,11 @@ dsi_esc_timing(u32 ns) return DIV_ROUND_UP(ns, ESC_TIME_NS); }
-static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) +static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge) {
- struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
- struct vc4_dsi *dsi = vc4_encoder->dsi;
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct device *dev = &dsi->pdev->dev;
- drm_bridge_disable(dsi->bridge);
- vc4_dsi_ulps(dsi, true);
- drm_bridge_post_disable(dsi->bridge);
- clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->escape_clock); clk_disable_unprepare(dsi->pixel_clock);
@@ -764,6 +768,13 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) pm_runtime_put(dev); }
+static void vc4_dsi_bridge_disable(struct drm_bridge *bridge) +{
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
- vc4_dsi_ulps(dsi, true);
+}
/* Extends the mode's blank intervals to handle BCM2835's integer-only
- DSI PLL divider.
@@ -777,12 +788,11 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
- higher-than-expected clock rate to the panel, but that's what the
- firmware does too.
*/ -static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
- struct vc4_dsi *dsi = vc4_encoder->dsi;
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); unsigned long parent_rate = clk_get_rate(phy_parent); unsigned long pixel_clock_hz = mode->clock * 1000;
@@ -816,11 +826,11 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, return true; }
-static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) +static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder = bridge->encoder; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
- struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
- struct vc4_dsi *dsi = vc4_encoder->dsi;
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct device *dev = &dsi->pdev->dev; bool debug_dump_regs = false; unsigned long hs_clock;
@@ -1054,8 +1064,12 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) }
vc4_dsi_ulps(dsi, false); +}
- drm_bridge_pre_enable(dsi->bridge);
If I'm not mistaken this switches the order of the DSI's encoder pre-enable and the next bridge's pre-enable. I think it's true for post-disable too. It may not be a problem, but have this been tested ?
+static void vc4_dsi_bridge_enable(struct drm_bridge *bridge) +{
struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
bool debug_dump_regs = false;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { DSI_PORT_WRITE(DISP0_CTRL,
@@ -1072,8 +1086,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) DSI_DISP0_ENABLE); }
- drm_bridge_enable(dsi->bridge);
- if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&dsi->pdev->dev); dev_info(&dsi->pdev->dev, "DSI regs after:\n");
@@ -1290,10 +1302,12 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = { .transfer = vc4_dsi_host_transfer, };
-static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
- .disable = vc4_dsi_encoder_disable,
- .enable = vc4_dsi_encoder_enable,
- .mode_fixup = vc4_dsi_encoder_mode_fixup,
+static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
- .pre_enable = vc4_dsi_bridge_pre_enable,
- .enable = vc4_dsi_bridge_enable,
- .disable = vc4_dsi_bridge_disable,
- .post_disable = vc4_dsi_bridge_post_disable,
- .mode_fixup = vc4_dsi_bridge_mode_fixup,
};
static const struct of_device_id vc4_dsi_dt_match[] = { @@ -1445,6 +1459,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 *next_bridge; struct drm_panel *panel; const struct of_device_id *match; dma_cap_mask_t dma_mask;
@@ -1561,7 +1576,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);
if (ret) { /* If the bridge or panel pointed by dev->of_node is not&panel, &next_bridge);
- enabled, just return 0 here so that we don't prevent the DRM
@@ -1576,10 +1591,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) }
if (panel) {
dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel,
next_bridge = devm_drm_panel_bridge_add_typed(dev, panel, DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(dsi->bridge))
return PTR_ERR(dsi->bridge);
if (IS_ERR(next_bridge))
return PTR_ERR(next_bridge);
}
/* The esc clock rate is supposed to always be 100Mhz. */
@@ -1598,19 +1613,20 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs, 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);
- /* Declare ourself as the first bridge element. */
- dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
- ret = drm_bridge_attach(dsi->encoder, &dsi->bridge, NULL);
- if (ret) {
dev_err(dev, "bridge attach failed: %d\n", ret);
return ret;
- }
- ret = drm_bridge_attach(dsi->encoder, next_bridge, &dsi->bridge); if (ret) { dev_err(dev, "bridge attach failed: %d\n", ret); return ret; }
This is usually done in the bridge attach operation. As we're in control we can attach the next bridge here, but I think the driver would look more standard if you moved the second attach call to this bridge's attach operation.
With this fixed, and if the driver has been tested and the enable/disable order change doesn't cause issues,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
/* 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;
if (dsi->port == 0) vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
@@ -1629,7 +1645,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->bridge.next) pm_runtime_disable(dev);
vc4_dsi_encoder_destroy(dsi->encoder);
On Sun, 24 Nov 2019 12:01:30 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:52PM +0200, Boris Brezillon wrote:
Encoder drivers will progressively transition to the drm_bridge interface in place of the drm_encoder one.
Let's start with the VC4 driver, and use the ->pre_{enable,disable}() hooks to get rid of the hack resetting encoder->bridge.next (which was needed to control the encoder/bridge enable/disable sequence).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Embed a drm_bridge object in vc4_dsi since drm_encoder no longer has a dummy bridge
Changes in v2:
- New patch (replaces "drm/vc4: Get rid of the dsi->bridge field")
drivers/gpu/drm/vc4/vc4_dsi.c | 88 +++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index c9ba83ed49b9..49f8a313e759 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -498,7 +498,11 @@ struct vc4_dsi {
struct mipi_dsi_host dsi_host; struct drm_encoder *encoder;
- struct drm_bridge *bridge;
/* Embed a bridge object so we can implement bridge funcs instead of
* encoder ones.
*/
struct drm_bridge bridge;
void __iomem *regs;
@@ -543,6 +547,11 @@ struct vc4_dsi { struct debugfs_regset32 regset; };
+static inline struct vc4_dsi *bridge_to_vc4_dsi(struct drm_bridge *bridge) +{
- return container_of(bridge, struct vc4_dsi, bridge);
+}
#define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host)
static inline void @@ -747,16 +756,11 @@ dsi_esc_timing(u32 ns) return DIV_ROUND_UP(ns, ESC_TIME_NS); }
-static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) +static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge) {
- struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
- struct vc4_dsi *dsi = vc4_encoder->dsi;
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct device *dev = &dsi->pdev->dev;
- drm_bridge_disable(dsi->bridge);
- vc4_dsi_ulps(dsi, true);
- drm_bridge_post_disable(dsi->bridge);
- clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->escape_clock); clk_disable_unprepare(dsi->pixel_clock);
@@ -764,6 +768,13 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) pm_runtime_put(dev); }
+static void vc4_dsi_bridge_disable(struct drm_bridge *bridge) +{
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
- vc4_dsi_ulps(dsi, true);
+}
/* Extends the mode's blank intervals to handle BCM2835's integer-only
- DSI PLL divider.
@@ -777,12 +788,11 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
- higher-than-expected clock rate to the panel, but that's what the
- firmware does too.
*/ -static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
- struct vc4_dsi *dsi = vc4_encoder->dsi;
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); unsigned long parent_rate = clk_get_rate(phy_parent); unsigned long pixel_clock_hz = mode->clock * 1000;
@@ -816,11 +826,11 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, return true; }
-static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) +static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder = bridge->encoder; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
- struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
- struct vc4_dsi *dsi = vc4_encoder->dsi;
- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct device *dev = &dsi->pdev->dev; bool debug_dump_regs = false; unsigned long hs_clock;
@@ -1054,8 +1064,12 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) }
vc4_dsi_ulps(dsi, false); +}
- drm_bridge_pre_enable(dsi->bridge);
If I'm not mistaken this switches the order of the DSI's encoder pre-enable and the next bridge's pre-enable. I think it's true for post-disable too. It may not be a problem, but have this been tested ?
No, it hasn't been tested (I don't have a Rpi with a DSI panel at hand), and now that you mention it, I don't think it will work (I had forgotten drm_bridge_pre_enable() iterates the bridge chain in reverse order). Well, it will work if the DSI encoder is connected to the RPi DSI panel since ->prepare() doesn't do anything, but I see that some DSI panel drivers send DSI commands in their ->prepare() method (BTW, we should really document the fact that panel drivers can send DSI commands in the ->prepare() hook).
The only way this can be fixed is by putting the vc4_dsi_pre_enable/post_disable() code in runtime PM resume/suspend hooks and let the vc4_dsi_host_transfer() call pm_runtime_get_sync(), pm_runtime_put() every time a msg is sent.
+static void vc4_dsi_bridge_enable(struct drm_bridge *bridge) +{
struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
bool debug_dump_regs = false;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { DSI_PORT_WRITE(DISP0_CTRL,
@@ -1072,8 +1086,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) DSI_DISP0_ENABLE); }
- drm_bridge_enable(dsi->bridge);
- if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&dsi->pdev->dev); dev_info(&dsi->pdev->dev, "DSI regs after:\n");
@@ -1290,10 +1302,12 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = { .transfer = vc4_dsi_host_transfer, };
-static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
- .disable = vc4_dsi_encoder_disable,
- .enable = vc4_dsi_encoder_enable,
- .mode_fixup = vc4_dsi_encoder_mode_fixup,
+static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
- .pre_enable = vc4_dsi_bridge_pre_enable,
- .enable = vc4_dsi_bridge_enable,
- .disable = vc4_dsi_bridge_disable,
- .post_disable = vc4_dsi_bridge_post_disable,
- .mode_fixup = vc4_dsi_bridge_mode_fixup,
};
static const struct of_device_id vc4_dsi_dt_match[] = { @@ -1445,6 +1459,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 *next_bridge; struct drm_panel *panel; const struct of_device_id *match; dma_cap_mask_t dma_mask;
@@ -1561,7 +1576,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);
if (ret) { /* If the bridge or panel pointed by dev->of_node is not&panel, &next_bridge);
- enabled, just return 0 here so that we don't prevent the DRM
@@ -1576,10 +1591,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) }
if (panel) {
dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel,
next_bridge = devm_drm_panel_bridge_add_typed(dev, panel, DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(dsi->bridge))
return PTR_ERR(dsi->bridge);
if (IS_ERR(next_bridge))
return PTR_ERR(next_bridge);
}
/* The esc clock rate is supposed to always be 100Mhz. */
@@ -1598,19 +1613,20 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs, 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);
- /* Declare ourself as the first bridge element. */
- dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
- ret = drm_bridge_attach(dsi->encoder, &dsi->bridge, NULL);
- if (ret) {
dev_err(dev, "bridge attach failed: %d\n", ret);
return ret;
- }
- ret = drm_bridge_attach(dsi->encoder, next_bridge, &dsi->bridge); if (ret) { dev_err(dev, "bridge attach failed: %d\n", ret); return ret; }
This is usually done in the bridge attach operation. As we're in control we can attach the next bridge here, but I think the driver would look more standard if you moved the second attach call to this bridge's attach operation.
I agree.
With this fixed, and if the driver has been tested and the enable/disable order change doesn't cause issues,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
/* 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;
if (dsi->port == 0) vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
@@ -1629,7 +1645,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->bridge.next) pm_runtime_disable(dev);
vc4_dsi_encoder_destroy(dsi->encoder);
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 Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- Changes in v2: * Add Laurent's R-b (I'd like to have a R-b from the DRM exynos maintainers before applying that one) --- 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; } }
2019년 10월 24일 (목) 오전 12:45, Boris Brezillon boris.brezillon@collabora.com님이 작성:
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 Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Changes in v2:
- Add Laurent's R-b (I'd like to have a R-b from the DRM exynos maintainers before applying that one)
Acked-by: Inki Dae inki.dae@samsung.com
Thanks, Inki Dae
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; } }
-- 2.21.0
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Encoder drivers will progressively transition to the drm_bridge interface in place of the drm_encoder one.
Converting the Exynos DSI encoder driver to this approach allows us to use the ->pre_{enable,disable}() hooks and get rid of the hack resetting encoder->bridge.next (which was needed to control the encoder/bridge enable/disable sequence).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * Embed a drm_bridge object in exynos_dsi since drm_encoder no longer has a dummy bridge
Changes in v2: * New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge") --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++---------- 1 file changed, 55 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 72726f2c7a9f..3915f50b005e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
struct exynos_dsi { struct drm_encoder encoder; + struct drm_bridge bridge; 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; @@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e) return container_of(e, struct exynos_dsi, encoder); }
+static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b) +{ + return container_of(b, struct exynos_dsi, bridge); +} + enum reg_idx { DSIM_STATUS_REG, /* Status register */ DSIM_SWRST_REG, /* Software reset register */ @@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi) } }
-static void exynos_dsi_enable(struct drm_encoder *encoder) +static void exynos_dsi_pre_enable(struct drm_bridge *bridge) { - struct exynos_dsi *dsi = encoder_to_dsi(encoder); + struct exynos_dsi *dsi = bridge_to_dsi(bridge); int ret;
if (dsi->state & DSIM_STATE_ENABLED) return;
pm_runtime_get_sync(dsi->dev); - dsi->state |= DSIM_STATE_ENABLED;
if (dsi->panel) { ret = drm_panel_prepare(dsi->panel); if (ret < 0) goto err_put_sync; - } else { - drm_bridge_pre_enable(dsi->out_bridge); }
+ dsi->state |= DSIM_STATE_ENABLED; + return; + +err_put_sync: + pm_runtime_put(dsi->dev); +} + +static void exynos_dsi_enable(struct drm_bridge *bridge) +{ + struct exynos_dsi *dsi = bridge_to_dsi(bridge); + int ret; + + if (!(dsi->state & DSIM_STATE_ENABLED) || + (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE)) + return; + exynos_dsi_set_display_mode(dsi); exynos_dsi_set_display_enable(dsi, true);
@@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) ret = drm_panel_enable(dsi->panel); if (ret < 0) goto err_display_disable; - } else { - drm_bridge_enable(dsi->out_bridge); }
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE; @@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) err_display_disable: exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel); - -err_put_sync: - dsi->state &= ~DSIM_STATE_ENABLED; - pm_runtime_put(dsi->dev); }
-static void exynos_dsi_disable(struct drm_encoder *encoder) +static void exynos_dsi_disable(struct drm_bridge *bridge) { - struct exynos_dsi *dsi = encoder_to_dsi(encoder); + struct exynos_dsi *dsi = bridge_to_dsi(bridge); + + if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE)) + return; + + drm_panel_disable(dsi->panel); + exynos_dsi_set_display_enable(dsi, false); + dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE; +} + +static void exynos_dsi_post_disable(struct drm_bridge *bridge) +{ + struct exynos_dsi *dsi = bridge_to_dsi(bridge);
if (!(dsi->state & DSIM_STATE_ENABLED)) return;
- dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE; - - drm_panel_disable(dsi->panel); - drm_bridge_disable(dsi->out_bridge); - exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel); - drm_bridge_post_disable(dsi->out_bridge); - dsi->state &= ~DSIM_STATE_ENABLED; pm_runtime_put_sync(dsi->dev); + dsi->state &= ~DSIM_STATE_ENABLED; }
static enum drm_connector_status @@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder) return 0; }
-static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { +static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = { + .pre_enable = exynos_dsi_pre_enable, .enable = exynos_dsi_enable, .disable = exynos_dsi_disable, + .post_disable = exynos_dsi_post_disable, };
static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = { @@ -1520,9 +1540,7 @@ 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; + drm_bridge_attach(encoder, out_bridge, &dsi->bridge); } else { int ret = exynos_dsi_create_connector(encoder);
@@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host); + struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
if (dsi->panel) { mutex_lock(&drm->mode_config.mutex); - exynos_dsi_disable(&dsi->encoder); + exynos_dsi_disable(&dsi->bridge); + exynos_dsi_post_disable(&dsi->bridge); drm_panel_detach(dsi->panel); 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; + } else if (out_bridge && out_bridge->funcs->detach) { + out_bridge->funcs->detach(out_bridge); }
if (drm->mode_config.poll_enabled) @@ -1687,16 +1705,18 @@ 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);
- drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs); - ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); if (ret < 0) return ret;
+ /* Declare ourself as the first bridge element. */ + dsi->bridge.funcs = &exynos_dsi_bridge_funcs; + drm_bridge_attach(encoder, &dsi->bridge, NULL); + if (dsi->in_bridge_node) { in_bridge = of_drm_find_bridge(dsi->in_bridge_node); if (in_bridge) - drm_bridge_attach(encoder, in_bridge, NULL); + drm_bridge_attach(encoder, in_bridge, &dsi->bridge); }
return mipi_dsi_host_register(&dsi->dsi_host); @@ -1708,7 +1728,8 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master, struct drm_encoder *encoder = dev_get_drvdata(dev); struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- exynos_dsi_disable(encoder); + exynos_dsi_disable(&dsi->bridge); + exynos_dsi_post_disable(&dsi->bridge);
mipi_dsi_host_unregister(&dsi->dsi_host); }
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:54PM +0200, Boris Brezillon wrote:
Encoder drivers will progressively transition to the drm_bridge interface in place of the drm_encoder one.
Converting the Exynos DSI encoder driver to this approach allows us to use the ->pre_{enable,disable}() hooks and get rid of the hack resetting encoder->bridge.next (which was needed to control the encoder/bridge enable/disable sequence).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Embed a drm_bridge object in exynos_dsi since drm_encoder no longer has a dummy bridge
Changes in v2:
- New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge")
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++---------- 1 file changed, 55 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 72726f2c7a9f..3915f50b005e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
struct exynos_dsi { struct drm_encoder encoder;
- struct drm_bridge bridge; 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;
@@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e) return container_of(e, struct exynos_dsi, encoder); }
+static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b) +{
- return container_of(b, struct exynos_dsi, bridge);
+}
enum reg_idx { DSIM_STATUS_REG, /* Status register */ DSIM_SWRST_REG, /* Software reset register */ @@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi) } }
-static void exynos_dsi_enable(struct drm_encoder *encoder) +static void exynos_dsi_pre_enable(struct drm_bridge *bridge) {
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
struct exynos_dsi *dsi = bridge_to_dsi(bridge); int ret;
if (dsi->state & DSIM_STATE_ENABLED) return;
This can probably be removed now as the core should ensure that double-enable or double-disable never occurs, but it can be done in a separate patch.
pm_runtime_get_sync(dsi->dev);
dsi->state |= DSIM_STATE_ENABLED;
if (dsi->panel) { ret = drm_panel_prepare(dsi->panel); if (ret < 0) goto err_put_sync;
} else {
drm_bridge_pre_enable(dsi->out_bridge);
}
It would be nice to switch to the drm panel bridge, but that can also be done on top of this series.
- dsi->state |= DSIM_STATE_ENABLED;
- return;
+err_put_sync:
- pm_runtime_put(dsi->dev);
+}
+static void exynos_dsi_enable(struct drm_bridge *bridge) +{
- struct exynos_dsi *dsi = bridge_to_dsi(bridge);
- int ret;
- if (!(dsi->state & DSIM_STATE_ENABLED) ||
(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
return;
- exynos_dsi_set_display_mode(dsi); exynos_dsi_set_display_enable(dsi, true);
@@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) ret = drm_panel_enable(dsi->panel); if (ret < 0) goto err_display_disable;
} else {
drm_bridge_enable(dsi->out_bridge);
}
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
@@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) err_display_disable: exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel);
Does this belong here, as drm_panel_prepare() was called in exynos_dsi_pre_enable() ?
-err_put_sync:
- dsi->state &= ~DSIM_STATE_ENABLED;
- pm_runtime_put(dsi->dev);
}
-static void exynos_dsi_disable(struct drm_encoder *encoder) +static void exynos_dsi_disable(struct drm_bridge *bridge) {
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct exynos_dsi *dsi = bridge_to_dsi(bridge);
- if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
return;
- drm_panel_disable(dsi->panel);
- exynos_dsi_set_display_enable(dsi, false);
- dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
+}
+static void exynos_dsi_post_disable(struct drm_bridge *bridge) +{
struct exynos_dsi *dsi = bridge_to_dsi(bridge);
if (!(dsi->state & DSIM_STATE_ENABLED)) return;
- dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
- drm_panel_disable(dsi->panel);
- drm_bridge_disable(dsi->out_bridge);
- exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel);
- drm_bridge_post_disable(dsi->out_bridge);
- dsi->state &= ~DSIM_STATE_ENABLED; pm_runtime_put_sync(dsi->dev);
- dsi->state &= ~DSIM_STATE_ENABLED;
}
static enum drm_connector_status @@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder) return 0; }
-static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { +static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
- .pre_enable = exynos_dsi_pre_enable, .enable = exynos_dsi_enable, .disable = exynos_dsi_disable,
- .post_disable = exynos_dsi_post_disable,
};
static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = { @@ -1520,9 +1540,7 @@ 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);drm_bridge_attach(encoder, out_bridge, &dsi->bridge);
@@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host);
struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
if (dsi->panel) { mutex_lock(&drm->mode_config.mutex);
exynos_dsi_disable(&dsi->encoder);
exynos_dsi_disable(&dsi->bridge);
drm_panel_detach(dsi->panel); dsi->panel = NULL; dsi->connector.status = connector_status_disconnected; mutex_unlock(&drm->mode_config.mutex);exynos_dsi_post_disable(&dsi->bridge);
- } else {
if (dsi->out_bridge->funcs->detach)
dsi->out_bridge->funcs->detach(dsi->out_bridge);
dsi->out_bridge = NULL;
- } else if (out_bridge && out_bridge->funcs->detach) {
out_bridge->funcs->detach(out_bridge);
Maybe drm_bridge_detach() ?
}
if (drm->mode_config.poll_enabled) @@ -1687,16 +1705,18 @@ 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);
- drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
- ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); if (ret < 0) return ret;
- /* Declare ourself as the first bridge element. */
- dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
- drm_bridge_attach(encoder, &dsi->bridge, NULL);
- if (dsi->in_bridge_node) { in_bridge = of_drm_find_bridge(dsi->in_bridge_node); if (in_bridge)
drm_bridge_attach(encoder, in_bridge, NULL);
}drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
Same as for patch 01/21, maybe this could be moved to this bridge's attach operation ? Actually, now that I've read the code, this in_bridge part looks weird. Why would the DSI encoder have an input bridge that is has to manage itself ?
I don't feel confident enough to ack this patch. It goes in the right direction as far as the API evolution is concerned, so if you get an ack from the Exynos maintainers, I'm happy enough to see it merged.
return mipi_dsi_host_register(&dsi->dsi_host); @@ -1708,7 +1728,8 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master, struct drm_encoder *encoder = dev_get_drvdata(dev); struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- exynos_dsi_disable(encoder);
exynos_dsi_disable(&dsi->bridge);
exynos_dsi_post_disable(&dsi->bridge);
mipi_dsi_host_unregister(&dsi->dsi_host);
}
Hi Laurent,
On Sun, 24 Nov 2019 12:24:33 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:54PM +0200, Boris Brezillon wrote:
Encoder drivers will progressively transition to the drm_bridge interface in place of the drm_encoder one.
Converting the Exynos DSI encoder driver to this approach allows us to use the ->pre_{enable,disable}() hooks and get rid of the hack resetting encoder->bridge.next (which was needed to control the encoder/bridge enable/disable sequence).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Embed a drm_bridge object in exynos_dsi since drm_encoder no longer has a dummy bridge
Changes in v2:
- New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge")
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++---------- 1 file changed, 55 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 72726f2c7a9f..3915f50b005e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
struct exynos_dsi { struct drm_encoder encoder;
- struct drm_bridge bridge; 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;
@@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e) return container_of(e, struct exynos_dsi, encoder); }
+static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b) +{
- return container_of(b, struct exynos_dsi, bridge);
+}
enum reg_idx { DSIM_STATUS_REG, /* Status register */ DSIM_SWRST_REG, /* Software reset register */ @@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi) } }
-static void exynos_dsi_enable(struct drm_encoder *encoder) +static void exynos_dsi_pre_enable(struct drm_bridge *bridge) {
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
struct exynos_dsi *dsi = bridge_to_dsi(bridge); int ret;
if (dsi->state & DSIM_STATE_ENABLED) return;
This can probably be removed now as the core should ensure that double-enable or double-disable never occurs, but it can be done in a separate patch.
Except the enable/disable() implementations handle failures (the framework does not expect those to fails BTW), and I guess it's important to know the actual HW state in order to keep runtime PM get/put calls balanced.
pm_runtime_get_sync(dsi->dev);
dsi->state |= DSIM_STATE_ENABLED;
if (dsi->panel) { ret = drm_panel_prepare(dsi->panel); if (ret < 0) goto err_put_sync;
} else {
drm_bridge_pre_enable(dsi->out_bridge);
}
It would be nice to switch to the drm panel bridge, but that can also be done on top of this series.
I agree, just didn't want to add more stuff to this series.
- dsi->state |= DSIM_STATE_ENABLED;
- return;
+err_put_sync:
- pm_runtime_put(dsi->dev);
+}
+static void exynos_dsi_enable(struct drm_bridge *bridge) +{
- struct exynos_dsi *dsi = bridge_to_dsi(bridge);
- int ret;
- if (!(dsi->state & DSIM_STATE_ENABLED) ||
(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
return;
- exynos_dsi_set_display_mode(dsi); exynos_dsi_set_display_enable(dsi, true);
@@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) ret = drm_panel_enable(dsi->panel); if (ret < 0) goto err_display_disable;
} else {
drm_bridge_enable(dsi->out_bridge);
}
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
@@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) err_display_disable: exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel);
Does this belong here, as drm_panel_prepare() was called in exynos_dsi_pre_enable() ?
Nope, this one should be dropped.
-err_put_sync:
- dsi->state &= ~DSIM_STATE_ENABLED;
- pm_runtime_put(dsi->dev);
}
-static void exynos_dsi_disable(struct drm_encoder *encoder) +static void exynos_dsi_disable(struct drm_bridge *bridge) {
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct exynos_dsi *dsi = bridge_to_dsi(bridge);
- if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
return;
- drm_panel_disable(dsi->panel);
- exynos_dsi_set_display_enable(dsi, false);
- dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
+}
+static void exynos_dsi_post_disable(struct drm_bridge *bridge) +{
struct exynos_dsi *dsi = bridge_to_dsi(bridge);
if (!(dsi->state & DSIM_STATE_ENABLED)) return;
- dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
- drm_panel_disable(dsi->panel);
- drm_bridge_disable(dsi->out_bridge);
- exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel);
- drm_bridge_post_disable(dsi->out_bridge);
- dsi->state &= ~DSIM_STATE_ENABLED; pm_runtime_put_sync(dsi->dev);
- dsi->state &= ~DSIM_STATE_ENABLED;
}
static enum drm_connector_status @@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder) return 0; }
-static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { +static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
- .pre_enable = exynos_dsi_pre_enable, .enable = exynos_dsi_enable, .disable = exynos_dsi_disable,
- .post_disable = exynos_dsi_post_disable,
};
static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = { @@ -1520,9 +1540,7 @@ 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);drm_bridge_attach(encoder, out_bridge, &dsi->bridge);
@@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host);
struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
if (dsi->panel) { mutex_lock(&drm->mode_config.mutex);
exynos_dsi_disable(&dsi->encoder);
exynos_dsi_disable(&dsi->bridge);
drm_panel_detach(dsi->panel); dsi->panel = NULL; dsi->connector.status = connector_status_disconnected; mutex_unlock(&drm->mode_config.mutex);exynos_dsi_post_disable(&dsi->bridge);
- } else {
if (dsi->out_bridge->funcs->detach)
dsi->out_bridge->funcs->detach(dsi->out_bridge);
dsi->out_bridge = NULL;
- } else if (out_bridge && out_bridge->funcs->detach) {
out_bridge->funcs->detach(out_bridge);
Maybe drm_bridge_detach() ?
This function is not exported, and I suppose that's why they used the function pointer in this driver. I bet there's a good reason for not exposing this function...
}
if (drm->mode_config.poll_enabled) @@ -1687,16 +1705,18 @@ 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);
- drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
- ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); if (ret < 0) return ret;
- /* Declare ourself as the first bridge element. */
- dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
- drm_bridge_attach(encoder, &dsi->bridge, NULL);
- if (dsi->in_bridge_node) { in_bridge = of_drm_find_bridge(dsi->in_bridge_node); if (in_bridge)
drm_bridge_attach(encoder, in_bridge, NULL);
}drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
Same as for patch 01/21, maybe this could be moved to this bridge's attach operation ? Actually, now that I've read the code, this in_bridge part looks weird. Why would the DSI encoder have an input bridge that is has to manage itself ?
Yes, I know, it doesn't make any sense. Either we're dealing with a bridge which can be chained to other bridges (can be placed in the middle of a chain as well), or we're dealing with an encoder which precedes any bridges. In the latter case (which is how exynos_dsi is implemented) in_bridge doesn't have any meaning, and that's even worse since we're placing the so-called input bridge (AKA previous bridge) after our encoder (that's what drm_bridge_attach(encoder, in_bridge, NULL) does).
TBH, I didn't want to go that far and fix existing drivers when I started this series, so I think I'll rework the patchset to get rid of the VC4 and exynos patches, even if that means having 2 drivers that mess up with the encoder->bridge_chain list.
Regards,
Boris
On Sun, 24 Nov 2019 14:17:27 +0100 Boris Brezillon boris.brezillon@collabora.com wrote:
@@ -1687,16 +1705,18 @@ 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);
- drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
- ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); if (ret < 0) return ret;
- /* Declare ourself as the first bridge element. */
- dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
- drm_bridge_attach(encoder, &dsi->bridge, NULL);
- if (dsi->in_bridge_node) { in_bridge = of_drm_find_bridge(dsi->in_bridge_node); if (in_bridge)
drm_bridge_attach(encoder, in_bridge, NULL);
}drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
Same as for patch 01/21, maybe this could be moved to this bridge's attach operation ? Actually, now that I've read the code, this in_bridge part looks weird. Why would the DSI encoder have an input bridge that is has to manage itself ?
Yes, I know, it doesn't make any sense. Either we're dealing with a bridge which can be chained to other bridges (can be placed in the middle of a chain as well), or we're dealing with an encoder which precedes any bridges. In the latter case (which is how exynos_dsi is implemented) in_bridge doesn't have any meaning, and that's even worse since we're placing the so-called input bridge (AKA previous bridge) after our encoder (that's what drm_bridge_attach(encoder, in_bridge, NULL) does).
More on that topic: I checked the exynos dts we have in mainline and no one is making use of the ports part of the exynos_dsim bindings, so maybe we should just deprecate it. The other option would be to patch the driver to act as a real bridge, but I can't do that without someone testing my changes and I didn't get much feedback from Exynos maintainers so far...
Hi Boris,
On Sun, Nov 24, 2019 at 02:17:27PM +0100, Boris Brezillon wrote:
On Sun, 24 Nov 2019 12:24:33 +0200 Laurent Pinchart wrote:
On Wed, Oct 23, 2019 at 05:44:54PM +0200, Boris Brezillon wrote:
Encoder drivers will progressively transition to the drm_bridge interface in place of the drm_encoder one.
Converting the Exynos DSI encoder driver to this approach allows us to use the ->pre_{enable,disable}() hooks and get rid of the hack resetting encoder->bridge.next (which was needed to control the encoder/bridge enable/disable sequence).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Embed a drm_bridge object in exynos_dsi since drm_encoder no longer has a dummy bridge
Changes in v2:
- New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge")
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++---------- 1 file changed, 55 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 72726f2c7a9f..3915f50b005e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
struct exynos_dsi { struct drm_encoder encoder;
- struct drm_bridge bridge; 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;
@@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e) return container_of(e, struct exynos_dsi, encoder); }
+static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b) +{
- return container_of(b, struct exynos_dsi, bridge);
+}
enum reg_idx { DSIM_STATUS_REG, /* Status register */ DSIM_SWRST_REG, /* Software reset register */ @@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi) } }
-static void exynos_dsi_enable(struct drm_encoder *encoder) +static void exynos_dsi_pre_enable(struct drm_bridge *bridge) {
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
struct exynos_dsi *dsi = bridge_to_dsi(bridge); int ret;
if (dsi->state & DSIM_STATE_ENABLED) return;
This can probably be removed now as the core should ensure that double-enable or double-disable never occurs, but it can be done in a separate patch.
Except the enable/disable() implementations handle failures (the framework does not expect those to fails BTW), and I guess it's important to know the actual HW state in order to keep runtime PM get/put calls balanced.
pm_runtime_get_sync(dsi->dev);
dsi->state |= DSIM_STATE_ENABLED;
if (dsi->panel) { ret = drm_panel_prepare(dsi->panel); if (ret < 0) goto err_put_sync;
} else {
drm_bridge_pre_enable(dsi->out_bridge);
}
It would be nice to switch to the drm panel bridge, but that can also be done on top of this series.
I agree, just didn't want to add more stuff to this series.
- dsi->state |= DSIM_STATE_ENABLED;
- return;
+err_put_sync:
- pm_runtime_put(dsi->dev);
+}
+static void exynos_dsi_enable(struct drm_bridge *bridge) +{
- struct exynos_dsi *dsi = bridge_to_dsi(bridge);
- int ret;
- if (!(dsi->state & DSIM_STATE_ENABLED) ||
(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
return;
- exynos_dsi_set_display_mode(dsi); exynos_dsi_set_display_enable(dsi, true);
@@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) ret = drm_panel_enable(dsi->panel); if (ret < 0) goto err_display_disable;
} else {
drm_bridge_enable(dsi->out_bridge);
}
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
@@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) err_display_disable: exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel);
Does this belong here, as drm_panel_prepare() was called in exynos_dsi_pre_enable() ?
Nope, this one should be dropped.
-err_put_sync:
- dsi->state &= ~DSIM_STATE_ENABLED;
- pm_runtime_put(dsi->dev);
}
-static void exynos_dsi_disable(struct drm_encoder *encoder) +static void exynos_dsi_disable(struct drm_bridge *bridge) {
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct exynos_dsi *dsi = bridge_to_dsi(bridge);
- if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
return;
- drm_panel_disable(dsi->panel);
- exynos_dsi_set_display_enable(dsi, false);
- dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
+}
+static void exynos_dsi_post_disable(struct drm_bridge *bridge) +{
struct exynos_dsi *dsi = bridge_to_dsi(bridge);
if (!(dsi->state & DSIM_STATE_ENABLED)) return;
- dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
- drm_panel_disable(dsi->panel);
- drm_bridge_disable(dsi->out_bridge);
- exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel);
- drm_bridge_post_disable(dsi->out_bridge);
- dsi->state &= ~DSIM_STATE_ENABLED; pm_runtime_put_sync(dsi->dev);
- dsi->state &= ~DSIM_STATE_ENABLED;
}
static enum drm_connector_status @@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder) return 0; }
-static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { +static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
- .pre_enable = exynos_dsi_pre_enable, .enable = exynos_dsi_enable, .disable = exynos_dsi_disable,
- .post_disable = exynos_dsi_post_disable,
};
static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = { @@ -1520,9 +1540,7 @@ 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);drm_bridge_attach(encoder, out_bridge, &dsi->bridge);
@@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host);
struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
if (dsi->panel) { mutex_lock(&drm->mode_config.mutex);
exynos_dsi_disable(&dsi->encoder);
exynos_dsi_disable(&dsi->bridge);
drm_panel_detach(dsi->panel); dsi->panel = NULL; dsi->connector.status = connector_status_disconnected; mutex_unlock(&drm->mode_config.mutex);exynos_dsi_post_disable(&dsi->bridge);
- } else {
if (dsi->out_bridge->funcs->detach)
dsi->out_bridge->funcs->detach(dsi->out_bridge);
dsi->out_bridge = NULL;
- } else if (out_bridge && out_bridge->funcs->detach) {
out_bridge->funcs->detach(out_bridge);
Maybe drm_bridge_detach() ?
This function is not exported, and I suppose that's why they used the function pointer in this driver. I bet there's a good reason for not exposing this function...
Indeed, my bad. It's called by drm_encoder_cleanup(), I assume it's not enough ? If there's a good use case for exporting it, then I think it should be exported.
}
if (drm->mode_config.poll_enabled) @@ -1687,16 +1705,18 @@ 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);
- drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
- ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); if (ret < 0) return ret;
- /* Declare ourself as the first bridge element. */
- dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
- drm_bridge_attach(encoder, &dsi->bridge, NULL);
- if (dsi->in_bridge_node) { in_bridge = of_drm_find_bridge(dsi->in_bridge_node); if (in_bridge)
drm_bridge_attach(encoder, in_bridge, NULL);
}drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
Same as for patch 01/21, maybe this could be moved to this bridge's attach operation ? Actually, now that I've read the code, this in_bridge part looks weird. Why would the DSI encoder have an input bridge that is has to manage itself ?
Yes, I know, it doesn't make any sense. Either we're dealing with a bridge which can be chained to other bridges (can be placed in the middle of a chain as well), or we're dealing with an encoder which precedes any bridges. In the latter case (which is how exynos_dsi is implemented) in_bridge doesn't have any meaning, and that's even worse since we're placing the so-called input bridge (AKA previous bridge) after our encoder (that's what drm_bridge_attach(encoder, in_bridge, NULL) does).
Can we get input from the exynos maintainers ? Or is the driver not actively maintained anymore ?
TBH, I didn't want to go that far and fix existing drivers when I started this series, so I think I'll rework the patchset to get rid of the VC4 and exynos patches, even if that means having 2 drivers that mess up with the encoder->bridge_chain list.
I don't mind the above changes really (and the one for VC4 seems pretty fine so far).
Change the prefix of bridge helpers targeting a bridge chain from drm_bridge_ to drm_bridge_chain_ to better reflect the fact that the operation will happen on all elements of chain, starting at the bridge passed in argument.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * None
Changes in v2: * Pass te bridge, not the encoder, so we can later act on a sub-chain instead of the whole chain --- drivers/gpu/drm/drm_atomic_helper.c | 19 +++-- drivers/gpu/drm/drm_bridge.c | 125 ++++++++++++++-------------- drivers/gpu/drm/drm_probe_helper.c | 2 +- drivers/gpu/drm/mediatek/mtk_hdmi.c | 4 +- include/drm/drm_bridge.h | 64 +++++++------- 5 files changed, 112 insertions(+), 102 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 587052751b48..cf678be58fa4 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -435,8 +435,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->bridge, + &new_crtc_state->mode, + &new_crtc_state->adjusted_mode); if (!ret) { DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); return -EINVAL; @@ -501,7 +502,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->bridge, mode); if (ret != MODE_OK) { DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); return ret; @@ -1020,7 +1021,7 @@ 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); + drm_atomic_bridge_chain_disable(encoder->bridge, old_state);
/* Right function depends upon target state. */ if (funcs) { @@ -1034,7 +1035,8 @@ 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); + drm_atomic_bridge_chain_post_disable(encoder->bridge, + old_state); }
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { @@ -1215,7 +1217,8 @@ 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->bridge, mode, + adjusted_mode); } }
@@ -1332,7 +1335,7 @@ 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); + drm_atomic_bridge_chain_pre_enable(encoder->bridge, old_state);
if (funcs) { if (funcs->atomic_enable) @@ -1343,7 +1346,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
- drm_atomic_bridge_enable(encoder->bridge, old_state); + drm_atomic_bridge_chain_enable(encoder->bridge, 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..54c874493c57 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -172,8 +172,8 @@ void drm_bridge_detach(struct drm_bridge *bridge) */
/** - * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the - * encoder chain + * drm_bridge_chain_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 @@ -186,9 +186,9 @@ void drm_bridge_detach(struct drm_bridge *bridge) * 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) +bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { bool ret = true;
@@ -198,15 +198,16 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge, 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); + ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode, + adjusted_mode);
return ret; } -EXPORT_SYMBOL(drm_bridge_mode_fixup); +EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
/** - * drm_bridge_mode_valid - validate the mode against all bridges in the - * encoder chain. + * drm_bridge_chain_mode_valid - validate the mode against all bridges in the + * encoder chain. * @bridge: bridge control structure * @mode: desired mode to be validated * @@ -219,8 +220,9 @@ EXPORT_SYMBOL(drm_bridge_mode_fixup); * 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) +enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) { enum drm_mode_status ret = MODE_OK;
@@ -233,12 +235,12 @@ enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, if (ret != MODE_OK) return ret;
- return drm_bridge_mode_valid(bridge->next, mode); + return drm_bridge_chain_mode_valid(bridge->next, mode); } -EXPORT_SYMBOL(drm_bridge_mode_valid); +EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
/** - * drm_bridge_disable - disables all bridges in the encoder chain + * drm_bridge_chain_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 @@ -247,20 +249,21 @@ EXPORT_SYMBOL(drm_bridge_mode_valid); * * Note: the bridge passed should be the one closest to the encoder */ -void drm_bridge_disable(struct drm_bridge *bridge) +void drm_bridge_chain_disable(struct drm_bridge *bridge) { if (!bridge) return;
- drm_bridge_disable(bridge->next); + drm_bridge_chain_disable(bridge->next);
if (bridge->funcs->disable) bridge->funcs->disable(bridge); } -EXPORT_SYMBOL(drm_bridge_disable); +EXPORT_SYMBOL(drm_bridge_chain_disable);
/** - * drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain + * drm_bridge_chain_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 @@ -269,7 +272,7 @@ EXPORT_SYMBOL(drm_bridge_disable); * * Note: the bridge passed should be the one closest to the encoder */ -void drm_bridge_post_disable(struct drm_bridge *bridge) +void drm_bridge_chain_post_disable(struct drm_bridge *bridge) { if (!bridge) return; @@ -277,25 +280,25 @@ void drm_bridge_post_disable(struct drm_bridge *bridge) if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge);
- drm_bridge_post_disable(bridge->next); + drm_bridge_chain_post_disable(bridge->next); } -EXPORT_SYMBOL(drm_bridge_post_disable); +EXPORT_SYMBOL(drm_bridge_chain_post_disable);
/** - * drm_bridge_mode_set - set proposed mode for all bridges in the - * encoder chain + * drm_bridge_chain_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 + * @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. * * 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) +void drm_bridge_chain_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) { if (!bridge) return; @@ -303,13 +306,13 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, if (bridge->funcs->mode_set) bridge->funcs->mode_set(bridge, mode, adjusted_mode);
- drm_bridge_mode_set(bridge->next, mode, adjusted_mode); + drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode); } -EXPORT_SYMBOL(drm_bridge_mode_set); +EXPORT_SYMBOL(drm_bridge_chain_mode_set);
/** - * drm_bridge_pre_enable - prepares for enabling all - * bridges in the encoder chain + * drm_bridge_chain_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 @@ -318,20 +321,20 @@ EXPORT_SYMBOL(drm_bridge_mode_set); * * Note: the bridge passed should be the one closest to the encoder */ -void drm_bridge_pre_enable(struct drm_bridge *bridge) +void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) { if (!bridge) return;
- drm_bridge_pre_enable(bridge->next); + drm_bridge_chain_pre_enable(bridge->next);
if (bridge->funcs->pre_enable) bridge->funcs->pre_enable(bridge); } -EXPORT_SYMBOL(drm_bridge_pre_enable); +EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
/** - * drm_bridge_enable - enables all bridges in the encoder chain + * drm_bridge_chain_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 @@ -340,7 +343,7 @@ EXPORT_SYMBOL(drm_bridge_pre_enable); * * Note that the bridge passed should be the one closest to the encoder */ -void drm_bridge_enable(struct drm_bridge *bridge) +void drm_bridge_chain_enable(struct drm_bridge *bridge) { if (!bridge) return; @@ -348,12 +351,12 @@ void drm_bridge_enable(struct drm_bridge *bridge) if (bridge->funcs->enable) bridge->funcs->enable(bridge);
- drm_bridge_enable(bridge->next); + drm_bridge_chain_enable(bridge->next); } -EXPORT_SYMBOL(drm_bridge_enable); +EXPORT_SYMBOL(drm_bridge_chain_enable);
/** - * drm_atomic_bridge_disable - disables all bridges in the encoder chain + * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain * @bridge: bridge control structure * @state: atomic state being committed * @@ -364,24 +367,24 @@ EXPORT_SYMBOL(drm_bridge_enable); * * Note: the bridge passed should be the one closest to the encoder */ -void drm_atomic_bridge_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return;
- drm_atomic_bridge_disable(bridge->next, state); + drm_atomic_bridge_chain_disable(bridge->next, state);
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_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
/** - * drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the - * encoder chain + * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges + * in the encoder chain * @bridge: bridge control structure * @state: atomic state being committed * @@ -392,8 +395,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_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) +void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return; @@ -403,13 +406,13 @@ void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, else if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge);
- drm_atomic_bridge_post_disable(bridge->next, state); + drm_atomic_bridge_chain_post_disable(bridge->next, state); } -EXPORT_SYMBOL(drm_atomic_bridge_post_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
/** - * drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the - * encoder chain + * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in + * the encoder chain * @bridge: bridge control structure * @state: atomic state being committed * @@ -420,23 +423,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_post_disable); * * 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) +void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return;
- drm_atomic_bridge_pre_enable(bridge->next, state); + drm_atomic_bridge_chain_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); } -EXPORT_SYMBOL(drm_atomic_bridge_pre_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
/** - * drm_atomic_bridge_enable - enables all bridges in the encoder chain + * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain * @bridge: bridge control structure * @state: atomic state being committed * @@ -447,8 +450,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_pre_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) +void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { if (!bridge) return; @@ -458,9 +461,9 @@ void drm_atomic_bridge_enable(struct drm_bridge *bridge, else if (bridge->funcs->enable) bridge->funcs->enable(bridge);
- drm_atomic_bridge_enable(bridge->next, state); + drm_atomic_bridge_chain_enable(bridge->next, state); } -EXPORT_SYMBOL(drm_atomic_bridge_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
#ifdef CONFIG_OF /** diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index a7c87abe88d0..c3ea722065c4 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -112,7 +112,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->bridge, mode); if (ret != MODE_OK) { /* There is also no point in continuing for crtc check * here. */ diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index c79b1f855d89..ea68b5adccbe 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.next, mode, + &adjusted_mode)) return MODE_BAD; }
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index c0a2286a81e9..726435baf4ad 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 @atomic_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 @atomic_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 @atomic_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_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode); +void drm_bridge_chain_disable(struct drm_bridge *bridge); +void drm_bridge_chain_post_disable(struct drm_bridge *bridge); +void drm_bridge_chain_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode); +void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); +void drm_bridge_chain_enable(struct drm_bridge *bridge);
-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_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, 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);
On 23/10/2019 17:44, Boris Brezillon wrote:
Change the prefix of bridge helpers targeting a bridge chain from drm_bridge_ to drm_bridge_chain_ to better reflect the fact that the operation will happen on all elements of chain, starting at the bridge passed in argument.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Pass te bridge, not the encoder, so we can later act on a sub-chain instead of the whole chain
drivers/gpu/drm/drm_atomic_helper.c | 19 +++-- drivers/gpu/drm/drm_bridge.c | 125 ++++++++++++++-------------- drivers/gpu/drm/drm_probe_helper.c | 2 +- drivers/gpu/drm/mediatek/mtk_hdmi.c | 4 +- include/drm/drm_bridge.h | 64 +++++++------- 5 files changed, 112 insertions(+), 102 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 587052751b48..cf678be58fa4 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -435,8 +435,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->bridge,
&new_crtc_state->mode,
if (!ret) { DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); return -EINVAL;&new_crtc_state->adjusted_mode);
@@ -501,7 +502,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->bridge, mode); if (ret != MODE_OK) { DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); return ret;
@@ -1020,7 +1021,7 @@ 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);
drm_atomic_bridge_chain_disable(encoder->bridge, old_state);
/* Right function depends upon target state. */ if (funcs) {
@@ -1034,7 +1035,8 @@ 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);
drm_atomic_bridge_chain_post_disable(encoder->bridge,
old_state);
}
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
@@ -1215,7 +1217,8 @@ 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->bridge, mode,
}adjusted_mode);
}
@@ -1332,7 +1335,7 @@ 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);
drm_atomic_bridge_chain_pre_enable(encoder->bridge, old_state);
if (funcs) { if (funcs->atomic_enable)
@@ -1343,7 +1346,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
drm_atomic_bridge_enable(encoder->bridge, old_state);
drm_atomic_bridge_chain_enable(encoder->bridge, 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..54c874493c57 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -172,8 +172,8 @@ void drm_bridge_detach(struct drm_bridge *bridge) */
/**
- drm_bridge_mode_fixup - fixup proposed mode for all bridges in the
encoder chain
- drm_bridge_chain_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
@@ -186,9 +186,9 @@ void drm_bridge_detach(struct drm_bridge *bridge)
- 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)
+bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{ bool ret = true;
@@ -198,15 +198,16 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge, 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);
ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
adjusted_mode);
return ret;
} -EXPORT_SYMBOL(drm_bridge_mode_fixup); +EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
/**
- drm_bridge_mode_valid - validate the mode against all bridges in the
encoder chain.
- drm_bridge_chain_mode_valid - validate the mode against all bridges in the
encoder chain.
- @bridge: bridge control structure
- @mode: desired mode to be validated
@@ -219,8 +220,9 @@ EXPORT_SYMBOL(drm_bridge_mode_fixup);
- 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)
+enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{ enum drm_mode_status ret = MODE_OK;
@@ -233,12 +235,12 @@ enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, if (ret != MODE_OK) return ret;
- return drm_bridge_mode_valid(bridge->next, mode);
- return drm_bridge_chain_mode_valid(bridge->next, mode);
} -EXPORT_SYMBOL(drm_bridge_mode_valid); +EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
/**
- drm_bridge_disable - disables all bridges in the encoder chain
- drm_bridge_chain_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
@@ -247,20 +249,21 @@ EXPORT_SYMBOL(drm_bridge_mode_valid);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_disable(struct drm_bridge *bridge) +void drm_bridge_chain_disable(struct drm_bridge *bridge) { if (!bridge) return;
- drm_bridge_disable(bridge->next);
drm_bridge_chain_disable(bridge->next);
if (bridge->funcs->disable) bridge->funcs->disable(bridge);
} -EXPORT_SYMBOL(drm_bridge_disable); +EXPORT_SYMBOL(drm_bridge_chain_disable);
/**
- drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain
- drm_bridge_chain_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
@@ -269,7 +272,7 @@ EXPORT_SYMBOL(drm_bridge_disable);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_post_disable(struct drm_bridge *bridge) +void drm_bridge_chain_post_disable(struct drm_bridge *bridge) { if (!bridge) return; @@ -277,25 +280,25 @@ void drm_bridge_post_disable(struct drm_bridge *bridge) if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge);
- drm_bridge_post_disable(bridge->next);
- drm_bridge_chain_post_disable(bridge->next);
} -EXPORT_SYMBOL(drm_bridge_post_disable); +EXPORT_SYMBOL(drm_bridge_chain_post_disable);
/**
- drm_bridge_mode_set - set proposed mode for all bridges in the
encoder chain
- drm_bridge_chain_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
- @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.
- 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)
+void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{ if (!bridge) return; @@ -303,13 +306,13 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, if (bridge->funcs->mode_set) bridge->funcs->mode_set(bridge, mode, adjusted_mode);
- drm_bridge_mode_set(bridge->next, mode, adjusted_mode);
- drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
} -EXPORT_SYMBOL(drm_bridge_mode_set); +EXPORT_SYMBOL(drm_bridge_chain_mode_set);
/**
- drm_bridge_pre_enable - prepares for enabling all
bridges in the encoder chain
- drm_bridge_chain_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
@@ -318,20 +321,20 @@ EXPORT_SYMBOL(drm_bridge_mode_set);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_pre_enable(struct drm_bridge *bridge) +void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) { if (!bridge) return;
- drm_bridge_pre_enable(bridge->next);
drm_bridge_chain_pre_enable(bridge->next);
if (bridge->funcs->pre_enable) bridge->funcs->pre_enable(bridge);
} -EXPORT_SYMBOL(drm_bridge_pre_enable); +EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
/**
- drm_bridge_enable - enables all bridges in the encoder chain
- drm_bridge_chain_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
@@ -340,7 +343,7 @@ EXPORT_SYMBOL(drm_bridge_pre_enable);
- Note that the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_enable(struct drm_bridge *bridge) +void drm_bridge_chain_enable(struct drm_bridge *bridge) { if (!bridge) return; @@ -348,12 +351,12 @@ void drm_bridge_enable(struct drm_bridge *bridge) if (bridge->funcs->enable) bridge->funcs->enable(bridge);
- drm_bridge_enable(bridge->next);
- drm_bridge_chain_enable(bridge->next);
} -EXPORT_SYMBOL(drm_bridge_enable); +EXPORT_SYMBOL(drm_bridge_chain_enable);
/**
- drm_atomic_bridge_disable - disables all bridges in the encoder chain
- drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -364,24 +367,24 @@ EXPORT_SYMBOL(drm_bridge_enable);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_atomic_bridge_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return;
- drm_atomic_bridge_disable(bridge->next, state);
drm_atomic_bridge_chain_disable(bridge->next, state);
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_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
/**
- drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the
encoder chain
- drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -392,8 +395,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_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)
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return; @@ -403,13 +406,13 @@ void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, else if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge);
- drm_atomic_bridge_post_disable(bridge->next, state);
- drm_atomic_bridge_chain_post_disable(bridge->next, state);
} -EXPORT_SYMBOL(drm_atomic_bridge_post_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
/**
- drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the
encoder chain
- drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -420,23 +423,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_post_disable);
- 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)
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return;
- drm_atomic_bridge_pre_enable(bridge->next, state);
drm_atomic_bridge_chain_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);
} -EXPORT_SYMBOL(drm_atomic_bridge_pre_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
/**
- drm_atomic_bridge_enable - enables all bridges in the encoder chain
- drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -447,8 +450,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_pre_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)
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return; @@ -458,9 +461,9 @@ void drm_atomic_bridge_enable(struct drm_bridge *bridge, else if (bridge->funcs->enable) bridge->funcs->enable(bridge);
- drm_atomic_bridge_enable(bridge->next, state);
- drm_atomic_bridge_chain_enable(bridge->next, state);
} -EXPORT_SYMBOL(drm_atomic_bridge_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
#ifdef CONFIG_OF /** diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index a7c87abe88d0..c3ea722065c4 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -112,7 +112,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, continue; }
ret = drm_bridge_mode_valid(encoder->bridge, mode);
if (ret != MODE_OK) { /* There is also no point in continuing for crtc check * here. */ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index c79b1f855d89..ea68b5adccbe 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.next, mode,
}&adjusted_mode)) return MODE_BAD;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index c0a2286a81e9..726435baf4ad 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 @atomic_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 @atomic_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 @atomic_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_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode);
+void drm_bridge_chain_disable(struct drm_bridge *bridge); +void drm_bridge_chain_post_disable(struct drm_bridge *bridge); +void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode);
+void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); +void drm_bridge_chain_enable(struct drm_bridge *bridge);
-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_bridge *bridge,
struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, 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);
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:55PM +0200, Boris Brezillon wrote:
Change the prefix of bridge helpers targeting a bridge chain from drm_bridge_ to drm_bridge_chain_ to better reflect the fact that the operation will happen on all elements of chain, starting at the bridge passed in argument.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
I haven't reviewed this in details but the renaming looks sensible, so
Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Changes in v3:
- None
Changes in v2:
- Pass te bridge, not the encoder, so we can later act on a sub-chain instead of the whole chain
drivers/gpu/drm/drm_atomic_helper.c | 19 +++-- drivers/gpu/drm/drm_bridge.c | 125 ++++++++++++++-------------- drivers/gpu/drm/drm_probe_helper.c | 2 +- drivers/gpu/drm/mediatek/mtk_hdmi.c | 4 +- include/drm/drm_bridge.h | 64 +++++++------- 5 files changed, 112 insertions(+), 102 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 587052751b48..cf678be58fa4 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -435,8 +435,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->bridge,
&new_crtc_state->mode,
if (!ret) { DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); return -EINVAL;&new_crtc_state->adjusted_mode);
@@ -501,7 +502,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->bridge, mode); if (ret != MODE_OK) { DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); return ret;
@@ -1020,7 +1021,7 @@ 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);
drm_atomic_bridge_chain_disable(encoder->bridge, old_state);
/* Right function depends upon target state. */ if (funcs) {
@@ -1034,7 +1035,8 @@ 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);
drm_atomic_bridge_chain_post_disable(encoder->bridge,
old_state);
}
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
@@ -1215,7 +1217,8 @@ 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->bridge, mode,
}adjusted_mode);
}
@@ -1332,7 +1335,7 @@ 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);
drm_atomic_bridge_chain_pre_enable(encoder->bridge, old_state);
if (funcs) { if (funcs->atomic_enable)
@@ -1343,7 +1346,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
drm_atomic_bridge_enable(encoder->bridge, old_state);
drm_atomic_bridge_chain_enable(encoder->bridge, 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..54c874493c57 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -172,8 +172,8 @@ void drm_bridge_detach(struct drm_bridge *bridge) */
/**
- drm_bridge_mode_fixup - fixup proposed mode for all bridges in the
encoder chain
- drm_bridge_chain_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
@@ -186,9 +186,9 @@ void drm_bridge_detach(struct drm_bridge *bridge)
- 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)
+bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{ bool ret = true;
@@ -198,15 +198,16 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge, 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);
ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
adjusted_mode);
return ret;
} -EXPORT_SYMBOL(drm_bridge_mode_fixup); +EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
/**
- drm_bridge_mode_valid - validate the mode against all bridges in the
encoder chain.
- drm_bridge_chain_mode_valid - validate the mode against all bridges in the
encoder chain.
- @bridge: bridge control structure
- @mode: desired mode to be validated
@@ -219,8 +220,9 @@ EXPORT_SYMBOL(drm_bridge_mode_fixup);
- 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)
+enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{ enum drm_mode_status ret = MODE_OK;
@@ -233,12 +235,12 @@ enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, if (ret != MODE_OK) return ret;
- return drm_bridge_mode_valid(bridge->next, mode);
- return drm_bridge_chain_mode_valid(bridge->next, mode);
} -EXPORT_SYMBOL(drm_bridge_mode_valid); +EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
/**
- drm_bridge_disable - disables all bridges in the encoder chain
- drm_bridge_chain_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
@@ -247,20 +249,21 @@ EXPORT_SYMBOL(drm_bridge_mode_valid);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_disable(struct drm_bridge *bridge) +void drm_bridge_chain_disable(struct drm_bridge *bridge) { if (!bridge) return;
- drm_bridge_disable(bridge->next);
drm_bridge_chain_disable(bridge->next);
if (bridge->funcs->disable) bridge->funcs->disable(bridge);
} -EXPORT_SYMBOL(drm_bridge_disable); +EXPORT_SYMBOL(drm_bridge_chain_disable);
/**
- drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain
- drm_bridge_chain_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
@@ -269,7 +272,7 @@ EXPORT_SYMBOL(drm_bridge_disable);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_post_disable(struct drm_bridge *bridge) +void drm_bridge_chain_post_disable(struct drm_bridge *bridge) { if (!bridge) return; @@ -277,25 +280,25 @@ void drm_bridge_post_disable(struct drm_bridge *bridge) if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge);
- drm_bridge_post_disable(bridge->next);
- drm_bridge_chain_post_disable(bridge->next);
} -EXPORT_SYMBOL(drm_bridge_post_disable); +EXPORT_SYMBOL(drm_bridge_chain_post_disable);
/**
- drm_bridge_mode_set - set proposed mode for all bridges in the
encoder chain
- drm_bridge_chain_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
- @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.
- 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)
+void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{ if (!bridge) return; @@ -303,13 +306,13 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, if (bridge->funcs->mode_set) bridge->funcs->mode_set(bridge, mode, adjusted_mode);
- drm_bridge_mode_set(bridge->next, mode, adjusted_mode);
- drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
} -EXPORT_SYMBOL(drm_bridge_mode_set); +EXPORT_SYMBOL(drm_bridge_chain_mode_set);
/**
- drm_bridge_pre_enable - prepares for enabling all
bridges in the encoder chain
- drm_bridge_chain_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
@@ -318,20 +321,20 @@ EXPORT_SYMBOL(drm_bridge_mode_set);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_pre_enable(struct drm_bridge *bridge) +void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) { if (!bridge) return;
- drm_bridge_pre_enable(bridge->next);
drm_bridge_chain_pre_enable(bridge->next);
if (bridge->funcs->pre_enable) bridge->funcs->pre_enable(bridge);
} -EXPORT_SYMBOL(drm_bridge_pre_enable); +EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
/**
- drm_bridge_enable - enables all bridges in the encoder chain
- drm_bridge_chain_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
@@ -340,7 +343,7 @@ EXPORT_SYMBOL(drm_bridge_pre_enable);
- Note that the bridge passed should be the one closest to the encoder
*/ -void drm_bridge_enable(struct drm_bridge *bridge) +void drm_bridge_chain_enable(struct drm_bridge *bridge) { if (!bridge) return; @@ -348,12 +351,12 @@ void drm_bridge_enable(struct drm_bridge *bridge) if (bridge->funcs->enable) bridge->funcs->enable(bridge);
- drm_bridge_enable(bridge->next);
- drm_bridge_chain_enable(bridge->next);
} -EXPORT_SYMBOL(drm_bridge_enable); +EXPORT_SYMBOL(drm_bridge_chain_enable);
/**
- drm_atomic_bridge_disable - disables all bridges in the encoder chain
- drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -364,24 +367,24 @@ EXPORT_SYMBOL(drm_bridge_enable);
- Note: the bridge passed should be the one closest to the encoder
*/ -void drm_atomic_bridge_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return;
- drm_atomic_bridge_disable(bridge->next, state);
drm_atomic_bridge_chain_disable(bridge->next, state);
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_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
/**
- drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the
encoder chain
- drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -392,8 +395,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_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)
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return; @@ -403,13 +406,13 @@ void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, else if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge);
- drm_atomic_bridge_post_disable(bridge->next, state);
- drm_atomic_bridge_chain_post_disable(bridge->next, state);
} -EXPORT_SYMBOL(drm_atomic_bridge_post_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
/**
- drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the
encoder chain
- drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -420,23 +423,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_post_disable);
- 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)
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return;
- drm_atomic_bridge_pre_enable(bridge->next, state);
drm_atomic_bridge_chain_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);
} -EXPORT_SYMBOL(drm_atomic_bridge_pre_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
/**
- drm_atomic_bridge_enable - enables all bridges in the encoder chain
- drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
@@ -447,8 +450,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_pre_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)
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{ if (!bridge) return; @@ -458,9 +461,9 @@ void drm_atomic_bridge_enable(struct drm_bridge *bridge, else if (bridge->funcs->enable) bridge->funcs->enable(bridge);
- drm_atomic_bridge_enable(bridge->next, state);
- drm_atomic_bridge_chain_enable(bridge->next, state);
} -EXPORT_SYMBOL(drm_atomic_bridge_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
#ifdef CONFIG_OF /** diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index a7c87abe88d0..c3ea722065c4 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -112,7 +112,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, continue; }
ret = drm_bridge_mode_valid(encoder->bridge, mode);
if (ret != MODE_OK) { /* There is also no point in continuing for crtc check * here. */ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index c79b1f855d89..ea68b5adccbe 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.next, mode,
}&adjusted_mode)) return MODE_BAD;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index c0a2286a81e9..726435baf4ad 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 @atomic_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 @atomic_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 @atomic_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_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode);
+void drm_bridge_chain_disable(struct drm_bridge *bridge); +void drm_bridge_chain_post_disable(struct drm_bridge *bridge); +void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode);
+void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); +void drm_bridge_chain_enable(struct drm_bridge *bridge);
-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_bridge *bridge,
struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, 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);
And use it in drivers accessing the bridge->next field 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 --- Changes in v3: * Inline drm_bridge_chain_get_next_bridge() (Suggested by Laurent)
Changes in v2: * Kill the last/first helpers (they're not really needed) * Drop the !bridge || !bridge->encoder test --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 3 ++- drivers/gpu/drm/mediatek/mtk_hdmi.c | 6 ++++-- drivers/gpu/drm/omapdrm/omap_drv.c | 4 ++-- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 ++- drivers/gpu/drm/vc4/vc4_dsi.c | 4 +++- include/drm/drm_bridge.h | 13 +++++++++++++ 6 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 3915f50b005e..005c67894b78 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host); - struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev; + struct drm_bridge *out_bridge;
+ out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge); if (dsi->panel) { mutex_lock(&drm->mode_config.mutex); exynos_dsi_disable(&dsi->bridge); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index ea68b5adccbe..cfaa5aab8876 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1238,16 +1238,18 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, struct drm_display_mode *mode) { struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn); + struct drm_bridge *next_bridge;
dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", mode->hdisplay, mode->vdisplay, mode->vrefresh, !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
- if (hdmi->bridge.next) { + next_bridge = drm_bridge_chain_get_next_bridge(&hdmi->bridge); + if (next_bridge) { struct drm_display_mode adjusted_mode;
drm_mode_copy(&adjusted_mode, mode); - if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode, + if (!drm_bridge_chain_mode_fixup(next_bridge, mode, &adjusted_mode)) return MODE_BAD; } diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index b3e22c890c51..865164fe28dc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -217,8 +217,8 @@ static int omap_display_id(struct omap_dss_device *output) } else if (output->bridge) { struct drm_bridge *bridge = output->bridge;
- while (bridge->next) - bridge = bridge->next; + while (drm_bridge_chain_get_next_bridge(bridge)) + bridge = drm_bridge_chain_get_next_bridge(bridge);
node = bridge->of_node; } else if (output->panel) { diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 24bbe9f2a32e..8ca54081997e 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 = output->bridge; bridge; + bridge = drm_bridge_chain_get_next_bridge(bridge)) { if (!bridge->timings) continue;
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 49f8a313e759..49c47185aff0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1644,8 +1644,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->bridge.next) + bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge); + 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 726435baf4ad..8aeba83fcf31 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -409,6 +409,19 @@ 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);
+/** + * 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 @bridge is the last. + */ +static inline struct drm_bridge * +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) +{ + return bridge->next; +} + bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
On 23/10/2019 17:44, Boris Brezillon wrote:
And use it in drivers accessing the bridge->next field 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
Changes in v3:
- Inline drm_bridge_chain_get_next_bridge() (Suggested by Laurent)
Changes in v2:
- Kill the last/first helpers (they're not really needed)
- Drop the !bridge || !bridge->encoder test
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 3 ++- drivers/gpu/drm/mediatek/mtk_hdmi.c | 6 ++++-- drivers/gpu/drm/omapdrm/omap_drv.c | 4 ++-- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 ++- drivers/gpu/drm/vc4/vc4_dsi.c | 4 +++- include/drm/drm_bridge.h | 13 +++++++++++++ 6 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 3915f50b005e..005c67894b78 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host);
- struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
struct drm_bridge *out_bridge;
out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge); if (dsi->panel) { mutex_lock(&drm->mode_config.mutex); exynos_dsi_disable(&dsi->bridge);
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index ea68b5adccbe..cfaa5aab8876 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1238,16 +1238,18 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, struct drm_display_mode *mode) { struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
struct drm_bridge *next_bridge;
dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", mode->hdisplay, mode->vdisplay, mode->vrefresh, !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
- if (hdmi->bridge.next) {
next_bridge = drm_bridge_chain_get_next_bridge(&hdmi->bridge);
if (next_bridge) { struct drm_display_mode adjusted_mode;
drm_mode_copy(&adjusted_mode, mode);
if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
}if (!drm_bridge_chain_mode_fixup(next_bridge, mode, &adjusted_mode)) return MODE_BAD;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index b3e22c890c51..865164fe28dc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -217,8 +217,8 @@ static int omap_display_id(struct omap_dss_device *output) } else if (output->bridge) { struct drm_bridge *bridge = output->bridge;
while (bridge->next)
bridge = bridge->next;
while (drm_bridge_chain_get_next_bridge(bridge))
bridge = drm_bridge_chain_get_next_bridge(bridge);
node = bridge->of_node; } else if (output->panel) {
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 24bbe9f2a32e..8ca54081997e 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 = output->bridge; bridge;
if (!bridge->timings) continue;bridge = drm_bridge_chain_get_next_bridge(bridge)) {
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 49f8a313e759..49c47185aff0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1644,8 +1644,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->bridge.next)
bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
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 726435baf4ad..8aeba83fcf31 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -409,6 +409,19 @@ 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);
+/**
- 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 @bridge is the last.
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) +{
- return bridge->next;
+}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:56PM +0200, Boris Brezillon wrote:
And use it in drivers accessing the bridge->next field 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
Changes in v3:
- Inline drm_bridge_chain_get_next_bridge() (Suggested by Laurent)
Changes in v2:
- Kill the last/first helpers (they're not really needed)
- Drop the !bridge || !bridge->encoder test
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 3 ++- drivers/gpu/drm/mediatek/mtk_hdmi.c | 6 ++++-- drivers/gpu/drm/omapdrm/omap_drv.c | 4 ++-- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 ++- drivers/gpu/drm/vc4/vc4_dsi.c | 4 +++- include/drm/drm_bridge.h | 13 +++++++++++++ 6 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 3915f50b005e..005c67894b78 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host);
- struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
struct drm_bridge *out_bridge;
out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
You may want to store this in the exynos_dsi structure in the previous patch where you rework this driver.
if (dsi->panel) { mutex_lock(&drm->mode_config.mutex); exynos_dsi_disable(&dsi->bridge); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index ea68b5adccbe..cfaa5aab8876 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1238,16 +1238,18 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, struct drm_display_mode *mode) { struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
struct drm_bridge *next_bridge;
dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", mode->hdisplay, mode->vdisplay, mode->vrefresh, !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
- if (hdmi->bridge.next) {
next_bridge = drm_bridge_chain_get_next_bridge(&hdmi->bridge);
if (next_bridge) { struct drm_display_mode adjusted_mode;
drm_mode_copy(&adjusted_mode, mode);
if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
}if (!drm_bridge_chain_mode_fixup(next_bridge, mode, &adjusted_mode)) return MODE_BAD;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index b3e22c890c51..865164fe28dc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -217,8 +217,8 @@ static int omap_display_id(struct omap_dss_device *output) } else if (output->bridge) { struct drm_bridge *bridge = output->bridge;
while (bridge->next)
bridge = bridge->next;
while (drm_bridge_chain_get_next_bridge(bridge))
bridge = drm_bridge_chain_get_next_bridge(bridge);
node = bridge->of_node; } else if (output->panel) {
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 24bbe9f2a32e..8ca54081997e 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 = output->bridge; bridge;
bridge = drm_bridge_chain_get_next_bridge(bridge)) {
A for_each_bridge() macro would be nice (in a separate patch). It could be used in omap_drv.c above too.
if (!bridge->timings) continue;
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 49f8a313e759..49c47185aff0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1644,8 +1644,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->bridge.next)
bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
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 726435baf4ad..8aeba83fcf31 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -409,6 +409,19 @@ 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);
+/**
- 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 @bridge is the last.
Maybe "the next bridge in the chain after @bridge, ..." ?
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)
Technically speaking this doesn't operate on a chain but on a bridge, so I'd name is drm_bridge_get_next_bridge(). I will not insist to the way of nacking the series for this, so with the rename, but also without,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
+{
- return bridge->next;
+}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
On Sun, 24 Nov 2019 12:33:35 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:56PM +0200, Boris Brezillon wrote:
And use it in drivers accessing the bridge->next field 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
Changes in v3:
- Inline drm_bridge_chain_get_next_bridge() (Suggested by Laurent)
Changes in v2:
- Kill the last/first helpers (they're not really needed)
- Drop the !bridge || !bridge->encoder test
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 3 ++- drivers/gpu/drm/mediatek/mtk_hdmi.c | 6 ++++-- drivers/gpu/drm/omapdrm/omap_drv.c | 4 ++-- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 ++- drivers/gpu/drm/vc4/vc4_dsi.c | 4 +++- include/drm/drm_bridge.h | 13 +++++++++++++ 6 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 3915f50b005e..005c67894b78 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host);
- struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
struct drm_bridge *out_bridge;
out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
You may want to store this in the exynos_dsi structure in the previous patch where you rework this driver.
Do we really need to store it there, since we already have a simple way to retrieve the next bridge in the chain?
if (dsi->panel) { mutex_lock(&drm->mode_config.mutex); exynos_dsi_disable(&dsi->bridge); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index ea68b5adccbe..cfaa5aab8876 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1238,16 +1238,18 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, struct drm_display_mode *mode) { struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
struct drm_bridge *next_bridge;
dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", mode->hdisplay, mode->vdisplay, mode->vrefresh, !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
- if (hdmi->bridge.next) {
next_bridge = drm_bridge_chain_get_next_bridge(&hdmi->bridge);
if (next_bridge) { struct drm_display_mode adjusted_mode;
drm_mode_copy(&adjusted_mode, mode);
if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
}if (!drm_bridge_chain_mode_fixup(next_bridge, mode, &adjusted_mode)) return MODE_BAD;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index b3e22c890c51..865164fe28dc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -217,8 +217,8 @@ static int omap_display_id(struct omap_dss_device *output) } else if (output->bridge) { struct drm_bridge *bridge = output->bridge;
while (bridge->next)
bridge = bridge->next;
while (drm_bridge_chain_get_next_bridge(bridge))
bridge = drm_bridge_chain_get_next_bridge(bridge);
node = bridge->of_node; } else if (output->panel) {
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 24bbe9f2a32e..8ca54081997e 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 = output->bridge; bridge;
bridge = drm_bridge_chain_get_next_bridge(bridge)) {
A for_each_bridge() macro would be nice (in a separate patch). It could be used in omap_drv.c above too.
It's coming later in the series (patch 8).
if (!bridge->timings) continue;
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 49f8a313e759..49c47185aff0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1644,8 +1644,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->bridge.next)
bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
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 726435baf4ad..8aeba83fcf31 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -409,6 +409,19 @@ 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);
+/**
- 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 @bridge is the last.
Maybe "the next bridge in the chain after @bridge, ..." ?
Agreed.
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)
Technically speaking this doesn't operate on a chain but on a bridge, so I'd name is drm_bridge_get_next_bridge(). I will not insist to the way of nacking the series for this, so with the rename, but also without,
You're absolutely right, I'll rename the function as suggested.
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
+{
- return bridge->next;
+}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
Hi Boris,
On Sun, Nov 24, 2019 at 11:56:16AM +0100, Boris Brezillon wrote:
On Sun, 24 Nov 2019 12:33:35 +0200 Laurent Pinchart wrote:
On Wed, Oct 23, 2019 at 05:44:56PM +0200, Boris Brezillon wrote:
And use it in drivers accessing the bridge->next field 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
Changes in v3:
- Inline drm_bridge_chain_get_next_bridge() (Suggested by Laurent)
Changes in v2:
- Kill the last/first helpers (they're not really needed)
- Drop the !bridge || !bridge->encoder test
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 3 ++- drivers/gpu/drm/mediatek/mtk_hdmi.c | 6 ++++-- drivers/gpu/drm/omapdrm/omap_drv.c | 4 ++-- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 ++- drivers/gpu/drm/vc4/vc4_dsi.c | 4 +++- include/drm/drm_bridge.h | 13 +++++++++++++ 6 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 3915f50b005e..005c67894b78 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct exynos_dsi *dsi = host_to_dsi(host);
- struct drm_bridge *out_bridge = dsi->bridge.next; struct drm_device *drm = dsi->encoder.dev;
struct drm_bridge *out_bridge;
out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
You may want to store this in the exynos_dsi structure in the previous patch where you rework this driver.
Do we really need to store it there, since we already have a simple way to retrieve the next bridge in the chain?
We don't have to indeed. I thought it would be simpler, but that's probably subjective.
if (dsi->panel) { mutex_lock(&drm->mode_config.mutex); exynos_dsi_disable(&dsi->bridge); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index ea68b5adccbe..cfaa5aab8876 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1238,16 +1238,18 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn, struct drm_display_mode *mode) { struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
struct drm_bridge *next_bridge;
dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", mode->hdisplay, mode->vdisplay, mode->vrefresh, !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
- if (hdmi->bridge.next) {
next_bridge = drm_bridge_chain_get_next_bridge(&hdmi->bridge);
if (next_bridge) { struct drm_display_mode adjusted_mode;
drm_mode_copy(&adjusted_mode, mode);
if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
}if (!drm_bridge_chain_mode_fixup(next_bridge, mode, &adjusted_mode)) return MODE_BAD;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index b3e22c890c51..865164fe28dc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -217,8 +217,8 @@ static int omap_display_id(struct omap_dss_device *output) } else if (output->bridge) { struct drm_bridge *bridge = output->bridge;
while (bridge->next)
bridge = bridge->next;
while (drm_bridge_chain_get_next_bridge(bridge))
bridge = drm_bridge_chain_get_next_bridge(bridge);
node = bridge->of_node; } else if (output->panel) {
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 24bbe9f2a32e..8ca54081997e 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 = output->bridge; bridge;
bridge = drm_bridge_chain_get_next_bridge(bridge)) {
A for_each_bridge() macro would be nice (in a separate patch). It could be used in omap_drv.c above too.
It's coming later in the series (patch 8).
if (!bridge->timings) continue;
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 49f8a313e759..49c47185aff0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1644,8 +1644,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->bridge.next)
bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
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 726435baf4ad..8aeba83fcf31 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -409,6 +409,19 @@ 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);
+/**
- 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 @bridge is the last.
Maybe "the next bridge in the chain after @bridge, ..." ?
Agreed.
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)
Technically speaking this doesn't operate on a chain but on a bridge, so I'd name is drm_bridge_get_next_bridge(). I will not insist to the way of nacking the series for this, so with the rename, but also without,
You're absolutely right, I'll rename the function as suggested.
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
+{
- return bridge->next;
+}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
We are about to replace the single-linked bridge list by a double-linked one based on list.h, leading to the suppression of the encoder->bridge field. But before we can do that we must provide a drm_bridge_chain_get_first_bridge() bridge helper and patch all drivers and core helpers to use it instead of directly accessing encoder->bridge.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- drivers/gpu/drm/drm_atomic_helper.c | 25 +++++++++++++++++-------- drivers/gpu/drm/drm_encoder.c | 3 ++- drivers/gpu/drm/drm_probe_helper.c | 4 +++- drivers/gpu/drm/msm/edp/edp_bridge.c | 10 ++++++++-- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 11 ++++++++--- include/drm/drm_bridge.h | 15 +++++++++++++++ 6 files changed, 53 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index cf678be58fa4..f02ddffd4960 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -419,6 +419,7 @@ mode_fixup(struct drm_atomic_state *state) for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; + struct drm_bridge *bridge;
WARN_ON(!!new_conn_state->best_encoder != !!new_conn_state->crtc);
@@ -435,7 +436,8 @@ mode_fixup(struct drm_atomic_state *state) encoder = new_conn_state->best_encoder; funcs = encoder->helper_private;
- ret = drm_bridge_chain_mode_fixup(encoder->bridge, + bridge = drm_bridge_chain_get_first_bridge(encoder); + ret = drm_bridge_chain_mode_fixup(bridge, &new_crtc_state->mode, &new_crtc_state->adjusted_mode); if (!ret) { @@ -493,6 +495,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector, struct drm_crtc *crtc, const struct drm_display_mode *mode) { + struct drm_bridge *bridge; enum drm_mode_status ret;
ret = drm_encoder_mode_valid(encoder, mode); @@ -502,7 +505,8 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector, return ret; }
- ret = drm_bridge_chain_mode_valid(encoder->bridge, mode); + bridge = drm_bridge_chain_get_first_bridge(encoder); + ret = drm_bridge_chain_mode_valid(bridge, mode); if (ret != MODE_OK) { DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); return ret; @@ -985,6 +989,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; + struct drm_bridge *bridge;
/* Shut down everything that's in the changeset and currently * still on. So need to check the old, saved state. */ @@ -1021,7 +1026,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_chain_disable(encoder->bridge, old_state); + bridge = drm_bridge_chain_get_first_bridge(encoder); + drm_atomic_bridge_chain_disable(bridge, old_state);
/* Right function depends upon target state. */ if (funcs) { @@ -1035,7 +1041,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); }
- drm_atomic_bridge_chain_post_disable(encoder->bridge, + drm_atomic_bridge_chain_post_disable(bridge, old_state); }
@@ -1190,6 +1196,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; struct drm_display_mode *mode, *adjusted_mode; + struct drm_bridge *bridge;
if (!new_conn_state->best_encoder) continue; @@ -1217,8 +1224,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->mode_set(encoder, mode, adjusted_mode); }
- drm_bridge_chain_mode_set(encoder->bridge, mode, - adjusted_mode); + bridge = drm_bridge_chain_get_first_bridge(encoder); + drm_bridge_chain_mode_set(bridge, mode, adjusted_mode); } }
@@ -1317,6 +1324,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; + struct drm_bridge *bridge;
if (!new_conn_state->best_encoder) continue; @@ -1335,7 +1343,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_chain_pre_enable(encoder->bridge, old_state); + bridge = drm_bridge_chain_get_first_bridge(encoder); + drm_atomic_bridge_chain_pre_enable(bridge, old_state);
if (funcs) { if (funcs->atomic_enable) @@ -1346,7 +1355,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
- drm_atomic_bridge_chain_enable(encoder->bridge, old_state); + drm_atomic_bridge_chain_enable(bridge, old_state); }
drm_atomic_helper_commit_writebacks(dev, old_state); diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 80d88a55302e..4fe9e723e227 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -167,9 +167,10 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) */
if (encoder->bridge) { - struct drm_bridge *bridge = encoder->bridge; + struct drm_bridge *bridge; struct drm_bridge *next;
+ bridge = drm_bridge_chain_get_first_bridge(encoder); while (bridge) { next = bridge->next; drm_bridge_detach(bridge); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index c3ea722065c4..576b4b7dcd89 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -101,6 +101,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
/* Step 2: Validate against encoders and crtcs */ drm_connector_for_each_possible_encoder(connector, encoder) { + struct drm_bridge *bridge; struct drm_crtc *crtc;
ret = drm_encoder_mode_valid(encoder, mode); @@ -112,7 +113,8 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, continue; }
- ret = drm_bridge_chain_mode_valid(encoder->bridge, mode); + bridge = drm_bridge_chain_get_first_bridge(encoder); + ret = drm_bridge_chain_mode_valid(bridge, mode); if (ret != MODE_OK) { /* There is also no point in continuing for crtc check * here. */ diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c index 2950bba4aca9..b65b5cc2dba2 100644 --- a/drivers/gpu/drm/msm/edp/edp_bridge.c +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c @@ -55,8 +55,14 @@ static void edp_bridge_mode_set(struct drm_bridge *bridge, DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if ((connector->encoder != NULL) && - (connector->encoder->bridge == bridge)) { + struct drm_encoder *encoder = connector->encoder; + struct drm_bridge *first_bridge; + + if (!connector->encoder) + continue; + + first_bridge = drm_bridge_chain_get_first_bridge(encoder); + if (bridge == first_bridge) { msm_edp_ctrl_timing_cfg(edp->ctrl, adjusted_mode, &connector->display_info); break; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 2da46e3dc4ae..7a1f1e5f0326 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,15 @@ 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/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 8aeba83fcf31..27eef63ce0ff 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_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h>
@@ -422,6 +423,20 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) return bridge->next; }
+/** + * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain + * @encoder: encoder object + * + * RETURNS: + * the first bridge in the chain, or NULL if @encoder has no bridge attached + * to it. + */ +static inline struct drm_bridge * +drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) +{ + return encoder->bridge; +} + bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
On 23/10/2019 17:44, Boris Brezillon wrote:
We are about to replace the single-linked bridge list by a double-linked one based on list.h, leading to the suppression of the encoder->bridge field. But before we can do that we must provide a drm_bridge_chain_get_first_bridge() bridge helper and patch all drivers and core helpers to use it instead of directly accessing encoder->bridge.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
drivers/gpu/drm/drm_atomic_helper.c | 25 +++++++++++++++++-------- drivers/gpu/drm/drm_encoder.c | 3 ++- drivers/gpu/drm/drm_probe_helper.c | 4 +++- drivers/gpu/drm/msm/edp/edp_bridge.c | 10 ++++++++-- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 11 ++++++++--- include/drm/drm_bridge.h | 15 +++++++++++++++ 6 files changed, 53 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index cf678be58fa4..f02ddffd4960 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -419,6 +419,7 @@ mode_fixup(struct drm_atomic_state *state) for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder;
struct drm_bridge *bridge;
WARN_ON(!!new_conn_state->best_encoder != !!new_conn_state->crtc);
@@ -435,7 +436,8 @@ mode_fixup(struct drm_atomic_state *state) encoder = new_conn_state->best_encoder; funcs = encoder->helper_private;
ret = drm_bridge_chain_mode_fixup(encoder->bridge,
bridge = drm_bridge_chain_get_first_bridge(encoder);
if (!ret) {ret = drm_bridge_chain_mode_fixup(bridge, &new_crtc_state->mode, &new_crtc_state->adjusted_mode);
@@ -493,6 +495,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector, struct drm_crtc *crtc, const struct drm_display_mode *mode) {
struct drm_bridge *bridge; enum drm_mode_status ret;
ret = drm_encoder_mode_valid(encoder, mode);
@@ -502,7 +505,8 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector, return ret; }
- ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
- bridge = drm_bridge_chain_get_first_bridge(encoder);
- ret = drm_bridge_chain_mode_valid(bridge, mode); if (ret != MODE_OK) { DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); return ret;
@@ -985,6 +989,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder;
struct drm_bridge *bridge;
/* Shut down everything that's in the changeset and currently
- still on. So need to check the old, saved state. */
@@ -1021,7 +1026,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_chain_disable(encoder->bridge, old_state);
bridge = drm_bridge_chain_get_first_bridge(encoder);
drm_atomic_bridge_chain_disable(bridge, old_state);
/* Right function depends upon target state. */ if (funcs) {
@@ -1035,7 +1041,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); }
drm_atomic_bridge_chain_post_disable(encoder->bridge,
}drm_atomic_bridge_chain_post_disable(bridge, old_state);
@@ -1190,6 +1196,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; struct drm_display_mode *mode, *adjusted_mode;
struct drm_bridge *bridge;
if (!new_conn_state->best_encoder) continue;
@@ -1217,8 +1224,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->mode_set(encoder, mode, adjusted_mode); }
drm_bridge_chain_mode_set(encoder->bridge, mode,
adjusted_mode);
bridge = drm_bridge_chain_get_first_bridge(encoder);
}drm_bridge_chain_mode_set(bridge, mode, adjusted_mode);
}
@@ -1317,6 +1324,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder;
struct drm_bridge *bridge;
if (!new_conn_state->best_encoder) continue;
@@ -1335,7 +1343,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_chain_pre_enable(encoder->bridge, old_state);
bridge = drm_bridge_chain_get_first_bridge(encoder);
drm_atomic_bridge_chain_pre_enable(bridge, old_state);
if (funcs) { if (funcs->atomic_enable)
@@ -1346,7 +1355,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
drm_atomic_bridge_chain_enable(encoder->bridge, old_state);
drm_atomic_bridge_chain_enable(bridge, old_state);
}
drm_atomic_helper_commit_writebacks(dev, old_state);
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 80d88a55302e..4fe9e723e227 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -167,9 +167,10 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) */
if (encoder->bridge) {
struct drm_bridge *bridge = encoder->bridge;
struct drm_bridge *bridge;
struct drm_bridge *next;
bridge = drm_bridge_chain_get_first_bridge(encoder);
while (bridge) { next = bridge->next; drm_bridge_detach(bridge);
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index c3ea722065c4..576b4b7dcd89 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -101,6 +101,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
/* Step 2: Validate against encoders and crtcs */ drm_connector_for_each_possible_encoder(connector, encoder) {
struct drm_bridge *bridge;
struct drm_crtc *crtc;
ret = drm_encoder_mode_valid(encoder, mode);
@@ -112,7 +113,8 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, continue; }
ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
bridge = drm_bridge_chain_get_first_bridge(encoder);
if (ret != MODE_OK) { /* There is also no point in continuing for crtc check * here. */ret = drm_bridge_chain_mode_valid(bridge, mode);
diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c index 2950bba4aca9..b65b5cc2dba2 100644 --- a/drivers/gpu/drm/msm/edp/edp_bridge.c +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c @@ -55,8 +55,14 @@ static void edp_bridge_mode_set(struct drm_bridge *bridge, DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if ((connector->encoder != NULL) &&
(connector->encoder->bridge == bridge)) {
struct drm_encoder *encoder = connector->encoder;
struct drm_bridge *first_bridge;
if (!connector->encoder)
continue;
first_bridge = drm_bridge_chain_get_first_bridge(encoder);
if (bridge == first_bridge) { msm_edp_ctrl_timing_cfg(edp->ctrl, adjusted_mode, &connector->display_info); break;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 2da46e3dc4ae..7a1f1e5f0326 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,15 @@ 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/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 8aeba83fcf31..27eef63ce0ff 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_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h>
@@ -422,6 +423,20 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) return bridge->next; }
+/**
- drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
- @encoder: encoder object
- RETURNS:
- the first bridge in the chain, or NULL if @encoder has no bridge attached
- to it.
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) +{
- return encoder->bridge;
+}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:57PM +0200, Boris Brezillon wrote:
We are about to replace the single-linked bridge list by a double-linked one based on list.h, leading to the suppression of the encoder->bridge field. But before we can do that we must provide a drm_bridge_chain_get_first_bridge() bridge helper and patch all drivers and core helpers to use it instead of directly accessing encoder->bridge.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
drivers/gpu/drm/drm_atomic_helper.c | 25 +++++++++++++++++-------- drivers/gpu/drm/drm_encoder.c | 3 ++- drivers/gpu/drm/drm_probe_helper.c | 4 +++- drivers/gpu/drm/msm/edp/edp_bridge.c | 10 ++++++++-- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 11 ++++++++--- include/drm/drm_bridge.h | 15 +++++++++++++++ 6 files changed, 53 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index cf678be58fa4..f02ddffd4960 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -419,6 +419,7 @@ mode_fixup(struct drm_atomic_state *state) for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder;
struct drm_bridge *bridge;
WARN_ON(!!new_conn_state->best_encoder != !!new_conn_state->crtc);
@@ -435,7 +436,8 @@ mode_fixup(struct drm_atomic_state *state) encoder = new_conn_state->best_encoder; funcs = encoder->helper_private;
ret = drm_bridge_chain_mode_fixup(encoder->bridge,
bridge = drm_bridge_chain_get_first_bridge(encoder);
if (!ret) {ret = drm_bridge_chain_mode_fixup(bridge, &new_crtc_state->mode, &new_crtc_state->adjusted_mode);
@@ -493,6 +495,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector, struct drm_crtc *crtc, const struct drm_display_mode *mode) {
struct drm_bridge *bridge; enum drm_mode_status ret;
ret = drm_encoder_mode_valid(encoder, mode);
@@ -502,7 +505,8 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector, return ret; }
- ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
- bridge = drm_bridge_chain_get_first_bridge(encoder);
- ret = drm_bridge_chain_mode_valid(bridge, mode); if (ret != MODE_OK) { DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); return ret;
@@ -985,6 +989,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder;
struct drm_bridge *bridge;
/* Shut down everything that's in the changeset and currently
- still on. So need to check the old, saved state. */
@@ -1021,7 +1026,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_chain_disable(encoder->bridge, old_state);
bridge = drm_bridge_chain_get_first_bridge(encoder);
drm_atomic_bridge_chain_disable(bridge, old_state);
/* Right function depends upon target state. */ if (funcs) {
@@ -1035,7 +1041,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); }
drm_atomic_bridge_chain_post_disable(encoder->bridge,
drm_atomic_bridge_chain_post_disable(bridge, old_state);
This now fits on a single line.
}
@@ -1190,6 +1196,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; struct drm_display_mode *mode, *adjusted_mode;
struct drm_bridge *bridge;
if (!new_conn_state->best_encoder) continue;
@@ -1217,8 +1224,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->mode_set(encoder, mode, adjusted_mode); }
drm_bridge_chain_mode_set(encoder->bridge, mode,
adjusted_mode);
bridge = drm_bridge_chain_get_first_bridge(encoder);
}drm_bridge_chain_mode_set(bridge, mode, adjusted_mode);
}
@@ -1317,6 +1324,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder;
struct drm_bridge *bridge;
if (!new_conn_state->best_encoder) continue;
@@ -1335,7 +1343,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_chain_pre_enable(encoder->bridge, old_state);
bridge = drm_bridge_chain_get_first_bridge(encoder);
drm_atomic_bridge_chain_pre_enable(bridge, old_state);
if (funcs) { if (funcs->atomic_enable)
@@ -1346,7 +1355,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
drm_atomic_bridge_chain_enable(encoder->bridge, old_state);
drm_atomic_bridge_chain_enable(bridge, old_state);
}
drm_atomic_helper_commit_writebacks(dev, old_state);
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 80d88a55302e..4fe9e723e227 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -167,9 +167,10 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) */
if (encoder->bridge) {
struct drm_bridge *bridge = encoder->bridge;
struct drm_bridge *bridge;
struct drm_bridge *next;
bridge = drm_bridge_chain_get_first_bridge(encoder);
while (bridge) { next = bridge->next;
Shouldn't this have been addressed in the previous patch ?
drm_bridge_detach(bridge);
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index c3ea722065c4..576b4b7dcd89 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -101,6 +101,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
/* Step 2: Validate against encoders and crtcs */ drm_connector_for_each_possible_encoder(connector, encoder) {
struct drm_bridge *bridge;
struct drm_crtc *crtc;
ret = drm_encoder_mode_valid(encoder, mode);
@@ -112,7 +113,8 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, continue; }
ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
bridge = drm_bridge_chain_get_first_bridge(encoder);
if (ret != MODE_OK) { /* There is also no point in continuing for crtc check * here. */ret = drm_bridge_chain_mode_valid(bridge, mode);
diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c index 2950bba4aca9..b65b5cc2dba2 100644 --- a/drivers/gpu/drm/msm/edp/edp_bridge.c +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c @@ -55,8 +55,14 @@ static void edp_bridge_mode_set(struct drm_bridge *bridge, DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if ((connector->encoder != NULL) &&
(connector->encoder->bridge == bridge)) {
struct drm_encoder *encoder = connector->encoder;
struct drm_bridge *first_bridge;
if (!connector->encoder)
continue;
first_bridge = drm_bridge_chain_get_first_bridge(encoder);
if (bridge == first_bridge) { msm_edp_ctrl_timing_cfg(edp->ctrl, adjusted_mode, &connector->display_info); break;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 2da46e3dc4ae..7a1f1e5f0326 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,15 @@ 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;
Extra blank line.
With those small issues fixed,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
/* * 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/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 8aeba83fcf31..27eef63ce0ff 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_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h>
@@ -422,6 +423,20 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) return bridge->next; }
+/**
- drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
- @encoder: encoder object
- RETURNS:
- the first bridge in the chain, or NULL if @encoder has no bridge attached
- to it.
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) +{
- return encoder->bridge;
+}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
On Sun, 24 Nov 2019 12:39:26 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
@@ -1346,7 +1355,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); }
drm_atomic_bridge_chain_enable(encoder->bridge, old_state);
drm_atomic_bridge_chain_enable(bridge, old_state);
}
drm_atomic_helper_commit_writebacks(dev, old_state);
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 80d88a55302e..4fe9e723e227 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -167,9 +167,10 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) */
if (encoder->bridge) {
struct drm_bridge *bridge = encoder->bridge;
struct drm_bridge *bridge;
struct drm_bridge *next;
bridge = drm_bridge_chain_get_first_bridge(encoder);
while (bridge) { next = bridge->next;
Shouldn't this have been addressed in the previous patch ?
It should and I will update patch 6 accordingly. This being said, it's not a big deal since the code is patched to use list_for_each_entry_safe() in patch 7 ;-).
So that each element in the chain can easily access its predecessor. This will be needed to support bus format negotiation between elements of the bridge chain.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * None
Changes in v2: * Adjust things to the "dummy encoder bridge" change (patch 2 in this series) --- drivers/gpu/drm/drm_bridge.c | 171 ++++++++++++++++++++++------------ drivers/gpu/drm/drm_encoder.c | 16 +--- include/drm/drm_bridge.h | 12 ++- include/drm/drm_encoder.h | 9 +- 4 files changed, 135 insertions(+), 73 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 54c874493c57..c5cf8a9c4237 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; }
@@ -190,18 +193,22 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - bool ret = true; + struct drm_encoder *encoder;
if (!bridge) return true;
- if (bridge->funcs->mode_fixup) - ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode); + encoder = bridge->encoder; + list_for_each_entry_from(bridge, &encoder->bridge_chain, + chain_node) { + if (!bridge->funcs->mode_fixup) + continue;
- ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode, - adjusted_mode); + if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode)) + return false; + }
- return ret; + return true; } EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
@@ -224,18 +231,24 @@ enum drm_mode_status drm_bridge_chain_mode_valid(struct drm_bridge *bridge, const struct drm_display_mode *mode) { - enum drm_mode_status ret = MODE_OK; + struct drm_encoder *encoder;
if (!bridge) - return ret; + return MODE_OK; + + encoder = bridge->encoder; + list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { + enum drm_mode_status ret; + + if (!bridge->funcs->mode_valid) + continue;
- if (bridge->funcs->mode_valid) ret = bridge->funcs->mode_valid(bridge, mode); + if (ret != MODE_OK) + return ret; + }
- if (ret != MODE_OK) - return ret; - - return drm_bridge_chain_mode_valid(bridge->next, mode); + return MODE_OK; } EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
@@ -251,13 +264,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid); */ void drm_bridge_chain_disable(struct drm_bridge *bridge) { + struct drm_encoder *encoder; + struct drm_bridge *iter; + if (!bridge) return;
- drm_bridge_chain_disable(bridge->next); + encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { + if (iter->funcs->disable) + iter->funcs->disable(iter);
- if (bridge->funcs->disable) - bridge->funcs->disable(bridge); + if (iter == bridge) + break; + } } EXPORT_SYMBOL(drm_bridge_chain_disable);
@@ -274,13 +294,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable); */ void drm_bridge_chain_post_disable(struct drm_bridge *bridge) { + struct drm_encoder *encoder; + if (!bridge) return;
- if (bridge->funcs->post_disable) - bridge->funcs->post_disable(bridge); - - drm_bridge_chain_post_disable(bridge->next); + encoder = bridge->encoder; + list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->post_disable) + bridge->funcs->post_disable(bridge); + } } EXPORT_SYMBOL(drm_bridge_chain_post_disable);
@@ -300,13 +323,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { + struct drm_encoder *encoder; + if (!bridge) return;
- if (bridge->funcs->mode_set) - bridge->funcs->mode_set(bridge, mode, adjusted_mode); - - drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode); + encoder = bridge->encoder; + list_for_each_entry_from(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);
@@ -323,13 +349,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set); */ void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) { + struct drm_encoder *encoder; + struct drm_bridge *iter; + if (!bridge) return;
- drm_bridge_chain_pre_enable(bridge->next); - - if (bridge->funcs->pre_enable) - bridge->funcs->pre_enable(bridge); + encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { + if (iter->funcs->pre_enable) + iter->funcs->pre_enable(iter); + } } EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
@@ -345,13 +375,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable); */ void drm_bridge_chain_enable(struct drm_bridge *bridge) { + struct drm_encoder *encoder; + if (!bridge) return;
- if (bridge->funcs->enable) - bridge->funcs->enable(bridge); - - drm_bridge_chain_enable(bridge->next); + encoder = bridge->encoder; + list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->enable) + bridge->funcs->enable(bridge); + } } EXPORT_SYMBOL(drm_bridge_chain_enable);
@@ -370,15 +403,23 @@ EXPORT_SYMBOL(drm_bridge_chain_enable); void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state) { + struct drm_encoder *encoder; + struct drm_bridge *iter; + if (!bridge) return;
- drm_atomic_bridge_chain_disable(bridge->next, state); + encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &encoder->bridge_chain, + chain_node) { + if (iter->funcs->atomic_disable) + iter->funcs->atomic_disable(iter, state); + else if (iter->funcs->disable) + iter->funcs->disable(iter);
- if (bridge->funcs->atomic_disable) - bridge->funcs->atomic_disable(bridge, state); - else if (bridge->funcs->disable) - bridge->funcs->disable(bridge); + if (iter == bridge) + break; + } } EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
@@ -398,15 +439,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, struct drm_atomic_state *state) { + struct drm_encoder *encoder; + 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_chain_post_disable(bridge->next, state); + encoder = bridge->encoder; + list_for_each_entry_from(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);
@@ -426,15 +471,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { + struct drm_encoder *encoder; + struct drm_bridge *iter; + if (!bridge) return;
- drm_atomic_bridge_chain_pre_enable(bridge->next, state); + encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain, + chain_node) { + if (iter->funcs->atomic_pre_enable) + iter->funcs->atomic_pre_enable(iter, state); + else if (iter->funcs->pre_enable) + iter->funcs->pre_enable(iter);
- if (bridge->funcs->atomic_pre_enable) - bridge->funcs->atomic_pre_enable(bridge, state); - else if (bridge->funcs->pre_enable) - bridge->funcs->pre_enable(bridge); + if (iter == bridge) + break; + } } EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
@@ -453,15 +506,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { + struct drm_encoder *encoder; + 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_chain_enable(bridge->next, state); + encoder = bridge->encoder; + list_for_each_entry_from(bridge, &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 4fe9e723e227..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,23 +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; - struct drm_bridge *next; - - bridge = drm_bridge_chain_get_first_bridge(encoder); - 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 27eef63ce0ff..3ab16c95e59e 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -384,8 +384,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; @@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, static inline struct drm_bridge * drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) { - return bridge->next; + if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain)) + return NULL; + + return list_next_entry(bridge, chain_node); }
/** @@ -434,7 +437,8 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) static inline 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); }
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index f06164f44efe..9b3dde177c81 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -172,7 +172,14 @@ struct drm_encoder { * &drm_connector_state.crtc. */ struct drm_crtc *crtc; - struct drm_bridge *bridge; + + /** + * @bridge_chain: Bridges attached to this encoder. The first entry of + * this list is always &drm_encoder.bridge. It may be followed by other + * bridge entities. + */ + struct list_head bridge_chain; + const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private; };
On 23/10/2019 17:44, Boris Brezillon wrote:
So that each element in the chain can easily access its predecessor. This will be needed to support bus format negotiation between elements of the bridge chain.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Adjust things to the "dummy encoder bridge" change (patch 2 in this series)
drivers/gpu/drm/drm_bridge.c | 171 ++++++++++++++++++++++------------ drivers/gpu/drm/drm_encoder.c | 16 +--- include/drm/drm_bridge.h | 12 ++- include/drm/drm_encoder.h | 9 +- 4 files changed, 135 insertions(+), 73 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 54c874493c57..c5cf8a9c4237 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;
elselist_splice(&tmp_list, &previous->chain_node);
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;
}
@@ -190,18 +193,22 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
- bool ret = true;
struct drm_encoder *encoder;
if (!bridge) return true;
- if (bridge->funcs->mode_fixup)
ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain,
chain_node) {
if (!bridge->funcs->mode_fixup)
continue;
- ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
adjusted_mode);
if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode))
return false;
- }
- return ret;
- return true;
} EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
@@ -224,18 +231,24 @@ enum drm_mode_status drm_bridge_chain_mode_valid(struct drm_bridge *bridge, const struct drm_display_mode *mode) {
- enum drm_mode_status ret = MODE_OK;
struct drm_encoder *encoder;
if (!bridge)
return ret;
return MODE_OK;
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
enum drm_mode_status ret;
if (!bridge->funcs->mode_valid)
continue;
- if (bridge->funcs->mode_valid) ret = bridge->funcs->mode_valid(bridge, mode);
if (ret != MODE_OK)
return ret;
- }
- if (ret != MODE_OK)
return ret;
- return drm_bridge_chain_mode_valid(bridge->next, mode);
- return MODE_OK;
} EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
@@ -251,13 +264,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid); */ void drm_bridge_chain_disable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_bridge_chain_disable(bridge->next);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->disable)
iter->funcs->disable(iter);
- if (bridge->funcs->disable)
bridge->funcs->disable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_bridge_chain_disable);
@@ -274,13 +294,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable); */ void drm_bridge_chain_post_disable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- if (!bridge) return;
- if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);
- drm_bridge_chain_post_disable(bridge->next);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);
- }
} EXPORT_SYMBOL(drm_bridge_chain_post_disable);
@@ -300,13 +323,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) {
- struct drm_encoder *encoder;
- if (!bridge) return;
- if (bridge->funcs->mode_set)
bridge->funcs->mode_set(bridge, mode, adjusted_mode);
- drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
- encoder = bridge->encoder;
- list_for_each_entry_from(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);
@@ -323,13 +349,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set); */ void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_bridge_chain_pre_enable(bridge->next);
- if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
- }
} EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
@@ -345,13 +375,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable); */ void drm_bridge_chain_enable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- if (!bridge) return;
- if (bridge->funcs->enable)
bridge->funcs->enable(bridge);
- drm_bridge_chain_enable(bridge->next);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->enable)
bridge->funcs->enable(bridge);
- }
} EXPORT_SYMBOL(drm_bridge_chain_enable);
@@ -370,15 +403,23 @@ EXPORT_SYMBOL(drm_bridge_chain_enable); void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_atomic_bridge_chain_disable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain,
chain_node) {
if (iter->funcs->atomic_disable)
iter->funcs->atomic_disable(iter, state);
else if (iter->funcs->disable)
iter->funcs->disable(iter);
- if (bridge->funcs->atomic_disable)
bridge->funcs->atomic_disable(bridge, state);
- else if (bridge->funcs->disable)
bridge->funcs->disable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
@@ -398,15 +439,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- 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_chain_post_disable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_from(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);
@@ -426,15 +471,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_atomic_bridge_chain_pre_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
chain_node) {
if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, state);
else if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
- if (bridge->funcs->atomic_pre_enable)
bridge->funcs->atomic_pre_enable(bridge, state);
- else if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
@@ -453,15 +506,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- 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_chain_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &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 4fe9e723e227..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,23 +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;
struct drm_bridge *next;
bridge = drm_bridge_chain_get_first_bridge(encoder);
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 27eef63ce0ff..3ab16c95e59e 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -384,8 +384,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; @@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, static inline struct drm_bridge * drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) {
- return bridge->next;
- if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain))
return NULL;
- return list_next_entry(bridge, chain_node);
}
/** @@ -434,7 +437,8 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) static inline 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);
}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index f06164f44efe..9b3dde177c81 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -172,7 +172,14 @@ struct drm_encoder { * &drm_connector_state.crtc. */ struct drm_crtc *crtc;
- struct drm_bridge *bridge;
- /**
* @bridge_chain: Bridges attached to this encoder. The first entry of
* this list is always &drm_encoder.bridge. It may be followed by other
* bridge entities.
*/
- struct list_head bridge_chain;
- const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private;
};
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi,
On 25/10/2019 15:29, Neil Armstrong wrote:
On 23/10/2019 17:44, Boris Brezillon wrote:
So that each element in the chain can easily access its predecessor. This will be needed to support bus format negotiation between elements of the bridge chain.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Adjust things to the "dummy encoder bridge" change (patch 2 in this series)
drivers/gpu/drm/drm_bridge.c | 171 ++++++++++++++++++++++------------ drivers/gpu/drm/drm_encoder.c | 16 +--- include/drm/drm_bridge.h | 12 ++- include/drm/drm_encoder.h | 9 +- 4 files changed, 135 insertions(+), 73 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 54c874493c57..c5cf8a9c4237 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c
[...]
@@ -426,15 +471,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_atomic_bridge_chain_pre_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
chain_node) {
This should use the encoder local variable in list_for_each_entry_reverse()
if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, state);
else if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
- if (bridge->funcs->atomic_pre_enable)
bridge->funcs->atomic_pre_enable(bridge, state);
- else if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
@@ -453,15 +506,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- 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_chain_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
chain_node) {
This should use encoder instead of bridge->encoder otherwise bridge will change and bridge->encoder->bridge_chain won't be valid during the for_each and cause the following :
[ 79.082861] WARNING: CPU: 2 PID: 1999 at drivers/gpu/drm/drm_bridge.c:607 drm_atomic_bridge_chain_enable+0xac/0xc0 ... [ 79.210153] drm_atomic_bridge_chain_enable+0xac/0xc0 [ 79.215156] drm_atomic_helper_commit_modeset_enables+0x138/0x248 [ 79.221190] drm_atomic_helper_commit_tail_rpm+0x2c/0x78 [ 79.226452] commit_tail+0x50/0xc0 [ 79.229815] drm_atomic_helper_commit+0xe8/0x168 [ 79.234386] drm_atomic_commit+0x48/0x58 [ 79.238269] drm_client_modeset_commit_atomic.isra.15+0x184/0x220 [ 79.244305] drm_client_modeset_commit_force+0x64/0x1a0 [ 79.249482] drm_fb_helper_restore_fbdev_mode_unlocked+0x70/0xe8 [ 79.255432] drm_fbdev_client_restore+0x14/0x20 [ 79.259920] drm_client_dev_restore+0x80/0xd8 [ 79.264231] drm_lastclose+0x4c/0x58 [ 79.267765] drm_release+0xa8/0x178
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 4fe9e723e227..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,23 +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;
struct drm_bridge *next;
bridge = drm_bridge_chain_get_first_bridge(encoder);
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 27eef63ce0ff..3ab16c95e59e 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -384,8 +384,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; @@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, static inline struct drm_bridge * drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) {
- return bridge->next;
- if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain))
return NULL;
- return list_next_entry(bridge, chain_node);
}
/** @@ -434,7 +437,8 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) static inline 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);
}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index f06164f44efe..9b3dde177c81 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -172,7 +172,14 @@ struct drm_encoder { * &drm_connector_state.crtc. */ struct drm_crtc *crtc;
- struct drm_bridge *bridge;
- /**
* @bridge_chain: Bridges attached to this encoder. The first entry of
* this list is always &drm_encoder.bridge. It may be followed by other
* bridge entities.
*/
- struct list_head bridge_chain;
- const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private;
};
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
With these fixed, you can keep my Reviewed-by
Neil
Hi Neil,
Sorry for the late reply.
On Tue, 5 Nov 2019 17:02:30 +0100 Neil Armstrong narmstrong@baylibre.com wrote:
Hi,
On 25/10/2019 15:29, Neil Armstrong wrote:
On 23/10/2019 17:44, Boris Brezillon wrote:
So that each element in the chain can easily access its predecessor. This will be needed to support bus format negotiation between elements of the bridge chain.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Adjust things to the "dummy encoder bridge" change (patch 2 in this series)
drivers/gpu/drm/drm_bridge.c | 171 ++++++++++++++++++++++------------ drivers/gpu/drm/drm_encoder.c | 16 +--- include/drm/drm_bridge.h | 12 ++- include/drm/drm_encoder.h | 9 +- 4 files changed, 135 insertions(+), 73 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 54c874493c57..c5cf8a9c4237 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c
[...]
@@ -426,15 +471,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_atomic_bridge_chain_pre_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
chain_node) {
This should use the encoder local variable in list_for_each_entry_reverse()
if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, state);
else if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
- if (bridge->funcs->atomic_pre_enable)
bridge->funcs->atomic_pre_enable(bridge, state);
- else if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
@@ -453,15 +506,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- 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_chain_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
chain_node) {
This should use encoder instead of bridge->encoder otherwise bridge will change and bridge->encoder->bridge_chain won't be valid during the for_each and cause the following :
Oops, indeed. I fixed the very same problem in another helper but somehow missed those 2. Thanks for testing/reporting the bug.
Boris
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:58PM +0200, Boris Brezillon wrote:
So that each element in the chain can easily access its predecessor. This will be needed to support bus format negotiation between elements of the bridge chain.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Adjust things to the "dummy encoder bridge" change (patch 2 in this series)
drivers/gpu/drm/drm_bridge.c | 171 ++++++++++++++++++++++------------ drivers/gpu/drm/drm_encoder.c | 16 +--- include/drm/drm_bridge.h | 12 ++- include/drm/drm_encoder.h | 9 +- 4 files changed, 135 insertions(+), 73 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 54c874493c57..c5cf8a9c4237 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);
Couldn't we simplify this by adding the bridge to the list here ? We would need to remove it in the error path of the attach operation though, but wouldn't it make the code easier to read, and more efficient in the most likely path ?
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;
elselist_splice(&tmp_list, &previous->chain_node);
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;
}
@@ -190,18 +193,22 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
- bool ret = true;
struct drm_encoder *encoder;
if (!bridge) return true;
- if (bridge->funcs->mode_fixup)
ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain,
chain_node) {
if (!bridge->funcs->mode_fixup)
continue;
- ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
adjusted_mode);
if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode))
return false;
- }
- return ret;
- return true;
} EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
@@ -224,18 +231,24 @@ enum drm_mode_status drm_bridge_chain_mode_valid(struct drm_bridge *bridge, const struct drm_display_mode *mode) {
- enum drm_mode_status ret = MODE_OK;
struct drm_encoder *encoder;
if (!bridge)
return ret;
return MODE_OK;
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
enum drm_mode_status ret;
if (!bridge->funcs->mode_valid)
continue;
- if (bridge->funcs->mode_valid) ret = bridge->funcs->mode_valid(bridge, mode);
if (ret != MODE_OK)
return ret;
- }
- if (ret != MODE_OK)
return ret;
- return drm_bridge_chain_mode_valid(bridge->next, mode);
- return MODE_OK;
} EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
@@ -251,13 +264,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid); */ void drm_bridge_chain_disable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_bridge_chain_disable(bridge->next);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->disable)
iter->funcs->disable(iter);
- if (bridge->funcs->disable)
bridge->funcs->disable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_bridge_chain_disable);
@@ -274,13 +294,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable); */ void drm_bridge_chain_post_disable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- if (!bridge) return;
- if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);
- drm_bridge_chain_post_disable(bridge->next);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);
- }
} EXPORT_SYMBOL(drm_bridge_chain_post_disable);
@@ -300,13 +323,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) {
- struct drm_encoder *encoder;
- if (!bridge) return;
- if (bridge->funcs->mode_set)
bridge->funcs->mode_set(bridge, mode, adjusted_mode);
- drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
- encoder = bridge->encoder;
- list_for_each_entry_from(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);
@@ -323,13 +349,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set); */ void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
You could reuse the bridge parameter instead of declaring a local variable, the same way you do in drm_atomic_bridge_chain_enable().
- if (!bridge) return;
- drm_bridge_chain_pre_enable(bridge->next);
- if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
Or are you missing the iter == bridge check ?
- }
} EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
@@ -345,13 +375,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable); */ void drm_bridge_chain_enable(struct drm_bridge *bridge) {
- struct drm_encoder *encoder;
- if (!bridge) return;
- if (bridge->funcs->enable)
bridge->funcs->enable(bridge);
- drm_bridge_chain_enable(bridge->next);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->enable)
bridge->funcs->enable(bridge);
- }
} EXPORT_SYMBOL(drm_bridge_chain_enable);
@@ -370,15 +403,23 @@ EXPORT_SYMBOL(drm_bridge_chain_enable); void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_atomic_bridge_chain_disable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain,
chain_node) {
This can hold on a single line.
if (iter->funcs->atomic_disable)
iter->funcs->atomic_disable(iter, state);
else if (iter->funcs->disable)
iter->funcs->disable(iter);
- if (bridge->funcs->atomic_disable)
bridge->funcs->atomic_disable(bridge, state);
- else if (bridge->funcs->disable)
bridge->funcs->disable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
@@ -398,15 +439,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- 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_chain_post_disable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain,
chain_node) {
This too can hold on a single line.
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);
@@ -426,15 +471,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- struct drm_bridge *iter;
- if (!bridge) return;
- drm_atomic_bridge_chain_pre_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
chain_node) {
As Neil pointed out, &encoder->bridge_chain.
if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, state);
else if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
- if (bridge->funcs->atomic_pre_enable)
bridge->funcs->atomic_pre_enable(bridge, state);
- else if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
if (iter == bridge)
break;
- }
} EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
@@ -453,15 +506,19 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) {
- struct drm_encoder *encoder;
- 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_chain_enable(bridge->next, state);
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
Here too.
By the way, should these bridge chain functions take an encoder instead of a bridge (in a separate patch series probably) ?
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 4fe9e723e227..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,23 +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;
struct drm_bridge *next;
bridge = drm_bridge_chain_get_first_bridge(encoder);
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 27eef63ce0ff..3ab16c95e59e 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -384,8 +384,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; @@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, static inline struct drm_bridge * drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) {
- return bridge->next;
- if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain))
return NULL;
- return list_next_entry(bridge, chain_node);
A list_next_entry_or_null() would be useful in list.h I think (of course not as a mandatory part of this series :-)).
}
/** @@ -434,7 +437,8 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) static inline 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);
}
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index f06164f44efe..9b3dde177c81 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -172,7 +172,14 @@ struct drm_encoder { * &drm_connector_state.crtc. */ struct drm_crtc *crtc;
- struct drm_bridge *bridge;
- /**
* @bridge_chain: Bridges attached to this encoder. The first entry of
* this list is always &drm_encoder.bridge. It may be followed by other
There's no &drm_encoder.bridge anymore :-)
With all those small issues fixed,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
* bridge entities.
*/
- struct list_head bridge_chain;
- const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private;
};
To iterate over all bridges attached to a specific encoder.
Suggested-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * None
Changes in v2: * New patch --- include/drm/drm_bridge.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3ab16c95e59e..238e84ab63a3 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -441,6 +441,17 @@ drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) struct drm_bridge, chain_node); }
+/** + * for_each_bridge_in_chain() - Iterate over all bridges present in a chain + * @encoder: the encoder to iterate bridges on + * @bridge: a bridge pointer updated to point to the current bridge at each + * iteration + * + * Iterate over all bridges present in the bridge chain attached to @encoder. + */ +#define drm_for_each_bridge_in_chain(encoder, bridge) \ + list_for_each_entry(bridge, &(encoder)->bridge_chain, chain_node) + bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
On 23/10/2019 17:44, Boris Brezillon wrote:
To iterate over all bridges attached to a specific encoder.
Suggested-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- New patch
include/drm/drm_bridge.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3ab16c95e59e..238e84ab63a3 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -441,6 +441,17 @@ drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) struct drm_bridge, chain_node); }
+/**
- for_each_bridge_in_chain() - Iterate over all bridges present in a chain
- @encoder: the encoder to iterate bridges on
- @bridge: a bridge pointer updated to point to the current bridge at each
iteration
- Iterate over all bridges present in the bridge chain attached to @encoder.
- */
+#define drm_for_each_bridge_in_chain(encoder, bridge) \
- list_for_each_entry(bridge, &(encoder)->bridge_chain, chain_node)
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi Boris,
Thank you for the patch. On Wed, Oct 23, 2019 at 05:44:59PM +0200, Boris Brezillon wrote:
To iterate over all bridges attached to a specific encoder.
Suggested-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- New patch
include/drm/drm_bridge.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3ab16c95e59e..238e84ab63a3 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -441,6 +441,17 @@ drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) struct drm_bridge, chain_node); }
+/**
- for_each_bridge_in_chain() - Iterate over all bridges present in a chain
The name doesn't match the macro below. Apart from that,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
- @encoder: the encoder to iterate bridges on
- @bridge: a bridge pointer updated to point to the current bridge at each
iteration
- Iterate over all bridges present in the bridge chain attached to @encoder.
- */
+#define drm_for_each_bridge_in_chain(encoder, bridge) \
- list_for_each_entry(bridge, &(encoder)->bridge_chain, chain_node)
bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
One of the last remaining objects 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 existing state initialization/tracking logic.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * None
Changes in v2: * Use drm_for_each_bridge_in_chain() * Rename helpers to be more consistent with the rest of the DRM API * Improve/fix the doc --- drivers/gpu/drm/drm_atomic.c | 39 +++++++ drivers/gpu/drm/drm_atomic_helper.c | 20 ++++ drivers/gpu/drm/drm_bridge.c | 168 +++++++++++++++++++++++++++- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 118 +++++++++++++++++++ 5 files changed, 343 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 419381abbdd1..6c249ce39380 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,44 @@ 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); + + drm_for_each_bridge_in_chain(encoder, 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 f02ddffd4960..de985ba7ce2d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -730,6 +730,26 @@ 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 c5cf8a9c4237..c53966767782 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/mutex.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_encoder.h>
@@ -89,6 +90,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_bridge_duplicate_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_bridge_destroy_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 +147,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 +166,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_reset) + state = bridge->funcs->atomic_reset(bridge); + else + state = drm_atomic_helper_bridge_reset(bridge); + + if (IS_ERR(state)) { + ret = PTR_ERR(state); + 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 +210,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);
@@ -522,6 +578,108 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
+/** + * drm_atomic_helper_bridge_destroy_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_bridge_destroy_state(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); + +/** + * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its + * default + * @bridge: the bridge this state is refers 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_bridge_reset(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + memset(state, 0, sizeof(*state)); + state->bridge = bridge; +} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset); + +/** + * drm_atomic_helper_bridge_reset() - default &drm_plane_funcs.reset hook for + * bridges + * @bridge: the bridge to reset state on + * + * Resets the atomic state for @bridge by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + * + * RETURNS: + * A valid drm_bridge_state object in case of success, an ERR_PTR() + * giving the reaon of the failure otherwise. + */ +struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) +{ + struct drm_bridge_state *bridge_state; + + bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL); + if (!bridge_state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_bridge_reset(bridge, bridge_state); + return bridge_state; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); + +/** + * __drm_atomic_helper_bridge_duplicate_state() - Copy the content of the + * current bridge state into a + * new one + * @bridge: bridge element the old and new states are referring to + * @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_bridge_duplicate_state(struct drm_bridge *bridge, + struct drm_bridge_state *new) +{ + __drm_atomic_helper_private_obj_duplicate_state(&bridge->base, + &new->base); + new->bridge = bridge; +} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_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_bridge_duplicate_state(struct drm_bridge *bridge) +{ + struct drm_bridge_state *new; + + if (WARN_ON(!bridge->base.state)) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (new) + __drm_atomic_helper_bridge_duplicate_state(bridge, new); + + return new; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_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..1c0a08217712 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 238e84ab63a3..a608c47d1de5 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_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> @@ -33,6 +34,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 */ @@ -338,6 +356,48 @@ 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 (which is guaranteed to be + * non-NULL). + * + * The atomic_duplicate_state() is optional, the core falls back on + * &drm_atomic_helper_bridge_duplicate_state() when not implemented. + * + * 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_destroy_state: + * + * Destroy a bridge state object previously allocated by + * &drm_bridge_funcs.atomic_duplicate_state(). + * + * The atomic_destroy_state hook is optional, the coref falls back on + * &drm_atomic_helper_bridge_destroy_state() when not implemented. + */ + void (*atomic_destroy_state)(struct drm_bridge *bridge, + struct drm_bridge_state *state); + + /** + * @atomic_reset: + * + * Reset the bridge to a predefined state (or retrieve its current + * state) and return a &drm_bridge_state object matching this state. + * This function is called at attach time. + * + * The atomic_reset hook is optional, the core falls back on + * &drm_atomic_helper_bridge_reset() when not implemented. + * + * RETURNS: + * A valid drm_bridge_state object in case of success, an ERR_PTR() + * giving the reaon of the failure otherwise. + */ + struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); };
/** @@ -380,6 +440,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 */ @@ -404,6 +466,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); @@ -475,6 +543,56 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state);
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, + struct drm_bridge_state *state); +struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, + struct drm_bridge_state *state); +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, + struct drm_bridge_state *new); +struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge); + +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); struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
On 23/10/2019 17:45, Boris Brezillon wrote:
One of the last remaining objects 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 existing state initialization/tracking logic.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Use drm_for_each_bridge_in_chain()
- Rename helpers to be more consistent with the rest of the DRM API
- Improve/fix the doc
drivers/gpu/drm/drm_atomic.c | 39 +++++++ drivers/gpu/drm/drm_atomic_helper.c | 20 ++++ drivers/gpu/drm/drm_bridge.c | 168 +++++++++++++++++++++++++++- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 118 +++++++++++++++++++ 5 files changed, 343 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 419381abbdd1..6c249ce39380 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,44 @@ 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);
- drm_for_each_bridge_in_chain(encoder, 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 f02ddffd4960..de985ba7ce2d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -730,6 +730,26 @@ 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 c5cf8a9c4237..c53966767782 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/mutex.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_encoder.h>
@@ -89,6 +90,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_bridge_duplicate_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_bridge_destroy_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 +147,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 +166,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_reset)
state = bridge->funcs->atomic_reset(bridge);
else
state = drm_atomic_helper_bridge_reset(bridge);
if (IS_ERR(state)) {
ret = PTR_ERR(state);
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 +210,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);
@@ -522,6 +578,108 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
+/**
- drm_atomic_helper_bridge_destroy_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_bridge_destroy_state(struct drm_bridge *bridge,
struct drm_bridge_state *state)
+{
- kfree(state);
+} +EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state);
+/**
- __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
default
- @bridge: the bridge this state is refers 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_bridge_reset(struct drm_bridge *bridge,
struct drm_bridge_state *state)
+{
- memset(state, 0, sizeof(*state));
- state->bridge = bridge;
+} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
+/**
- drm_atomic_helper_bridge_reset() - default &drm_plane_funcs.reset hook for
bridges
- @bridge: the bridge to reset state on
- Resets the atomic state for @bridge by freeing the state pointer (which
- might be NULL, e.g. at driver load time) and allocating a new empty state
- object.
- RETURNS:
- A valid drm_bridge_state object in case of success, an ERR_PTR()
- giving the reaon of the failure otherwise.
- */
+struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) +{
- struct drm_bridge_state *bridge_state;
- bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL);
- if (!bridge_state)
return ERR_PTR(-ENOMEM);
- __drm_atomic_helper_bridge_reset(bridge, bridge_state);
- return bridge_state;
+} +EXPORT_SYMBOL(drm_atomic_helper_bridge_reset);
+/**
- __drm_atomic_helper_bridge_duplicate_state() - Copy the content of the
current bridge state into a
new one
- @bridge: bridge element the old and new states are referring to
- @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_bridge_duplicate_state(struct drm_bridge *bridge,
struct drm_bridge_state *new)
+{
- __drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
&new->base);
- new->bridge = bridge;
+} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_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_bridge_duplicate_state(struct drm_bridge *bridge) +{
- struct drm_bridge_state *new;
- if (WARN_ON(!bridge->base.state))
return NULL;
- new = kzalloc(sizeof(*new), GFP_KERNEL);
- if (new)
__drm_atomic_helper_bridge_duplicate_state(bridge, new);
- return new;
+} +EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_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..1c0a08217712 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 238e84ab63a3..a608c47d1de5 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_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> @@ -33,6 +34,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
*/ @@ -338,6 +356,48 @@ 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 (which is guaranteed to be
* non-NULL).
*
* The atomic_duplicate_state() is optional, the core falls back on
* &drm_atomic_helper_bridge_duplicate_state() when not implemented.
*
* 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_destroy_state:
*
* Destroy a bridge state object previously allocated by
* &drm_bridge_funcs.atomic_duplicate_state().
*
* The atomic_destroy_state hook is optional, the coref falls back on
* &drm_atomic_helper_bridge_destroy_state() when not implemented.
*/
- void (*atomic_destroy_state)(struct drm_bridge *bridge,
struct drm_bridge_state *state);
- /**
* @atomic_reset:
*
* Reset the bridge to a predefined state (or retrieve its current
* state) and return a &drm_bridge_state object matching this state.
* This function is called at attach time.
*
* The atomic_reset hook is optional, the core falls back on
* &drm_atomic_helper_bridge_reset() when not implemented.
*
* RETURNS:
* A valid drm_bridge_state object in case of success, an ERR_PTR()
* giving the reaon of the failure otherwise.
*/
- struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
};
/** @@ -380,6 +440,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 */
@@ -404,6 +466,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); @@ -475,6 +543,56 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state);
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
struct drm_bridge_state *new);
+struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
+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); struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
On 23/10/2019 17:45, Boris Brezillon wrote:
One of the last remaining objects 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 existing state initialization/tracking logic.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Use drm_for_each_bridge_in_chain()
- Rename helpers to be more consistent with the rest of the DRM API
- Improve/fix the doc
drivers/gpu/drm/drm_atomic.c | 39 +++++++ drivers/gpu/drm/drm_atomic_helper.c | 20 ++++ drivers/gpu/drm/drm_bridge.c | 168 +++++++++++++++++++++++++++- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 118 +++++++++++++++++++ 5 files changed, 343 insertions(+), 5 deletions(-)
[...]
--- 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 238e84ab63a3..a608c47d1de5 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_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> @@ -33,6 +34,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
*/ @@ -338,6 +356,48 @@ 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 (which is guaranteed to be
* non-NULL).
*
* The atomic_duplicate_state() is optional, the core falls back on
* &drm_atomic_helper_bridge_duplicate_state() when not implemented.
*
* 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_destroy_state:
*
* Destroy a bridge state object previously allocated by
* &drm_bridge_funcs.atomic_duplicate_state().
*
* The atomic_destroy_state hook is optional, the coref falls back on
* &drm_atomic_helper_bridge_destroy_state() when not implemented.
*/
- void (*atomic_destroy_state)(struct drm_bridge *bridge,
struct drm_bridge_state *state);
- /**
* @atomic_reset:
*
* Reset the bridge to a predefined state (or retrieve its current
* state) and return a &drm_bridge_state object matching this state.
* This function is called at attach time.
*
* The atomic_reset hook is optional, the core falls back on
* &drm_atomic_helper_bridge_reset() when not implemented.
*
* RETURNS:
* A valid drm_bridge_state object in case of success, an ERR_PTR()
* giving the reaon of the failure otherwise.
*/
- struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
};
/** @@ -380,6 +440,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 */
@@ -404,6 +466,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); @@ -475,6 +543,56 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state);
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
struct drm_bridge_state *new);
+struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
+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;
drm_atomic_get_private_obj_state will return an error object on error, so should be: if (IS_ERR(obj_state)) return ERR_CAST(obj_state);
- 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); struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
With this fixed, you can keep my Reviewed-by
Neil
On Tue, 5 Nov 2019 17:05:16 +0100 Neil Armstrong narmstrong@baylibre.com wrote:
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); @@ -475,6 +543,56 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state);
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
struct drm_bridge_state *new);
+struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
+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;
drm_atomic_get_private_obj_state will return an error object on error, so should be: if (IS_ERR(obj_state)) return ERR_CAST(obj_state);
Right, I'll fix it in v4.
Thanks,
Boris
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:00PM +0200, Boris Brezillon wrote:
One of the last remaining objects 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 existing state initialization/tracking logic.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Use drm_for_each_bridge_in_chain()
- Rename helpers to be more consistent with the rest of the DRM API
- Improve/fix the doc
drivers/gpu/drm/drm_atomic.c | 39 +++++++ drivers/gpu/drm/drm_atomic_helper.c | 20 ++++ drivers/gpu/drm/drm_bridge.c | 168 +++++++++++++++++++++++++++- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 118 +++++++++++++++++++ 5 files changed, 343 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 419381abbdd1..6c249ce39380 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,44 @@ 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);
- drm_for_each_bridge_in_chain(encoder, 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 f02ddffd4960..de985ba7ce2d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -730,6 +730,26 @@ 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 c5cf8a9c4237..c53966767782 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/mutex.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_encoder.h>
@@ -89,6 +90,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_bridge_duplicate_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_bridge_destroy_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 +147,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 +166,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_reset)
state = bridge->funcs->atomic_reset(bridge);
else
state = drm_atomic_helper_bridge_reset(bridge);
if (IS_ERR(state)) {
ret = PTR_ERR(state);
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 +210,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);
@@ -522,6 +578,108 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
+/**
- drm_atomic_helper_bridge_destroy_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_bridge_destroy_state(struct drm_bridge *bridge,
struct drm_bridge_state *state)
+{
- kfree(state);
+} +EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state);
As this function is called as the default in +drm_bridge_atomic_destroy_priv_state(), does it need to be exported ?
+/**
- __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
default
- @bridge: the bridge this state is refers to
s/is refers/refers/
- @state: bridge state to initialize
- For now it's just a memset(0) plus a state->bridge assignment. Might
- be extended in the future.
"Might" is an understatement :-) This function will be extended when the drm_bridge_state structure will gain new fields that need to be initialized. I would have dropped this paragraph, up to you. You could maybe replace it with
"Initialize the bridge state to default values. This is meant to be called by the bridge &drm_plane_funcs.reset hook for bridges that subclass the bridge state."
- */
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
struct drm_bridge_state *state)
+{
- memset(state, 0, sizeof(*state));
- state->bridge = bridge;
+} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
+/**
- drm_atomic_helper_bridge_reset() - default &drm_plane_funcs.reset hook for
bridges
- @bridge: the bridge to reset state on
- Resets the atomic state for @bridge by freeing the state pointer (which
- might be NULL, e.g. at driver load time) and allocating a new empty state
- object.
The function doesn't free the state pointer...
- RETURNS:
- A valid drm_bridge_state object in case of success, an ERR_PTR()
- giving the reaon of the failure otherwise.
s/reaon/reason/
- */
+struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) +{
- struct drm_bridge_state *bridge_state;
- bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL);
- if (!bridge_state)
return ERR_PTR(-ENOMEM);
- __drm_atomic_helper_bridge_reset(bridge, bridge_state);
- return bridge_state;
+} +EXPORT_SYMBOL(drm_atomic_helper_bridge_reset);
+/**
- __drm_atomic_helper_bridge_duplicate_state() - Copy the content of the
current bridge state into a
new one
Maybe "Copy atomic bridge state" to match the other helpers ?
- @bridge: bridge element the old and new states are referring to
- @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.
The documentation of these helpers seems to differ from the other atomic state helpers. Shouldn't they be consistent ?
- */
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
struct drm_bridge_state *new)
+{
- __drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
&new->base);
- new->bridge = bridge;
+} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_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_bridge_duplicate_state(struct drm_bridge *bridge) +{
- struct drm_bridge_state *new;
- if (WARN_ON(!bridge->base.state))
return NULL;
- new = kzalloc(sizeof(*new), GFP_KERNEL);
- if (new)
__drm_atomic_helper_bridge_duplicate_state(bridge, new);
- return new;
+} +EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state);
As this function is called as the default in drm_bridge_atomic_duplicate_priv_state(), does it need to be exported ?
#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..1c0a08217712 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 238e84ab63a3..a608c47d1de5 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>
While at it I'd add a blank line above.
#include <drm/drm_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> @@ -33,6 +34,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
*/ @@ -338,6 +356,48 @@ 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 (which is guaranteed to be
* non-NULL).
*
* The atomic_duplicate_state() is optional, the core falls back on
* &drm_atomic_helper_bridge_duplicate_state() when not implemented.
*
* 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_destroy_state:
*
* Destroy a bridge state object previously allocated by
* &drm_bridge_funcs.atomic_duplicate_state().
*
* The atomic_destroy_state hook is optional, the coref falls back on
s/coref/core/
* &drm_atomic_helper_bridge_destroy_state() when not implemented.
*/
- void (*atomic_destroy_state)(struct drm_bridge *bridge,
struct drm_bridge_state *state);
- /**
* @atomic_reset:
*
* Reset the bridge to a predefined state (or retrieve its current
* state) and return a &drm_bridge_state object matching this state.
* This function is called at attach time.
*
* The atomic_reset hook is optional, the core falls back on
* &drm_atomic_helper_bridge_reset() when not implemented.
*
* RETURNS:
* A valid drm_bridge_state object in case of success, an ERR_PTR()
* giving the reaon of the failure otherwise.
s/reaon/reason/
*/
- struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
};
/** @@ -380,6 +440,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 */
@@ -404,6 +466,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); @@ -475,6 +543,56 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state);
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
struct drm_bridge_state *state);
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
struct drm_bridge_state *new);
+struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
+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); struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
The [pre_]enable/[post_]disable hooks are passed the old atomic state. Update the doc and rename the arguments to make it clear.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * New patch --- drivers/gpu/drm/drm_bridge.c | 24 ++++++++++++------------ include/drm/drm_bridge.h | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c53966767782..ca74bfe028c9 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -447,7 +447,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable); /** * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state * * Calls &drm_bridge_funcs.atomic_disable (falls back on * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain, @@ -457,7 +457,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable); * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_atomic_state *old_state) { struct drm_encoder *encoder; struct drm_bridge *iter; @@ -469,7 +469,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { if (iter->funcs->atomic_disable) - iter->funcs->atomic_disable(iter, state); + iter->funcs->atomic_disable(iter, old_state); else if (iter->funcs->disable) iter->funcs->disable(iter);
@@ -483,7 +483,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges * in the encoder chain * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state * * Calls &drm_bridge_funcs.atomic_post_disable (falls back on * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain, @@ -493,7 +493,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_atomic_state *old_state) { struct drm_encoder *encoder;
@@ -504,7 +504,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { if (bridge->funcs->atomic_post_disable) - bridge->funcs->atomic_post_disable(bridge, state); + bridge->funcs->atomic_post_disable(bridge, old_state); else if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge); } @@ -515,7 +515,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in * the encoder chain * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state * * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain, @@ -525,7 +525,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_atomic_state *old_state) { struct drm_encoder *encoder; struct drm_bridge *iter; @@ -537,7 +537,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain, chain_node) { if (iter->funcs->atomic_pre_enable) - iter->funcs->atomic_pre_enable(iter, state); + iter->funcs->atomic_pre_enable(iter, old_state); else if (iter->funcs->pre_enable) iter->funcs->pre_enable(iter);
@@ -550,7 +550,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); /** * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state * * Calls &drm_bridge_funcs.atomic_enable (falls back on * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain, @@ -560,7 +560,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_atomic_state *old_state) { struct drm_encoder *encoder;
@@ -571,7 +571,7 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain, chain_node) { if (bridge->funcs->atomic_enable) - bridge->funcs->atomic_enable(bridge, state); + bridge->funcs->atomic_enable(bridge, old_state); else if (bridge->funcs->enable) bridge->funcs->enable(bridge); } diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index a608c47d1de5..e814e6d6e7c2 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -266,6 +266,8 @@ struct drm_bridge_funcs { * bridge's @atomic_pre_enable or @pre_enable function. If the preceding * element is a &drm_encoder it's called right before the encoder's * &drm_encoder_helper_funcs.atomic_enable hook. + * This hook is passed the old atomic state (atomic state after new/old + * states have been swapped). * * The display pipe (i.e. clocks and timing signals) feeding this bridge * will not yet be running when this callback is called. The bridge must @@ -281,7 +283,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_atomic_state *old_state);
/** * @atomic_enable: @@ -292,6 +294,8 @@ struct drm_bridge_funcs { * bridge's @atomic_enable or @enable function. If the preceding element * is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_enable hook. + * This hook is passed the old atomic state (atomic state after new/old + * states have been swapped). * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is running when this callback is called. This @@ -306,7 +310,7 @@ struct drm_bridge_funcs { * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *state); + struct drm_atomic_state *old_state); /** * @atomic_disable: * @@ -316,6 +320,8 @@ struct drm_bridge_funcs { * bridge's @atomic_disable or @disable vfunc. If the preceding element * is a &drm_encoder it's called right before the * &drm_encoder_helper_funcs.atomic_disable hook. + * This hook is passed the old atomic state (atomic state after new/old + * states have been swapped). * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is still running when this callback is called. @@ -329,7 +335,7 @@ struct drm_bridge_funcs { * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *state); + struct drm_atomic_state *old_state);
/** * @atomic_post_disable: @@ -340,6 +346,8 @@ struct drm_bridge_funcs { * @atomic_post_disable or @post_disable function. If the preceding * element is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_disable hook. + * This hook is passed the old atomic state (atomic state after new/old + * states have been swapped). * * The bridge must assume that the display pipe (i.e. clocks and timing * signals) feeding it is no longer running when this callback is @@ -355,7 +363,7 @@ struct drm_bridge_funcs { * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *state); + struct drm_atomic_state *old_state);
/** * @atomic_duplicate_state:
On 23/10/2019 17:45, Boris Brezillon wrote:
The [pre_]enable/[post_]disable hooks are passed the old atomic state. Update the doc and rename the arguments to make it clear.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- New patch
drivers/gpu/drm/drm_bridge.c | 24 ++++++++++++------------ include/drm/drm_bridge.h | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c53966767782..ca74bfe028c9 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -447,7 +447,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable); /**
- drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_disable (falls back on
- &drm_bridge_funcs.disable) op for all the bridges in the encoder chain,
@@ -457,7 +457,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder; struct drm_bridge *iter; @@ -469,7 +469,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { if (iter->funcs->atomic_disable)
iter->funcs->atomic_disable(iter, state);
else if (iter->funcs->disable) iter->funcs->disable(iter);iter->funcs->atomic_disable(iter, old_state);
@@ -483,7 +483,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
- drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_post_disable (falls back on
- &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain,
@@ -493,7 +493,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder;
@@ -504,7 +504,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, list_for_each_entry_from(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); }bridge->funcs->atomic_post_disable(bridge, old_state);
@@ -515,7 +515,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
- drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_pre_enable (falls back on
- &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain,
@@ -525,7 +525,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder; struct drm_bridge *iter; @@ -537,7 +537,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain, chain_node) { if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, state);
else if (iter->funcs->pre_enable) iter->funcs->pre_enable(iter);iter->funcs->atomic_pre_enable(iter, old_state);
@@ -550,7 +550,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); /**
- drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_enable (falls back on
- &drm_bridge_funcs.enable) op for all the bridges in the encoder chain,
@@ -560,7 +560,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder;
@@ -571,7 +571,7 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, list_for_each_entry_from(bridge, &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); }bridge->funcs->atomic_enable(bridge, old_state);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index a608c47d1de5..e814e6d6e7c2 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -266,6 +266,8 @@ struct drm_bridge_funcs { * bridge's @atomic_pre_enable or @pre_enable function. If the preceding * element is a &drm_encoder it's called right before the encoder's * &drm_encoder_helper_funcs.atomic_enable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
- The display pipe (i.e. clocks and timing signals) feeding this bridge
- will not yet be running when this callback is called. The bridge must
@@ -281,7 +283,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_atomic_state *old_state);
/**
- @atomic_enable:
@@ -292,6 +294,8 @@ struct drm_bridge_funcs { * bridge's @atomic_enable or @enable function. If the preceding element * is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_enable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
- The bridge can assume that the display pipe (i.e. clocks and timing
- signals) feeding it is running when this callback is called. This
@@ -306,7 +310,7 @@ struct drm_bridge_funcs { * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge,
struct drm_atomic_state *state);
/**struct drm_atomic_state *old_state);
- @atomic_disable:
@@ -316,6 +320,8 @@ struct drm_bridge_funcs { * bridge's @atomic_disable or @disable vfunc. If the preceding element * is a &drm_encoder it's called right before the * &drm_encoder_helper_funcs.atomic_disable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
- The bridge can assume that the display pipe (i.e. clocks and timing
- signals) feeding it is still running when this callback is called.
@@ -329,7 +335,7 @@ struct drm_bridge_funcs { * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge,
struct drm_atomic_state *state);
struct drm_atomic_state *old_state);
/**
- @atomic_post_disable:
@@ -340,6 +346,8 @@ struct drm_bridge_funcs { * @atomic_post_disable or @post_disable function. If the preceding * element is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_disable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
- The bridge must assume that the display pipe (i.e. clocks and timing
- signals) feeding it is no longer running when this callback is
@@ -355,7 +363,7 @@ struct drm_bridge_funcs { * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge,
struct drm_atomic_state *state);
struct drm_atomic_state *old_state);
/**
- @atomic_duplicate_state:
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:01PM +0200, Boris Brezillon wrote:
The [pre_]enable/[post_]disable hooks are passed the old atomic state. Update the doc and rename the arguments to make it clear.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- New patch
drivers/gpu/drm/drm_bridge.c | 24 ++++++++++++------------ include/drm/drm_bridge.h | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c53966767782..ca74bfe028c9 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -447,7 +447,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable); /**
- drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_disable (falls back on
- &drm_bridge_funcs.disable) op for all the bridges in the encoder chain,
@@ -457,7 +457,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder; struct drm_bridge *iter; @@ -469,7 +469,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { if (iter->funcs->atomic_disable)
iter->funcs->atomic_disable(iter, state);
else if (iter->funcs->disable) iter->funcs->disable(iter);iter->funcs->atomic_disable(iter, old_state);
@@ -483,7 +483,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
- drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_post_disable (falls back on
- &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain,
@@ -493,7 +493,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder;
@@ -504,7 +504,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, list_for_each_entry_from(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); }bridge->funcs->atomic_post_disable(bridge, old_state);
@@ -515,7 +515,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
- drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_pre_enable (falls back on
- &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain,
@@ -525,7 +525,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder; struct drm_bridge *iter; @@ -537,7 +537,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain, chain_node) { if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, state);
else if (iter->funcs->pre_enable) iter->funcs->pre_enable(iter);iter->funcs->atomic_pre_enable(iter, old_state);
@@ -550,7 +550,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); /**
- drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
- @bridge: bridge control structure
- @state: atomic state being committed
- @old_state: old atomic state
- Calls &drm_bridge_funcs.atomic_enable (falls back on
- &drm_bridge_funcs.enable) op for all the bridges in the encoder chain,
@@ -560,7 +560,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
- Note: the bridge passed should be the one closest to the encoder
*/ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
struct drm_atomic_state *old_state)
{ struct drm_encoder *encoder;
@@ -571,7 +571,7 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, list_for_each_entry_from(bridge, &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); }bridge->funcs->atomic_enable(bridge, old_state);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index a608c47d1de5..e814e6d6e7c2 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -266,6 +266,8 @@ struct drm_bridge_funcs { * bridge's @atomic_pre_enable or @pre_enable function. If the preceding * element is a &drm_encoder it's called right before the encoder's * &drm_encoder_helper_funcs.atomic_enable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
I think the name change is enough to explain this. If you want to make it explicit in the documentation, I find the part between parentheses a bit confusing.
With this comment addressed,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
* * The display pipe (i.e. clocks and timing signals) feeding this bridge * will not yet be running when this callback is called. The bridge must
@@ -281,7 +283,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_atomic_state *old_state);
/**
- @atomic_enable:
@@ -292,6 +294,8 @@ struct drm_bridge_funcs { * bridge's @atomic_enable or @enable function. If the preceding element * is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_enable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
- The bridge can assume that the display pipe (i.e. clocks and timing
- signals) feeding it is running when this callback is called. This
@@ -306,7 +310,7 @@ struct drm_bridge_funcs { * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge,
struct drm_atomic_state *state);
/**struct drm_atomic_state *old_state);
- @atomic_disable:
@@ -316,6 +320,8 @@ struct drm_bridge_funcs { * bridge's @atomic_disable or @disable vfunc. If the preceding element * is a &drm_encoder it's called right before the * &drm_encoder_helper_funcs.atomic_disable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
- The bridge can assume that the display pipe (i.e. clocks and timing
- signals) feeding it is still running when this callback is called.
@@ -329,7 +335,7 @@ struct drm_bridge_funcs { * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge,
struct drm_atomic_state *state);
struct drm_atomic_state *old_state);
/**
- @atomic_post_disable:
@@ -340,6 +346,8 @@ struct drm_bridge_funcs { * @atomic_post_disable or @post_disable function. If the preceding * element is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_disable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
- The bridge must assume that the display pipe (i.e. clocks and timing
- signals) feeding it is no longer running when this callback is
@@ -355,7 +363,7 @@ struct drm_bridge_funcs { * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge,
struct drm_atomic_state *state);
struct drm_atomic_state *old_state);
/**
- @atomic_duplicate_state:
This way the drm_bridge_funcs interface is consistent with the rest of the subsystem.
The only driver implementing those hooks (analogix DP) is patched too.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * Old state clarification moved to a separate patch
Changes in v2: * Pass the old bridge state --- .../drm/bridge/analogix/analogix_dp_core.c | 12 ++-- drivers/gpu/drm/drm_bridge.c | 61 +++++++++++++++---- include/drm/drm_bridge.h | 24 ++++---- 3 files changed, 69 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index bb411fe52ae8..e438e757f2ce 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 ca74bfe028c9..377866e3214f 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -468,10 +468,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_disable) - iter->funcs->atomic_disable(iter, old_state); - else if (iter->funcs->disable) + if (iter->funcs->atomic_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + iter); + if (WARN_ON(!old_bridge_state)) + return; + + iter->funcs->atomic_disable(iter, old_bridge_state); + } else if (iter->funcs->disable) { iter->funcs->disable(iter); + }
if (iter == bridge) break; @@ -503,10 +512,20 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_post_disable) - bridge->funcs->atomic_post_disable(bridge, old_state); - else if (bridge->funcs->post_disable) + if (bridge->funcs->atomic_post_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_post_disable(bridge, + old_bridge_state); + } else if (bridge->funcs->post_disable) { bridge->funcs->post_disable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); @@ -536,10 +555,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_pre_enable) - iter->funcs->atomic_pre_enable(iter, old_state); - else if (iter->funcs->pre_enable) + if (iter->funcs->atomic_pre_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + iter); + if (WARN_ON(!old_bridge_state)) + return; + + iter->funcs->atomic_pre_enable(iter, old_bridge_state); + } else if (iter->funcs->pre_enable) { iter->funcs->pre_enable(iter); + }
if (iter == bridge) break; @@ -570,10 +598,19 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_enable) - bridge->funcs->atomic_enable(bridge, old_state); - else if (bridge->funcs->enable) + if (bridge->funcs->atomic_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_enable(bridge, old_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 e814e6d6e7c2..b1f557d8dba9 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -266,8 +266,8 @@ struct drm_bridge_funcs { * bridge's @atomic_pre_enable or @pre_enable function. If the preceding * element is a &drm_encoder it's called right before the encoder's * &drm_encoder_helper_funcs.atomic_enable hook. - * This hook is passed the old atomic state (atomic state after new/old - * states have been swapped). + * This hook is passed the old bridge state (the new one can be + * retrieved from bridge->state). * * The display pipe (i.e. clocks and timing signals) feeding this bridge * will not yet be running when this callback is called. The bridge must @@ -283,7 +283,7 @@ struct drm_bridge_funcs { * The @atomic_pre_enable callback is optional. */ void (*atomic_pre_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state);
/** * @atomic_enable: @@ -294,8 +294,8 @@ struct drm_bridge_funcs { * bridge's @atomic_enable or @enable function. If the preceding element * is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_enable hook. - * This hook is passed the old atomic state (atomic state after new/old - * states have been swapped). + * This hook is passed the old bridge state (the new one can be + * retrieved from bridge->state). * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is running when this callback is called. This @@ -310,7 +310,7 @@ struct drm_bridge_funcs { * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); /** * @atomic_disable: * @@ -320,8 +320,8 @@ struct drm_bridge_funcs { * bridge's @atomic_disable or @disable vfunc. If the preceding element * is a &drm_encoder it's called right before the * &drm_encoder_helper_funcs.atomic_disable hook. - * This hook is passed the old atomic state (atomic state after new/old - * states have been swapped). + * This hook is passed the old bridge state (the new one can be + * retrieved from bridge->state). * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is still running when this callback is called. @@ -335,7 +335,7 @@ struct drm_bridge_funcs { * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state);
/** * @atomic_post_disable: @@ -346,8 +346,8 @@ struct drm_bridge_funcs { * @atomic_post_disable or @post_disable function. If the preceding * element is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_disable hook. - * This hook is passed the old atomic state (atomic state after new/old - * states have been swapped). + * This hook is passed the old bridge state (the new one can be + * retrieved from bridge->state). * * The bridge must assume that the display pipe (i.e. clocks and timing * signals) feeding it is no longer running when this callback is @@ -363,7 +363,7 @@ struct drm_bridge_funcs { * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state);
/** * @atomic_duplicate_state:
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:02PM +0200, Boris Brezillon wrote:
This way the drm_bridge_funcs interface is consistent with the rest of the subsystem.
The only driver implementing those hooks (analogix DP) is patched too.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Old state clarification moved to a separate patch
Changes in v2:
- Pass the old bridge state
.../drm/bridge/analogix/analogix_dp_core.c | 12 ++-- drivers/gpu/drm/drm_bridge.c | 61 +++++++++++++++---- include/drm/drm_bridge.h | 24 ++++---- 3 files changed, 69 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index bb411fe52ae8..e438e757f2ce 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)
Should we name the parameter old_state ? Same comment below.
With this addressed,
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
{
- 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 ca74bfe028c9..377866e3214f 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -468,10 +468,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->atomic_disable)
iter->funcs->atomic_disable(iter, old_state);
else if (iter->funcs->disable)
if (iter->funcs->atomic_disable) {
struct drm_bridge_state *old_bridge_state;
old_bridge_state =
drm_atomic_get_old_bridge_state(old_state,
iter);
if (WARN_ON(!old_bridge_state))
return;
iter->funcs->atomic_disable(iter, old_bridge_state);
} else if (iter->funcs->disable) { iter->funcs->disable(iter);
}
if (iter == bridge) break;
@@ -503,10 +512,20 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->atomic_post_disable)
bridge->funcs->atomic_post_disable(bridge, old_state);
else if (bridge->funcs->post_disable)
if (bridge->funcs->atomic_post_disable) {
struct drm_bridge_state *old_bridge_state;
old_bridge_state =
drm_atomic_get_old_bridge_state(old_state,
bridge);
if (WARN_ON(!old_bridge_state))
return;
bridge->funcs->atomic_post_disable(bridge,
old_bridge_state);
} else if (bridge->funcs->post_disable) { bridge->funcs->post_disable(bridge);
}}
} EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); @@ -536,10 +555,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain, chain_node) {
if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, old_state);
else if (iter->funcs->pre_enable)
if (iter->funcs->atomic_pre_enable) {
struct drm_bridge_state *old_bridge_state;
old_bridge_state =
drm_atomic_get_old_bridge_state(old_state,
iter);
if (WARN_ON(!old_bridge_state))
return;
iter->funcs->atomic_pre_enable(iter, old_bridge_state);
} else if (iter->funcs->pre_enable) { iter->funcs->pre_enable(iter);
}
if (iter == bridge) break;
@@ -570,10 +598,19 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain, chain_node) {
if (bridge->funcs->atomic_enable)
bridge->funcs->atomic_enable(bridge, old_state);
else if (bridge->funcs->enable)
if (bridge->funcs->atomic_enable) {
struct drm_bridge_state *old_bridge_state;
old_bridge_state =
drm_atomic_get_old_bridge_state(old_state,
bridge);
if (WARN_ON(!old_bridge_state))
return;
bridge->funcs->atomic_enable(bridge, old_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 e814e6d6e7c2..b1f557d8dba9 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -266,8 +266,8 @@ struct drm_bridge_funcs { * bridge's @atomic_pre_enable or @pre_enable function. If the preceding * element is a &drm_encoder it's called right before the encoder's * &drm_encoder_helper_funcs.atomic_enable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
* This hook is passed the old bridge state (the new one can be
* retrieved from bridge->state).
- The display pipe (i.e. clocks and timing signals) feeding this bridge
- will not yet be running when this callback is called. The bridge must
@@ -283,7 +283,7 @@ struct drm_bridge_funcs { * The @atomic_pre_enable callback is optional. */ void (*atomic_pre_enable)(struct drm_bridge *bridge,
struct drm_atomic_state *old_state);
struct drm_bridge_state *old_bridge_state);
/**
- @atomic_enable:
@@ -294,8 +294,8 @@ struct drm_bridge_funcs { * bridge's @atomic_enable or @enable function. If the preceding element * is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_enable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
* This hook is passed the old bridge state (the new one can be
* retrieved from bridge->state).
- The bridge can assume that the display pipe (i.e. clocks and timing
- signals) feeding it is running when this callback is called. This
@@ -310,7 +310,7 @@ struct drm_bridge_funcs { * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge,
struct drm_atomic_state *old_state);
/**struct drm_bridge_state *old_bridge_state);
- @atomic_disable:
@@ -320,8 +320,8 @@ struct drm_bridge_funcs { * bridge's @atomic_disable or @disable vfunc. If the preceding element * is a &drm_encoder it's called right before the * &drm_encoder_helper_funcs.atomic_disable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
* This hook is passed the old bridge state (the new one can be
* retrieved from bridge->state).
- The bridge can assume that the display pipe (i.e. clocks and timing
- signals) feeding it is still running when this callback is called.
@@ -335,7 +335,7 @@ struct drm_bridge_funcs { * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge,
struct drm_atomic_state *old_state);
struct drm_bridge_state *old_bridge_state);
/**
- @atomic_post_disable:
@@ -346,8 +346,8 @@ struct drm_bridge_funcs { * @atomic_post_disable or @post_disable function. If the preceding * element is a &drm_encoder it's called right after the encoder's * &drm_encoder_helper_funcs.atomic_disable hook.
* This hook is passed the old atomic state (atomic state after new/old
* states have been swapped).
* This hook is passed the old bridge state (the new one can be
* retrieved from bridge->state).
- The bridge must assume that the display pipe (i.e. clocks and timing
- signals) feeding it is no longer running when this callback is
@@ -363,7 +363,7 @@ struct drm_bridge_funcs { * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge,
struct drm_atomic_state *old_state);
struct drm_bridge_state *old_bridge_state);
/**
- @atomic_duplicate_state:
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 on ->mode_fixup(), so the behavior should stay the same for existing bridge drivers).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * None
Changes in v2: * Clarify the fact that ->atomic_check() is replacing ->mode_fixup() --- drivers/gpu/drm/drm_atomic_helper.c | 12 +++--- drivers/gpu/drm/drm_bridge.c | 62 +++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 29 +++++++++++++- 3 files changed, 96 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index de985ba7ce2d..1d0a19511a0d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state) funcs = encoder->helper_private;
bridge = drm_bridge_chain_get_first_bridge(encoder); - ret = drm_bridge_chain_mode_fixup(bridge, - &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(bridge, + 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 377866e3214f..990e056296bd 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -615,6 +615,68 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, } 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 + * @bridge: bridge control structure + * @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_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_encoder *encoder = bridge->encoder; + struct drm_bridge *iter; + + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { + int ret; + + ret = drm_atomic_bridge_check(iter, crtc_state, conn_state); + if (ret) + return ret; + + if (iter == bridge) + break; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_check); + /** * drm_atomic_helper_bridge_destroy_state() - Default destroy state helper * @bridge: the bridge this state refers to diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index b1f557d8dba9..2beb1ef9a733 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -127,7 +127,9 @@ struct drm_bridge_funcs { * this function passes all other callbacks must succeed for this * configuration. * - * The @mode_fixup callback is optional. + * The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup() + * is not called when &drm_bridge_funcs.atomic_check() is implemented, + * so only one of them should be provided. * * NOTE: * @@ -391,6 +393,28 @@ 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. &drm_bridge_funcs.mode_fixup() is not + * called when &drm_bridge_funcs.atomic_check() is implemented, so only + * one of them should be provided. + * + * 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); + /** * @atomic_reset: * @@ -542,6 +566,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); void drm_bridge_chain_enable(struct drm_bridge *bridge);
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
On 23/10/2019 17:45, Boris Brezillon wrote:
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 on ->mode_fixup(), so the behavior should stay the same for existing bridge drivers).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Clarify the fact that ->atomic_check() is replacing ->mode_fixup()
drivers/gpu/drm/drm_atomic_helper.c | 12 +++--- drivers/gpu/drm/drm_bridge.c | 62 +++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 29 +++++++++++++- 3 files changed, 96 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index de985ba7ce2d..1d0a19511a0d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state) funcs = encoder->helper_private;
bridge = drm_bridge_chain_get_first_bridge(encoder);
ret = drm_bridge_chain_mode_fixup(bridge,
&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(bridge,
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 377866e3214f..990e056296bd 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -615,6 +615,68 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, } 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
- @bridge: bridge control structure
- @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_bridge *bridge,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+{
- struct drm_encoder *encoder = bridge->encoder;
- struct drm_bridge *iter;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
int ret;
ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
if (ret)
return ret;
if (iter == bridge)
break;
- }
- return 0;
+} +EXPORT_SYMBOL(drm_atomic_bridge_chain_check);
/**
- drm_atomic_helper_bridge_destroy_state() - Default destroy state helper
- @bridge: the bridge this state refers to
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index b1f557d8dba9..2beb1ef9a733 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -127,7 +127,9 @@ struct drm_bridge_funcs { * this function passes all other callbacks must succeed for this * configuration. *
* The @mode_fixup callback is optional.
* The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup()
* is not called when &drm_bridge_funcs.atomic_check() is implemented,
* so only one of them should be provided.
- NOTE:
@@ -391,6 +393,28 @@ 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. &drm_bridge_funcs.mode_fixup() is not
* called when &drm_bridge_funcs.atomic_check() is implemented, so only
* one of them should be provided.
*
* 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);
- /**
- @atomic_reset:
@@ -542,6 +566,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); void drm_bridge_chain_enable(struct drm_bridge *bridge);
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:03PM +0200, Boris Brezillon wrote:
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 on ->mode_fixup(), so the behavior should stay the same for existing bridge drivers).
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
Changes in v2:
- Clarify the fact that ->atomic_check() is replacing ->mode_fixup()
drivers/gpu/drm/drm_atomic_helper.c | 12 +++--- drivers/gpu/drm/drm_bridge.c | 62 +++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 29 +++++++++++++- 3 files changed, 96 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index de985ba7ce2d..1d0a19511a0d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state) funcs = encoder->helper_private;
bridge = drm_bridge_chain_get_first_bridge(encoder);
ret = drm_bridge_chain_mode_fixup(bridge,
&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(bridge,
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 377866e3214f..990e056296bd 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -615,6 +615,68 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, } 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
- @bridge: bridge control structure
- @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_bridge *bridge,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+{
- struct drm_encoder *encoder = bridge->encoder;
- struct drm_bridge *iter;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
int ret;
ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
if (ret)
return ret;
if (iter == bridge)
break;
- }
- return 0;
+} +EXPORT_SYMBOL(drm_atomic_bridge_chain_check);
/**
- drm_atomic_helper_bridge_destroy_state() - Default destroy state helper
- @bridge: the bridge this state refers to
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index b1f557d8dba9..2beb1ef9a733 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -127,7 +127,9 @@ struct drm_bridge_funcs { * this function passes all other callbacks must succeed for this * configuration. *
* The @mode_fixup callback is optional.
* The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup()
* is not called when &drm_bridge_funcs.atomic_check() is implemented,
* so only one of them should be provided.
- NOTE:
@@ -391,6 +393,28 @@ 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.
As explained in the review of the RFC, I think it's a mistake not to define the semantics of this operation precisely, and in particular not to define explictly what parameters bridge drivers are allowed to modify here. I however don't want to make this a prerequisite for your series, so
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
but I'm sure we'll regret this later when different bridges will have slightly incompatible implementations.
*
* &drm_bridge_funcs.atomic_check() hooks are called in reverse
* order (from the last to the first bridge).
*
* This method is optional. &drm_bridge_funcs.mode_fixup() is not
* called when &drm_bridge_funcs.atomic_check() is implemented, so only
* one of them should be provided.
*
* 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);
- /**
- @atomic_reset:
@@ -542,6 +566,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); void drm_bridge_chain_enable(struct drm_bridge *bridge);
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
On Mon, 2 Dec 2019 19:03:36 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
- /**
* @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.
As explained in the review of the RFC, I think it's a mistake not to define the semantics of this operation precisely, and in particular not to define explictly what parameters bridge drivers are allowed to modify here. I however don't want to make this a prerequisite for your series, so
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
but I'm sure we'll regret this later when different bridges will have slightly incompatible implementations.
Nothing prevents us from working on this clarification after the patch series has been merged. I just said I couldn't come up with a good set of rules on my own, as I don't really know what bridge->mode_fixup() allows us to modify in the first place.
Hi Boris,
On Tue, Dec 03, 2019 at 11:11:51AM +0100, Boris Brezillon wrote:
On Mon, 2 Dec 2019 19:03:36 +0200 Laurent Pinchart wrote:
- /**
* @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.
As explained in the review of the RFC, I think it's a mistake not to define the semantics of this operation precisely, and in particular not to define explictly what parameters bridge drivers are allowed to modify here. I however don't want to make this a prerequisite for your series, so
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
but I'm sure we'll regret this later when different bridges will have slightly incompatible implementations.
Nothing prevents us from working on this clarification after the patch series has been merged. I just said I couldn't come up with a good set of rules on my own, as I don't really know what bridge->mode_fixup() allows us to modify in the first place.
We can of course work on it later, but we both know it won't happen for some time, until different bridges will behave differently and we'll have to sort a really big mess :-)
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 --- Changes in v3: * Inline drm_bridge_chain_get_prev_bridge() * Fix the doc
Changes in v2: * Fix the kerneldoc * Drop the !bridge || !bridge->encoder check --- include/drm/drm_bridge.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2beb1ef9a733..3fb518494640 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -526,6 +526,22 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) return list_next_entry(bridge, chain_node); }
+/** + * 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 @bridge is the first. + */ +static inline struct drm_bridge * +drm_bridge_chain_get_prev_bridge(struct drm_bridge *bridge) +{ + if (list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) + return NULL; + + return list_prev_entry(bridge, chain_node); +} + /** * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain * @encoder: encoder object
On 23/10/2019 17:45, Boris Brezillon wrote:
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
Changes in v3:
- Inline drm_bridge_chain_get_prev_bridge()
- Fix the doc
Changes in v2:
- Fix the kerneldoc
- Drop the !bridge || !bridge->encoder check
include/drm/drm_bridge.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2beb1ef9a733..3fb518494640 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -526,6 +526,22 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) return list_next_entry(bridge, chain_node); }
+/**
- 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 @bridge is the first.
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_prev_bridge(struct drm_bridge *bridge) +{
- if (list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain))
return NULL;
- return list_prev_entry(bridge, chain_node);
+}
/**
- drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
- @encoder: encoder object
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:04PM +0200, Boris Brezillon wrote:
Will be useful for bridge drivers that want to do bus format negotiation with their neighbours.
A general comment, I think the body of the commit message should be readable on its own, without the subject line. It may be a matter of personal taste.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Changes in v3:
- Inline drm_bridge_chain_get_prev_bridge()
- Fix the doc
Changes in v2:
- Fix the kerneldoc
- Drop the !bridge || !bridge->encoder check
include/drm/drm_bridge.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2beb1ef9a733..3fb518494640 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -526,6 +526,22 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge) return list_next_entry(bridge, chain_node); }
+/**
- 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 @bridge is the first.
- */
+static inline struct drm_bridge * +drm_bridge_chain_get_prev_bridge(struct drm_bridge *bridge) +{
- if (list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain))
return NULL;
- return list_prev_entry(bridge, chain_node);
+}
/**
- drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
- @encoder: encoder object
drm_bridge_state is extended to describe the input and output bus configuration. This bus configuration is exposed through the drm_bus_cfg struct which contains 2 properties: the bus format and the bus flags.
Bus format negotiation is automated by the core, drivers just have to implement the ->atomic_get_{output,input}_bus_fmts() hooks if they want to take part to this negotiation. Negotiation happens in reserve order, starting from the last element of the chain (the one directly connected to the display) up to the first element of the chain (the one connected to the encoder). During this negotiation all supported formats are tested until we find one that works, meaning that the formats array should be in decreasing preference order (assuming the driver has a preference order).
Note that the bus format negotiation works even if some elements in the chain don't implement the ->atomic_get_{output,input}_bus_fmts() hooks. In that case, the core advertises only MEDIA_BUS_FMT_FIXED and let the previous bridge element decide what to do (most of the time, bridge drivers will pick a default bus format of extract this piece of information from somewhere else, like a FW property).
Bus flags negotiation is left to drivers which can simply propagate the flags from the input of the next bridge element if there's no conversion done inside the bridge, or tweak them if the bridge does some kind of signal inversion.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * Fix the commit message (Reported by Laurent) * Document the fact that bus formats should not be directly modified by drivers (Suggested by Laurent) * Document the fact that format order matters (Suggested by Laurent) * Propagate bus flags by default * Document the fact that drivers can tweak bus flags if needed * Let ->atomic_get_{output,input}_bus_fmts() allocate the bus format array (Suggested by Laurent) * Add a drm_atomic_helper_bridge_propagate_bus_fmt() * Mandate that bridge drivers return accurate input_fmts even if they are known to be the first element in the bridge chain
Changes in v2: * Rework things to support more complex use cases --- drivers/gpu/drm/drm_bridge.c | 257 ++++++++++++++++++++++++++++++++++- include/drm/drm_bridge.h | 106 +++++++++++++++ 2 files changed, 362 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 990e056296bd..6022fb3d406a 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -641,13 +641,251 @@ static int drm_atomic_bridge_check(struct drm_bridge *bridge, return 0; }
+static int select_bus_fmt_recursive(struct drm_bridge *first, + struct drm_bridge *cur, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 out_bus_fmt) +{ + struct drm_bridge_state *cur_state; + unsigned int num_in_bus_fmts, i; + struct drm_bridge *prev; + u32 *in_bus_fmts; + int ret; + + prev = drm_bridge_chain_get_prev_bridge(cur); + cur_state = drm_atomic_get_new_bridge_state(crtc_state->state, cur); + if (WARN_ON(!cur_state)) + return -EINVAL; + + /* + * Bus format negotiation is not supported by this bridge, let's pass + * MEDIA_BUS_FMT_FIXED to the previous bridge in the chain and hope + * that it can handle this situation gracefully (by providing + * appropriate default values). + */ + if (!cur->funcs->atomic_get_input_bus_fmts) { + if (cur != first) { + ret = select_bus_fmt_recursive(first, prev, crtc_state, + conn_state, + MEDIA_BUS_FMT_FIXED); + if (ret) + return ret; + } + + cur_state->input_bus_cfg.fmt = MEDIA_BUS_FMT_FIXED; + cur_state->output_bus_cfg.fmt = out_bus_fmt; + return 0; + } + + in_bus_fmts = cur->funcs->atomic_get_input_bus_fmts(cur, cur_state, + crtc_state, + conn_state, + out_bus_fmt, + &num_in_bus_fmts); + if (!num_in_bus_fmts) + return -ENOTSUPP; + else if (!in_bus_fmts) + return -ENOMEM; + + if (first == cur) { + cur_state->input_bus_cfg.fmt = in_bus_fmts[0]; + cur_state->output_bus_cfg.fmt = out_bus_fmt; + kfree(in_bus_fmts); + return 0; + } + + for (i = 0; i < num_in_bus_fmts; i++) { + ret = select_bus_fmt_recursive(first, prev, crtc_state, + conn_state, in_bus_fmts[i]); + if (ret != -ENOTSUPP) + break; + } + + if (!ret) { + cur_state->input_bus_cfg.fmt = in_bus_fmts[i]; + cur_state->output_bus_cfg.fmt = out_bus_fmt; + } + + kfree(in_bus_fmts); + return ret; +} + +/* + * This function is called by &drm_atomic_bridge_chain_check() just before + * calling &drm_bridge_funcs.atomic_check() on all elements of the chain. + * It's providing bus format negotiation between bridge elements. The + * negotiation happens in reverse order, starting from the last element in + * the chain up to @bridge. + * + * Negotiation starts by retrieving supported output bus formats on the last + * bridge element and testing them one by one. The test is recursive, meaning + * that for each tested output format, the whole chain will be walked backward, + * and each element will have to choose an input bus format that can be + * transcoded to the requested output format. When a bridge element does not + * support transcoding into a specific output format -ENOTSUPP is returned and + * the next bridge element will have to try a different format. If none of the + * combinations worked, -ENOTSUPP is returned and the atomic modeset will fail. + * + * This implementation is relying on + * &drm_bridge_funcs.atomic_get_output_bus_fmts() and + * &drm_bridge_funcs.atomic_get_input_bus_fmts() to gather supported + * input/output formats. + * When &drm_bridge_funcs.atomic_get_output_bus_fmts() is not implemented by + * the last element of the chain, &drm_atomic_bridge_chain_select_bus_fmts() + * tries a single format: &drm_connector.display_info.bus_formats[0] if + * available, MEDIA_BUS_FMT_FIXED otherwise. + * When &drm_bridge_funcs.atomic_get_input_bus_fmts() is not implemented, + * &drm_atomic_bridge_chain_select_bus_fmts() skips the negotiation on the + * bridge element that lacks this hook and asks the previous element in the + * chain to try MEDIA_BUS_FMT_FIXED. It's up to bridge drivers to decide what + * to do in that case (fail if they want to enforce bus format negotiation, or + * provide a reasonable default if they need to support pipelines where not + * all elements support bus format negotiation). + */ +static int +drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_connector *conn = conn_state->connector; + struct drm_encoder *encoder = bridge->encoder; + struct drm_bridge_state *last_bridge_state; + unsigned int i, num_out_bus_fmts; + struct drm_bridge *last_bridge; + u32 *out_bus_fmts; + int ret = 0; + + last_bridge = list_last_entry(&encoder->bridge_chain, + struct drm_bridge, chain_node); + last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + last_bridge); + if (WARN_ON(!last_bridge_state)) + return -EINVAL; + + if (last_bridge->funcs->atomic_get_output_bus_fmts) { + const struct drm_bridge_funcs *funcs = last_bridge->funcs; + + out_bus_fmts = funcs->atomic_get_output_bus_fmts(last_bridge, + last_bridge_state, + crtc_state, + conn_state, + &num_out_bus_fmts); + if (!num_out_bus_fmts) + return -ENOTSUPP; + else if (!out_bus_fmts) + return -ENOMEM; + } else { + num_out_bus_fmts = 1; + out_bus_fmts = kzalloc(sizeof(*out_bus_fmts), GFP_KERNEL); + if (!out_bus_fmts) + return -ENOMEM; + + if (conn->display_info.num_bus_formats && + conn->display_info.bus_formats) + out_bus_fmts[0] = conn->display_info.bus_formats[0]; + else + out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; + } + + for (i = 0; i < num_out_bus_fmts; i++) { + ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state, + conn_state, out_bus_fmts[i]); + if (ret != -ENOTSUPP) + break; + } + + kfree(out_bus_fmts); + + return ret; +} + +static void +drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge, + struct drm_connector *conn, + struct drm_atomic_state *state) +{ + struct drm_bridge_state *bridge_state, *next_bridge_state; + struct drm_bridge *next_bridge; + u32 output_flags; + + bridge_state = drm_atomic_get_new_bridge_state(state, bridge); + next_bridge = drm_bridge_chain_get_next_bridge(bridge); + + /* + * Let's try to apply the most common case here, that is, propagate + * display_info flags for the last bridge, and propagate the input + * flags of the next bridge element to the output end of the current + * bridge when the bridge is not the last one. + * There are exceptions to this rule, like when signal inversion is + * happening at the board level, but that's something drivers can deal + * with from their &drm_bridge_funcs.atomic_check() implementation by + * simply overriding the flags value we've set here. + */ + if (!next_bridge) { + output_flags = conn->display_info.bus_flags; + } else { + next_bridge_state = drm_atomic_get_new_bridge_state(state, + next_bridge); + output_flags = next_bridge_state->input_bus_cfg.flags; + } + + bridge_state->output_bus_cfg.flags = output_flags; + + /* + * Propage the output flags to the input end of the bridge. Again, it's + * not necessarily what all bridges want, but that's what most of them + * do, and by doing that by default we avoid forcing drivers to + * duplicate the "dummy propagation" logic. + */ + bridge_state->input_bus_cfg.flags = output_flags; +} + +/** + * drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to + * the input end of a bridge + * @bridge: bridge control structure + * @bridge_state: new bridge state + * @crtc_state: new CRTC state + * @conn_state: new connector state + * @output_fmt: tested output bus format + * @num_input_fmts: will contain the size of the returned array + * + * Helper that propagate the output format to the input end of bridge. + * Particularly useful for dummy bridge elements like the panel_bridge. + * + * RETURNS: + * a valid format array of size @num_input_fmts, or NULL if the allocation + * failed or if @num_input_fmts is set to 0 + */ +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + *num_input_fmts = 1; + input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = output_fmt; + return input_fmts; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt); + /** * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain * @bridge: bridge control structure * @crtc_state: new CRTC state * @conn_state: new connector state * - * Calls &drm_bridge_funcs.atomic_check() (falls back on + * First trigger a bus format negotiation before calling + * &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() @@ -659,12 +897,29 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { + struct drm_connector *conn = conn_state->connector; struct drm_encoder *encoder = bridge->encoder; struct drm_bridge *iter; + int ret; + + ret = drm_atomic_bridge_chain_select_bus_fmts(bridge, crtc_state, + conn_state); + if (ret) + return ret;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { int ret;
+ /* + * Bus flags are propagated by default. If a bridge needs to + * tweak the input bus flags for any reason, it should happen + * in its &drm_bridge_funcs.atomic_check() implementation such + * that preceding bridges in the chain can propagate the new + * bus flags. + */ + drm_atomic_bridge_propagate_bus_flags(iter, conn, + crtc_state->state); + ret = drm_atomic_bridge_check(iter, crtc_state, conn_state); if (ret) return ret; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3fb518494640..d37aaf34b882 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -34,15 +34,43 @@ struct drm_bridge; struct drm_bridge_timings; struct drm_panel;
+/** + * struct drm_bus_cfg - bus configuration + * @fmt: format used on this bus. This field should not be directly modified + * by drivers (&drm_atomic_bridge_chain_select_bus_fmts() takes care of + * the bus format negotiation). + * @flags: DRM_BUS_ flags used on this bus. Drivers should set the output bus + * flags to the input bus flags of the next bridge element (unless they + * have a good reason not to, like a passive signal inversion between + * those 2 elements). Input bus flags can be different from the output + * ones if there's some kind of conversion happening inside the bridge, + * but most of the times it's safe to just propagate the output bus + * flags to the input end. + * This bus flags selection/propagation should be done in the driver + * &drm_bridge_funcs.atomic_check() hook. + * + * 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_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 * @@ -393,6 +421,69 @@ struct drm_bridge_funcs { void (*atomic_destroy_state)(struct drm_bridge *bridge, struct drm_bridge_state *state);
+ /** + * @atomic_get_output_bus_fmts: + * + * Return the supported bus formats on the output end of a bridge. + * The returned array must be allocated with kmalloc() and will be + * freed by the caller, if the allocation fails, NULL should be + * returned. num_output_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). + * + * This method is only called on the last element of the bridge chain + * as part of the bus format negotiation process that happens in + * &drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will + * fallback to &drm_connector.display_info.bus_formats[0] if + * &drm_connector.display_info.num_bus_formats > 0, + * MEDIA_BUS_FMT_FIXED otherwise. + */ + u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts); + + /** + * @atomic_get_input_bus_fmts: + * + * Return the supported bus formats on the input end of a bridge for + * a specific output bus format. + * The returned array must be allocated with kmalloc() and will be + * freed by the caller, if the allocation fails, NULL should be + * returned. num_output_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). + * + * This method is called on all element of the bridge chain as part of + * the bus format negotiation process that happens in + * &drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will bypass + * bus format negotiation on this element of the bridge without + * failing, and the previous element in the chain will be passed + * MEDIA_BUS_FMT_FIXED as its output bus format. + * + * Bridge drivers that need to support being linked to bridges that are + * not supporting bus format negotiation should handle the + * output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a + * sensible default value or extracting this information from somewhere + * else (FW property, &drm_display_mode, &drm_display_info, ...) + * + * Note: even if input format selection on the first bridge has no + * impact on the negotiation process (bus format negotiation stops once + * we reach the first element of the chain), drivers are expected to + * return accurate input formats. + */ + u32 *(*atomic_get_input_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + /** * @atomic_check: * @@ -407,6 +498,13 @@ struct drm_bridge_funcs { * called when &drm_bridge_funcs.atomic_check() is implemented, so only * one of them should be provided. * + * If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or + * &drm_bridge_state.input_bus_cfg.flags it should should happen in + * this function. By default those fields are set to the next bridge + * &drm_bridge_state.input_bus_cfg.flags value or + * &drm_connector.display_info.bus_flags if the bridge is the last + * element in the chain. + * * RETURNS: * zero if the check passed, a negative error code otherwise. */ @@ -594,6 +692,14 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state);
+u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, struct drm_bridge_state *state); struct drm_bridge_state *
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:05PM +0200, Boris Brezillon wrote:
drm_bridge_state is extended to describe the input and output bus configuration. This bus configuration is exposed through the
s/configuration/configurations/ s/This bus configuration is/These bus configurations are/
drm_bus_cfg struct which contains 2 properties: the bus format and the bus flags.
Bus format negotiation is automated by the core, drivers just have to implement the ->atomic_get_{output,input}_bus_fmts() hooks if they want to take part to this negotiation. Negotiation happens in reserve
s/reserve/reverse/
order, starting from the last element of the chain (the one directly connected to the display) up to the first element of the chain (the one connected to the encoder). During this negotiation all supported formats are tested until we find one that works, meaning that the formats array should be in decreasing preference order (assuming the driver has a preference order).
Note that the bus format negotiation works even if some elements in the chain don't implement the ->atomic_get_{output,input}_bus_fmts() hooks. In that case, the core advertises only MEDIA_BUS_FMT_FIXED and let
s/let/lets/
the previous bridge element decide what to do (most of the time, bridge drivers will pick a default bus format of extract this piece of
s/of extract/or extract/
information from somewhere else, like a FW property).
Bus flags negotiation is left to drivers which can simply propagate the flags from the input of the next bridge element if there's no conversion done inside the bridge, or tweak them if the bridge does some kind of signal inversion.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Fix the commit message (Reported by Laurent)
- Document the fact that bus formats should not be directly modified by drivers (Suggested by Laurent)
- Document the fact that format order matters (Suggested by Laurent)
- Propagate bus flags by default
- Document the fact that drivers can tweak bus flags if needed
- Let ->atomic_get_{output,input}_bus_fmts() allocate the bus format array (Suggested by Laurent)
- Add a drm_atomic_helper_bridge_propagate_bus_fmt()
- Mandate that bridge drivers return accurate input_fmts even if they are known to be the first element in the bridge chain
Changes in v2:
- Rework things to support more complex use cases
drivers/gpu/drm/drm_bridge.c | 257 ++++++++++++++++++++++++++++++++++- include/drm/drm_bridge.h | 106 +++++++++++++++ 2 files changed, 362 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 990e056296bd..6022fb3d406a 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -641,13 +641,251 @@ static int drm_atomic_bridge_check(struct drm_bridge *bridge, return 0; }
+static int select_bus_fmt_recursive(struct drm_bridge *first,
s/first/first_bridge/ to make the code more explicit ?
struct drm_bridge *cur,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 out_bus_fmt)
+{
- struct drm_bridge_state *cur_state;
- unsigned int num_in_bus_fmts, i;
- struct drm_bridge *prev;
- u32 *in_bus_fmts;
- int ret;
- prev = drm_bridge_chain_get_prev_bridge(cur);
- cur_state = drm_atomic_get_new_bridge_state(crtc_state->state, cur);
- if (WARN_ON(!cur_state))
return -EINVAL;
- /*
* Bus format negotiation is not supported by this bridge, let's pass
s/Bus format/If bus format/
or move the comment within the if () block.
* MEDIA_BUS_FMT_FIXED to the previous bridge in the chain and hope
* that it can handle this situation gracefully (by providing
* appropriate default values).
*/
- if (!cur->funcs->atomic_get_input_bus_fmts) {
if (cur != first) {
ret = select_bus_fmt_recursive(first, prev, crtc_state,
conn_state,
MEDIA_BUS_FMT_FIXED);
if (ret)
return ret;
}
cur_state->input_bus_cfg.fmt = MEDIA_BUS_FMT_FIXED;
cur_state->output_bus_cfg.fmt = out_bus_fmt;
return 0;
- }
- in_bus_fmts = cur->funcs->atomic_get_input_bus_fmts(cur, cur_state,
crtc_state,
conn_state,
out_bus_fmt,
&num_in_bus_fmts);
- if (!num_in_bus_fmts)
return -ENOTSUPP;
- else if (!in_bus_fmts)
return -ENOMEM;
- if (first == cur) {
cur_state->input_bus_cfg.fmt = in_bus_fmts[0];
cur_state->output_bus_cfg.fmt = out_bus_fmt;
kfree(in_bus_fmts);
return 0;
- }
- for (i = 0; i < num_in_bus_fmts; i++) {
ret = select_bus_fmt_recursive(first, prev, crtc_state,
conn_state, in_bus_fmts[i]);
if (ret != -ENOTSUPP)
break;
- }
- if (!ret) {
cur_state->input_bus_cfg.fmt = in_bus_fmts[i];
cur_state->output_bus_cfg.fmt = out_bus_fmt;
- }
- kfree(in_bus_fmts);
- return ret;
+}
+/*
- This function is called by &drm_atomic_bridge_chain_check() just before
- calling &drm_bridge_funcs.atomic_check() on all elements of the chain.
- It's providing bus format negotiation between bridge elements. The
s/It's providing/It performs/
- negotiation happens in reverse order, starting from the last element in
- the chain up to @bridge.
- Negotiation starts by retrieving supported output bus formats on the last
- bridge element and testing them one by one. The test is recursive, meaning
- that for each tested output format, the whole chain will be walked backward,
- and each element will have to choose an input bus format that can be
- transcoded to the requested output format. When a bridge element does not
- support transcoding into a specific output format -ENOTSUPP is returned and
- the next bridge element will have to try a different format. If none of the
- combinations worked, -ENOTSUPP is returned and the atomic modeset will fail.
- This implementation is relying on
- &drm_bridge_funcs.atomic_get_output_bus_fmts() and
- &drm_bridge_funcs.atomic_get_input_bus_fmts() to gather supported
- input/output formats.
Blank line or reflow.
- When &drm_bridge_funcs.atomic_get_output_bus_fmts() is not implemented by
- the last element of the chain, &drm_atomic_bridge_chain_select_bus_fmts()
- tries a single format: &drm_connector.display_info.bus_formats[0] if
- available, MEDIA_BUS_FMT_FIXED otherwise.
Blank line or reflow.
- When &drm_bridge_funcs.atomic_get_input_bus_fmts() is not implemented,
- &drm_atomic_bridge_chain_select_bus_fmts() skips the negotiation on the
- bridge element that lacks this hook and asks the previous element in the
- chain to try MEDIA_BUS_FMT_FIXED. It's up to bridge drivers to decide what
- to do in that case (fail if they want to enforce bus format negotiation, or
- provide a reasonable default if they need to support pipelines where not
- all elements support bus format negotiation).
- */
+static int +drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+{
- struct drm_connector *conn = conn_state->connector;
- struct drm_encoder *encoder = bridge->encoder;
- struct drm_bridge_state *last_bridge_state;
- unsigned int i, num_out_bus_fmts;
- struct drm_bridge *last_bridge;
- u32 *out_bus_fmts;
- int ret = 0;
- last_bridge = list_last_entry(&encoder->bridge_chain,
struct drm_bridge, chain_node);
- last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
last_bridge);
- if (WARN_ON(!last_bridge_state))
return -EINVAL;
- if (last_bridge->funcs->atomic_get_output_bus_fmts) {
const struct drm_bridge_funcs *funcs = last_bridge->funcs;
out_bus_fmts = funcs->atomic_get_output_bus_fmts(last_bridge,
last_bridge_state,
crtc_state,
conn_state,
&num_out_bus_fmts);
if (!num_out_bus_fmts)
return -ENOTSUPP;
else if (!out_bus_fmts)
return -ENOMEM;
- } else {
num_out_bus_fmts = 1;
out_bus_fmts = kzalloc(sizeof(*out_bus_fmts), GFP_KERNEL);
kmalloc would do.
if (!out_bus_fmts)
return -ENOMEM;
if (conn->display_info.num_bus_formats &&
conn->display_info.bus_formats)
out_bus_fmts[0] = conn->display_info.bus_formats[0];
else
out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED;
- }
- for (i = 0; i < num_out_bus_fmts; i++) {
ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state,
conn_state, out_bus_fmts[i]);
if (ret != -ENOTSUPP)
break;
- }
- kfree(out_bus_fmts);
- return ret;
+}
+static void +drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge,
struct drm_connector *conn,
struct drm_atomic_state *state)
+{
- struct drm_bridge_state *bridge_state, *next_bridge_state;
- struct drm_bridge *next_bridge;
- u32 output_flags;
- bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
- next_bridge = drm_bridge_chain_get_next_bridge(bridge);
- /*
* Let's try to apply the most common case here, that is, propagate
* display_info flags for the last bridge, and propagate the input
* flags of the next bridge element to the output end of the current
* bridge when the bridge is not the last one.
* There are exceptions to this rule, like when signal inversion is
* happening at the board level, but that's something drivers can deal
* with from their &drm_bridge_funcs.atomic_check() implementation by
* simply overriding the flags value we've set here.
*/
- if (!next_bridge) {
output_flags = conn->display_info.bus_flags;
- } else {
next_bridge_state = drm_atomic_get_new_bridge_state(state,
next_bridge);
output_flags = next_bridge_state->input_bus_cfg.flags;
- }
- bridge_state->output_bus_cfg.flags = output_flags;
- /*
* Propage the output flags to the input end of the bridge. Again, it's
* not necessarily what all bridges want, but that's what most of them
* do, and by doing that by default we avoid forcing drivers to
* duplicate the "dummy propagation" logic.
*/
- bridge_state->input_bus_cfg.flags = output_flags;
+}
+/**
- drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to
the input end of a bridge
I would move this function above select_bus_fmt_recursive() to avoid putting it in-between functions that are related to each other.
- @bridge: bridge control structure
- @bridge_state: new bridge state
- @crtc_state: new CRTC state
- @conn_state: new connector state
- @output_fmt: tested output bus format
- @num_input_fmts: will contain the size of the returned array
- Helper that propagate the output format to the input end of bridge.
- Particularly useful for dummy bridge elements like the panel_bridge.
I think this needs to be clarified.
* This helper is a pluggable implementation of the * &drm_bridge_funcs.atomic_get_input_bus_fmts operation for bridges that don't * modify the bus configuration between their input and their output. It returns * an array of input formats with a single element set to @output_fmt.
- RETURNS:
- a valid format array of size @num_input_fmts, or NULL if the allocation
- failed or if @num_input_fmts is set to 0
s/0/NULL/
and that's actually not true, you just crash if num_input_fmts == NULL :-) That's fine as num_input_fmts can't be NULL, so just drop the last part of the sentence.
- */
+u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts)
+{
- u32 *input_fmts;
- *num_input_fmts = 1;
- input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL);
- if (!input_fmts)
return NULL;
- input_fmts[0] = output_fmt;
- return input_fmts;
+} +EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
/**
- drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain
- @bridge: bridge control structure
- @crtc_state: new CRTC state
- @conn_state: new connector state
- Calls &drm_bridge_funcs.atomic_check() (falls back on
- First trigger a bus format negotiation before calling
- &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()
@@ -659,12 +897,29 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) {
struct drm_connector *conn = conn_state->connector; struct drm_encoder *encoder = bridge->encoder; struct drm_bridge *iter;
int ret;
ret = drm_atomic_bridge_chain_select_bus_fmts(bridge, crtc_state,
conn_state);
if (ret)
return ret;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { int ret;
/*
* Bus flags are propagated by default. If a bridge needs to
* tweak the input bus flags for any reason, it should happen
* in its &drm_bridge_funcs.atomic_check() implementation such
* that preceding bridges in the chain can propagate the new
* bus flags.
*/
drm_atomic_bridge_propagate_bus_flags(iter, conn,
crtc_state->state);
ret = drm_atomic_bridge_check(iter, crtc_state, conn_state); if (ret) return ret;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3fb518494640..d37aaf34b882 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -34,15 +34,43 @@ struct drm_bridge; struct drm_bridge_timings; struct drm_panel;
+/**
- struct drm_bus_cfg - bus configuration
- @fmt: format used on this bus. This field should not be directly modified
by drivers (&drm_atomic_bridge_chain_select_bus_fmts() takes care of
the bus format negotiation).
I would mention that the value stored here is one of MEDIA_BUS_FMT_*.
- @flags: DRM_BUS_ flags used on this bus. Drivers should set the output bus
flags to the input bus flags of the next bridge element (unless they
have a good reason not to, like a passive signal inversion between
those 2 elements). Input bus flags can be different from the output
ones if there's some kind of conversion happening inside the bridge,
but most of the times it's safe to just propagate the output bus
flags to the input end.
This bus flags selection/propagation should be done in the driver
&drm_bridge_funcs.atomic_check() hook.
- Encodes the bus format and bus flags used by one end of the bridge or
- by the encoder output.
I think this needs to be extended a little bit. I have no trouble understanding what this structure describes, but it may be less evident for someone who hasn't followed the development. The commit message suffers from the same issue, perhaps even more.
How about
* This structure stores the configuration of a physical bus between two * components in an output pipeline, usually between two bridges, an encoder and * a bridge, or a bridge and a connector. * * The bus configuration is stored in &drm_bridge_state separately for the input * and output buses, as seen from the point of view of each bridge. The bus * configuration of a bridge output is usually identical to the configuration of * the next bridge's input, but may differ if the signals are modified between * the two bridges, for instance by an inverter on the board. The input and * output configurations of a bridge may differ if the bridge modifies the * signals internally, for instance by performing format conversion, or * modifying signals polarities.
With this I would also simplify the description of the flags field as it would otherwise contain redundant information.
- */
+struct drm_bus_cfg {
- u32 fmt;
How about spelling this out as format instead of abbreviating it ?
- u32 flags;
+};
/**
- 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
The fields are named s/info/cfg/
*/ 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 * @@ -393,6 +421,69 @@ struct drm_bridge_funcs { void (*atomic_destroy_state)(struct drm_bridge *bridge, struct drm_bridge_state *state);
- /**
* @atomic_get_output_bus_fmts:
*
* Return the supported bus formats on the output end of a bridge.
* The returned array must be allocated with kmalloc() and will be
* freed by the caller, if the allocation fails, NULL should be
s/caller,/caller;/ or s/caller, if/caller if/
* returned. num_output_fmts must be set to the returned array size.
* Formats listed in the returned array should be listed in decreasing
* preference order (the core will try all formats until it finds one
* that works).
*
* This method is only called on the last element of the bridge chain
* as part of the bus format negotiation process that happens in
* &drm_atomic_bridge_chain_select_bus_fmts().
* This method is optional. When not implemented, the core will
* fallback to &drm_connector.display_info.bus_formats[0] if
s/fallback/fall back/
* &drm_connector.display_info.num_bus_formats > 0,
* MEDIA_BUS_FMT_FIXED otherwise.
s/MEDIA_BUS_FMT_FIXED/or to MEDIA_BUS_FMT_FIXED/
*/
- u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
unsigned int *num_output_fmts);
- /**
* @atomic_get_input_bus_fmts:
*
* Return the supported bus formats on the input end of a bridge for
* a specific output bus format.
You need either a blank line, or a reflow of the paragraph.
* The returned array must be allocated with kmalloc() and will be
* freed by the caller, if the allocation fails, NULL should be
s/caller,/caller;/ or s/caller, if/caller if/
* returned. num_output_fmts must be set to the returned array size.
* Formats listed in the returned array should be listed in decreasing
* preference order (the core will try all formats until it finds one
* that works).
You need to describe what happens if the output_fmt isn't supported. Should this return an allocated array of zero size or NULL ?
*
* This method is called on all element of the bridge chain as part of
s/element/elements/
* the bus format negotiation process that happens in
* &drm_atomic_bridge_chain_select_bus_fmts().
* This method is optional. When not implemented, the core will bypass
* bus format negotiation on this element of the bridge without
* failing, and the previous element in the chain will be passed
* MEDIA_BUS_FMT_FIXED as its output bus format.
*
* Bridge drivers that need to support being linked to bridges that are
* not supporting bus format negotiation should handle the
* output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a
* sensible default value or extracting this information from somewhere
* else (FW property, &drm_display_mode, &drm_display_info, ...)
*
* Note: even if input format selection on the first bridge has no
s/even/Even/
* impact on the negotiation process (bus format negotiation stops once
* we reach the first element of the chain), drivers are expected to
* return accurate input formats.
I would add "as the input format may be used to configure the CRTC output appropriately.".
*/
- u32 *(*atomic_get_input_bus_fmts)(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts);
- /**
- @atomic_check:
@@ -407,6 +498,13 @@ struct drm_bridge_funcs { * called when &drm_bridge_funcs.atomic_check() is implemented, so only * one of them should be provided. *
* If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or
* &drm_bridge_state.input_bus_cfg.flags it should should happen in
That's twice the same field, did you mean output_bus_cfg.flags for the second one ?
* this function. By default those fields are set to the next bridge
s/those fields are set/the &drm_bridge_state.output_bus_cfg.flags field is set/
* &drm_bridge_state.input_bus_cfg.flags value or
* &drm_connector.display_info.bus_flags if the bridge is the last
* element in the chain.
*
*/
- RETURNS:
- zero if the check passed, a negative error code otherwise.
@@ -594,6 +692,14 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state);
+u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts);
void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, struct drm_bridge_state *state); struct drm_bridge_state *
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 --- Hi Philipp,
I think I addressed all your comments except the addition of SYNC_DRIVE_POSEDGE/NEGEDGE flags, which would require changing ipu_crtc_mode_set_nofb() to take those flags into account or turning those flags into their PIXDATA counterpart. If you don't mind, I'd like to leave that for later.
Regards,
Boris
Changes in v3 (all suggested by Philipp): * Adjust to match core changes * Propagate output format to input format * Pick a default value when output_fmt = _FIXED * Add missing BGR888 and GBR888 fmts to imx_pd_bus_fmts[]
Changes in v2: * Adjust things to match the new bus-format negotiation infra --- drivers/gpu/drm/imx/parallel-display.c | 174 +++++++++++++++++++++---- 1 file changed, 150 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 35518e5de356..28ae083df3ca 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -24,6 +24,7 @@ struct imx_parallel_display { struct drm_connector connector; struct drm_encoder encoder; + struct drm_bridge bridge; struct device *dev; void *edid; int edid_len; @@ -31,7 +32,7 @@ struct imx_parallel_display { u32 bus_flags; struct drm_display_mode mode; struct drm_panel *panel; - struct drm_bridge *bridge; + struct drm_bridge *next_bridge; };
static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c) @@ -44,6 +45,11 @@ static inline struct imx_parallel_display *enc_to_imxpd(struct drm_encoder *e) return container_of(e, struct imx_parallel_display, encoder); }
+static inline struct imx_parallel_display *bridge_to_imxpd(struct drm_bridge *b) +{ + return container_of(b, struct imx_parallel_display, bridge); +} + static int imx_pd_connector_get_modes(struct drm_connector *connector) { struct imx_parallel_display *imxpd = con_to_imxpd(connector); @@ -89,37 +95,151 @@ static struct drm_encoder *imx_pd_connector_best_encoder( return &imxpd->encoder; }
-static void imx_pd_encoder_enable(struct drm_encoder *encoder) +static void imx_pd_bridge_enable(struct drm_bridge *bridge) { - struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
drm_panel_prepare(imxpd->panel); drm_panel_enable(imxpd->panel); }
-static void imx_pd_encoder_disable(struct drm_encoder *encoder) +static void imx_pd_bridge_disable(struct drm_bridge *bridge) { - struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
drm_panel_disable(imxpd->panel); drm_panel_unprepare(imxpd->panel); }
-static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +static const u32 imx_pd_bus_fmts[] = { + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_BGR888_1X24, + MEDIA_BUS_FMT_GBR888_1X24, + MEDIA_BUS_FMT_RGB666_1X18, + MEDIA_BUS_FMT_RGB666_1X24_CPADHI, + MEDIA_BUS_FMT_RGB565_1X16, +}; + +static u32 * +imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + struct drm_display_info *di = &conn_state->connector->display_info; + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); + u32 *output_fmts; + + if (!imxpd->bus_format && !di->num_bus_formats) + *num_output_fmts = ARRAY_SIZE(imx_pd_bus_fmts); + else + *num_output_fmts = 1; + + output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts), + GFP_KERNEL); + if (!output_fmts) + return NULL; + + if (!imxpd->bus_format && di->num_bus_formats) + output_fmts[0] = di->bus_formats[0]; + else if (!imxpd->bus_format) + memcpy(output_fmts, imx_pd_bus_fmts, + ARRAY_SIZE(imx_pd_bus_fmts)); + else + output_fmts[0] = imxpd->bus_format; + + return output_fmts; +} + +static u32 * +imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); + u32 *input_fmts; + + *num_input_fmts = 0; + if (output_fmt == MEDIA_BUS_FMT_FIXED) { + /* + * The next bridge does not support bus format negotiation, + * let's use the default RGB888 value. + */ + *num_input_fmts = 1; + output_fmt = MEDIA_BUS_FMT_RGB888_1X24; + } else if (!imxpd->bus_format) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) { + if (imx_pd_bus_fmts[i] == output_fmt) { + *num_input_fmts = 1; + break; + } + } + } else if (imxpd->bus_format == output_fmt) { + *num_input_fmts = 1; + } + + if (!*num_input_fmts) + return NULL; + + input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = output_fmt; + return input_fmts; +} + +static int imx_pd_bridge_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 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 imx_parallel_display *imxpd = bridge_to_imxpd(bridge); + struct drm_bridge_state *next_bridge_state = NULL; + struct drm_bridge *next_bridge; + u32 bus_flags, bus_fmt; + unsigned int i;
- 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 { - imx_crtc_state->bus_flags = imxpd->bus_flags; - imx_crtc_state->bus_format = imxpd->bus_format; + next_bridge = drm_bridge_chain_get_next_bridge(bridge); + if (next_bridge) + next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + next_bridge); + + if (next_bridge_state) + bus_flags = next_bridge_state->input_bus_cfg.flags; + else if (!imxpd->bus_format && di->num_bus_formats) + bus_flags = di->bus_flags; + else + bus_flags = imxpd->bus_flags; + + bus_fmt = bridge_state->input_bus_cfg.fmt; + for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) { + if (imx_pd_bus_fmts[i] == bus_fmt) + break; } + + if (i == ARRAY_SIZE(imx_pd_bus_fmts)) + return -EINVAL; + + if (bus_flags & + ~(DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE | + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)) + return -EINVAL; + + bridge_state->output_bus_cfg.flags = bus_flags; + bridge_state->input_bus_cfg.flags = bus_flags; + imx_crtc_state->bus_flags = bus_flags; + imx_crtc_state->bus_format = bridge_state->input_bus_cfg.fmt; imx_crtc_state->di_hsync_pin = 2; imx_crtc_state->di_vsync_pin = 3;
@@ -143,10 +263,12 @@ static const struct drm_encoder_funcs imx_pd_encoder_funcs = { .destroy = imx_drm_encoder_destroy, };
-static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { - .enable = imx_pd_encoder_enable, - .disable = imx_pd_encoder_disable, - .atomic_check = imx_pd_encoder_atomic_check, +static const struct drm_bridge_funcs imx_pd_bridge_funcs = { + .enable = imx_pd_bridge_enable, + .disable = imx_pd_bridge_disable, + .atomic_check = imx_pd_bridge_atomic_check, + .atomic_get_input_bus_fmts = imx_pd_bridge_atomic_get_input_bus_fmts, + .atomic_get_output_bus_fmts = imx_pd_bridge_atomic_get_output_bus_fmts, };
static int imx_pd_register(struct drm_device *drm, @@ -166,11 +288,13 @@ static int imx_pd_register(struct drm_device *drm, */ imxpd->connector.dpms = DRM_MODE_DPMS_OFF;
- drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs); drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
- if (!imxpd->bridge) { + imxpd->bridge.funcs = &imx_pd_bridge_funcs; + drm_bridge_attach(encoder, &imxpd->bridge, NULL); + + if (!imxpd->next_bridge) { drm_connector_helper_add(&imxpd->connector, &imx_pd_connector_helper_funcs); drm_connector_init(drm, &imxpd->connector, @@ -181,8 +305,9 @@ static int imx_pd_register(struct drm_device *drm, if (imxpd->panel) drm_panel_attach(imxpd->panel, &imxpd->connector);
- if (imxpd->bridge) { - ret = drm_bridge_attach(encoder, imxpd->bridge, NULL); + if (imxpd->next_bridge) { + ret = drm_bridge_attach(encoder, imxpd->next_bridge, + &imxpd->bridge); if (ret < 0) { dev_err(imxpd->dev, "failed to attach bridge: %d\n", ret); @@ -227,7 +352,8 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) imxpd->bus_format = bus_format;
/* port@1 is the output port */ - ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, &imxpd->bridge); + ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, + &imxpd->next_bridge); if (ret && ret != -ENODEV) return ret;
Hi Boris,
On Wed, 2019-10-23 at 17:45 +0200, Boris Brezillon wrote:
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
Hi Philipp,
I think I addressed all your comments except the addition of SYNC_DRIVE_POSEDGE/NEGEDGE flags, which would require changing ipu_crtc_mode_set_nofb() to take those flags into account or turning those flags into their PIXDATA counterpart. If you don't mind, I'd like to leave that for later.
I'm fine with this. I think it was phrased as a suggestion, too.
[...]
-static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+static const u32 imx_pd_bus_fmts[] = {
- MEDIA_BUS_FMT_RGB888_1X24,
- MEDIA_BUS_FMT_BGR888_1X24,
- MEDIA_BUS_FMT_GBR888_1X24,
GBR888 likely isn't useful (it's only used for the internal TV Encoder on i.MX5 so far), but it doesn't really hurt to include it either.
- MEDIA_BUS_FMT_RGB666_1X18,
- MEDIA_BUS_FMT_RGB666_1X24_CPADHI,
- MEDIA_BUS_FMT_RGB565_1X16,
+};
+static u32 * +imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
unsigned int *num_output_fmts)
+{
- struct drm_display_info *di = &conn_state->connector->display_info;
- struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
- u32 *output_fmts;
- if (!imxpd->bus_format && !di->num_bus_formats)
*num_output_fmts = ARRAY_SIZE(imx_pd_bus_fmts);
In this case we could just:
return kmemdup(imx_pd_bus_fmts, sizeof(imx_pd_bus_fmts));
and simplify the remaining function a tiny bit.
- else
*num_output_fmts = 1;
- output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
GFP_KERNEL);
- if (!output_fmts)
return NULL;
- if (!imxpd->bus_format && di->num_bus_formats)
output_fmts[0] = di->bus_formats[0];
- else if (!imxpd->bus_format)
memcpy(output_fmts, imx_pd_bus_fmts,
ARRAY_SIZE(imx_pd_bus_fmts));
- else
output_fmts[0] = imxpd->bus_format;
- return output_fmts;
+}
+static u32 * +imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts)
+{
- struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
- u32 *input_fmts;
- *num_input_fmts = 0;
- if (output_fmt == MEDIA_BUS_FMT_FIXED) {
/*
* The next bridge does not support bus format negotiation,
* let's use the default RGB888 value.
*/
*num_input_fmts = 1;
output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
- } else if (!imxpd->bus_format) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {
if (imx_pd_bus_fmts[i] == output_fmt) {
*num_input_fmts = 1;
break;
}
}
If this loop was split out into a helper function, it could be reused in .atomic_check below, for example:
if (imx_pd_format_supported(output_fmt)) *num_input_fmts = 1;
- } else if (imxpd->bus_format == output_fmt) {
*num_input_fmts = 1;
- }
- if (!*num_input_fmts)
return NULL;
- input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts),
GFP_KERNEL);
- if (!input_fmts)
return NULL;
- input_fmts[0] = output_fmt;
- return input_fmts;
+}
+static int imx_pd_bridge_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 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 imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
- struct drm_bridge_state *next_bridge_state = NULL;
- struct drm_bridge *next_bridge;
- u32 bus_flags, bus_fmt;
- unsigned int i;
- 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 {
imx_crtc_state->bus_flags = imxpd->bus_flags;
imx_crtc_state->bus_format = imxpd->bus_format;
- next_bridge = drm_bridge_chain_get_next_bridge(bridge);
- if (next_bridge)
next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
next_bridge);
- if (next_bridge_state)
bus_flags = next_bridge_state->input_bus_cfg.flags;
- else if (!imxpd->bus_format && di->num_bus_formats)
bus_flags = di->bus_flags;
- else
bus_flags = imxpd->bus_flags;
- bus_fmt = bridge_state->input_bus_cfg.fmt;
- for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {
if (imx_pd_bus_fmts[i] == bus_fmt)
}break;
- if (i == ARRAY_SIZE(imx_pd_bus_fmts))
return -EINVAL;
See above, this could just reuse:
if (!imx_pd_format_supported(bus_fmt)) return -EINVAL;
- if (bus_flags &
~(DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_DE_HIGH |
DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE |
DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE))
return -EINVAL;
- bridge_state->output_bus_cfg.flags = bus_flags;
- bridge_state->input_bus_cfg.flags = bus_flags;
- imx_crtc_state->bus_flags = bus_flags;
- imx_crtc_state->bus_format = bridge_state->input_bus_cfg.fmt; imx_crtc_state->di_hsync_pin = 2; imx_crtc_state->di_vsync_pin = 3;
[...]
Otherwise this looks good to me.
regards Philipp
Some LVDS encoder might support several input/output bus formats. Add a way to describe the one used on a specific design by adding optional 'data-mapping' properties to the input/output ports.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * Use bus-width for the rgb24/rgb18 distinction * Adjust code to match core changes * Get rid of of_get_data_mapping() * Don't implement ->atomic_check() (the core now takes care of bus flags propagation)
Changes in v2: * Make the bus-format negotiation logic more generic --- drivers/gpu/drm/bridge/lvds-encoder.c | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c index e2132a8d5106..a2a8f7f4ac97 100644 --- a/drivers/gpu/drm/bridge/lvds-encoder.c +++ b/drivers/gpu/drm/bridge/lvds-encoder.c @@ -6,6 +6,7 @@ #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>
@@ -16,6 +17,8 @@ struct lvds_encoder { struct drm_bridge bridge; struct drm_bridge *panel_bridge; struct gpio_desc *powerdown_gpio; + u32 output_fmt; + u32 input_fmt; };
static int lvds_encoder_attach(struct drm_bridge *bridge) @@ -48,10 +51,40 @@ static void lvds_encoder_disable(struct drm_bridge *bridge) gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1); }
+static u32 *lvds_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct lvds_encoder *lvds_encoder = container_of(bridge, + struct lvds_encoder, + bridge); + u32 *input_fmts; + + if (output_fmt == MEDIA_BUS_FMT_FIXED || + output_fmt == lvds_encoder->output_fmt) + *num_input_fmts = 1; + else + *num_input_fmts = 0; + + if (!*num_input_fmts) + return NULL; + + input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = lvds_encoder->input_fmt; + return input_fmts; +} + static struct drm_bridge_funcs funcs = { .attach = lvds_encoder_attach, .enable = lvds_encoder_enable, .disable = lvds_encoder_disable, + .atomic_get_input_bus_fmts = lvds_atomic_get_input_bus_fmts, };
static int lvds_encoder_probe(struct platform_device *pdev) @@ -62,11 +95,16 @@ static int lvds_encoder_probe(struct platform_device *pdev) struct device_node *panel_node; struct drm_panel *panel; struct lvds_encoder *lvds_encoder; + const char *output_data_mapping = NULL; + u32 input_bus_width = 0;
lvds_encoder = devm_kzalloc(dev, sizeof(*lvds_encoder), GFP_KERNEL); if (!lvds_encoder) return -ENOMEM;
+ lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED; + lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED; + lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(lvds_encoder->powerdown_gpio)) { @@ -77,6 +115,25 @@ static int lvds_encoder_probe(struct platform_device *pdev) return err; }
+ port = of_graph_get_port_by_id(dev->of_node, 0); + if (!port) { + dev_dbg(dev, "port 0 not found\n"); + return -ENXIO; + } + + of_node_put(port); + + if (of_property_read_u32(port, "bus-width", &input_bus_width)) { + lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED; + } else if (input_bus_width == 18) { + lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB666_1X18; + } else if (input_bus_width == 24) { + lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB888_1X24; + } else { + dev_dbg(dev, "unsupported bus-width)\n"); + return -ENOTSUPP; + } + /* Locate the panel DT node. */ port = of_graph_get_port_by_id(dev->of_node, 1); if (!port) { @@ -84,6 +141,21 @@ static int lvds_encoder_probe(struct platform_device *pdev) return -ENXIO; }
+ of_property_read_string(port, "data-mapping", &output_data_mapping); + if (!output_data_mapping) { + lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED; + } else if (!strcmp(output_data_mapping, "jeida-18")) { + lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG; + } else if (!strcmp(output_data_mapping, "jeida-24")) { + lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; + } else if (!strcmp(output_data_mapping, "vesa-24")) { + lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG; + } else { + of_node_put(port); + dev_dbg(dev, "unsupported output data-mapping\n"); + return -ENOTSUPP; + } + endpoint = of_get_child_by_name(port, "endpoint"); of_node_put(port); if (!endpoint) {
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:07PM +0200, Boris Brezillon wrote:
Some LVDS encoder might support several input/output bus formats. Add
s/encoder/encoders/
a way to describe the one used on a specific design by adding optional
s/the one/the format/
'data-mapping' properties to the input/output ports.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Use bus-width for the rgb24/rgb18 distinction
- Adjust code to match core changes
- Get rid of of_get_data_mapping()
- Don't implement ->atomic_check() (the core now takes care of bus flags propagation)
Changes in v2:
- Make the bus-format negotiation logic more generic
drivers/gpu/drm/bridge/lvds-encoder.c | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c index e2132a8d5106..a2a8f7f4ac97 100644 --- a/drivers/gpu/drm/bridge/lvds-encoder.c +++ b/drivers/gpu/drm/bridge/lvds-encoder.c @@ -6,6 +6,7 @@ #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>
@@ -16,6 +17,8 @@ struct lvds_encoder { struct drm_bridge bridge; struct drm_bridge *panel_bridge; struct gpio_desc *powerdown_gpio;
- u32 output_fmt;
- u32 input_fmt;
};
static int lvds_encoder_attach(struct drm_bridge *bridge) @@ -48,10 +51,40 @@ static void lvds_encoder_disable(struct drm_bridge *bridge) gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1); }
+static u32 *lvds_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts)
+{
- struct lvds_encoder *lvds_encoder = container_of(bridge,
struct lvds_encoder,
bridge);
- u32 *input_fmts;
- if (output_fmt == MEDIA_BUS_FMT_FIXED ||
output_fmt == lvds_encoder->output_fmt)
*num_input_fmts = 1;
- else
*num_input_fmts = 0;
- if (!*num_input_fmts)
return NULL;
- input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts), GFP_KERNEL);
As *num_input_fmts == 1, you can
input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
- if (!input_fmts)
return NULL;
- input_fmts[0] = lvds_encoder->input_fmt;
- return input_fmts;
+}
static struct drm_bridge_funcs funcs = { .attach = lvds_encoder_attach, .enable = lvds_encoder_enable, .disable = lvds_encoder_disable,
- .atomic_get_input_bus_fmts = lvds_atomic_get_input_bus_fmts,
};
static int lvds_encoder_probe(struct platform_device *pdev) @@ -62,11 +95,16 @@ static int lvds_encoder_probe(struct platform_device *pdev) struct device_node *panel_node; struct drm_panel *panel; struct lvds_encoder *lvds_encoder;
const char *output_data_mapping = NULL;
u32 input_bus_width = 0;
lvds_encoder = devm_kzalloc(dev, sizeof(*lvds_encoder), GFP_KERNEL); if (!lvds_encoder) return -ENOMEM;
lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED;
lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED;
lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(lvds_encoder->powerdown_gpio)) {
@@ -77,6 +115,25 @@ static int lvds_encoder_probe(struct platform_device *pdev) return err; }
- port = of_graph_get_port_by_id(dev->of_node, 0);
- if (!port) {
dev_dbg(dev, "port 0 not found\n");
return -ENXIO;
- }
- of_node_put(port);
- if (of_property_read_u32(port, "bus-width", &input_bus_width)) {
You're using port after calling of_node_put().
lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED;
- } else if (input_bus_width == 18) {
lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB666_1X18;
- } else if (input_bus_width == 24) {
lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB888_1X24;
- } else {
dev_dbg(dev, "unsupported bus-width)\n");
s/bus-width)/bus-width/
but I think you should make the error message a bit clearer
dev_dbg(dev, "unsupported bus-width value %u on port 0\n", input_bus_width);
return -ENOTSUPP;
- }
- /* Locate the panel DT node. */ port = of_graph_get_port_by_id(dev->of_node, 1); if (!port) {
@@ -84,6 +141,21 @@ static int lvds_encoder_probe(struct platform_device *pdev) return -ENXIO; }
- of_property_read_string(port, "data-mapping", &output_data_mapping);
- if (!output_data_mapping) {
lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED;
- } else if (!strcmp(output_data_mapping, "jeida-18")) {
lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG;
- } else if (!strcmp(output_data_mapping, "jeida-24")) {
lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
- } else if (!strcmp(output_data_mapping, "vesa-24")) {
lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
- } else {
of_node_put(port);
dev_dbg(dev, "unsupported output data-mapping\n");
return -ENOTSUPP;
- }
- endpoint = of_get_child_by_name(port, "endpoint"); of_node_put(port); if (!endpoint) {
Add the data-mapping property to describe the output bus format and the bus-width property to describe the input bus format.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * New patch --- .../bindings/display/bridge/lvds-transmitter.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt index 60091db5dfa5..7b43b6f20279 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt. - Video port 0 for parallel input - Video port 1 for LVDS output
+Optional port 0 node properties: + +- bus-width: number of data lines use to transmit the RGB data. + Can be 18 or 24. + +Optional port 1 node properties: + +- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml + for more details about this LVDS data-mapping property. + Supported values: + "jeida-18" + "jeida-24" + "vesa-24"
Example -------
On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
Add the data-mapping property to describe the output bus format and the bus-width property to describe the input bus format.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- New patch
.../bindings/display/bridge/lvds-transmitter.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt index 60091db5dfa5..7b43b6f20279 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for parallel input
- Video port 1 for LVDS output
+Optional port 0 node properties:
+- bus-width: number of data lines use to transmit the RGB data.
Can be 18 or 24.
+Optional port 1 node properties:
+- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
for more details about this LVDS data-mapping property.
Supported values:
"jeida-18"
"jeida-24"
"vesa-24"
This is already defined to be a panel property. Do we need it at both ends?
Also, why duplicate all the supported values.
Rob
On Fri, 25 Oct 2019 14:57:11 -0500 Rob Herring robh@kernel.org wrote:
On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
Add the data-mapping property to describe the output bus format and the bus-width property to describe the input bus format.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- New patch
.../bindings/display/bridge/lvds-transmitter.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt index 60091db5dfa5..7b43b6f20279 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for parallel input
- Video port 1 for LVDS output
+Optional port 0 node properties:
+- bus-width: number of data lines use to transmit the RGB data.
Can be 18 or 24.
+Optional port 1 node properties:
+- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
for more details about this LVDS data-mapping property.
Supported values:
"jeida-18"
"jeida-24"
"vesa-24"
This is already defined to be a panel property. Do we need it at both ends?
That's a valid point. I'll patch the panel-simple driver to takes this DT prop into account.
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
Add the data-mapping property to describe the output bus format and the bus-width property to describe the input bus format.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- New patch
.../bindings/display/bridge/lvds-transmitter.txt | 13 +++++++++++++
Please note that lvds-transmitter has been renamed to lvds-codec and converted to YAML as part of a pull request I've recently sent ([1]). This patch will thus need to be rebased.
[1] https://patchwork.freedesktop.org/patch/342754/
1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt index 60091db5dfa5..7b43b6f20279 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for parallel input
- Video port 1 for LVDS output
+Optional port 0 node properties:
+- bus-width: number of data lines use to transmit the RGB data.
Can be 18 or 24.
+Optional port 1 node properties:
+- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
for more details about this LVDS data-mapping property.
Supported values:
"jeida-18"
"jeida-24"
"vesa-24"
Example
On Mon, 2 Dec 2019 19:11:40 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
Add the data-mapping property to describe the output bus format and the bus-width property to describe the input bus format.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- New patch
.../bindings/display/bridge/lvds-transmitter.txt | 13 +++++++++++++
Please note that lvds-transmitter has been renamed to lvds-codec and converted to YAML as part of a pull request I've recently sent ([1]). This patch will thus need to be rebased.
Let me know when it's been merged, I'll ask for a drm-next -> drm-misc-next backmerge.
[1] https://patchwork.freedesktop.org/patch/342754/
1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt index 60091db5dfa5..7b43b6f20279 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for parallel input
- Video port 1 for LVDS output
+Optional port 0 node properties:
+- bus-width: number of data lines use to transmit the RGB data.
Can be 18 or 24.
+Optional port 1 node properties:
+- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
for more details about this LVDS data-mapping property.
Supported values:
"jeida-18"
"jeida-24"
"vesa-24"
Example
Hi Boris,
On Tue, Dec 03, 2019 at 01:38:43PM +0100, Boris Brezillon wrote:
On Mon, 2 Dec 2019 19:11:40 +0200 Laurent Pinchart wrote:
On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
Add the data-mapping property to describe the output bus format and the bus-width property to describe the input bus format.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- New patch
.../bindings/display/bridge/lvds-transmitter.txt | 13 +++++++++++++
Please note that lvds-transmitter has been renamed to lvds-codec and converted to YAML as part of a pull request I've recently sent ([1]). This patch will thus need to be rebased.
Let me know when it's been merged, I'll ask for a drm-next -> drm-misc-next backmerge.
I expect Dave to merge it when the merge window will close.
[1] https://patchwork.freedesktop.org/patch/342754/
1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt index 60091db5dfa5..7b43b6f20279 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for parallel input
- Video port 1 for LVDS output
+Optional port 0 node properties:
+- bus-width: number of data lines use to transmit the RGB data.
Can be 18 or 24.
+Optional port 1 node properties:
+- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
for more details about this LVDS data-mapping property.
Supported values:
"jeida-18"
"jeida-24"
"vesa-24"
Example
So that the previous bridge element in the chain knows which input format the panel bridge expects.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * Adjust things to match the new bus-format negotiation approach * Use drm_atomic_helper_bridge_propagate_bus_fmt * Don't implement ->atomic_check() (the core now takes care of bus flags propagation)
Changes in v2: * Adjust things to match the new bus-format negotiation approach --- drivers/gpu/drm/bridge/panel.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index f4e293e7cf64..a70c363a2bd0 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -127,6 +127,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .enable = panel_bridge_enable, .disable = panel_bridge_disable, .post_disable = panel_bridge_post_disable, + .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, };
/**
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:09PM +0200, Boris Brezillon wrote:
So that the previous bridge element in the chain knows which input format the panel bridge expects.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Adjust things to match the new bus-format negotiation approach
- Use drm_atomic_helper_bridge_propagate_bus_fmt
- Don't implement ->atomic_check() (the core now takes care of bus flags propagation)
Changes in v2:
- Adjust things to match the new bus-format negotiation approach
drivers/gpu/drm/bridge/panel.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index f4e293e7cf64..a70c363a2bd0 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -127,6 +127,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .enable = panel_bridge_enable, .disable = panel_bridge_disable, .post_disable = panel_bridge_post_disable,
- .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
Shouldn't the format be retrieved from the panel instead of from the connector ? We're moving towards removing connector creation from bridges, so I think it would be more future-proof.
};
/**
Hi Laurent,
Sorry for the late reply.
On Tue, 3 Dec 2019 12:17:30 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:09PM +0200, Boris Brezillon wrote:
So that the previous bridge element in the chain knows which input format the panel bridge expects.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- Adjust things to match the new bus-format negotiation approach
- Use drm_atomic_helper_bridge_propagate_bus_fmt
- Don't implement ->atomic_check() (the core now takes care of bus flags propagation)
Changes in v2:
- Adjust things to match the new bus-format negotiation approach
drivers/gpu/drm/bridge/panel.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index f4e293e7cf64..a70c363a2bd0 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -127,6 +127,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .enable = panel_bridge_enable, .disable = panel_bridge_disable, .post_disable = panel_bridge_post_disable,
- .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
Shouldn't the format be retrieved from the panel instead of from the connector ? We're moving towards removing connector creation from bridges, so I think it would be more future-proof.
Right now the panel bus_format is not exposed by the drm_panel abstraction (only panel_simple have access to this information through their panel_desc). I'd rather not add new things to this series so I'm in favor of keeping the current implementation, but I add this idea (expose bus format through the drm_panel abstraction) to my TODO list.
Thanks,
Boris
Add a new entry for the Toshiba LTA089AC29000 panel.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- Changes in v3: * None --- drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5d487686d25c..27c92b44bd95 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, };
+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, @@ -3392,6 +3425,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 Wed, Oct 23, 2019 at 05:45:10PM +0200, Boris Brezillon wrote:
Add a new entry for the Toshiba LTA089AC29000 panel.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5d487686d25c..27c92b44bd95 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, };
+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, @@ -3392,6 +3425,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "toshiba,lt089ac29000", .data = &toshiba_lt089ac29000,
- }, {
.compatible = "toshiba,lta089ac29000",
Is this really different than "toshiba,lt089ac29000" ? Both have the exact same timing, the only difference is .bus_format, and according to https://www.avnet-integrated.eu/fileadmin/user_upload/Files/Displays/Colour_... the "toshiba,lt089ac29000" is an LVDS panel.
}, { .compatible = "tpk,f07a-0102", .data = &tpk_f07a_0102,.data = &toshiba_lta089ac29000,
On Mon, 2 Dec 2019 19:17:24 +0200 Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:10PM +0200, Boris Brezillon wrote:
Add a new entry for the Toshiba LTA089AC29000 panel.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5d487686d25c..27c92b44bd95 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, };
+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, @@ -3392,6 +3425,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "toshiba,lt089ac29000", .data = &toshiba_lt089ac29000,
- }, {
.compatible = "toshiba,lta089ac29000",
Is this really different than "toshiba,lt089ac29000" ? Both have the exact same timing, the only difference is .bus_format, and according to https://www.avnet-integrated.eu/fileadmin/user_upload/Files/Displays/Colour_... the "toshiba,lt089ac29000" is an LVDS panel.
I think you can select the LVDS format on the LTA variant which you can't do on the LT089AC29000. But I agree, LT089AC29000 is definitely not an DPI/RGB panel. At the same time, changing the definition is likely to break existing users :-(.
}, { .compatible = "tpk,f07a-0102", .data = &tpk_f07a_0102,.data = &toshiba_lta089ac29000,
Hi Boris,
On Tue, Dec 03, 2019 at 01:42:22PM +0100, Boris Brezillon wrote:
On Mon, 2 Dec 2019 19:17:24 +0200 Laurent Pinchart wrote:
On Wed, Oct 23, 2019 at 05:45:10PM +0200, Boris Brezillon wrote:
Add a new entry for the Toshiba LTA089AC29000 panel.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
Changes in v3:
- None
drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5d487686d25c..27c92b44bd95 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, };
+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, @@ -3392,6 +3425,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "toshiba,lt089ac29000", .data = &toshiba_lt089ac29000,
- }, {
.compatible = "toshiba,lta089ac29000",
Is this really different than "toshiba,lt089ac29000" ? Both have the exact same timing, the only difference is .bus_format, and according to https://www.avnet-integrated.eu/fileadmin/user_upload/Files/Displays/Colour_... the "toshiba,lt089ac29000" is an LVDS panel.
I think you can select the LVDS format on the LTA variant which you can't do on the LT089AC29000. But I agree, LT089AC29000 is definitely not an DPI/RGB panel. At the same time, changing the definition is likely to break existing users :-(.
Or the information could just be unused :-)
}, { .compatible = "tpk,f07a-0102", .data = &tpk_f07a_0102,.data = &toshiba_lta089ac29000,
The LTA089AC29000 and LT089AC29000 are not exactly the same. Let's add a new compatible for the LTA variant.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com --- .../bindings/display/panel/toshiba,lt089ac29000.txt | 5 ++++- 1 file changed, 4 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
On Wed, 23 Oct 2019 17:45:11 +0200, Boris Brezillon wrote:
The LTA089AC29000 and LT089AC29000 are not exactly the same. Let's add a new compatible for the LTA variant.
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
.../bindings/display/panel/toshiba,lt089ac29000.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
Acked-by: Rob Herring robh@kernel.org
Hi Boris,
Thank you for the patch.
On Wed, Oct 23, 2019 at 05:45:11PM +0200, Boris Brezillon wrote:
The LTA089AC29000 and LT089AC29000 are not exactly the same. Let's add a new compatible for the LTA variant.
What is the difference ? :-)
Signed-off-by: Boris Brezillon boris.brezillon@collabora.com
.../bindings/display/panel/toshiba,lt089ac29000.txt | 5 ++++- 1 file changed, 4 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
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 --- Changes in v3: * None
Changes in v2: * None --- arch/arm/boot/dts/imx51-zii-rdu1.dts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx51-zii-rdu1.dts b/arch/arm/boot/dts/imx51-zii-rdu1.dts index 3596060f52e7..3fb84ea7f993 100644 --- a/arch/arm/boot/dts/imx51-zii-rdu1.dts +++ b/arch/arm/boot/dts/imx51-zii-rdu1.dts @@ -95,6 +95,28 @@ reg = <1>;
display_out: endpoint { + remote-endpoint = <&lvds_encoder_in>; + }; + }; + }; + + lvds-encoder { + compatible = "lvds-encoder"; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + bus-width = <24>; + lvds_encoder_in: endpoint { + remote-endpoint = <&display_out>; + }; + }; + + port@1 { + reg = <1>; + data-mapping = "jeida-24"; + lvds_encoder_out: endpoint { remote-endpoint = <&panel_in>; }; }; @@ -110,7 +132,7 @@
port { panel_in: endpoint { - remote-endpoint = <&display_out>; + remote-endpoint = <&lvds_encoder_out>; }; }; };
hi Boris,
On 23/10/2019 17:44, Boris Brezillon wrote:
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 adding a drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook. Once those basic building blocks are in place, we can add new hooks to allow bus format negotiation (those are called just before ->atomic_check()). The bus format selection is done at runtime by testing all possible combinations across the whole bridge chain until one is reported to work.
Major changes since v2:
- Get rid of the dummy bridge embedded in drm_encoder and let encoder drivers provide their own bridge element
- Clarify APIs and improve the doc
- Propagate bus flags by default
Seems you forgot my reviewed-bys on patches 5, 8, 11 & 13
Major changes since the RFC:
- Add a dummy bridge to the drm_encoder object so that vc4 and exynos DSI drivers can implement the pre_enable/post_disable hooks instead of manually setting encoder->bridge to NULL to control the enable/disable sequence. This change is also a first step towards drm_bridge/drm_encoder unification. New encoder drivers should stop implementing drm_encoder_helper_funcs and switch to drm_bridge_funcs. Existing drivers can be converted progressively (already have a branch where I started converting some of them [1])
- rework the bus format negotiation to give more control to bridge drivers in the selection process (driver can select at runtime which input bus format they support for a specific output bus format based on any information available in the connector, crtc and bridge state.
A more detailed changelog is provided in each patch.
This patch series is also available here [2].
Will test ASAP.
Thanks,
Boris
[1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
*** BLURB HERE ***
Blurp
Neil
Boris Brezillon (21): drm/vc4: Declare the DSI encoder as a bridge element drm/exynos: Don't reset bridge->next drm/exynos: Declare the DSI encoder as a bridge element drm/bridge: Rename bridge helpers targeting a bridge chain drm/bridge: Introduce drm_bridge_chain_get_next_bridge() drm: Stop accessing encoder->bridge directly drm/bridge: Make the bridge chain a double-linked list drm/bridge: Add the drm_for_each_bridge_in_chain() helper drm/bridge: Add a drm_bridge_state object drm/bridge: Clarify the atomic enable/disable hooks semantics 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: Implement basic bus format negotiation dt-bindings: display: bridge: lvds-transmitter: Add new props drm/bridge: panel: Propage bus format/flags drm/panel: simple: Add support for Toshiba LTA089AC29000 panel dt-bindings: display: panel: Add the LTA089AC29000 variant ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition
.../display/bridge/lvds-transmitter.txt | 13 + .../display/panel/toshiba,lt089ac29000.txt | 5 +- arch/arm/boot/dts/imx51-zii-rdu1.dts | 24 +- .../drm/bridge/analogix/analogix_dp_core.c | 12 +- drivers/gpu/drm/bridge/lvds-encoder.c | 72 ++ drivers/gpu/drm/bridge/panel.c | 1 + drivers/gpu/drm/drm_atomic.c | 39 + drivers/gpu/drm/drm_atomic_helper.c | 54 +- drivers/gpu/drm/drm_bridge.c | 800 +++++++++++++++--- drivers/gpu/drm/drm_encoder.c | 15 +- drivers/gpu/drm/drm_probe_helper.c | 4 +- drivers/gpu/drm/exynos/exynos_dp.c | 1 - drivers/gpu/drm/exynos/exynos_drm_dsi.c | 90 +- drivers/gpu/drm/imx/parallel-display.c | 174 +++- drivers/gpu/drm/mediatek/mtk_hdmi.c | 8 +- drivers/gpu/drm/msm/edp/edp_bridge.c | 10 +- drivers/gpu/drm/omapdrm/omap_drv.c | 4 +- drivers/gpu/drm/omapdrm/omap_encoder.c | 3 +- drivers/gpu/drm/panel/panel-simple.c | 36 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 11 +- drivers/gpu/drm/vc4/vc4_dsi.c | 90 +- include/drm/drm_atomic.h | 3 + include/drm/drm_bridge.h | 396 ++++++++- include/drm/drm_encoder.h | 9 +- 24 files changed, 1588 insertions(+), 286 deletions(-)
On Thu, 24 Oct 2019 13:27:16 +0200 Neil Armstrong narmstrong@baylibre.com wrote:
hi Boris,
On 23/10/2019 17:44, Boris Brezillon wrote:
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 adding a drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook. Once those basic building blocks are in place, we can add new hooks to allow bus format negotiation (those are called just before ->atomic_check()). The bus format selection is done at runtime by testing all possible combinations across the whole bridge chain until one is reported to work.
Major changes since v2:
- Get rid of the dummy bridge embedded in drm_encoder and let encoder drivers provide their own bridge element
- Clarify APIs and improve the doc
- Propagate bus flags by default
Seems you forgot my reviewed-bys on patches 5, 8, 11 & 13
Oops, indeed. Can you add them back?
Hi Boris, Neil,
On Wed, 2019-10-23 at 17:44 +0200, Boris Brezillon wrote:
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 adding a drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook. Once those basic building blocks are in place, we can add new hooks to allow bus format negotiation (those are called just before ->atomic_check()). The bus format selection is done at runtime by testing all possible combinations across the whole bridge chain until one is reported to work.
Major changes since v2:
- Get rid of the dummy bridge embedded in drm_encoder and let encoder drivers provide their own bridge element
- Clarify APIs and improve the doc
- Propagate bus flags by default
Major changes since the RFC:
- Add a dummy bridge to the drm_encoder object so that vc4 and exynos DSI drivers can implement the pre_enable/post_disable hooks instead of manually setting encoder->bridge to NULL to control the enable/disable sequence. This change is also a first step towards drm_bridge/drm_encoder unification. New encoder drivers should stop implementing drm_encoder_helper_funcs and switch to drm_bridge_funcs. Existing drivers can be converted progressively (already have a branch where I started converting some of them [1])
- rework the bus format negotiation to give more control to bridge drivers in the selection process (driver can select at runtime which input bus format they support for a specific output bus format based on any information available in the connector, crtc and bridge state.
A more detailed changelog is provided in each patch.
This patch series is also available here [2].
Thanks,
Boris
[1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
*** BLURB HERE ***
Boris Brezillon (21): drm/vc4: Declare the DSI encoder as a bridge element drm/exynos: Don't reset bridge->next drm/exynos: Declare the DSI encoder as a bridge element drm/bridge: Rename bridge helpers targeting a bridge chain drm/bridge: Introduce drm_bridge_chain_get_next_bridge() drm: Stop accessing encoder->bridge directly
Patches 1 to 6 seem to be reviewed, and appear as a good step forward.
Perhaps we can consider merging these first? That way, we can reduce the patches needed to rebase and submit on each iteration.
Regards, Ezequiel
On Sun, 24 Nov 2019 09:46:41 +0900 Ezequiel Garcia ezequiel@collabora.com wrote:
Hi Boris, Neil,
On Wed, 2019-10-23 at 17:44 +0200, Boris Brezillon wrote:
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 adding a drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook. Once those basic building blocks are in place, we can add new hooks to allow bus format negotiation (those are called just before ->atomic_check()). The bus format selection is done at runtime by testing all possible combinations across the whole bridge chain until one is reported to work.
Major changes since v2:
- Get rid of the dummy bridge embedded in drm_encoder and let encoder drivers provide their own bridge element
- Clarify APIs and improve the doc
- Propagate bus flags by default
Major changes since the RFC:
- Add a dummy bridge to the drm_encoder object so that vc4 and exynos DSI drivers can implement the pre_enable/post_disable hooks instead of manually setting encoder->bridge to NULL to control the enable/disable sequence. This change is also a first step towards drm_bridge/drm_encoder unification. New encoder drivers should stop implementing drm_encoder_helper_funcs and switch to drm_bridge_funcs. Existing drivers can be converted progressively (already have a branch where I started converting some of them [1])
- rework the bus format negotiation to give more control to bridge drivers in the selection process (driver can select at runtime which input bus format they support for a specific output bus format based on any information available in the connector, crtc and bridge state.
A more detailed changelog is provided in each patch.
This patch series is also available here [2].
Thanks,
Boris
[1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
*** BLURB HERE ***
Boris Brezillon (21): drm/vc4: Declare the DSI encoder as a bridge element drm/exynos: Don't reset bridge->next drm/exynos: Declare the DSI encoder as a bridge element drm/bridge: Rename bridge helpers targeting a bridge chain drm/bridge: Introduce drm_bridge_chain_get_next_bridge() drm: Stop accessing encoder->bridge directly
Patches 1 to 6 seem to be reviewed, and appear as a good step forward.
AFAICT, patch 1 and 3 are not reviewed, which is kind of blocking me for patch 4-6. I can (and plan to) apply patch 2.
On Sun, 2019-11-24 at 08:32 +0100, Boris Brezillon wrote:
On Sun, 24 Nov 2019 09:46:41 +0900 Ezequiel Garcia ezequiel@collabora.com wrote:
Hi Boris, Neil,
On Wed, 2019-10-23 at 17:44 +0200, Boris Brezillon wrote:
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 adding a drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook. Once those basic building blocks are in place, we can add new hooks to allow bus format negotiation (those are called just before ->atomic_check()). The bus format selection is done at runtime by testing all possible combinations across the whole bridge chain until one is reported to work.
Major changes since v2:
- Get rid of the dummy bridge embedded in drm_encoder and let encoder drivers provide their own bridge element
- Clarify APIs and improve the doc
- Propagate bus flags by default
Major changes since the RFC:
- Add a dummy bridge to the drm_encoder object so that vc4 and exynos DSI drivers can implement the pre_enable/post_disable hooks instead of manually setting encoder->bridge to NULL to control the enable/disable sequence. This change is also a first step towards drm_bridge/drm_encoder unification. New encoder drivers should stop implementing drm_encoder_helper_funcs and switch to drm_bridge_funcs. Existing drivers can be converted progressively (already have a branch where I started converting some of them [1])
- rework the bus format negotiation to give more control to bridge drivers in the selection process (driver can select at runtime which input bus format they support for a specific output bus format based on any information available in the connector, crtc and bridge state.
A more detailed changelog is provided in each patch.
This patch series is also available here [2].
Thanks,
Boris
[1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
*** BLURB HERE ***
Boris Brezillon (21): drm/vc4: Declare the DSI encoder as a bridge element drm/exynos: Don't reset bridge->next drm/exynos: Declare the DSI encoder as a bridge element drm/bridge: Rename bridge helpers targeting a bridge chain drm/bridge: Introduce drm_bridge_chain_get_next_bridge() drm: Stop accessing encoder->bridge directly
Patches 1 to 6 seem to be reviewed, and appear as a good step forward.
AFAICT, patch 1 and 3 are not reviewed, which is kind of blocking me for patch 4-6. I can (and plan to) apply patch 2.
Ah, you are right. Let's add Eric for vc4 and Inki for exynos.
For reference, here is the series: https://patchwork.kernel.org/project/dri-devel/list/?series=192359
Thanks, Ezequiel
dri-devel@lists.freedesktop.org