Replace the asynchronous work item with synchronous initialization in lt9211_atomic_enable. This should fix 3 issues flagged by sashiko [1].
The next bridge or panel in the chain may depend on the LVDS signal being active after atomic_enable, so arbitrarily deferring bridge initialization is not an option. If deferring initialization into an asynchronous work item fixes DSI stream detection, there might be a bug in the upstream display controller or MIPI DSI host controller bridge drivers, as the DSI signal should already be streaming when lt9211_atomic_enable is called. [1] https://lore.kernel.org/all/[email protected]/ Signed-off-by: Philipp Zabel <[email protected]> --- drivers/gpu/drm/bridge/lontium-lt9211.c | 121 ++++++++---------------- 1 file changed, 38 insertions(+), 83 deletions(-) diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index 9226d967b271..244979fb9ae6 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -20,7 +20,6 @@ #include <linux/of_graph.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> -#include <linux/workqueue.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> @@ -72,14 +71,6 @@ struct lt9211 { bool lvds_dual_link; bool lvds_dual_link_even_odd_swap; const struct lt9211_chip_data *chip_data; - /* LT9211C specific fields */ - struct workqueue_struct *wq; - struct work_struct lt9211_work; - struct drm_crtc *lt9211c_crtc; - struct drm_display_mode lt9211c_mode; - bool lt9211c_bpp24; - bool lt9211c_jeida; - bool lt9211c_de_high; }; static const struct regmap_range lt9211_rw_ranges[] = { @@ -1157,51 +1148,6 @@ static int lt9211c_configure_tx(struct lt9211 *ctx, bool jeida, bool bpp24, return 0; } -static void lt9211_work_func(struct work_struct *work) -{ - struct lt9211 *ctx = container_of(work, struct lt9211, lt9211_work); - const struct drm_display_mode *mode = &ctx->lt9211c_mode; - int ret; - - /* wait for vblank irq to correctly configure bridge and autodetect stream */ - ret = drm_crtc_wait_one_vblank(ctx->lt9211c_crtc); - if (ret) { - dev_err(ctx->dev, "LT9211C: wait for vblank failed: %d\n", ret); - return; - } - - ret = lt9211c_configure_rx(ctx); - if (ret) { - dev_err(ctx->dev, "LT9211C: configure_rx failed: %d\n", ret); - return; - } - - ret = lt9211c_autodetect_rx(ctx, mode); - if (ret) { - dev_err(ctx->dev, "LT9211C: autodetect_rx failed: %d\n", ret); - return; - } - - ret = lt9211c_configure_timing(ctx, mode); - if (ret) { - dev_err(ctx->dev, "LT9211C: configure_timing failed: %d\n", ret); - return; - } - - ret = lt9211c_configure_plls(ctx, mode); - if (ret) { - dev_err(ctx->dev, "LT9211C: configure_plls failed: %d\n", ret); - return; - } - - ret = lt9211c_configure_tx(ctx, ctx->lt9211c_jeida, ctx->lt9211c_bpp24, - ctx->lt9211c_de_high, mode); - if (ret) { - dev_err(ctx->dev, "LT9211C: configure_tx failed: %d\n", ret); - return; - } -} - static void lt9211_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { @@ -1272,17 +1218,45 @@ static void lt9211_atomic_enable(struct drm_bridge *bridge, return; if (ctx->chip_data->chip_type == LT9211C) { - drm_mode_copy(&ctx->lt9211c_mode, mode); - ctx->lt9211c_bpp24 = lvds_format_24bpp; - ctx->lt9211c_jeida = lvds_format_jeida; - ctx->lt9211c_de_high = !!(bus_flags & DRM_BUS_FLAG_DE_HIGH); - ctx->lt9211c_crtc = crtc; + /* wait for vblank irq to correctly configure bridge and autodetect stream */ + ret = drm_crtc_wait_one_vblank(crtc); + if (ret) { + dev_err(ctx->dev, "LT9211C: wait for vblank failed: %d\n", ret); + return; + } - /* - * Needs a live DSI stream to auto-detect RX resolution/format; - * defer bridge configuration until vblank inside the work item. - */ - queue_work(ctx->wq, &ctx->lt9211_work); + ret = lt9211c_configure_rx(ctx); + if (ret) { + dev_err(ctx->dev, "LT9211C: configure_rx failed: %d\n", ret); + return; + } + + ret = lt9211c_autodetect_rx(ctx, mode); + if (ret) { + dev_err(ctx->dev, "LT9211C: autodetect_rx failed: %d\n", ret); + return; + } + + ret = lt9211c_configure_timing(ctx, mode); + if (ret) { + dev_err(ctx->dev, "LT9211C: configure_timing failed: %d\n", ret); + return; + } + + ret = lt9211c_configure_plls(ctx, mode); + if (ret) { + dev_err(ctx->dev, "LT9211C: configure_plls failed: %d\n", ret); + return; + } + + ret = lt9211c_configure_tx(ctx, lvds_format_jeida, lvds_format_24bpp, + bus_flags & DRM_BUS_FLAG_DE_HIGH, mode); + if (ret) { + dev_err(ctx->dev, "LT9211C: configure_tx failed: %d\n", ret); + return; + } + + dev_dbg(ctx->dev, "LT9211C enabled.\n"); return; } @@ -1320,9 +1294,6 @@ static void lt9211_atomic_disable(struct drm_bridge *bridge, struct lt9211 *ctx = bridge_to_lt9211(bridge); int ret; - if (ctx->chip_data->chip_type == LT9211C) - cancel_work_sync(&ctx->lt9211_work); - /* * Put the chip in reset, pull nRST line low, * and assure lengthy 10ms reset low timing. @@ -1483,11 +1454,6 @@ static int lt9211_host_attach(struct lt9211 *ctx) return 0; } -static void lt9211_destroy_wq(void *data) -{ - destroy_workqueue(data); -} - static int lt9211_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1532,17 +1498,6 @@ static int lt9211_probe(struct i2c_client *client) if (ret) return ret; - if (ctx->chip_data->chip_type == LT9211C) { - ctx->wq = create_workqueue("lt9211_work"); - if (!ctx->wq) - return -ENOMEM; - /* Register after DSI attach so devres flushes work before freeing ctx->dsi. */ - ret = devm_add_action_or_reset(dev, lt9211_destroy_wq, ctx->wq); - if (ret) - return ret; - INIT_WORK(&ctx->lt9211_work, lt9211_work_func); - } - return 0; } -- 2.47.3
