mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-22 15:02:32 +00:00
267 lines
8.6 KiB
Diff
267 lines
8.6 KiB
Diff
|
From c0611a36761fb6abf5a806bbfe417db1f4464a56 Mon Sep 17 00:00:00 2001
|
||
|
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||
|
Date: Wed, 15 Dec 2021 17:57:45 +0000
|
||
|
Subject: [PATCH] drm/vc4: Convert vc4_dsi to using a bridge instead of
|
||
|
encoder.
|
||
|
|
||
|
Remove the encoder functions, and create a bridge attached to
|
||
|
this dumb encoder which implements the same functionality.
|
||
|
|
||
|
As a bridge has state which an encoder doesn't, we need to
|
||
|
add the state management functions as well.
|
||
|
|
||
|
As there is no bridge atomic_mode_set, move the initialisation
|
||
|
code that was in mode_set into _pre_enable.
|
||
|
The code to actually enable and disable sending video are split
|
||
|
from the general control into _enable and _disable.
|
||
|
|
||
|
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||
|
---
|
||
|
drivers/gpu/drm/vc4/vc4_dsi.c | 122 +++++++++++++++++++++++++---------
|
||
|
1 file changed, 90 insertions(+), 32 deletions(-)
|
||
|
|
||
|
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
|
||
|
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
|
||
|
@@ -557,6 +557,7 @@ struct vc4_dsi {
|
||
|
struct platform_device *pdev;
|
||
|
|
||
|
struct drm_bridge *out_bridge;
|
||
|
+ struct drm_bridge bridge;
|
||
|
|
||
|
void __iomem *regs;
|
||
|
|
||
|
@@ -608,6 +609,12 @@ to_vc4_dsi(struct drm_encoder *encoder)
|
||
|
return container_of(encoder, struct vc4_dsi, encoder.base);
|
||
|
}
|
||
|
|
||
|
+static inline struct vc4_dsi *
|
||
|
+bridge_to_vc4_dsi(struct drm_bridge *bridge)
|
||
|
+{
|
||
|
+ return container_of(bridge, struct vc4_dsi, bridge);
|
||
|
+}
|
||
|
+
|
||
|
static inline void
|
||
|
dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val)
|
||
|
{
|
||
|
@@ -789,10 +796,21 @@ dsi_esc_timing(u32 ns)
|
||
|
return DIV_ROUND_UP(ns, ESC_TIME_NS);
|
||
|
}
|
||
|
|
||
|
-static void vc4_dsi_encoder_disable(struct drm_encoder *encoder,
|
||
|
- struct drm_atomic_state *state)
|
||
|
+static void vc4_dsi_bridge_disable(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *state)
|
||
|
{
|
||
|
- struct vc4_dsi *dsi = to_vc4_dsi(encoder);
|
||
|
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
|
||
|
+ u32 disp0_ctrl;
|
||
|
+
|
||
|
+ disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
|
||
|
+ disp0_ctrl &= ~DSI_DISP0_ENABLE;
|
||
|
+ DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
|
||
|
+}
|
||
|
+
|
||
|
+static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *state)
|
||
|
+{
|
||
|
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
|
||
|
struct device *dev = &dsi->pdev->dev;
|
||
|
|
||
|
vc4_dsi_ulps(dsi, true);
|
||
|
@@ -817,11 +835,11 @@ static void vc4_dsi_encoder_disable(stru
|
||
|
* 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 *dsi = to_vc4_dsi(encoder);
|
||
|
+ 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;
|
||
|
@@ -853,15 +871,18 @@ static bool vc4_dsi_encoder_mode_fixup(s
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
-static void vc4_dsi_encoder_mode_set(struct drm_encoder *encoder,
|
||
|
- struct drm_crtc_state *crtc_state,
|
||
|
- struct drm_connector_state *conn_state)
|
||
|
+static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *old_state)
|
||
|
{
|
||
|
- struct vc4_dsi *dsi = to_vc4_dsi(encoder);
|
||
|
+ struct drm_atomic_state *state = old_state->base.state;
|
||
|
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
|
||
|
+ const struct drm_crtc_state *crtc_state;
|
||
|
struct device *dev = &dsi->pdev->dev;
|
||
|
const struct drm_display_mode *mode;
|
||
|
+ struct drm_connector *connector;
|
||
|
bool debug_dump_regs = false;
|
||
|
unsigned long hs_clock;
|
||
|
+ struct drm_crtc *crtc;
|
||
|
u32 ui_ns;
|
||
|
/* Minimum LP state duration in escape clock cycles. */
|
||
|
u32 lpx = dsi_esc_timing(60);
|
||
|
@@ -882,6 +903,14 @@ static void vc4_dsi_encoder_mode_set(str
|
||
|
drm_print_regset32(&p, &dsi->regset);
|
||
|
}
|
||
|
|
||
|
+ /*
|
||
|
+ * Retrieve the CRTC adjusted mode. This requires a little dance to go
|
||
|
+ * from the bridge to the encoder, to the connector and to the CRTC.
|
||
|
+ */
|
||
|
+ connector = drm_atomic_get_new_connector_for_encoder(state,
|
||
|
+ bridge->encoder);
|
||
|
+ crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
|
||
|
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||
|
mode = &crtc_state->adjusted_mode;
|
||
|
|
||
|
pixel_clock_hz = mode->clock * 1000;
|
||
|
@@ -1096,14 +1125,6 @@ static void vc4_dsi_encoder_mode_set(str
|
||
|
~DSI_PORT_BIT(PHY_AFEC0_RESET));
|
||
|
|
||
|
vc4_dsi_ulps(dsi, false);
|
||
|
-}
|
||
|
-
|
||
|
-static void vc4_dsi_encoder_enable(struct drm_encoder *encoder,
|
||
|
- struct drm_atomic_state *state)
|
||
|
-{
|
||
|
- struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
|
||
|
- struct vc4_dsi *dsi = vc4_encoder->dsi;
|
||
|
- bool debug_dump_regs = false;
|
||
|
|
||
|
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
|
||
|
DSI_PORT_WRITE(DISP0_CTRL,
|
||
|
@@ -1112,13 +1133,23 @@ static void vc4_dsi_encoder_enable(struc
|
||
|
VC4_SET_FIELD(dsi->format, DSI_DISP0_PFORMAT) |
|
||
|
VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME,
|
||
|
DSI_DISP0_LP_STOP_CTRL) |
|
||
|
- DSI_DISP0_ST_END |
|
||
|
- DSI_DISP0_ENABLE);
|
||
|
+ DSI_DISP0_ST_END);
|
||
|
} else {
|
||
|
DSI_PORT_WRITE(DISP0_CTRL,
|
||
|
- DSI_DISP0_COMMAND_MODE |
|
||
|
- DSI_DISP0_ENABLE);
|
||
|
+ DSI_DISP0_COMMAND_MODE);
|
||
|
}
|
||
|
+}
|
||
|
+
|
||
|
+static void vc4_dsi_bridge_enable(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *old_state)
|
||
|
+{
|
||
|
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
|
||
|
+ bool debug_dump_regs = false;
|
||
|
+ u32 disp0_ctrl;
|
||
|
+
|
||
|
+ disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
|
||
|
+ disp0_ctrl |= DSI_DISP0_ENABLE;
|
||
|
+ DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
|
||
|
|
||
|
if (debug_dump_regs) {
|
||
|
struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
|
||
|
@@ -1127,6 +1158,16 @@ static void vc4_dsi_encoder_enable(struc
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+static int vc4_dsi_bridge_attach(struct drm_bridge *bridge,
|
||
|
+ enum drm_bridge_attach_flags flags)
|
||
|
+{
|
||
|
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
|
||
|
+
|
||
|
+ /* Attach the panel or bridge to the dsi bridge */
|
||
|
+ return drm_bridge_attach(bridge->encoder, dsi->out_bridge,
|
||
|
+ &dsi->bridge, flags);
|
||
|
+}
|
||
|
+
|
||
|
static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
|
||
|
const struct mipi_dsi_msg *msg)
|
||
|
{
|
||
|
@@ -1303,6 +1344,7 @@ static int vc4_dsi_host_attach(struct mi
|
||
|
struct mipi_dsi_device *device)
|
||
|
{
|
||
|
struct vc4_dsi *dsi = host_to_dsi(host);
|
||
|
+ int ret;
|
||
|
|
||
|
dsi->lanes = device->lanes;
|
||
|
dsi->channel = device->channel;
|
||
|
@@ -1337,7 +1379,15 @@ static int vc4_dsi_host_attach(struct mi
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
- return component_add(&dsi->pdev->dev, &vc4_dsi_ops);
|
||
|
+ drm_bridge_add(&dsi->bridge);
|
||
|
+
|
||
|
+ ret = component_add(&dsi->pdev->dev, &vc4_dsi_ops);
|
||
|
+ if (ret) {
|
||
|
+ drm_bridge_remove(&dsi->bridge);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
}
|
||
|
|
||
|
static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
|
||
|
@@ -1346,6 +1396,7 @@ static int vc4_dsi_host_detach(struct mi
|
||
|
struct vc4_dsi *dsi = host_to_dsi(host);
|
||
|
|
||
|
component_del(&dsi->pdev->dev, &vc4_dsi_ops);
|
||
|
+ drm_bridge_remove(&dsi->bridge);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -1355,11 +1406,16 @@ static const struct mipi_dsi_host_ops vc
|
||
|
.transfer = vc4_dsi_host_transfer,
|
||
|
};
|
||
|
|
||
|
-static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
|
||
|
- .atomic_disable = vc4_dsi_encoder_disable,
|
||
|
- .atomic_enable = vc4_dsi_encoder_enable,
|
||
|
- .mode_fixup = vc4_dsi_encoder_mode_fixup,
|
||
|
- .atomic_mode_set = vc4_dsi_encoder_mode_set,
|
||
|
+static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
|
||
|
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||
|
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||
|
+ .atomic_reset = drm_atomic_helper_bridge_reset,
|
||
|
+ .atomic_pre_enable = vc4_dsi_bridge_pre_enable,
|
||
|
+ .atomic_enable = vc4_dsi_bridge_enable,
|
||
|
+ .atomic_disable = vc4_dsi_bridge_disable,
|
||
|
+ .atomic_post_disable = vc4_dsi_bridge_post_disable,
|
||
|
+ .attach = vc4_dsi_bridge_attach,
|
||
|
+ .mode_fixup = vc4_dsi_bridge_mode_fixup,
|
||
|
};
|
||
|
|
||
|
static int vc4_dsi_late_register(struct drm_encoder *encoder)
|
||
|
@@ -1734,13 +1790,11 @@ static int vc4_dsi_bind(struct device *d
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
- drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs);
|
||
|
-
|
||
|
ret = devm_pm_runtime_enable(dev);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
- ret = drm_bridge_attach(encoder, dsi->out_bridge, NULL, 0);
|
||
|
+ ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
@@ -1762,7 +1816,11 @@ static int vc4_dsi_dev_probe(struct plat
|
||
|
dev_set_drvdata(dev, dsi);
|
||
|
|
||
|
kref_init(&dsi->kref);
|
||
|
+
|
||
|
dsi->pdev = pdev;
|
||
|
+ dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
|
||
|
+ dsi->bridge.of_node = dev->of_node;
|
||
|
+ dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
|
||
|
dsi->dsi_host.ops = &vc4_dsi_host_ops;
|
||
|
dsi->dsi_host.dev = dev;
|
||
|
mipi_dsi_host_register(&dsi->dsi_host);
|