Hello,
This patch series adds i.MX7 support to the mxsfb driver. The eLCDIF instance found in the i.MX7 is backward-compatible with the already supported LCDC v4, but has extended features amongst which the most notable one is a second plane.
The first 9 patches (01/21 to 09/21) contain miscellaneous cleanups and refactoring to prepare for what is to come. Patch 10/21 starts the real work with removal of the DRM simple display pipeline helper, as it doesn't support multiple planes. The next patch (11/21) is an additional cleanup.
Patches 12/21 to 14/21 fix vblank handling that I found to be broken when testing on my device. Patch 15/21 then performs an additional small cleanup, and patch 16/21 starts official support for i.MX7 by mentioning it in Kconfig.
Patch 17/21 adds a new device model for the i.MX6SX and i.MX7 eLCDIF. After three additional cleanups in patches 18/21 to 20/21, patch 21/21 finally adds support for the second plane.
The code is based on drm-misc-next and has been tested on an i.MX7D platform with a DPI panel.
Laurent Pinchart (21): drm: mxsfb: Remove fbdev leftovers drm: mxsfb: Use drm_panel_bridge drm: mxsfb: Use BIT() macro to define register bitfields drm: mxsfb: Remove unused macros from mxsfb_regs.h drm: mxsfb: Clarify format and bus width configuration drm: mxsfb: Pass mxsfb_drm_private pointer to mxsfb_reset_block() drm: mxsfb: Use LCDC_CTRL register name explicitly drm: mxsfb: Remove register definitions from mxsfb_crtc.c drm: mxsfb: Remove unneeded includes drm: mxsfb: Stop using DRM simple display pipeline helper drm: mxsfb: Rename mxsfb_crtc.c to mxsfb_kms.c drm: mxsfb: Move vblank event arm to CRTC .atomic_flush() drm: mxsfb: Don't touch AXI clock in IRQ context drm: mxsfb: Enable vblank handling drm: mxsfb: Remove mxsfb_devdata unused fields drm: mxsfb: Add i.MX7 to the list of supported SoCs in Kconfig drm: mxsfb: Update internal IP version number for i.MX6SX drm: mxsfb: Drop non-OF support drm: mxsfb: Turn mxsfb_set_pixel_fmt() into a void function drm: mxsfb: Merge mxsfb_set_pixel_fmt() and mxsfb_set_bus_fmt() drm: mxsfb: Support the alpha plane
drivers/gpu/drm/mxsfb/Kconfig | 4 +- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 343 ----------------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 246 ++++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 42 ++- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 565 +++++++++++++++++++++++++++++ drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ----- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 107 +++--- 8 files changed, 730 insertions(+), 678 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_crtc.c create mode 100644 drivers/gpu/drm/mxsfb/mxsfb_kms.c delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
Commit 8e93f1028d74 ("drm/mxsfb: Use drm_fbdev_generic_setup()") replaced fbdev handling with drm_fbdev_generic_setup() but left inclusion of the drm/drm_fb_cma_helper.h header. Remove it.
Fixes: 8e93f1028d74 ("drm/mxsfb: Use drm_fbdev_generic_setup()") Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 497cf443a9af..2e6068d96034 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -24,7 +24,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h>
On 2020-03-09 20:51, Laurent Pinchart wrote:
Commit 8e93f1028d74 ("drm/mxsfb: Use drm_fbdev_generic_setup()") replaced fbdev handling with drm_fbdev_generic_setup() but left inclusion of the drm/drm_fb_cma_helper.h header. Remove it.
Fixes: 8e93f1028d74 ("drm/mxsfb: Use drm_fbdev_generic_setup()") Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
I guess this could be merge independent of the rest already today.
-- Stefan
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 497cf443a9af..2e6068d96034 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -24,7 +24,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h>
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) { - struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
- if (!mxsfb->connector) { - list_for_each_entry(connector, - &drm->mode_config.connector_list, - head) - if (connector->encoder == &mxsfb->pipe.encoder) { - mxsfb->connector = connector; - break; - } - } - - if (!mxsfb->connector) { - dev_warn(drm->dev, "No connector attached, using default\n"); - mxsfb->connector = &mxsfb->panel_connector; - } - pm_runtime_get_sync(drm->dev); - drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb); - drm_panel_enable(mxsfb->panel); }
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
- drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb); - drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock); @@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock); - - if (mxsfb->connector != &mxsfb->panel_connector) - mxsfb->connector = NULL; }
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{ + struct drm_device *drm = mxsfb->drm; + struct drm_connector_list_iter iter; + struct drm_panel *panel; + struct drm_bridge *bridge; + int ret; + + ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel, + &bridge); + if (ret) + return ret; + + if (panel) { + bridge = devm_drm_panel_bridge_add(drm->dev, panel); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + } + + if (!bridge) + return -ENODEV; + + ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge); + if (ret) { + DRM_DEV_ERROR(drm->dev, + "failed to attach bridge: %d\n", ret); + return ret; + } + + mxsfb->bridge = bridge; + + /* + * Get hold of the connector. This is a bit of a hack, until the bridge + * API gives us bus flags and formats. + */ + drm_connector_list_iter_begin(drm, &iter); + mxsfb->connector = drm_connector_list_iter_next(&iter); + drm_connector_list_iter_end(&iter); + + return 0; +} + static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
+ mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm); - if (ret < 0) { - dev_err(drm->dev, "Failed to create outputs\n"); - goto err_vblank; - } - ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs, - mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, - mxsfb->connector); + mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL); if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }
- /* - * Attach panel only if there is one. - * If there is no panel attach, it must be a bridge. In this case, we - * need a reference to its connector for a proper initialization. - * We will do this check in pipe->enable(), since the connector won't - * be attached to an encoder until then. - */ - - if (mxsfb->panel) { - ret = drm_panel_attach(mxsfb->panel, mxsfb->connector); - if (ret) { - dev_err(drm->dev, "Cannot connect panel: %d\n", ret); - goto err_vblank; - } - } else if (mxsfb->bridge) { - ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, - mxsfb->bridge); - if (ret) { - dev_err(drm->dev, "Cannot connect bridge: %d\n", ret); - goto err_vblank; - } + ret = mxsfb_attach_bridge(mxsfb); + if (ret) { + dev_err(drm->dev, "Cannot connect bridge: %d\n", ret); + goto err_vblank; }
drm->mode_config.min_width = MXSFB_MIN_XRES; @@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n"); - goto err_irq; + goto err_vblank; }
drm_kms_helper_poll_init(drm); @@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq: - drm_panel_detach(mxsfb->panel); err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device; + struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
+ struct drm_device *drm; struct drm_simple_display_pipe pipe; - struct drm_connector panel_connector; struct drm_connector *connector; - struct drm_panel *panel; struct drm_bridge *bridge; };
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Marek Vasut marex@denx.de - */ - -#include <linux/of_graph.h> - -#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> - -#include "mxsfb_drv.h" - -static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{ - return container_of(connector, struct mxsfb_drm_private, - panel_connector); -} - -static int mxsfb_panel_get_modes(struct drm_connector *connector) -{ - struct mxsfb_drm_private *mxsfb = - drm_connector_to_mxsfb_drm_private(connector); - - if (mxsfb->panel) - return drm_panel_get_modes(mxsfb->panel, connector); - - return 0; -} - -static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = { - .get_modes = mxsfb_panel_get_modes, -}; - -static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{ - struct mxsfb_drm_private *mxsfb = - drm_connector_to_mxsfb_drm_private(connector); - - if (mxsfb->panel) - return connector_status_connected; - - return connector_status_disconnected; -} - -static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{ - struct mxsfb_drm_private *mxsfb = - drm_connector_to_mxsfb_drm_private(connector); - - if (mxsfb->panel) - drm_panel_detach(mxsfb->panel); - - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - -static const struct drm_connector_funcs mxsfb_panel_connector_funcs = { - .detect = mxsfb_panel_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = mxsfb_panel_connector_destroy, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -int mxsfb_create_output(struct drm_device *drm) -{ - struct mxsfb_drm_private *mxsfb = drm->dev_private; - int ret; - - ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, - &mxsfb->panel, &mxsfb->bridge); - if (ret) - return ret; - - if (mxsfb->panel) { - mxsfb->connector = &mxsfb->panel_connector; - mxsfb->connector->dpms = DRM_MODE_DPMS_OFF; - mxsfb->connector->polled = 0; - drm_connector_helper_add(mxsfb->connector, - &mxsfb_panel_connector_helper_funcs); - ret = drm_connector_init(drm, mxsfb->connector, - &mxsfb_panel_connector_funcs, - DRM_MODE_CONNECTOR_Unknown); - } - - return ret; -}
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
-- Stefan
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
Hi Stefan.
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28
...
I think you hit one of the panels that does not yet specify a connector_type.
If this is the case, we should fix the definition in panel-simple.
Sam
On 2020-03-23 22:38, Sam Ravnborg wrote:
Hi Stefan.
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28
...
I think you hit one of the panels that does not yet specify a connector_type.
If this is the case, we should fix the definition in panel-simple.
Yes, this indeed the case, I used the "logictechno,lt161010-2nhc" compatible string. Will get this fixed!
But I think its still worth defining a default in the driver as DPI is what this IP outputs...
-- Stefan
Hi Stefan,
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
As Sam reported, this is caused by the panel not setting connector_type. We could use devm_drm_panel_bridge_add_typed(), even if I like the warning as it uncovers the problematic panels and gets them fixed. What would be your preference ?
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
You're right, I'll add it.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
There's no guarantee that mxsfb->bridge is a panel bridge, the LCDIF output could be connected to another bridge in which case drm_of_find_panel_or_bridge() will set panel to NULL and bridge to a non-NULL value).
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
On Sat, May 30, 2020 at 05:14:21AM +0300, Laurent Pinchart wrote:
Hi Stefan,
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
As Sam reported, this is caused by the panel not setting connector_type. We could use devm_drm_panel_bridge_add_typed(), even if I like the warning as it uncovers the problematic panels and gets them fixed. What would be your preference ?
Adding warnings everywhere is kinda uncool, at least my experience is that if there's too much you get warning overload and train people to ignore them.
Imo either hide the wwarning for now, or if it's not too much work, review all the panel drivers and make sure they set the connector type somewhere. Warnings are kinda ok once you're pretty sure you got them all, and want to make sure newly added drivers get this all right. But not before we've reached that.
Cheers, Daniel
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
You're right, I'll add it.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
There's no guarantee that mxsfb->bridge is a panel bridge, the LCDIF output could be connected to another bridge in which case drm_of_find_panel_or_bridge() will set panel to NULL and bridge to a non-NULL value).
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
-- Regards,
Laurent Pinchart _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
On 2020-06-02 15:12, Daniel Vetter wrote:
On Sat, May 30, 2020 at 05:14:21AM +0300, Laurent Pinchart wrote:
Hi Stefan,
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
As Sam reported, this is caused by the panel not setting connector_type. We could use devm_drm_panel_bridge_add_typed(), even if I like the warning as it uncovers the problematic panels and gets them fixed. What would be your preference ?
Adding warnings everywhere is kinda uncool, at least my experience is that if there's too much you get warning overload and train people to ignore them.
Imo either hide the wwarning for now, or if it's not too much work, review all the panel drivers and make sure they set the connector type somewhere. Warnings are kinda ok once you're pretty sure you got them all, and want to make sure newly added drivers get this all right. But not before we've reached that.
I am with Daniel on this, too many warnings make users blind of them, so we should save them when really attention is needed.
I guess the only question which connector type we should default to. I think DRM_MODE_CONNECTOR_DPI make sense for this IP.
-- Stefan
Cheers, Daniel
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
You're right, I'll add it.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
There's no guarantee that mxsfb->bridge is a panel bridge, the LCDIF output could be connected to another bridge in which case drm_of_find_panel_or_bridge() will set panel to NULL and bridge to a non-NULL value).
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
-- Regards,
Laurent Pinchart _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi Stefan,
On Tue, Jun 02, 2020 at 04:34:07PM +0200, Stefan Agner wrote:
On 2020-06-02 15:12, Daniel Vetter wrote:
On Sat, May 30, 2020 at 05:14:21AM +0300, Laurent Pinchart wrote:
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
As Sam reported, this is caused by the panel not setting connector_type. We could use devm_drm_panel_bridge_add_typed(), even if I like the warning as it uncovers the problematic panels and gets them fixed. What would be your preference ?
Adding warnings everywhere is kinda uncool, at least my experience is that if there's too much you get warning overload and train people to ignore them.
Imo either hide the wwarning for now, or if it's not too much work, review all the panel drivers and make sure they set the connector type somewhere. Warnings are kinda ok once you're pretty sure you got them all, and want to make sure newly added drivers get this all right. But not before we've reached that.
I am with Daniel on this, too many warnings make users blind of them, so we should save them when really attention is needed.
I guess the only question which connector type we should default to. I think DRM_MODE_CONNECTOR_DPI make sense for this IP.
Yes, that makes sense. I'll fix this in v3, but will wait until you review the remaining patches from v2 before sending v3 out.
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
You're right, I'll add it.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
There's no guarantee that mxsfb->bridge is a panel bridge, the LCDIF output could be connected to another bridge in which case drm_of_find_panel_or_bridge() will set panel to NULL and bridge to a non-NULL value).
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
Hi Daniel,
On Tue, Jun 02, 2020 at 03:12:37PM +0200, Daniel Vetter wrote:
On Sat, May 30, 2020 at 05:14:21AM +0300, Laurent Pinchart wrote:
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
As Sam reported, this is caused by the panel not setting connector_type. We could use devm_drm_panel_bridge_add_typed(), even if I like the warning as it uncovers the problematic panels and gets them fixed. What would be your preference ?
Adding warnings everywhere is kinda uncool, at least my experience is that if there's too much you get warning overload and train people to ignore them.
Imo either hide the wwarning for now, or if it's not too much work, review all the panel drivers and make sure they set the connector type somewhere.
All panel drivers but panel-simple set the connector type. For panel-simple, the type is stored in the panel_desc panel description, and out of the 123 supported panels, only 48 have a connector type. I've just sent a patch to fix this for the 7 DSI panels, so only 68 panels to go :-) This will require trying to find the corresponding datasheets, so that's a large effort for a single developer. That's why I was hoping a WARN_ON() could help distributing the work :-) I hear your concern though, and I'll set a default in this driver for the time being.
Warnings are kinda ok once you're pretty sure you got them all, and want to make sure newly added drivers get this all right. But not before we've reached that.
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
You're right, I'll add it.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
There's no guarantee that mxsfb->bridge is a panel bridge, the LCDIF output could be connected to another bridge in which case drm_of_find_panel_or_bridge() will set panel to NULL and bridge to a non-NULL value).
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
On Tue, Jun 02, 2020 at 08:13:17PM +0300, Laurent Pinchart wrote:
Hi Daniel,
On Tue, Jun 02, 2020 at 03:12:37PM +0200, Daniel Vetter wrote:
On Sat, May 30, 2020 at 05:14:21AM +0300, Laurent Pinchart wrote:
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
As Sam reported, this is caused by the panel not setting connector_type. We could use devm_drm_panel_bridge_add_typed(), even if I like the warning as it uncovers the problematic panels and gets them fixed. What would be your preference ?
Adding warnings everywhere is kinda uncool, at least my experience is that if there's too much you get warning overload and train people to ignore them.
Imo either hide the wwarning for now, or if it's not too much work, review all the panel drivers and make sure they set the connector type somewhere.
All panel drivers but panel-simple set the connector type. For panel-simple, the type is stored in the panel_desc panel description, and out of the 123 supported panels, only 48 have a connector type. I've just sent a patch to fix this for the 7 DSI panels, so only 68 panels to go :-) This will require trying to find the corresponding datasheets, so that's a large effort for a single developer. That's why I was hoping a WARN_ON() could help distributing the work :-) I hear your concern though, and I'll set a default in this driver for the time being.
I think some fallback in simple-panel.c with maybe a fixme would be ok for this case. I don't think this has to be perfect, afaik userspace at best just checks "is this a panel", not which specific type. -Daniel
Warnings are kinda ok once you're pretty sure you got them all, and want to make sure newly added drivers get this all right. But not before we've reached that.
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
You're right, I'll add it.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
There's no guarantee that mxsfb->bridge is a panel bridge, the LCDIF output could be connected to another bridge in which case drm_of_find_panel_or_bridge() will set panel to NULL and bridge to a non-NULL value).
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
-- Regards,
Laurent Pinchart
On Wed, Jun 03, 2020 at 10:16:17AM +0200, Daniel Vetter wrote:
On Tue, Jun 02, 2020 at 08:13:17PM +0300, Laurent Pinchart wrote:
Hi Daniel,
On Tue, Jun 02, 2020 at 03:12:37PM +0200, Daniel Vetter wrote:
On Sat, May 30, 2020 at 05:14:21AM +0300, Laurent Pinchart wrote:
On Mon, Mar 23, 2020 at 10:27:21PM +0100, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Replace the manual connector implementation based on drm_panel with the drm_panel_bridge helper. This simplifies the mxsfb driver by removing connector-related code, and standardizing all pipeline control operations on bridges.
A hack is needed to get hold of the connector, as that's our only source of bus flags and formats for now. As soon as the bridge API provides us with that information this can be fixed.
This seems like a nice simplification.
I gave this a go applied on today's drm-misc-next using a Colibri iMX7 (imx7d-colibri-emmc-eval-v3.dts device tree). This device makes use of the simple panel driver. I get this when booting:
[ 2.976698] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [ 2.983526] ------------[ cut here ]------------ [ 2.988180] WARNING: CPU: 0 PID: 1 at drivers/gpu/drm/bridge/panel.c:267 devm_drm_panel_bridge_add+0x25/0x28 [ 2.998059] Modules linked in: [ 3.001159] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.0-rc5-yocto-standard-01250-ga4ce5db7c9f1 #29 [ 3.010493] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 3.016281] [<c010f089>] (unwind_backtrace) from [<c010a9bb>] (show_stack+0xb/0xc) [ 3.023887] [<c010a9bb>] (show_stack) from [<c08b33cf>] (dump_stack+0x63/0x70) [ 3.031144] [<c08b33cf>] (dump_stack) from [<c0124385>] (__warn+0x9d/0xb0) [ 3.038047] [<c0124385>] (__warn) from [<c0124403>] (warn_slowpath_fmt+0x6b/0x70) [ 3.045564] [<c0124403>] (warn_slowpath_fmt) from [<c04ebf65>] (devm_drm_panel_bridge_add+0x25/0x28) [ 3.054736] [<c04ebf65>] (devm_drm_panel_bridge_add) from [<c051fe7f>] (mxsfb_probe+0x197/0x2e0) [ 3.063559] [<c051fe7f>] (mxsfb_probe) from [<c0532e91>] (platform_drv_probe+0x2d/0x60) [ 3.071598] [<c0532e91>] (platform_drv_probe) from [<c0531615>] (really_probe+0x1dd/0x330) [ 3.079897] [<c0531615>] (really_probe) from [<c05318b3>] (driver_probe_device+0x4f/0x154) [ 3.088195] [<c05318b3>] (driver_probe_device) from [<c0531b3f>] (device_driver_attach+0x37/0x44) [ 3.097101] [<c0531b3f>] (device_driver_attach) from [<c0531bc7>] (__driver_attach+0x7b/0xec) [ 3.105660] [<c0531bc7>] (__driver_attach) from [<c0530069>] (bus_for_each_dev+0x3d/0x64) [ 3.113870] [<c0530069>] (bus_for_each_dev) from [<c0530b9b>] (bus_add_driver+0xef/0x160) [ 3.122081] [<c0530b9b>] (bus_add_driver) from [<c053238d>] (driver_register+0x35/0x9c) [ 3.130119] [<c053238d>] (driver_register) from [<c0102de9>] (do_one_initcall+0x3d/0x1e4) [ 3.138333] [<c0102de9>] (do_one_initcall) from [<c1100d13>] (kernel_init_freeable+0x1b3/0x1fc) [ 3.147069] [<c1100d13>] (kernel_init_freeable) from [<c08c3f7b>] (kernel_init+0x7/0xd0) [ 3.155194] [<c08c3f7b>] (kernel_init) from [<c01010f9>] (ret_from_fork+0x11/0x38) [ 3.162789] Exception stack(0xec0e3fb0 to 0xec0e3ff8) [ 3.167862] 3fa0: 00000000 00000000 00000000 00000000 [ 3.176071] 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.184278] 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 3.191029] ---[ end trace b69e1f44de470959 ]--- [ 3.195671] mxsfb 30730000.lcdif: Cannot connect bridge: -19
Should we maybe use devm_drm_panel_bridge_add_typed?
As Sam reported, this is caused by the panel not setting connector_type. We could use devm_drm_panel_bridge_add_typed(), even if I like the warning as it uncovers the problematic panels and gets them fixed. What would be your preference ?
Adding warnings everywhere is kinda uncool, at least my experience is that if there's too much you get warning overload and train people to ignore them.
Imo either hide the wwarning for now, or if it's not too much work, review all the panel drivers and make sure they set the connector type somewhere.
All panel drivers but panel-simple set the connector type. For panel-simple, the type is stored in the panel_desc panel description, and out of the 123 supported panels, only 48 have a connector type. I've just sent a patch to fix this for the 7 DSI panels, so only 68 panels to go :-) This will require trying to find the corresponding datasheets, so that's a large effort for a single developer. That's why I was hoping a WARN_ON() could help distributing the work :-) I hear your concern though, and I'll set a default in this driver for the time being.
I think some fallback in simple-panel.c with maybe a fixme would be ok for this case. I don't think this has to be perfect, afaik userspace at best just checks "is this a panel", not which specific type.
I just applied Laurent's patch for the DSI panels in panel-simple. In another patch-set posted as part of a bridge update I used DRM_MODE_CONNECTOR_DPI as the default for all panels in panel-simple that did not have any connector type specified. I think this matches what you suggest above.
I will re-post the patch series when time permits.
Sam
-Daniel
Warnings are kinda ok once you're pretty sure you got them all, and want to make sure newly added drivers get this all right. But not before we've reached that.
Two more comments below.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 105 ++++++++++++++---------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 5 +- drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ---------------------------- 4 files changed, 53 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index ff6e358088fa..811584e54ad1 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2e6068d96034..cffc70257bd3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,7 +29,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> @@ -100,29 +99,11 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
struct drm_connector *connector; struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); struct drm_device *drm = pipe->plane.dev;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel); mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -132,9 +113,7 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel); mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel); pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
@@ -144,9 +123,6 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, @@ -190,6 +166,48 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, };
+static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) +{
- struct drm_device *drm = mxsfb->drm;
- struct drm_connector_list_iter iter;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
- if (ret)
return ret;
- if (panel) {
bridge = devm_drm_panel_bridge_add(drm->dev, panel);
Do we need a select DRM_PANEL_BRIDGE in Kconfig maybe? From what I can tell there is no devm_drm_panel_bridge_add() stub if DRM_PANEL_BRIDGE is not selected.
You're right, I'll add it.
if (IS_ERR(bridge))
return PTR_ERR(bridge);
- }
- if (!bridge)
return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
- }
- mxsfb->bridge = bridge;
- /*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
- drm_connector_list_iter_begin(drm, &iter);
- mxsfb->connector = drm_connector_list_iter_next(&iter);
- drm_connector_list_iter_end(&iter);
Could we not just use drm_panel_bridge_connector() here?
There's no guarantee that mxsfb->bridge is a panel bridge, the LCDIF output could be connected to another bridge in which case drm_of_find_panel_or_bridge() will set panel to NULL and bridge to a non-NULL value).
- return 0;
+}
static int mxsfb_load(struct drm_device *drm, unsigned long flags) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -201,6 +219,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) if (!mxsfb) return -ENOMEM;
- mxsfb->drm = drm; drm->dev_private = mxsfb; mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
@@ -236,41 +255,17 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = mxsfb_create_output(drm);
- if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
goto err_vblank;
- }
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
mxsfb->connector);
if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; }mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- /*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
- if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
- } else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
@@ -288,7 +283,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
if (ret < 0) { dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@@ -299,8 +294,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
return 0;
-err_irq:
- drm_panel_detach(mxsfb->panel);
err_vblank: pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0b65b5194a9c..0e3e5a63bbf9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,6 +8,8 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
+struct drm_device;
struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; @@ -26,10 +28,9 @@ struct mxsfb_drm_private { struct clk *clk_axi; struct clk *clk_disp_axi;
- struct drm_device *drm; struct drm_simple_display_pipe pipe;
- struct drm_connector panel_connector; struct drm_connector *connector;
- struct drm_panel *panel; struct drm_bridge *bridge;
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c deleted file mode 100644 index 9eca1605d11d..000000000000 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*
- Copyright (C) 2016 Marek Vasut marex@denx.de
- */
-#include <linux/of_graph.h>
-#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h>
-#include "mxsfb_drv.h"
-static struct mxsfb_drm_private * -drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) -{
- return container_of(connector, struct mxsfb_drm_private,
panel_connector);
-}
-static int mxsfb_panel_get_modes(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return drm_panel_get_modes(mxsfb->panel, connector);
- return 0;
-}
-static const struct -drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
- .get_modes = mxsfb_panel_get_modes,
-};
-static enum drm_connector_status -mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
return connector_status_connected;
- return connector_status_disconnected;
-}
-static void mxsfb_panel_connector_destroy(struct drm_connector *connector) -{
- struct mxsfb_drm_private *mxsfb =
drm_connector_to_mxsfb_drm_private(connector);
- if (mxsfb->panel)
drm_panel_detach(mxsfb->panel);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
- .detect = mxsfb_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mxsfb_panel_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-int mxsfb_create_output(struct drm_device *drm) -{
- struct mxsfb_drm_private *mxsfb = drm->dev_private;
- int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
&mxsfb->panel, &mxsfb->bridge);
- if (ret)
return ret;
- if (mxsfb->panel) {
mxsfb->connector = &mxsfb->panel_connector;
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
mxsfb->connector->polled = 0;
drm_connector_helper_add(mxsfb->connector,
&mxsfb_panel_connector_helper_funcs);
ret = drm_connector_init(drm, mxsfb->connector,
&mxsfb_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
- }
- return ret;
-}
-- Regards,
Laurent Pinchart
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Using BIT() is preferred over manual shifts as it's more readable, handles the 1 << 31 case properly, and avoids other mistakes as shown by the DEBUG0_HSYNC and DEBUG0_VSYNC bits (that are currently unused). Use it.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 56 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 932d7ea08fd5..713d8f830135 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -28,51 +28,51 @@ #define LCDC_V4_DEBUG0 0x1d0 #define LCDC_V3_DEBUG0 0x1f0
-#define CTRL_SFTRST (1 << 31) -#define CTRL_CLKGATE (1 << 30) -#define CTRL_BYPASS_COUNT (1 << 19) -#define CTRL_VSYNC_MODE (1 << 18) -#define CTRL_DOTCLK_MODE (1 << 17) -#define CTRL_DATA_SELECT (1 << 16) +#define CTRL_SFTRST BIT(31) +#define CTRL_CLKGATE BIT(30) +#define CTRL_BYPASS_COUNT BIT(19) +#define CTRL_VSYNC_MODE BIT(18) +#define CTRL_DOTCLK_MODE BIT(17) +#define CTRL_DATA_SELECT BIT(16) #define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10) #define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3) #define CTRL_BUS_WIDTH_MASK (0x3 << 10) #define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8) #define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3) -#define CTRL_MASTER (1 << 5) -#define CTRL_DF16 (1 << 3) -#define CTRL_DF18 (1 << 2) -#define CTRL_DF24 (1 << 1) -#define CTRL_RUN (1 << 0) +#define CTRL_MASTER BIT(5) +#define CTRL_DF16 BIT(3) +#define CTRL_DF18 BIT(2) +#define CTRL_DF24 BIT(1) +#define CTRL_RUN BIT(0)
-#define CTRL1_FIFO_CLEAR (1 << 21) +#define CTRL1_FIFO_CLEAR BIT(21) #define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16) #define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf) -#define CTRL1_CUR_FRAME_DONE_IRQ_EN (1 << 13) -#define CTRL1_CUR_FRAME_DONE_IRQ (1 << 9) +#define CTRL1_CUR_FRAME_DONE_IRQ_EN BIT(13) +#define CTRL1_CUR_FRAME_DONE_IRQ BIT(9)
#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16) #define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff) #define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff) #define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)
-#define VDCTRL0_ENABLE_PRESENT (1 << 28) -#define VDCTRL0_VSYNC_ACT_HIGH (1 << 27) -#define VDCTRL0_HSYNC_ACT_HIGH (1 << 26) -#define VDCTRL0_DOTCLK_ACT_FALLING (1 << 25) -#define VDCTRL0_ENABLE_ACT_HIGH (1 << 24) -#define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21) -#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20) -#define VDCTRL0_HALF_LINE (1 << 19) -#define VDCTRL0_HALF_LINE_MODE (1 << 18) +#define VDCTRL0_ENABLE_PRESENT BIT(28) +#define VDCTRL0_VSYNC_ACT_HIGH BIT(27) +#define VDCTRL0_HSYNC_ACT_HIGH BIT(26) +#define VDCTRL0_DOTCLK_ACT_FALLING BIT(25) +#define VDCTRL0_ENABLE_ACT_HIGH BIT(24) +#define VDCTRL0_VSYNC_PERIOD_UNIT BIT(21) +#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT BIT(20) +#define VDCTRL0_HALF_LINE BIT(19) +#define VDCTRL0_HALF_LINE_MODE BIT(18) #define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff) #define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff) #define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
-#define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29) -#define VDCTRL3_VSYNC_ONLY (1 << 28) +#define VDCTRL3_MUX_SYNC_SIGNALS BIT(29) +#define VDCTRL3_VSYNC_ONLY BIT(28) #define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16) #define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff) #define SET_VERT_WAIT_CNT(x) ((x) & 0xffff) @@ -80,11 +80,11 @@
#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */ #define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */ -#define VDCTRL4_SYNC_SIGNALS_ON (1 << 18) +#define VDCTRL4_SYNC_SIGNALS_ON BIT(18) #define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)
-#define DEBUG0_HSYNC (1 < 26) -#define DEBUG0_VSYNC (1 < 25) +#define DEBUG0_HSYNC BIT(26) +#define DEBUG0_VSYNC BIT(25)
#define MXSFB_MIN_XRES 120 #define MXSFB_MIN_YRES 120
On 2020-03-09 20:51, Laurent Pinchart wrote:
Using BIT() is preferred over manual shifts as it's more readable, handles the 1 << 31 case properly, and avoids other mistakes as shown by the DEBUG0_HSYNC and DEBUG0_VSYNC bits (that are currently unused). Use it.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_regs.h | 56 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 932d7ea08fd5..713d8f830135 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -28,51 +28,51 @@ #define LCDC_V4_DEBUG0 0x1d0 #define LCDC_V3_DEBUG0 0x1f0
-#define CTRL_SFTRST (1 << 31) -#define CTRL_CLKGATE (1 << 30) -#define CTRL_BYPASS_COUNT (1 << 19) -#define CTRL_VSYNC_MODE (1 << 18) -#define CTRL_DOTCLK_MODE (1 << 17) -#define CTRL_DATA_SELECT (1 << 16) +#define CTRL_SFTRST BIT(31) +#define CTRL_CLKGATE BIT(30) +#define CTRL_BYPASS_COUNT BIT(19) +#define CTRL_VSYNC_MODE BIT(18) +#define CTRL_DOTCLK_MODE BIT(17) +#define CTRL_DATA_SELECT BIT(16) #define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10) #define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3) #define CTRL_BUS_WIDTH_MASK (0x3 << 10) #define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8) #define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3) -#define CTRL_MASTER (1 << 5) -#define CTRL_DF16 (1 << 3) -#define CTRL_DF18 (1 << 2) -#define CTRL_DF24 (1 << 1) -#define CTRL_RUN (1 << 0) +#define CTRL_MASTER BIT(5) +#define CTRL_DF16 BIT(3) +#define CTRL_DF18 BIT(2) +#define CTRL_DF24 BIT(1) +#define CTRL_RUN BIT(0)
-#define CTRL1_FIFO_CLEAR (1 << 21) +#define CTRL1_FIFO_CLEAR BIT(21) #define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16) #define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf) -#define CTRL1_CUR_FRAME_DONE_IRQ_EN (1 << 13) -#define CTRL1_CUR_FRAME_DONE_IRQ (1 << 9) +#define CTRL1_CUR_FRAME_DONE_IRQ_EN BIT(13) +#define CTRL1_CUR_FRAME_DONE_IRQ BIT(9)
#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16) #define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff) #define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff) #define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)
-#define VDCTRL0_ENABLE_PRESENT (1 << 28) -#define VDCTRL0_VSYNC_ACT_HIGH (1 << 27) -#define VDCTRL0_HSYNC_ACT_HIGH (1 << 26) -#define VDCTRL0_DOTCLK_ACT_FALLING (1 << 25) -#define VDCTRL0_ENABLE_ACT_HIGH (1 << 24) -#define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21) -#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20) -#define VDCTRL0_HALF_LINE (1 << 19) -#define VDCTRL0_HALF_LINE_MODE (1 << 18) +#define VDCTRL0_ENABLE_PRESENT BIT(28) +#define VDCTRL0_VSYNC_ACT_HIGH BIT(27) +#define VDCTRL0_HSYNC_ACT_HIGH BIT(26) +#define VDCTRL0_DOTCLK_ACT_FALLING BIT(25) +#define VDCTRL0_ENABLE_ACT_HIGH BIT(24) +#define VDCTRL0_VSYNC_PERIOD_UNIT BIT(21) +#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT BIT(20) +#define VDCTRL0_HALF_LINE BIT(19) +#define VDCTRL0_HALF_LINE_MODE BIT(18) #define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff) #define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff) #define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
-#define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29) -#define VDCTRL3_VSYNC_ONLY (1 << 28) +#define VDCTRL3_MUX_SYNC_SIGNALS BIT(29) +#define VDCTRL3_VSYNC_ONLY BIT(28) #define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16) #define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff) #define SET_VERT_WAIT_CNT(x) ((x) & 0xffff) @@ -80,11 +80,11 @@
#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */ #define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */ -#define VDCTRL4_SYNC_SIGNALS_ON (1 << 18) +#define VDCTRL4_SYNC_SIGNALS_ON BIT(18) #define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)
-#define DEBUG0_HSYNC (1 < 26) -#define DEBUG0_VSYNC (1 < 25) +#define DEBUG0_HSYNC BIT(26) +#define DEBUG0_VSYNC BIT(25)
#define MXSFB_MIN_XRES 120 #define MXSFB_MIN_YRES 120
mxsfb_regs.h defines macros related to register bits. Some of them are not used and don't clearly map to any particular register, so their purpose isn't known. Remove them.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 8 -------- 1 file changed, 8 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 713d8f830135..78e6cb754712 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -91,17 +91,9 @@ #define MXSFB_MAX_XRES 0xffff #define MXSFB_MAX_YRES 0xffff
-#define RED 0 -#define GREEN 1 -#define BLUE 2 -#define TRANSP 3 - #define STMLCDIF_8BIT 1 /* pixel data bus to the display is of 8 bit width */ #define STMLCDIF_16BIT 0 /* pixel data bus to the display is of 16 bit width */ #define STMLCDIF_18BIT 2 /* pixel data bus to the display is of 18 bit width */ #define STMLCDIF_24BIT 3 /* pixel data bus to the display is of 24 bit width */
-#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6) -#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negative edge sampling */ - #endif /* __MXSFB_REGS_H__ */
On 2020-03-09 20:51, Laurent Pinchart wrote:
mxsfb_regs.h defines macros related to register bits. Some of them are not used and don't clearly map to any particular register, so their purpose isn't known. Remove them.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_regs.h | 8 -------- 1 file changed, 8 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 713d8f830135..78e6cb754712 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -91,17 +91,9 @@ #define MXSFB_MAX_XRES 0xffff #define MXSFB_MAX_YRES 0xffff
-#define RED 0 -#define GREEN 1 -#define BLUE 2 -#define TRANSP 3
#define STMLCDIF_8BIT 1 /* pixel data bus to the display is of 8 bit width */ #define STMLCDIF_16BIT 0 /* pixel data bus to the display is of 16 bit width */ #define STMLCDIF_18BIT 2 /* pixel data bus to the display is of 18 bit width */ #define STMLCDIF_24BIT 3 /* pixel data bus to the display is of 24 bit width */
-#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6) -#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negative edge sampling */
#endif /* __MXSFB_REGS_H__ */
Replace the convoluted way to set the format and bus width through difficult to read macros with more explicit ones. Also remove the outdated comment related to the limitations on bus width setting as it doesn't apply anymore (the bus width can be specified through the display_info bus format).
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 17 +++++------------ drivers/gpu/drm/mxsfb/mxsfb_regs.h | 17 ++++++++--------- 2 files changed, 13 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index b69ace8bf526..8b6339316929 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -52,13 +52,6 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
- /* - * WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to - * match the selected mode here. This differs from the original - * MXSFB driver, which had the option to configure the bus width - * to arbitrary value. This limitation should not pose an issue. - */ - /* CTRL1 contains IRQ config and status bits, preserve those. */ ctrl1 = readl(mxsfb->base + LCDC_CTRL1); ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ; @@ -66,12 +59,12 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) switch (format) { case DRM_FORMAT_RGB565: dev_dbg(drm->dev, "Setting up RGB565 mode\n"); - ctrl |= CTRL_SET_WORD_LENGTH(0); + ctrl |= CTRL_WORD_LENGTH_16; ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf); break; case DRM_FORMAT_XRGB8888: dev_dbg(drm->dev, "Setting up XRGB8888 mode\n"); - ctrl |= CTRL_SET_WORD_LENGTH(3); + ctrl |= CTRL_WORD_LENGTH_24; /* Do not use packed pixels = one pixel per word instead. */ ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7); break; @@ -104,13 +97,13 @@ static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) reg &= ~CTRL_BUS_WIDTH_MASK; switch (bus_format) { case MEDIA_BUS_FMT_RGB565_1X16: - reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT); + reg |= CTRL_BUS_WIDTH_16; break; case MEDIA_BUS_FMT_RGB666_1X18: - reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_18BIT); + reg |= CTRL_BUS_WIDTH_18; break; case MEDIA_BUS_FMT_RGB888_1X24: - reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT); + reg |= CTRL_BUS_WIDTH_24; break; default: dev_err(drm->dev, "Unknown media bus format %d\n", bus_format); diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 78e6cb754712..8ebb52bb1b46 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -34,11 +34,15 @@ #define CTRL_VSYNC_MODE BIT(18) #define CTRL_DOTCLK_MODE BIT(17) #define CTRL_DATA_SELECT BIT(16) -#define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10) -#define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3) +#define CTRL_BUS_WIDTH_16 (0 << 10) +#define CTRL_BUS_WIDTH_8 (1 << 10) +#define CTRL_BUS_WIDTH_18 (2 << 10) +#define CTRL_BUS_WIDTH_24 (3 << 10) #define CTRL_BUS_WIDTH_MASK (0x3 << 10) -#define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8) -#define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3) +#define CTRL_WORD_LENGTH_16 (0 << 8) +#define CTRL_WORD_LENGTH_8 (1 << 8) +#define CTRL_WORD_LENGTH_18 (2 << 8) +#define CTRL_WORD_LENGTH_24 (3 << 8) #define CTRL_MASTER BIT(5) #define CTRL_DF16 BIT(3) #define CTRL_DF18 BIT(2) @@ -91,9 +95,4 @@ #define MXSFB_MAX_XRES 0xffff #define MXSFB_MAX_YRES 0xffff
-#define STMLCDIF_8BIT 1 /* pixel data bus to the display is of 8 bit width */ -#define STMLCDIF_16BIT 0 /* pixel data bus to the display is of 16 bit width */ -#define STMLCDIF_18BIT 2 /* pixel data bus to the display is of 18 bit width */ -#define STMLCDIF_24BIT 3 /* pixel data bus to the display is of 24 bit width */ - #endif /* __MXSFB_REGS_H__ */
On 2020-03-09 20:52, Laurent Pinchart wrote:
Replace the convoluted way to set the format and bus width through difficult to read macros with more explicit ones. Also remove the outdated comment related to the limitations on bus width setting as it
Nice catch.
doesn't apply anymore (the bus width can be specified through the display_info bus format).
Reviewed-by: Stefan Agner stefan@agner.ch
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 17 +++++------------ drivers/gpu/drm/mxsfb/mxsfb_regs.h | 17 ++++++++--------- 2 files changed, 13 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index b69ace8bf526..8b6339316929 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -52,13 +52,6 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
- /*
* WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to
* match the selected mode here. This differs from the original
* MXSFB driver, which had the option to configure the bus width
* to arbitrary value. This limitation should not pose an issue.
*/
- /* CTRL1 contains IRQ config and status bits, preserve those. */ ctrl1 = readl(mxsfb->base + LCDC_CTRL1); ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
@@ -66,12 +59,12 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) switch (format) { case DRM_FORMAT_RGB565: dev_dbg(drm->dev, "Setting up RGB565 mode\n");
ctrl |= CTRL_SET_WORD_LENGTH(0);
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf); break; case DRM_FORMAT_XRGB8888: dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");ctrl |= CTRL_WORD_LENGTH_16;
ctrl |= CTRL_SET_WORD_LENGTH(3);
/* Do not use packed pixels = one pixel per word instead. */ ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7); break;ctrl |= CTRL_WORD_LENGTH_24;
@@ -104,13 +97,13 @@ static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) reg &= ~CTRL_BUS_WIDTH_MASK; switch (bus_format) { case MEDIA_BUS_FMT_RGB565_1X16:
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT);
break; case MEDIA_BUS_FMT_RGB666_1X18:reg |= CTRL_BUS_WIDTH_16;
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_18BIT);
break; case MEDIA_BUS_FMT_RGB888_1X24:reg |= CTRL_BUS_WIDTH_18;
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT);
break; default: dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);reg |= CTRL_BUS_WIDTH_24;
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 78e6cb754712..8ebb52bb1b46 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -34,11 +34,15 @@ #define CTRL_VSYNC_MODE BIT(18) #define CTRL_DOTCLK_MODE BIT(17) #define CTRL_DATA_SELECT BIT(16) -#define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10) -#define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3) +#define CTRL_BUS_WIDTH_16 (0 << 10) +#define CTRL_BUS_WIDTH_8 (1 << 10) +#define CTRL_BUS_WIDTH_18 (2 << 10) +#define CTRL_BUS_WIDTH_24 (3 << 10) #define CTRL_BUS_WIDTH_MASK (0x3 << 10) -#define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8) -#define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3) +#define CTRL_WORD_LENGTH_16 (0 << 8) +#define CTRL_WORD_LENGTH_8 (1 << 8) +#define CTRL_WORD_LENGTH_18 (2 << 8) +#define CTRL_WORD_LENGTH_24 (3 << 8) #define CTRL_MASTER BIT(5) #define CTRL_DF16 BIT(3) #define CTRL_DF18 BIT(2) @@ -91,9 +95,4 @@ #define MXSFB_MAX_XRES 0xffff #define MXSFB_MAX_YRES 0xffff
-#define STMLCDIF_8BIT 1 /* pixel data bus to the display is of 8 bit width */ -#define STMLCDIF_16BIT 0 /* pixel data bus to the display is of 16 bit width */ -#define STMLCDIF_18BIT 2 /* pixel data bus to the display is of 18 bit width */ -#define STMLCDIF_24BIT 3 /* pixel data bus to the display is of 24 bit width */
#endif /* __MXSFB_REGS_H__ */
The mxsfb_reset_block() function isn't special, pass it the mxsfb_drm_private pointer instead of a pointer to the base address.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index 8b6339316929..be60c4021e2f 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -166,21 +166,21 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT); }
-static int mxsfb_reset_block(void __iomem *reset_addr) +static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) { int ret;
- ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST); if (ret) return ret;
- writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); + writel(MODULE_CLKGATE, mxsfb->base + MXS_CLR_ADDR);
- ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST); if (ret) return ret;
- return clear_poll_bit(reset_addr, MODULE_CLKGATE); + return clear_poll_bit(mxsfb->base, MODULE_CLKGATE); }
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb) @@ -213,7 +213,7 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) */
/* Mandatory eLCDIF reset as per the Reference Manual */ - err = mxsfb_reset_block(mxsfb->base); + err = mxsfb_reset_block(mxsfb); if (err) return;
On 2020-03-09 20:52, Laurent Pinchart wrote:
The mxsfb_reset_block() function isn't special, pass it the mxsfb_drm_private pointer instead of a pointer to the base address.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index 8b6339316929..be60c4021e2f 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -166,21 +166,21 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT); }
-static int mxsfb_reset_block(void __iomem *reset_addr) +static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) { int ret;
- ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
- ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST); if (ret) return ret;
- writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
- writel(MODULE_CLKGATE, mxsfb->base + MXS_CLR_ADDR);
- ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
- ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST); if (ret) return ret;
- return clear_poll_bit(reset_addr, MODULE_CLKGATE);
- return clear_poll_bit(mxsfb->base, MODULE_CLKGATE);
}
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb) @@ -213,7 +213,7 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) */
/* Mandatory eLCDIF reset as per the Reference Manual */
- err = mxsfb_reset_block(mxsfb->base);
- err = mxsfb_reset_block(mxsfb); if (err) return;
The LCDC_CTRL register is located at address 0x0000. Some of the accesses to the register simply use the mxsfb->base address. Reference the LCDC_CTRL register explicitly instead to clarify the code.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index be60c4021e2f..722bd9b4f5f9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -170,17 +170,17 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) { int ret;
- ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST); + ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST); if (ret) return ret;
- writel(MODULE_CLKGATE, mxsfb->base + MXS_CLR_ADDR); + writel(MODULE_CLKGATE, mxsfb->base + LCDC_CTRL + MXS_CLR_ADDR);
- ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST); + ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST); if (ret) return ret;
- return clear_poll_bit(mxsfb->base, MODULE_CLKGATE); + return clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_CLKGATE); }
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
On 2020-03-09 20:52, Laurent Pinchart wrote:
The LCDC_CTRL register is located at address 0x0000. Some of the accesses to the register simply use the mxsfb->base address. Reference the LCDC_CTRL register explicitly instead to clarify the code.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index be60c4021e2f..722bd9b4f5f9 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -170,17 +170,17 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) { int ret;
- ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST);
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST); if (ret) return ret;
- writel(MODULE_CLKGATE, mxsfb->base + MXS_CLR_ADDR);
- writel(MODULE_CLKGATE, mxsfb->base + LCDC_CTRL + MXS_CLR_ADDR);
- ret = clear_poll_bit(mxsfb->base, MODULE_SFTRST);
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST); if (ret) return ret;
- return clear_poll_bit(mxsfb->base, MODULE_CLKGATE);
- return clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_CLKGATE);
}
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
mxsfb_crtc.c defines several macros related to register addresses and bit, which duplicates macros from mxsfb_regs.h. Use the macros from mxsfb_regs.h instead and remove them.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index 722bd9b4f5f9..aef72adabf41 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -29,10 +29,6 @@ #include "mxsfb_drv.h" #include "mxsfb_regs.h"
-#define MXS_SET_ADDR 0x4 -#define MXS_CLR_ADDR 0x8 -#define MODULE_CLKGATE BIT(30) -#define MODULE_SFTRST BIT(31) /* 1 second delay should be plenty of time for block reset */ #define RESET_TIMEOUT 1000000
@@ -162,7 +158,7 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) { u32 reg;
- writel(mask, addr + MXS_CLR_ADDR); + writel(mask, addr + REG_CLR); return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT); }
@@ -170,17 +166,17 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) { int ret;
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST); + ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST); if (ret) return ret;
- writel(MODULE_CLKGATE, mxsfb->base + LCDC_CTRL + MXS_CLR_ADDR); + writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST); + ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST); if (ret) return ret;
- return clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_CLKGATE); + return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE); }
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
On 2020-03-09 20:52, Laurent Pinchart wrote:
mxsfb_crtc.c defines several macros related to register addresses and bit, which duplicates macros from mxsfb_regs.h. Use the macros from mxsfb_regs.h instead and remove them.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index 722bd9b4f5f9..aef72adabf41 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -29,10 +29,6 @@ #include "mxsfb_drv.h" #include "mxsfb_regs.h"
-#define MXS_SET_ADDR 0x4 -#define MXS_CLR_ADDR 0x8 -#define MODULE_CLKGATE BIT(30) -#define MODULE_SFTRST BIT(31) /* 1 second delay should be plenty of time for block reset */ #define RESET_TIMEOUT 1000000
@@ -162,7 +158,7 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) { u32 reg;
- writel(mask, addr + MXS_CLR_ADDR);
- writel(mask, addr + REG_CLR); return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
}
@@ -170,17 +166,17 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) { int ret;
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST);
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST); if (ret) return ret;
- writel(MODULE_CLKGATE, mxsfb->base + LCDC_CTRL + MXS_CLR_ADDR);
- writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_SFTRST);
- ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST); if (ret) return ret;
- return clear_poll_bit(mxsfb->base + LCDC_CTRL, MODULE_CLKGATE);
- return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);
}
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
A fair number of includes are not needed. Drop them, and add a couple of required includes that were included indirectly.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 12 +++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 5 ----- 2 files changed, 3 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index aef72adabf41..c4f1575b4210 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -10,19 +10,13 @@
#include <linux/clk.h> #include <linux/iopoll.h> -#include <linux/of_graph.h> -#include <linux/platform_data/simplefb.h> +#include <linux/spinlock.h>
-#include <video/videomode.h> - -#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_fb_cma_helper.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index cffc70257bd3..204c1e52e9aa 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -9,15 +9,10 @@ */
#include <linux/clk.h> -#include <linux/component.h> #include <linux/dma-mapping.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/of_device.h> -#include <linux/of_graph.h> -#include <linux/of_reserved_mem.h> #include <linux/pm_runtime.h> -#include <linux/dma-resv.h> #include <linux/spinlock.h>
#include <drm/drm_atomic.h>
On 2020-03-09 20:52, Laurent Pinchart wrote:
A fair number of includes are not needed. Drop them, and add a couple of required includes that were included indirectly.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Out of curiosity, do you have some kind of tool helping with this?
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 12 +++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 5 ----- 2 files changed, 3 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index aef72adabf41..c4f1575b4210 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -10,19 +10,13 @@
#include <linux/clk.h> #include <linux/iopoll.h> -#include <linux/of_graph.h> -#include <linux/platform_data/simplefb.h> +#include <linux/spinlock.h>
-#include <video/videomode.h>
-#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_fb_cma_helper.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index cffc70257bd3..204c1e52e9aa 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -9,15 +9,10 @@ */
#include <linux/clk.h> -#include <linux/component.h> #include <linux/dma-mapping.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/of_device.h> -#include <linux/of_graph.h> -#include <linux/of_reserved_mem.h> #include <linux/pm_runtime.h> -#include <linux/dma-resv.h> #include <linux/spinlock.h>
#include <drm/drm_atomic.h>
Hi Stefan,
On Mon, Mar 23, 2020 at 10:53:50PM +0100, Stefan Agner wrote:
On 2020-03-09 20:52, Laurent Pinchart wrote:
A fair number of includes are not needed. Drop them, and add a couple of required includes that were included indirectly.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Out of curiosity, do you have some kind of tool helping with this?
No, that was manual.
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 12 +++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 5 ----- 2 files changed, 3 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index aef72adabf41..c4f1575b4210 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -10,19 +10,13 @@
#include <linux/clk.h> #include <linux/iopoll.h> -#include <linux/of_graph.h> -#include <linux/platform_data/simplefb.h> +#include <linux/spinlock.h>
-#include <video/videomode.h>
-#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_fb_cma_helper.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <drm/drm_plane_helper.h> -#include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index cffc70257bd3..204c1e52e9aa 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -9,15 +9,10 @@ */
#include <linux/clk.h> -#include <linux/component.h> #include <linux/dma-mapping.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/of_device.h> -#include <linux/of_graph.h> -#include <linux/of_reserved_mem.h> #include <linux/pm_runtime.h> -#include <linux/dma-resv.h> #include <linux/spinlock.h>
#include <drm/drm_atomic.h>
The DRM simple display pipeline helper only supports a single plane. In order to prepare for support of the alpha plane on i.MX6SX and i.MX7, move away from the helper. No new feature is added.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 184 ++++++++++++++++++++++++++--- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 ++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- 3 files changed, 197 insertions(+), 118 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -9,15 +9,21 @@ */
#include <linux/clk.h> +#include <linux/io.h> #include <linux/iopoll.h> +#include <linux/pm_runtime.h> #include <linux/spinlock.h>
+#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> -#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h> #include <drm/drm_vblank.h>
#include "mxsfb_drv.h" @@ -26,6 +32,10 @@ /* 1 second delay should be plenty of time for block reset */ #define RESET_TIMEOUT 1000000
+/* ----------------------------------------------------------------------------- + * CRTC + */ + static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) { return (val & mxsfb->devdata->hs_wdth_mask) << @@ -35,9 +45,8 @@ static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) /* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) { - struct drm_crtc *crtc = &mxsfb->pipe.crtc; - struct drm_device *drm = crtc->dev; - const u32 format = crtc->primary->state->fb->format->format; + struct drm_device *drm = mxsfb->drm; + const u32 format = mxsfb->crtc.primary->state->fb->format->format; u32 ctrl, ctrl1;
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER; @@ -71,8 +80,7 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) { - struct drm_crtc *crtc = &mxsfb->pipe.crtc; - struct drm_device *drm = crtc->dev; + struct drm_device *drm = mxsfb->drm; u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; u32 reg;
@@ -175,7 +183,7 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb) { - struct drm_framebuffer *fb = mxsfb->pipe.plane.state->fb; + struct drm_framebuffer *fb = mxsfb->plane.state->fb; struct drm_gem_cma_object *gem;
if (!fb) @@ -190,8 +198,8 @@ static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) { - struct drm_device *drm = mxsfb->pipe.crtc.dev; - struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode; + struct drm_device *drm = mxsfb->crtc.dev; + struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode; u32 bus_flags = mxsfb->connector->display_info.bus_flags; u32 vdctrl0, vsync_pulse_len, hsync_pulse_len; int err; @@ -273,10 +281,29 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) mxsfb->base + LCDC_VDCTRL4); }
-void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb) +static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) { + bool has_primary = state->plane_mask & + drm_plane_mask(crtc->primary); + + /* The primary plane has to be enabled when the CRTC is active. */ + if (has_primary != state->active) + return -EINVAL; + + /* TODO: Is this needed ? */ + return drm_atomic_add_affected_planes(state->state, crtc); +} + +static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev); + struct drm_device *drm = mxsfb->drm; dma_addr_t paddr;
+ pm_runtime_get_sync(drm->dev); + mxsfb_enable_axi_clk(mxsfb); mxsfb_crtc_mode_set_nofb(mxsfb);
@@ -290,17 +317,100 @@ void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb) mxsfb_enable_controller(mxsfb); }
-void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb) +static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) { + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev); + struct drm_device *drm = mxsfb->drm; + struct drm_pending_vblank_event *event; + mxsfb_disable_controller(mxsfb); mxsfb_disable_axi_clk(mxsfb); + + pm_runtime_put_sync(drm->dev); + + spin_lock_irq(&drm->event_lock); + event = crtc->state->event; + if (event) { + crtc->state->event = NULL; + drm_crtc_send_vblank_event(crtc, event); + } + spin_unlock_irq(&drm->event_lock); +} + +static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev); + + /* Clear and enable VBLANK IRQ */ + mxsfb_enable_axi_clk(mxsfb); + writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); + writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET); + mxsfb_disable_axi_clk(mxsfb); + + return 0; +} + +static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev); + + /* Disable and clear VBLANK IRQ */ + mxsfb_enable_axi_clk(mxsfb); + writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR); + writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); + mxsfb_disable_axi_clk(mxsfb); +} + +static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = { + .atomic_check = mxsfb_crtc_atomic_check, + .atomic_enable = mxsfb_crtc_atomic_enable, + .atomic_disable = mxsfb_crtc_atomic_disable, +}; + +static const struct drm_crtc_funcs mxsfb_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = mxsfb_crtc_enable_vblank, + .disable_vblank = mxsfb_crtc_disable_vblank, +}; + +/* ----------------------------------------------------------------------------- + * Encoder + */ + +static const struct drm_encoder_funcs mxsfb_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +/* ----------------------------------------------------------------------------- + * Planes + */ + +static int mxsfb_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, + &mxsfb->crtc); + + return drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); }
-void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, - struct drm_plane_state *state) +static void mxsfb_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_pstate) { - struct drm_simple_display_pipe *pipe = &mxsfb->pipe; - struct drm_crtc *crtc = &pipe->crtc; + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); + struct drm_crtc *crtc = &mxsfb->crtc; struct drm_pending_vblank_event *event; dma_addr_t paddr;
@@ -324,3 +434,47 @@ void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, mxsfb_disable_axi_clk(mxsfb); } } + +static const struct drm_plane_helper_funcs mxsfb_plane_helper_funcs = { + .atomic_check = mxsfb_plane_atomic_check, + .atomic_update = mxsfb_plane_atomic_update, +}; + +static const struct drm_plane_funcs mxsfb_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static const uint32_t mxsfb_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565 +}; + +int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb) +{ + struct drm_encoder *encoder = &mxsfb->encoder; + struct drm_plane *plane = &mxsfb->plane; + struct drm_crtc *crtc = &mxsfb->crtc; + int ret; + + drm_plane_helper_add(plane, &mxsfb_plane_helper_funcs); + ret = drm_universal_plane_init(mxsfb->drm, plane, 0, &mxsfb_plane_funcs, + mxsfb_formats, ARRAY_SIZE(mxsfb_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + + drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs); + ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, plane, NULL, + &mxsfb_crtc_funcs, NULL); + if (ret) + return ret; + + encoder->possible_crtcs = drm_crtc_mask(crtc); + return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); +} diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 204c1e52e9aa..a8da92976d13 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -10,22 +10,23 @@
#include <linux/clk.h> #include <linux/dma-mapping.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/spinlock.h>
-#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> #include <drm/drm_drv.h> #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_mode_config.h> #include <drm/drm_of.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h>
#include "mxsfb_drv.h" @@ -57,17 +58,6 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { }, };
-static const uint32_t mxsfb_formats[] = { - DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB565 -}; - -static struct mxsfb_drm_private * -drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe) -{ - return container_of(pipe, struct mxsfb_drm_private, pipe); -} - void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb) { if (mxsfb->clk_axi) @@ -90,77 +80,6 @@ static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers = { .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, };
-static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) -{ - struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); - struct drm_device *drm = pipe->plane.dev; - - pm_runtime_get_sync(drm->dev); - mxsfb_crtc_enable(mxsfb); -} - -static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) -{ - struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); - struct drm_device *drm = pipe->plane.dev; - struct drm_crtc *crtc = &pipe->crtc; - struct drm_pending_vblank_event *event; - - mxsfb_crtc_disable(mxsfb); - pm_runtime_put_sync(drm->dev); - - spin_lock_irq(&drm->event_lock); - event = crtc->state->event; - if (event) { - crtc->state->event = NULL; - drm_crtc_send_vblank_event(crtc, event); - } - spin_unlock_irq(&drm->event_lock); -} - -static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state) -{ - struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); - - mxsfb_plane_atomic_update(mxsfb, plane_state); -} - -static int mxsfb_pipe_enable_vblank(struct drm_simple_display_pipe *pipe) -{ - struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); - - /* Clear and enable VBLANK IRQ */ - mxsfb_enable_axi_clk(mxsfb); - writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); - writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET); - mxsfb_disable_axi_clk(mxsfb); - - return 0; -} - -static void mxsfb_pipe_disable_vblank(struct drm_simple_display_pipe *pipe) -{ - struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); - - /* Disable and clear VBLANK IRQ */ - mxsfb_enable_axi_clk(mxsfb); - writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR); - writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); - mxsfb_disable_axi_clk(mxsfb); -} - -static struct drm_simple_display_pipe_funcs mxsfb_funcs = { - .enable = mxsfb_pipe_enable, - .disable = mxsfb_pipe_disable, - .update = mxsfb_pipe_update, - .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, - .enable_vblank = mxsfb_pipe_enable_vblank, - .disable_vblank = mxsfb_pipe_disable_vblank, -}; - static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) { struct drm_device *drm = mxsfb->drm; @@ -183,7 +102,7 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) if (!bridge) return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge); + ret = drm_bridge_attach(&mxsfb->encoder, bridge, NULL, 0); if (ret) { DRM_DEV_ERROR(drm->dev, "failed to attach bridge: %d\n", ret); @@ -250,10 +169,9 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs, - mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL); + ret = mxsfb_kms_init(mxsfb); if (ret < 0) { - dev_err(drm->dev, "Cannot setup simple display pipe\n"); + dev_err(drm->dev, "Failed to initialize KMS pipeline\n"); goto err_vblank; }
@@ -309,11 +227,11 @@ static void mxsfb_unload(struct drm_device *drm) pm_runtime_disable(drm->dev); }
-static void mxsfb_irq_preinstall(struct drm_device *drm) +static void mxsfb_irq_disable(struct drm_device *drm) { struct mxsfb_drm_private *mxsfb = drm->dev_private;
- mxsfb_pipe_disable_vblank(&mxsfb->pipe); + mxsfb->crtc.funcs->disable_vblank(&mxsfb->crtc); }
static irqreturn_t mxsfb_irq_handler(int irq, void *data) @@ -327,7 +245,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) reg = readl(mxsfb->base + LCDC_CTRL1);
if (reg & CTRL1_CUR_FRAME_DONE_IRQ) - drm_crtc_handle_vblank(&mxsfb->pipe.crtc); + drm_crtc_handle_vblank(&mxsfb->crtc);
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
@@ -341,8 +259,8 @@ DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver mxsfb_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .irq_handler = mxsfb_irq_handler, - .irq_preinstall = mxsfb_irq_preinstall, - .irq_uninstall = mxsfb_irq_preinstall, + .irq_preinstall = mxsfb_irq_disable, + .irq_uninstall = mxsfb_irq_disable, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0e3e5a63bbf9..edd766ad254f 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,7 +8,12 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
-struct drm_device; +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_encoder.h> +#include <drm/drm_plane.h> + +struct clk;
struct mxsfb_devdata { unsigned int transfer_count; @@ -29,20 +34,22 @@ struct mxsfb_drm_private { struct clk *clk_disp_axi;
struct drm_device *drm; - struct drm_simple_display_pipe pipe; + struct drm_plane plane; + struct drm_crtc crtc; + struct drm_encoder encoder; struct drm_connector *connector; struct drm_bridge *bridge; };
-int mxsfb_setup_crtc(struct drm_device *dev); -int mxsfb_create_output(struct drm_device *dev); +static inline struct mxsfb_drm_private * +to_mxsfb_drm_private(struct drm_device *drm) +{ + return drm->dev_private; +}
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb); void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb);
-void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb); -void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb); -void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, - struct drm_plane_state *state); +int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb);
#endif /* __MXSFB_DRV_H__ */
On 2020-03-09 20:52, Laurent Pinchart wrote:
The DRM simple display pipeline helper only supports a single plane. In order to prepare for support of the alpha plane on i.MX6SX and i.MX7, move away from the helper. No new feature is added.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
I actually would prefer if we order this patch a bit later, especially after the mxsfb_crtc -> mxsfb_kms rename. It will make git log -p easier later.
Looks quite straight forward otherwise. One small comment below.
drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 184 ++++++++++++++++++++++++++--- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 ++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- 3 files changed, 197 insertions(+), 118 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -9,15 +9,21 @@ */
#include <linux/clk.h> +#include <linux/io.h> #include <linux/iopoll.h> +#include <linux/pm_runtime.h> #include <linux/spinlock.h>
+#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> -#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h> #include <drm/drm_vblank.h>
#include "mxsfb_drv.h" @@ -26,6 +32,10 @@ /* 1 second delay should be plenty of time for block reset */ #define RESET_TIMEOUT 1000000
+/*
- CRTC
- */
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) { return (val & mxsfb->devdata->hs_wdth_mask) << @@ -35,9 +45,8 @@ static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) /* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) {
- struct drm_crtc *crtc = &mxsfb->pipe.crtc;
- struct drm_device *drm = crtc->dev;
- const u32 format = crtc->primary->state->fb->format->format;
struct drm_device *drm = mxsfb->drm;
const u32 format = mxsfb->crtc.primary->state->fb->format->format; u32 ctrl, ctrl1;
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
@@ -71,8 +80,7 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) {
- struct drm_crtc *crtc = &mxsfb->pipe.crtc;
- struct drm_device *drm = crtc->dev;
- struct drm_device *drm = mxsfb->drm; u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; u32 reg;
@@ -175,7 +183,7 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb) {
- struct drm_framebuffer *fb = mxsfb->pipe.plane.state->fb;
struct drm_framebuffer *fb = mxsfb->plane.state->fb; struct drm_gem_cma_object *gem;
if (!fb)
@@ -190,8 +198,8 @@ static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) {
- struct drm_device *drm = mxsfb->pipe.crtc.dev;
- struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
- struct drm_device *drm = mxsfb->crtc.dev;
- struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode; u32 bus_flags = mxsfb->connector->display_info.bus_flags; u32 vdctrl0, vsync_pulse_len, hsync_pulse_len; int err;
@@ -273,10 +281,29 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) mxsfb->base + LCDC_VDCTRL4); }
-void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb) +static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
- bool has_primary = state->plane_mask &
drm_plane_mask(crtc->primary);
- /* The primary plane has to be enabled when the CRTC is active. */
- if (has_primary != state->active)
return -EINVAL;
- /* TODO: Is this needed ? */
Did you come to a conclusion? I guess since drm_simple_kms_crtc_check() is using it is the safe choice to keep it.
-- Stefan
- return drm_atomic_add_affected_planes(state->state, crtc);
+}
+static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
+{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
struct drm_device *drm = mxsfb->drm; dma_addr_t paddr;
pm_runtime_get_sync(drm->dev);
mxsfb_enable_axi_clk(mxsfb); mxsfb_crtc_mode_set_nofb(mxsfb);
@@ -290,17 +317,100 @@ void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb) mxsfb_enable_controller(mxsfb); }
-void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb) +static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
- struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
- struct drm_device *drm = mxsfb->drm;
- struct drm_pending_vblank_event *event;
- mxsfb_disable_controller(mxsfb); mxsfb_disable_axi_clk(mxsfb);
- pm_runtime_put_sync(drm->dev);
- spin_lock_irq(&drm->event_lock);
- event = crtc->state->event;
- if (event) {
crtc->state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
- }
- spin_unlock_irq(&drm->event_lock);
+}
+static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc) +{
- struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
- /* Clear and enable VBLANK IRQ */
- mxsfb_enable_axi_clk(mxsfb);
- writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
- mxsfb_disable_axi_clk(mxsfb);
- return 0;
+}
+static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc) +{
- struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
- /* Disable and clear VBLANK IRQ */
- mxsfb_enable_axi_clk(mxsfb);
- writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- mxsfb_disable_axi_clk(mxsfb);
+}
+static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
- .atomic_check = mxsfb_crtc_atomic_check,
- .atomic_enable = mxsfb_crtc_atomic_enable,
- .atomic_disable = mxsfb_crtc_atomic_disable,
+};
+static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
- .destroy = drm_crtc_cleanup,
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
- .enable_vblank = mxsfb_crtc_enable_vblank,
- .disable_vblank = mxsfb_crtc_disable_vblank,
+};
+/*
- Encoder
- */
+static const struct drm_encoder_funcs mxsfb_encoder_funcs = {
- .destroy = drm_encoder_cleanup,
+};
+/*
- Planes
- */
+static int mxsfb_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *plane_state)
+{
- struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
- struct drm_crtc_state *crtc_state;
- crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
&mxsfb->crtc);
- return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
false, true);
}
-void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
struct drm_plane_state *state)
+static void mxsfb_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
{
- struct drm_simple_display_pipe *pipe = &mxsfb->pipe;
- struct drm_crtc *crtc = &pipe->crtc;
- struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
- struct drm_crtc *crtc = &mxsfb->crtc; struct drm_pending_vblank_event *event; dma_addr_t paddr;
@@ -324,3 +434,47 @@ void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, mxsfb_disable_axi_clk(mxsfb); } }
+static const struct drm_plane_helper_funcs mxsfb_plane_helper_funcs = {
- .atomic_check = mxsfb_plane_atomic_check,
- .atomic_update = mxsfb_plane_atomic_update,
+};
+static const struct drm_plane_funcs mxsfb_plane_funcs = {
- .update_plane = drm_atomic_helper_update_plane,
- .disable_plane = drm_atomic_helper_disable_plane,
- .destroy = drm_plane_cleanup,
- .reset = drm_atomic_helper_plane_reset,
- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+static const uint32_t mxsfb_formats[] = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_RGB565
+};
+int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb) +{
- struct drm_encoder *encoder = &mxsfb->encoder;
- struct drm_plane *plane = &mxsfb->plane;
- struct drm_crtc *crtc = &mxsfb->crtc;
- int ret;
- drm_plane_helper_add(plane, &mxsfb_plane_helper_funcs);
- ret = drm_universal_plane_init(mxsfb->drm, plane, 0, &mxsfb_plane_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats),
NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
- if (ret)
return ret;
- drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
- ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, plane, NULL,
&mxsfb_crtc_funcs, NULL);
- if (ret)
return ret;
- encoder->possible_crtcs = drm_crtc_mask(crtc);
- return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
+} diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 204c1e52e9aa..a8da92976d13 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -10,22 +10,23 @@
#include <linux/clk.h> #include <linux/dma-mapping.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/spinlock.h>
-#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> #include <drm/drm_drv.h> #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_mode_config.h> #include <drm/drm_of.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h>
#include "mxsfb_drv.h" @@ -57,17 +58,6 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { }, };
-static const uint32_t mxsfb_formats[] = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_RGB565
-};
-static struct mxsfb_drm_private * -drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe) -{
- return container_of(pipe, struct mxsfb_drm_private, pipe);
-}
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb) { if (mxsfb->clk_axi) @@ -90,77 +80,6 @@ static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers = { .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, };
-static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
-{
- struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
- struct drm_device *drm = pipe->plane.dev;
- pm_runtime_get_sync(drm->dev);
- mxsfb_crtc_enable(mxsfb);
-}
-static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) -{
- struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
- struct drm_device *drm = pipe->plane.dev;
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_pending_vblank_event *event;
- mxsfb_crtc_disable(mxsfb);
- pm_runtime_put_sync(drm->dev);
- spin_lock_irq(&drm->event_lock);
- event = crtc->state->event;
- if (event) {
crtc->state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
- }
- spin_unlock_irq(&drm->event_lock);
-}
-static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
-{
- struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
- mxsfb_plane_atomic_update(mxsfb, plane_state);
-}
-static int mxsfb_pipe_enable_vblank(struct drm_simple_display_pipe *pipe) -{
- struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
- /* Clear and enable VBLANK IRQ */
- mxsfb_enable_axi_clk(mxsfb);
- writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
- mxsfb_disable_axi_clk(mxsfb);
- return 0;
-}
-static void mxsfb_pipe_disable_vblank(struct drm_simple_display_pipe *pipe) -{
- struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
- /* Disable and clear VBLANK IRQ */
- mxsfb_enable_axi_clk(mxsfb);
- writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- mxsfb_disable_axi_clk(mxsfb);
-}
-static struct drm_simple_display_pipe_funcs mxsfb_funcs = {
- .enable = mxsfb_pipe_enable,
- .disable = mxsfb_pipe_disable,
- .update = mxsfb_pipe_update,
- .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
- .enable_vblank = mxsfb_pipe_enable_vblank,
- .disable_vblank = mxsfb_pipe_disable_vblank,
-};
static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) { struct drm_device *drm = mxsfb->drm; @@ -183,7 +102,7 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) if (!bridge) return -ENODEV;
- ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe, bridge);
- ret = drm_bridge_attach(&mxsfb->encoder, bridge, NULL, 0); if (ret) { DRM_DEV_ERROR(drm->dev, "failed to attach bridge: %d\n", ret);
@@ -250,10 +169,9 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) /* Modeset init */ drm_mode_config_init(drm);
- ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, NULL);
- ret = mxsfb_kms_init(mxsfb); if (ret < 0) {
dev_err(drm->dev, "Cannot setup simple display pipe\n");
goto err_vblank; }dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
@@ -309,11 +227,11 @@ static void mxsfb_unload(struct drm_device *drm) pm_runtime_disable(drm->dev); }
-static void mxsfb_irq_preinstall(struct drm_device *drm) +static void mxsfb_irq_disable(struct drm_device *drm) { struct mxsfb_drm_private *mxsfb = drm->dev_private;
- mxsfb_pipe_disable_vblank(&mxsfb->pipe);
- mxsfb->crtc.funcs->disable_vblank(&mxsfb->crtc);
}
static irqreturn_t mxsfb_irq_handler(int irq, void *data) @@ -327,7 +245,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) reg = readl(mxsfb->base + LCDC_CTRL1);
if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
drm_crtc_handle_vblank(&mxsfb->pipe.crtc);
drm_crtc_handle_vblank(&mxsfb->crtc);
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
@@ -341,8 +259,8 @@ DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver mxsfb_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .irq_handler = mxsfb_irq_handler,
- .irq_preinstall = mxsfb_irq_preinstall,
- .irq_uninstall = mxsfb_irq_preinstall,
- .irq_preinstall = mxsfb_irq_disable,
- .irq_uninstall = mxsfb_irq_disable, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create,
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 0e3e5a63bbf9..edd766ad254f 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -8,7 +8,12 @@ #ifndef __MXSFB_DRV_H__ #define __MXSFB_DRV_H__
-struct drm_device; +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_encoder.h> +#include <drm/drm_plane.h>
+struct clk;
struct mxsfb_devdata { unsigned int transfer_count; @@ -29,20 +34,22 @@ struct mxsfb_drm_private { struct clk *clk_disp_axi;
struct drm_device *drm;
- struct drm_simple_display_pipe pipe;
- struct drm_plane plane;
- struct drm_crtc crtc;
- struct drm_encoder encoder; struct drm_connector *connector; struct drm_bridge *bridge;
};
-int mxsfb_setup_crtc(struct drm_device *dev); -int mxsfb_create_output(struct drm_device *dev); +static inline struct mxsfb_drm_private * +to_mxsfb_drm_private(struct drm_device *drm) +{
- return drm->dev_private;
+}
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb); void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb);
-void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb); -void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb); -void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
struct drm_plane_state *state);
+int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb);
#endif /* __MXSFB_DRV_H__ */
The mxsfb_crtc.c file doesn't handle just the CRTC, but also the other KMS objects. Rename it accordingly.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/{mxsfb_crtc.c => mxsfb_kms.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/gpu/drm/mxsfb/{mxsfb_crtc.c => mxsfb_kms.c} (100%)
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index 811584e54ad1..26d153896d72 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o +mxsfb-y := mxsfb_drv.o mxsfb_kms.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c similarity index 100% rename from drivers/gpu/drm/mxsfb/mxsfb_crtc.c rename to drivers/gpu/drm/mxsfb/mxsfb_kms.c
On 2020-03-09 20:52, Laurent Pinchart wrote:
The mxsfb_crtc.c file doesn't handle just the CRTC, but also the other KMS objects. Rename it accordingly.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/{mxsfb_crtc.c => mxsfb_kms.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/gpu/drm/mxsfb/{mxsfb_crtc.c => mxsfb_kms.c} (100%)
diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile index 811584e54ad1..26d153896d72 100644 --- a/drivers/gpu/drm/mxsfb/Makefile +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -mxsfb-y := mxsfb_drv.o mxsfb_crtc.o +mxsfb-y := mxsfb_drv.o mxsfb_kms.o obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c similarity index 100% rename from drivers/gpu/drm/mxsfb/mxsfb_crtc.c rename to drivers/gpu/drm/mxsfb/mxsfb_kms.c
The vblank event is armed in the plane .atomic_update(). This works fine as we have a single plane, but will break as soon as multiple planes are supported (not to mention it's logically the wrong place to perform the operation). Move it to CRTC .atomic_flush().
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 8f339adb8d04..ebe0785694cb 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -295,6 +295,25 @@ static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc, return drm_atomic_add_affected_planes(state->state, crtc); }
+static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_pending_vblank_event *event; + + event = crtc->state->event; + crtc->state->event = NULL; + + if (!event) + return; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); +} + static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { @@ -364,6 +383,7 @@ static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = { .atomic_check = mxsfb_crtc_atomic_check, + .atomic_flush = mxsfb_crtc_atomic_flush, .atomic_enable = mxsfb_crtc_atomic_enable, .atomic_disable = mxsfb_crtc_atomic_disable, }; @@ -410,23 +430,8 @@ static void mxsfb_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_pstate) { struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); - struct drm_crtc *crtc = &mxsfb->crtc; - struct drm_pending_vblank_event *event; dma_addr_t paddr;
- spin_lock_irq(&crtc->dev->event_lock); - event = crtc->state->event; - if (event) { - crtc->state->event = NULL; - - if (drm_crtc_vblank_get(crtc) == 0) { - drm_crtc_arm_vblank_event(crtc, event); - } else { - drm_crtc_send_vblank_event(crtc, event); - } - } - spin_unlock_irq(&crtc->dev->event_lock); - paddr = mxsfb_get_fb_paddr(mxsfb); if (paddr) { mxsfb_enable_axi_clk(mxsfb);
On 2020-03-09 20:52, Laurent Pinchart wrote:
The vblank event is armed in the plane .atomic_update(). This works fine as we have a single plane, but will break as soon as multiple planes are supported (not to mention it's logically the wrong place to perform the operation). Move it to CRTC .atomic_flush().
The comment in the bracket make it sound as if there was something wrong with the code before, but from what I understand that was just fine and the only option we had with drm_simple_kms_helper, correct? Can you maybe make that a bit clearer? (e.g. by stating that now that we no longer use the simple display pipeline we can and should use the flush callback).
Otherwise looks good.
Reviewed-by: Stefan Agner stefan@agner.ch
-- Stefan
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/mxsfb_kms.c | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 8f339adb8d04..ebe0785694cb 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -295,6 +295,25 @@ static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc, return drm_atomic_add_affected_planes(state->state, crtc); }
+static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
+{
- struct drm_pending_vblank_event *event;
- event = crtc->state->event;
- crtc->state->event = NULL;
- if (!event)
return;
- spin_lock_irq(&crtc->dev->event_lock);
- if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
- else
drm_crtc_send_vblank_event(crtc, event);
- spin_unlock_irq(&crtc->dev->event_lock);
+}
static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { @@ -364,6 +383,7 @@ static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = { .atomic_check = mxsfb_crtc_atomic_check,
- .atomic_flush = mxsfb_crtc_atomic_flush, .atomic_enable = mxsfb_crtc_atomic_enable, .atomic_disable = mxsfb_crtc_atomic_disable,
}; @@ -410,23 +430,8 @@ static void mxsfb_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_pstate) { struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
struct drm_crtc *crtc = &mxsfb->crtc;
struct drm_pending_vblank_event *event; dma_addr_t paddr;
spin_lock_irq(&crtc->dev->event_lock);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
if (drm_crtc_vblank_get(crtc) == 0) {
drm_crtc_arm_vblank_event(crtc, event);
} else {
drm_crtc_send_vblank_event(crtc, event);
}
}
spin_unlock_irq(&crtc->dev->event_lock);
paddr = mxsfb_get_fb_paddr(mxsfb); if (paddr) { mxsfb_enable_axi_clk(mxsfb);
The driver attempts agressive power management by enabling and disabling the AXI clock around register accesses. This results in attempts to enable and disable the clock in the IRQ handler, which is a no-go as preparing or unpreparing the clock may sleep.
On the other hand, the driver enables the AXI clock when enabling the CRTC and keeps it enabled until the CRTC is disabled. This is correct, and renders the power management attempt pointless, as interrupts are not supposed to occur when the CRTC is off.
The same reasoning can be applied to the CRTC .enable_vblank() and .disable_vblank() that are not supposed to be called when the CRTC off and thus don't require manual handling of the AXI clock. Furthermore, vblank handling is never enabled, which results in the vblank enable and disable handlers never being called.
To fix this, remove the manual clock handling in the IRQ, the CRTC .enable_vblank() and .disable_vblank() handlers and the plane .atomic_update() handler. We however need to handle the clock manually in mxsfb_irq_disable() as is calls .disable_vblank() manually and is used both at probe and remove time.
The clock disabling is also moved to the last step of the mxsfb_crtc_atomic_disable() function, to prepare for enabling vblank handling.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 6 ++---- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 15 ++++----------- 2 files changed, 6 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index a8da92976d13..e324bd2a63a5 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -231,7 +231,9 @@ static void mxsfb_irq_disable(struct drm_device *drm) { struct mxsfb_drm_private *mxsfb = drm->dev_private;
+ mxsfb_enable_axi_clk(mxsfb); mxsfb->crtc.funcs->disable_vblank(&mxsfb->crtc); + mxsfb_disable_axi_clk(mxsfb); }
static irqreturn_t mxsfb_irq_handler(int irq, void *data) @@ -240,8 +242,6 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) struct mxsfb_drm_private *mxsfb = drm->dev_private; u32 reg;
- mxsfb_enable_axi_clk(mxsfb); - reg = readl(mxsfb->base + LCDC_CTRL1);
if (reg & CTRL1_CUR_FRAME_DONE_IRQ) @@ -249,8 +249,6 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- mxsfb_disable_axi_clk(mxsfb); - return IRQ_HANDLED; }
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index ebe0785694cb..ac2696c8483d 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -344,9 +344,6 @@ static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_pending_vblank_event *event;
mxsfb_disable_controller(mxsfb); - mxsfb_disable_axi_clk(mxsfb); - - pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock); event = crtc->state->event; @@ -355,6 +352,9 @@ static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock); + + mxsfb_disable_axi_clk(mxsfb); + pm_runtime_put_sync(drm->dev); }
static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc) @@ -362,10 +362,8 @@ static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc) struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
/* Clear and enable VBLANK IRQ */ - mxsfb_enable_axi_clk(mxsfb); writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET); - mxsfb_disable_axi_clk(mxsfb);
return 0; } @@ -375,10 +373,8 @@ static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc) struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
/* Disable and clear VBLANK IRQ */ - mxsfb_enable_axi_clk(mxsfb); writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR); writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); - mxsfb_disable_axi_clk(mxsfb); }
static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = { @@ -433,11 +429,8 @@ static void mxsfb_plane_atomic_update(struct drm_plane *plane, dma_addr_t paddr;
paddr = mxsfb_get_fb_paddr(mxsfb); - if (paddr) { - mxsfb_enable_axi_clk(mxsfb); + if (paddr) writel(paddr, mxsfb->base + mxsfb->devdata->next_buf); - mxsfb_disable_axi_clk(mxsfb); - } }
static const struct drm_plane_helper_funcs mxsfb_plane_helper_funcs = {
On 2020-03-09 20:52, Laurent Pinchart wrote:
The driver attempts agressive power management by enabling and disabling the AXI clock around register accesses. This results in attempts to enable and disable the clock in the IRQ handler, which is a no-go as preparing or unpreparing the clock may sleep.
On the other hand, the driver enables the AXI clock when enabling the CRTC and keeps it enabled until the CRTC is disabled. This is correct, and renders the power management attempt pointless, as interrupts are not supposed to occur when the CRTC is off.
The same reasoning can be applied to the CRTC .enable_vblank() and .disable_vblank() that are not supposed to be called when the CRTC off and thus don't require manual handling of the AXI clock. Furthermore, vblank handling is never enabled, which results in the vblank enable and disable handlers never being called.
To fix this, remove the manual clock handling in the IRQ, the CRTC .enable_vblank() and .disable_vblank() handlers and the plane .atomic_update() handler. We however need to handle the clock manually in mxsfb_irq_disable() as is calls .disable_vblank() manually and is used both at probe and remove time.
The clock disabling is also moved to the last step of the mxsfb_crtc_atomic_disable() function, to prepare for enabling vblank handling.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Looks good to me!
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 6 ++---- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 15 ++++----------- 2 files changed, 6 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index a8da92976d13..e324bd2a63a5 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -231,7 +231,9 @@ static void mxsfb_irq_disable(struct drm_device *drm) { struct mxsfb_drm_private *mxsfb = drm->dev_private;
- mxsfb_enable_axi_clk(mxsfb); mxsfb->crtc.funcs->disable_vblank(&mxsfb->crtc);
- mxsfb_disable_axi_clk(mxsfb);
}
static irqreturn_t mxsfb_irq_handler(int irq, void *data) @@ -240,8 +242,6 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) struct mxsfb_drm_private *mxsfb = drm->dev_private; u32 reg;
mxsfb_enable_axi_clk(mxsfb);
reg = readl(mxsfb->base + LCDC_CTRL1);
if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
@@ -249,8 +249,6 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- mxsfb_disable_axi_clk(mxsfb);
- return IRQ_HANDLED;
}
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index ebe0785694cb..ac2696c8483d 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -344,9 +344,6 @@ static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_pending_vblank_event *event;
mxsfb_disable_controller(mxsfb);
mxsfb_disable_axi_clk(mxsfb);
pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock); event = crtc->state->event;
@@ -355,6 +352,9 @@ static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&drm->event_lock);
- mxsfb_disable_axi_clk(mxsfb);
- pm_runtime_put_sync(drm->dev);
}
static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc) @@ -362,10 +362,8 @@ static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc) struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
/* Clear and enable VBLANK IRQ */
mxsfb_enable_axi_clk(mxsfb); writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
mxsfb_disable_axi_clk(mxsfb);
return 0;
} @@ -375,10 +373,8 @@ static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc) struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
/* Disable and clear VBLANK IRQ */
- mxsfb_enable_axi_clk(mxsfb); writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR); writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
- mxsfb_disable_axi_clk(mxsfb);
}
static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = { @@ -433,11 +429,8 @@ static void mxsfb_plane_atomic_update(struct drm_plane *plane, dma_addr_t paddr;
paddr = mxsfb_get_fb_paddr(mxsfb);
- if (paddr) {
mxsfb_enable_axi_clk(mxsfb);
- if (paddr) writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
mxsfb_disable_axi_clk(mxsfb);
- }
}
static const struct drm_plane_helper_funcs mxsfb_plane_helper_funcs = {
Enable vblank handling when the CRTC is turned on and disable it when it is turned off. This requires moving vblank init after the KMS pipeline initialisation, otherwise drm_vblank_init() gets called with 0 CRTCs.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 15 +++++++++------ drivers/gpu/drm/mxsfb/mxsfb_kms.c | 6 +++++- 2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index e324bd2a63a5..72b4f6a947a4 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -160,12 +160,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
pm_runtime_enable(drm->dev);
- ret = drm_vblank_init(drm, drm->mode_config.num_crtc); - if (ret < 0) { - dev_err(drm->dev, "Failed to initialise vblank\n"); - goto err_vblank; - } - /* Modeset init */ drm_mode_config_init(drm);
@@ -175,6 +169,15 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) goto err_vblank; }
+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret < 0) { + dev_err(drm->dev, "Failed to initialise vblank\n"); + goto err_vblank; + } + + /* Start with vertical blanking interrupt reporting disabled. */ + drm_crtc_vblank_off(&mxsfb->crtc); + ret = mxsfb_attach_bridge(mxsfb); if (ret) { dev_err(drm->dev, "Cannot connect bridge: %d\n", ret); diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index ac2696c8483d..640305fb1068 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -322,8 +322,10 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, dma_addr_t paddr;
pm_runtime_get_sync(drm->dev); - mxsfb_enable_axi_clk(mxsfb); + + drm_crtc_vblank_on(crtc); + mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */ @@ -353,6 +355,8 @@ static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, } spin_unlock_irq(&drm->event_lock);
+ drm_crtc_vblank_off(crtc); + mxsfb_disable_axi_clk(mxsfb); pm_runtime_put_sync(drm->dev); }
On 2020-03-09 20:52, Laurent Pinchart wrote:
Enable vblank handling when the CRTC is turned on and disable it when it is turned off. This requires moving vblank init after the KMS pipeline initialisation, otherwise drm_vblank_init() gets called with 0 CRTCs.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
With this patch applied I start to get system resets on my Colibri iMX7 test device during system boot shortly after initializing mxsfb. It happens not on every boot, but rather often, I would say every second or third. From what I can tell the interrupt handling should be safe, but it seems that every now and then something races. Any idea what that could be?
-- Stefan
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 15 +++++++++------ drivers/gpu/drm/mxsfb/mxsfb_kms.c | 6 +++++- 2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index e324bd2a63a5..72b4f6a947a4 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -160,12 +160,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
pm_runtime_enable(drm->dev);
- ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
- if (ret < 0) {
dev_err(drm->dev, "Failed to initialise vblank\n");
goto err_vblank;
- }
- /* Modeset init */ drm_mode_config_init(drm);
@@ -175,6 +169,15 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) goto err_vblank; }
- ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
- if (ret < 0) {
dev_err(drm->dev, "Failed to initialise vblank\n");
goto err_vblank;
- }
- /* Start with vertical blanking interrupt reporting disabled. */
- drm_crtc_vblank_off(&mxsfb->crtc);
- ret = mxsfb_attach_bridge(mxsfb); if (ret) { dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index ac2696c8483d..640305fb1068 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -322,8 +322,10 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, dma_addr_t paddr;
pm_runtime_get_sync(drm->dev);
- mxsfb_enable_axi_clk(mxsfb);
drm_crtc_vblank_on(crtc);
mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */
@@ -353,6 +355,8 @@ static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, } spin_unlock_irq(&drm->event_lock);
- drm_crtc_vblank_off(crtc);
- mxsfb_disable_axi_clk(mxsfb); pm_runtime_put_sync(drm->dev);
}
On 2020-03-24 00:08, Stefan Agner wrote:
On 2020-03-09 20:52, Laurent Pinchart wrote:
Enable vblank handling when the CRTC is turned on and disable it when it is turned off. This requires moving vblank init after the KMS pipeline initialisation, otherwise drm_vblank_init() gets called with 0 CRTCs.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
With this patch applied I start to get system resets on my Colibri iMX7 test device during system boot shortly after initializing mxsfb. It happens not on every boot, but rather often, I would say every second or third. From what I can tell the interrupt handling should be safe, but it seems that every now and then something races. Any idea what that could be?
Disregard this: It seems that my power supply was a bit too weak which lead to the system reset every now and then. With enough juice, I run now quite some boots with the complete patchset applied without seeing any issues.
Sorry about that.
-- Stefan
-- Stefan
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 15 +++++++++------ drivers/gpu/drm/mxsfb/mxsfb_kms.c | 6 +++++- 2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index e324bd2a63a5..72b4f6a947a4 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -160,12 +160,6 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
pm_runtime_enable(drm->dev);
- ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
- if (ret < 0) {
dev_err(drm->dev, "Failed to initialise vblank\n");
goto err_vblank;
- }
- /* Modeset init */ drm_mode_config_init(drm);
@@ -175,6 +169,15 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) goto err_vblank; }
- ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
- if (ret < 0) {
dev_err(drm->dev, "Failed to initialise vblank\n");
goto err_vblank;
- }
- /* Start with vertical blanking interrupt reporting disabled. */
- drm_crtc_vblank_off(&mxsfb->crtc);
- ret = mxsfb_attach_bridge(mxsfb); if (ret) { dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index ac2696c8483d..640305fb1068 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -322,8 +322,10 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, dma_addr_t paddr;
pm_runtime_get_sync(drm->dev);
- mxsfb_enable_axi_clk(mxsfb);
drm_crtc_vblank_on(crtc);
mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */
@@ -353,6 +355,8 @@ static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc, } spin_unlock_irq(&drm->event_lock);
- drm_crtc_vblank_off(crtc);
- mxsfb_disable_axi_clk(mxsfb); pm_runtime_put_sync(drm->dev);
}
The debug0 and ipversion fields of the mxsfb_devdata structure are unused. Remove them.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 4 ---- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 2 -- 2 files changed, 6 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 72b4f6a947a4..7c9a041f5f6d 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -42,19 +42,15 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .transfer_count = LCDC_V3_TRANSFER_COUNT, .cur_buf = LCDC_V3_CUR_BUF, .next_buf = LCDC_V3_NEXT_BUF, - .debug0 = LCDC_V3_DEBUG0, .hs_wdth_mask = 0xff, .hs_wdth_shift = 24, - .ipversion = 3, }, [MXSFB_V4] = { .transfer_count = LCDC_V4_TRANSFER_COUNT, .cur_buf = LCDC_V4_CUR_BUF, .next_buf = LCDC_V4_NEXT_BUF, - .debug0 = LCDC_V4_DEBUG0, .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18, - .ipversion = 4, }, };
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index edd766ad254f..607a6a5e6be3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -19,10 +19,8 @@ struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; unsigned int next_buf; - unsigned int debug0; unsigned int hs_wdth_mask; unsigned int hs_wdth_shift; - unsigned int ipversion; };
struct mxsfb_drm_private {
On 2020-03-09 20:52, Laurent Pinchart wrote:
The debug0 and ipversion fields of the mxsfb_devdata structure are unused. Remove them.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 4 ---- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 2 -- 2 files changed, 6 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 72b4f6a947a4..7c9a041f5f6d 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -42,19 +42,15 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .transfer_count = LCDC_V3_TRANSFER_COUNT, .cur_buf = LCDC_V3_CUR_BUF, .next_buf = LCDC_V3_NEXT_BUF,
.hs_wdth_mask = 0xff, .hs_wdth_shift = 24,.debug0 = LCDC_V3_DEBUG0,
}, [MXSFB_V4] = { .transfer_count = LCDC_V4_TRANSFER_COUNT, .cur_buf = LCDC_V4_CUR_BUF, .next_buf = LCDC_V4_NEXT_BUF,.ipversion = 3,
.hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18,.debug0 = LCDC_V4_DEBUG0,
},.ipversion = 4,
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index edd766ad254f..607a6a5e6be3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -19,10 +19,8 @@ struct mxsfb_devdata { unsigned int transfer_count; unsigned int cur_buf; unsigned int next_buf;
- unsigned int debug0; unsigned int hs_wdth_mask; unsigned int hs_wdth_shift;
- unsigned int ipversion;
};
struct mxsfb_drm_private {
Extend the Kconfig option description by listing the i.MX7 SoCs, as they are supported by the same driver.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig index 0dca8f27169e..e91841f8f8a2 100644 --- a/drivers/gpu/drm/mxsfb/Kconfig +++ b/drivers/gpu/drm/mxsfb/Kconfig @@ -5,7 +5,7 @@ config DRM_MXS Choose this option to select drivers for MXS FB devices
config DRM_MXSFB - tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller" + tristate "i.MX23/i.MX28/i.MX6SX/i.MX7 MXSFB LCD controller" depends on DRM && OF depends on COMMON_CLK select DRM_MXS @@ -14,7 +14,7 @@ config DRM_MXSFB select DRM_KMS_CMA_HELPER select DRM_PANEL help - Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB + Choose this option if you have an i.MX23/i.MX28/i.MX6SX/i.MX7 MXSFB LCD controller.
If M is selected the module will be called mxsfb.
Hi Laurent,
On Mon, Mar 9, 2020 at 4:53 PM Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
config DRM_MXSFB
tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller"
tristate "i.MX23/i.MX28/i.MX6SX/i.MX7 MXSFB LCD controller"
Can't we just make it simpler and write: tristate "i.MX eLCDIF controller" instead?
Otherwise this list will get longer each time a new SoC is supported:
tristate "i.MX23/i.MX28/i.MX6SX/i.MX6SL/i.MX7/i.MX7ULP/i.MX8M/i.MX8MM/i.MX8QXP/i.MX8M MXSFB LCD controller"
and probably more :-)
Hi Fabio,
On Tue, Mar 10, 2020 at 01:28:35PM -0300, Fabio Estevam wrote:
On Mon, Mar 9, 2020 at 4:53 PM Laurent Pinchart wrote:
config DRM_MXSFB
tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller"
tristate "i.MX23/i.MX28/i.MX6SX/i.MX7 MXSFB LCD controller"
Can't we just make it simpler and write: tristate "i.MX eLCDIF controller" instead?
It should then be "LCDIF and eLCDIF controllers" or "(e)LCDIF controller", as the versions prior to i.MX6 were LCDIF and got later extended to eLCDIF.
Otherwise this list will get longer each time a new SoC is supported:
tristate "i.MX23/i.MX28/i.MX6SX/i.MX6SL/i.MX7/i.MX7ULP/i.MX8M/i.MX8MM/i.MX8QXP/i.MX8M MXSFB LCD controller"
and probably more :-)
It's a good point, and I agree. I'll fix this.
Extend the Kconfig option description by listing the i.MX7 SoCs, as they are supported by the same driver. Replace the list of SoCs in the short description with just "(e)LCDIF LCD controller" to avoid expanding it further in the future as support for more SoCs is added.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig index 0dca8f27169e..0b407338dd46 100644 --- a/drivers/gpu/drm/mxsfb/Kconfig +++ b/drivers/gpu/drm/mxsfb/Kconfig @@ -5,7 +5,7 @@ config DRM_MXS Choose this option to select drivers for MXS FB devices
config DRM_MXSFB - tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller" + tristate "i.MX (e)LCDIF LCD controller" depends on DRM && OF depends on COMMON_CLK select DRM_MXS @@ -14,7 +14,8 @@ config DRM_MXSFB select DRM_KMS_CMA_HELPER select DRM_PANEL help - Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB - LCD controller. + Choose this option if you have an LCDIF or eLCDIF LCD controller. + Those devices are found in various i.MX SoC (including i.MX23, + i.MX28, i.MX6SX and i.MX7).
If M is selected the module will be called mxsfb.
On 2020-03-09 20:52, Laurent Pinchart wrote:
Extend the Kconfig option description by listing the i.MX7 SoCs, as they are supported by the same driver.
Can you also add "i.MX8M" to the list since the bindings for this driver are also used in arch/arm64/boot/dts/freescale/imx8mq.dtsi.
-- Stefan
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig index 0dca8f27169e..e91841f8f8a2 100644 --- a/drivers/gpu/drm/mxsfb/Kconfig +++ b/drivers/gpu/drm/mxsfb/Kconfig @@ -5,7 +5,7 @@ config DRM_MXS Choose this option to select drivers for MXS FB devices
config DRM_MXSFB
- tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller"
- tristate "i.MX23/i.MX28/i.MX6SX/i.MX7 MXSFB LCD controller" depends on DRM && OF depends on COMMON_CLK select DRM_MXS
@@ -14,7 +14,7 @@ config DRM_MXSFB select DRM_KMS_CMA_HELPER select DRM_PANEL help
Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB
Choose this option if you have an i.MX23/i.MX28/i.MX6SX/i.MX7 MXSFB
LCD controller.
If M is selected the module will be called mxsfb.
The LCDIF present in the i.MX6SX has extra features compared to the i.MX28. It has however lost its IP version register, so no official version number is known. Bump the version to MXSFB_V6 following the i.MX version, in preparation for support for the additional features.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 7c9a041f5f6d..2316c12c5c42 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -35,6 +35,11 @@ enum mxsfb_devtype { MXSFB_V3, MXSFB_V4, + /* + * Starting at i.MX6 the hardware version register is gone, use the + * i.MX family number as the version. + */ + MXSFB_V6, };
static const struct mxsfb_devdata mxsfb_devdata[] = { @@ -52,6 +57,13 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18, }, + [MXSFB_V6] = { + .transfer_count = LCDC_V4_TRANSFER_COUNT, + .cur_buf = LCDC_V4_CUR_BUF, + .next_buf = LCDC_V4_NEXT_BUF, + .hs_wdth_mask = 0x3fff, + .hs_wdth_shift = 18, + }, };
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb) @@ -279,7 +291,7 @@ static struct drm_driver mxsfb_driver = { static const struct platform_device_id mxsfb_devtype[] = { { .name = "imx23-fb", .driver_data = MXSFB_V3, }, { .name = "imx28-fb", .driver_data = MXSFB_V4, }, - { .name = "imx6sx-fb", .driver_data = MXSFB_V4, }, + { .name = "imx6sx-fb", .driver_data = MXSFB_V6, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
On 2020-03-09 20:52, Laurent Pinchart wrote:
The LCDIF present in the i.MX6SX has extra features compared to the i.MX28. It has however lost its IP version register, so no official version number is known. Bump the version to MXSFB_V6 following the i.MX version, in preparation for support for the additional features.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 7c9a041f5f6d..2316c12c5c42 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -35,6 +35,11 @@ enum mxsfb_devtype { MXSFB_V3, MXSFB_V4,
- /*
* Starting at i.MX6 the hardware version register is gone, use the
* i.MX family number as the version.
*/
- MXSFB_V6,
};
static const struct mxsfb_devdata mxsfb_devdata[] = { @@ -52,6 +57,13 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18, },
- [MXSFB_V6] = {
.transfer_count = LCDC_V4_TRANSFER_COUNT,
.cur_buf = LCDC_V4_CUR_BUF,
.next_buf = LCDC_V4_NEXT_BUF,
.hs_wdth_mask = 0x3fff,
.hs_wdth_shift = 18,
- },
};
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb) @@ -279,7 +291,7 @@ static struct drm_driver mxsfb_driver = { static const struct platform_device_id mxsfb_devtype[] = { { .name = "imx23-fb", .driver_data = MXSFB_V3, }, { .name = "imx28-fb", .driver_data = MXSFB_V4, },
- { .name = "imx6sx-fb", .driver_data = MXSFB_V4, },
- { .name = "imx6sx-fb", .driver_data = MXSFB_V6, }, { /* sentinel */ }
}; MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
The mxsfb driver is only used by OF platforms. Drop non-OF support.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2316c12c5c42..ed8e3f7bc27c 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -130,7 +130,8 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) return 0; }
-static int mxsfb_load(struct drm_device *drm, unsigned long flags) +static int mxsfb_load(struct drm_device *drm, + const struct mxsfb_devdata *devdata) { struct platform_device *pdev = to_platform_device(drm->dev); struct mxsfb_drm_private *mxsfb; @@ -143,7 +144,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
mxsfb->drm = drm; drm->dev_private = mxsfb; - mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data]; + mxsfb->devdata = devdata;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mxsfb->base = devm_ioremap_resource(drm->dev, res); @@ -288,18 +289,10 @@ static struct drm_driver mxsfb_driver = { .minor = 0, };
-static const struct platform_device_id mxsfb_devtype[] = { - { .name = "imx23-fb", .driver_data = MXSFB_V3, }, - { .name = "imx28-fb", .driver_data = MXSFB_V4, }, - { .name = "imx6sx-fb", .driver_data = MXSFB_V6, }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, mxsfb_devtype); - static const struct of_device_id mxsfb_dt_ids[] = { - { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], }, - { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], }, - { .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devtype[2], }, + { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devdata[MXSFB_V3], }, + { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devdata[MXSFB_V4], }, + { .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devdata[MXSFB_V6], }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxsfb_dt_ids); @@ -314,14 +307,11 @@ static int mxsfb_probe(struct platform_device *pdev) if (!pdev->dev.of_node) return -ENODEV;
- if (of_id) - pdev->id_entry = of_id->data; - drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev); if (IS_ERR(drm)) return PTR_ERR(drm);
- ret = mxsfb_load(drm, 0); + ret = mxsfb_load(drm, of_id->data); if (ret) goto err_free;
@@ -375,7 +365,6 @@ static const struct dev_pm_ops mxsfb_pm_ops = { static struct platform_driver mxsfb_platform_driver = { .probe = mxsfb_probe, .remove = mxsfb_remove, - .id_table = mxsfb_devtype, .driver = { .name = "mxsfb", .of_match_table = mxsfb_dt_ids,
On 2020-03-09 20:52, Laurent Pinchart wrote:
The mxsfb driver is only used by OF platforms. Drop non-OF support.
Nice cleanup. Actually only supported of anyways due to the pdev->dev.of_node check.
Reviewed-by: Stefan Agner stefan@agner.ch
-- Stefan
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2316c12c5c42..ed8e3f7bc27c 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -130,7 +130,8 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) return 0; }
-static int mxsfb_load(struct drm_device *drm, unsigned long flags) +static int mxsfb_load(struct drm_device *drm,
const struct mxsfb_devdata *devdata)
{ struct platform_device *pdev = to_platform_device(drm->dev); struct mxsfb_drm_private *mxsfb; @@ -143,7 +144,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
mxsfb->drm = drm; drm->dev_private = mxsfb;
- mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
mxsfb->devdata = devdata;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mxsfb->base = devm_ioremap_resource(drm->dev, res);
@@ -288,18 +289,10 @@ static struct drm_driver mxsfb_driver = { .minor = 0, };
-static const struct platform_device_id mxsfb_devtype[] = {
- { .name = "imx23-fb", .driver_data = MXSFB_V3, },
- { .name = "imx28-fb", .driver_data = MXSFB_V4, },
- { .name = "imx6sx-fb", .driver_data = MXSFB_V6, },
- { /* sentinel */ }
-}; -MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
static const struct of_device_id mxsfb_dt_ids[] = {
- { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
- { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
- { .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devtype[2], },
- { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devdata[MXSFB_V3], },
- { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devdata[MXSFB_V4], },
- { .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devdata[MXSFB_V6], }, { /* sentinel */ }
}; MODULE_DEVICE_TABLE(of, mxsfb_dt_ids); @@ -314,14 +307,11 @@ static int mxsfb_probe(struct platform_device *pdev) if (!pdev->dev.of_node) return -ENODEV;
if (of_id)
pdev->id_entry = of_id->data;
drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev); if (IS_ERR(drm)) return PTR_ERR(drm);
ret = mxsfb_load(drm, 0);
- ret = mxsfb_load(drm, of_id->data); if (ret) goto err_free;
@@ -375,7 +365,6 @@ static const struct dev_pm_ops mxsfb_pm_ops = { static struct platform_driver mxsfb_platform_driver = { .probe = mxsfb_probe, .remove = mxsfb_remove,
- .id_table = mxsfb_devtype, .driver = { .name = "mxsfb", .of_match_table = mxsfb_dt_ids,
The mxsfb_set_pixel_fmt() function returns an error when the selected pixel format is unsupported. This can never happen, as such errors are caught by the DRM core. Remove the error check.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 640305fb1068..19b937b383cf 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -43,7 +43,7 @@ static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) }
/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ -static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) +static void mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) { struct drm_device *drm = mxsfb->drm; const u32 format = mxsfb->crtc.primary->state->fb->format->format; @@ -67,15 +67,10 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) /* Do not use packed pixels = one pixel per word instead. */ ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7); break; - default: - dev_err(drm->dev, "Unhandled pixel format %08x\n", format); - return -EINVAL; }
writel(ctrl1, mxsfb->base + LCDC_CTRL1); writel(ctrl, mxsfb->base + LCDC_CTRL); - - return 0; }
static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) @@ -218,9 +213,7 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) /* Clear the FIFOs */ writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
- err = mxsfb_set_pixel_fmt(mxsfb); - if (err) - return; + mxsfb_set_pixel_fmt(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
On 2020-03-09 20:52, Laurent Pinchart wrote:
The mxsfb_set_pixel_fmt() function returns an error when the selected pixel format is unsupported. This can never happen, as such errors are caught by the DRM core. Remove the error check.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
I see, we specify the supported plane formats when initializing the primary/overlay plane.
Reviewed-by: Stefan Agner stefan@agner.ch
-- Stefan
drivers/gpu/drm/mxsfb/mxsfb_kms.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 640305fb1068..19b937b383cf 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -43,7 +43,7 @@ static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) }
/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ -static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) +static void mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) { struct drm_device *drm = mxsfb->drm; const u32 format = mxsfb->crtc.primary->state->fb->format->format; @@ -67,15 +67,10 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) /* Do not use packed pixels = one pixel per word instead. */ ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7); break;
default:
dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
return -EINVAL;
}
writel(ctrl1, mxsfb->base + LCDC_CTRL1); writel(ctrl, mxsfb->base + LCDC_CTRL);
return 0;
}
static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) @@ -218,9 +213,7 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) /* Clear the FIFOs */ writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
- err = mxsfb_set_pixel_fmt(mxsfb);
- if (err)
return;
mxsfb_set_pixel_fmt(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
The mxsfb_set_pixel_fmt() and mxsfb_set_bus_fmt() functions both deal with format configuration, are always called in a row from mxsfb_crtc_mode_set_nofb(), and set fields from the LCDC_CTRL register. This requires a read-modify-update cycle in mxsfb_set_bus_fmt(). Make this more efficient by merging them together.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 47 +++++++++++++------------------ 1 file changed, 19 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 19b937b383cf..f81f8c222c13 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -42,13 +42,23 @@ static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) mxsfb->devdata->hs_wdth_shift; }
-/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ -static void mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) +/* + * Setup the MXSFB registers for decoding the pixels out of the framebuffer and + * outputting them on the bus. + */ +static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb) { struct drm_device *drm = mxsfb->drm; const u32 format = mxsfb->crtc.primary->state->fb->format->format; + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; u32 ctrl, ctrl1;
+ if (mxsfb->connector->display_info.num_bus_formats) + bus_format = mxsfb->connector->display_info.bus_formats[0]; + + DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n", + bus_format); + ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
/* CTRL1 contains IRQ config and status bits, preserve those. */ @@ -69,40 +79,23 @@ static void mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) break; }
- writel(ctrl1, mxsfb->base + LCDC_CTRL1); - writel(ctrl, mxsfb->base + LCDC_CTRL); -} - -static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) -{ - struct drm_device *drm = mxsfb->drm; - u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; - u32 reg; - - reg = readl(mxsfb->base + LCDC_CTRL); - - if (mxsfb->connector->display_info.num_bus_formats) - bus_format = mxsfb->connector->display_info.bus_formats[0]; - - DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n", - bus_format); - - reg &= ~CTRL_BUS_WIDTH_MASK; switch (bus_format) { case MEDIA_BUS_FMT_RGB565_1X16: - reg |= CTRL_BUS_WIDTH_16; + ctrl |= CTRL_BUS_WIDTH_16; break; case MEDIA_BUS_FMT_RGB666_1X18: - reg |= CTRL_BUS_WIDTH_18; + ctrl |= CTRL_BUS_WIDTH_18; break; case MEDIA_BUS_FMT_RGB888_1X24: - reg |= CTRL_BUS_WIDTH_24; + ctrl |= CTRL_BUS_WIDTH_24; break; default: dev_err(drm->dev, "Unknown media bus format %d\n", bus_format); break; } - writel(reg, mxsfb->base + LCDC_CTRL); + + writel(ctrl1, mxsfb->base + LCDC_CTRL1); + writel(ctrl, mxsfb->base + LCDC_CTRL); }
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb) @@ -213,7 +206,7 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) /* Clear the FIFOs */ writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
- mxsfb_set_pixel_fmt(mxsfb); + mxsfb_set_formats(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
@@ -255,8 +248,6 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
- mxsfb_set_bus_fmt(mxsfb); - /* Frame length in lines. */ writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
On 2020-03-09 20:52, Laurent Pinchart wrote:
The mxsfb_set_pixel_fmt() and mxsfb_set_bus_fmt() functions both deal with format configuration, are always called in a row from mxsfb_crtc_mode_set_nofb(), and set fields from the LCDC_CTRL register. This requires a read-modify-update cycle in mxsfb_set_bus_fmt(). Make this more efficient by merging them together.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_kms.c | 47 +++++++++++++------------------ 1 file changed, 19 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 19b937b383cf..f81f8c222c13 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -42,13 +42,23 @@ static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) mxsfb->devdata->hs_wdth_shift; }
-/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ -static void mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) +/*
- Setup the MXSFB registers for decoding the pixels out of the framebuffer and
- outputting them on the bus.
- */
+static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb) { struct drm_device *drm = mxsfb->drm; const u32 format = mxsfb->crtc.primary->state->fb->format->format;
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; u32 ctrl, ctrl1;
if (mxsfb->connector->display_info.num_bus_formats)
bus_format = mxsfb->connector->display_info.bus_formats[0];
DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
bus_format);
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
/* CTRL1 contains IRQ config and status bits, preserve those. */
@@ -69,40 +79,23 @@ static void mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) break; }
- writel(ctrl1, mxsfb->base + LCDC_CTRL1);
- writel(ctrl, mxsfb->base + LCDC_CTRL);
-}
-static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) -{
- struct drm_device *drm = mxsfb->drm;
- u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
- u32 reg;
- reg = readl(mxsfb->base + LCDC_CTRL);
- if (mxsfb->connector->display_info.num_bus_formats)
bus_format = mxsfb->connector->display_info.bus_formats[0];
- DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
bus_format);
- reg &= ~CTRL_BUS_WIDTH_MASK; switch (bus_format) { case MEDIA_BUS_FMT_RGB565_1X16:
reg |= CTRL_BUS_WIDTH_16;
break; case MEDIA_BUS_FMT_RGB666_1X18:ctrl |= CTRL_BUS_WIDTH_16;
reg |= CTRL_BUS_WIDTH_18;
break; case MEDIA_BUS_FMT_RGB888_1X24:ctrl |= CTRL_BUS_WIDTH_18;
reg |= CTRL_BUS_WIDTH_24;
break; default: dev_err(drm->dev, "Unknown media bus format %d\n", bus_format); break; }ctrl |= CTRL_BUS_WIDTH_24;
- writel(reg, mxsfb->base + LCDC_CTRL);
- writel(ctrl1, mxsfb->base + LCDC_CTRL1);
- writel(ctrl, mxsfb->base + LCDC_CTRL);
}
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb) @@ -213,7 +206,7 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) /* Clear the FIFOs */ writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
- mxsfb_set_pixel_fmt(mxsfb);
mxsfb_set_formats(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
@@ -255,8 +248,6 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
- mxsfb_set_bus_fmt(mxsfb);
- /* Frame length in lines. */ writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
The LCDIF in the i.MX6SX and i.MX7 have a second plane called the alpha plane. Support it.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 3 + drivers/gpu/drm/mxsfb/mxsfb_drv.h | 16 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 129 +++++++++++++++++++++++++---- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 22 +++++ 4 files changed, 149 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index ed8e3f7bc27c..ab3a212375f1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -49,6 +49,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V3_NEXT_BUF, .hs_wdth_mask = 0xff, .hs_wdth_shift = 24, + .has_overlay = false, }, [MXSFB_V4] = { .transfer_count = LCDC_V4_TRANSFER_COUNT, @@ -56,6 +57,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V4_NEXT_BUF, .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18, + .has_overlay = false, }, [MXSFB_V6] = { .transfer_count = LCDC_V4_TRANSFER_COUNT, @@ -63,6 +65,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V4_NEXT_BUF, .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18, + .has_overlay = true, }, };
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 607a6a5e6be3..399d23e91ed1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -16,11 +16,12 @@ struct clk;
struct mxsfb_devdata { - unsigned int transfer_count; - unsigned int cur_buf; - unsigned int next_buf; - unsigned int hs_wdth_mask; - unsigned int hs_wdth_shift; + unsigned int transfer_count; + unsigned int cur_buf; + unsigned int next_buf; + unsigned int hs_wdth_mask; + unsigned int hs_wdth_shift; + bool has_overlay; };
struct mxsfb_drm_private { @@ -32,7 +33,10 @@ struct mxsfb_drm_private { struct clk *clk_disp_axi;
struct drm_device *drm; - struct drm_plane plane; + struct { + struct drm_plane primary; + struct drm_plane overlay; + } planes; struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector *connector; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index f81f8c222c13..c9c394f7cbe2 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -169,9 +169,9 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE); }
-static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb) +static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane) { - struct drm_framebuffer *fb = mxsfb->plane.state->fb; + struct drm_framebuffer *fb = plane->state->fb; struct drm_gem_cma_object *gem;
if (!fb) @@ -206,6 +206,9 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) /* Clear the FIFOs */ writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
+ if (mxsfb->devdata->has_overlay) + writel(0, mxsfb->base + LCDC_AS_CTRL); + mxsfb_set_formats(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000); @@ -313,7 +316,7 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */ - paddr = mxsfb_get_fb_paddr(mxsfb); + paddr = mxsfb_get_fb_paddr(crtc->primary); if (paddr) { writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf); writel(paddr, mxsfb->base + mxsfb->devdata->next_buf); @@ -410,20 +413,85 @@ static int mxsfb_plane_atomic_check(struct drm_plane *plane, false, true); }
-static void mxsfb_plane_atomic_update(struct drm_plane *plane, - struct drm_plane_state *old_pstate) +static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_pstate) { struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); dma_addr_t paddr;
- paddr = mxsfb_get_fb_paddr(mxsfb); + paddr = mxsfb_get_fb_paddr(plane); if (paddr) writel(paddr, mxsfb->base + mxsfb->devdata->next_buf); }
-static const struct drm_plane_helper_funcs mxsfb_plane_helper_funcs = { +static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_pstate) +{ + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); + struct drm_plane_state *state = plane->state; + dma_addr_t paddr; + u32 ctrl; + + paddr = mxsfb_get_fb_paddr(plane); + if (!paddr) { + writel(0, mxsfb->base + LCDC_AS_CTRL); + return; + } + + /* + * HACK: The hardware seems to output 64 bytes of data of unknown + * origin, and then to proceed with the framebuffer. Until the reason + * is understood, live with the 16 initial invalid pixels on the first + * line and start 64 bytes within the framebuffer. + */ + paddr += 64; + + writel(paddr, mxsfb->base + LCDC_AS_NEXT_BUF); + + /* + * If the plane was previously disabled, write LCDC_AS_BUF as well to + * provide the first buffer. + */ + if (!old_pstate->fb) + writel(paddr, mxsfb->base + LCDC_AS_BUF); + + ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255); + + switch (state->fb->format->format) { + case DRM_FORMAT_XRGB4444: + ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE; + break; + case DRM_FORMAT_ARGB4444: + ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED; + break; + case DRM_FORMAT_XRGB1555: + ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE; + break; + case DRM_FORMAT_ARGB1555: + ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED; + break; + case DRM_FORMAT_RGB565: + ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE; + break; + case DRM_FORMAT_XRGB8888: + ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE; + break; + case DRM_FORMAT_ARGB8888: + ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED; + break; + } + + writel(ctrl, mxsfb->base + LCDC_AS_CTRL); +} + +static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = { + .atomic_check = mxsfb_plane_atomic_check, + .atomic_update = mxsfb_plane_primary_atomic_update, +}; + +static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = { .atomic_check = mxsfb_plane_atomic_check, - .atomic_update = mxsfb_plane_atomic_update, + .atomic_update = mxsfb_plane_overlay_atomic_update, };
static const struct drm_plane_funcs mxsfb_plane_funcs = { @@ -435,27 +503,58 @@ static const struct drm_plane_funcs mxsfb_plane_funcs = { .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, };
-static const uint32_t mxsfb_formats[] = { +static const uint32_t mxsfb_primary_plane_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, +}; + +static const uint32_t mxsfb_overlay_plane_formats[] = { + DRM_FORMAT_XRGB4444, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB565 + DRM_FORMAT_ARGB8888, };
+/* ----------------------------------------------------------------------------- + * Initialization + */ + int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb) { struct drm_encoder *encoder = &mxsfb->encoder; - struct drm_plane *plane = &mxsfb->plane; struct drm_crtc *crtc = &mxsfb->crtc; int ret;
- drm_plane_helper_add(plane, &mxsfb_plane_helper_funcs); - ret = drm_universal_plane_init(mxsfb->drm, plane, 0, &mxsfb_plane_funcs, - mxsfb_formats, ARRAY_SIZE(mxsfb_formats), + drm_plane_helper_add(&mxsfb->planes.primary, + &mxsfb_plane_primary_helper_funcs); + ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1, + &mxsfb_plane_funcs, + mxsfb_primary_plane_formats, + ARRAY_SIZE(mxsfb_primary_plane_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) return ret;
+ if (mxsfb->devdata->has_overlay) { + drm_plane_helper_add(&mxsfb->planes.overlay, + &mxsfb_plane_overlay_helper_funcs); + ret = drm_universal_plane_init(mxsfb->drm, + &mxsfb->planes.overlay, 1, + &mxsfb_plane_funcs, + mxsfb_overlay_plane_formats, + ARRAY_SIZE(mxsfb_overlay_plane_formats), + NULL, DRM_PLANE_TYPE_OVERLAY, + NULL); + if (ret) + return ret; + } + drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs); - ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, plane, NULL, + ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, + &mxsfb->planes.primary, NULL, &mxsfb_crtc_funcs, NULL); if (ret) return ret; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 8ebb52bb1b46..55d28a27f912 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -27,6 +27,11 @@ #define LCDC_VDCTRL4 0xb0 #define LCDC_V4_DEBUG0 0x1d0 #define LCDC_V3_DEBUG0 0x1f0 +#define LCDC_AS_CTRL 0x210 +#define LCDC_AS_BUF 0x220 +#define LCDC_AS_NEXT_BUF 0x230 +#define LCDC_AS_CLRKEYLOW 0x240 +#define LCDC_AS_CLRKEYHIGH 0x250
#define CTRL_SFTRST BIT(31) #define CTRL_CLKGATE BIT(30) @@ -90,6 +95,23 @@ #define DEBUG0_HSYNC BIT(26) #define DEBUG0_VSYNC BIT(25)
+#define AS_CTRL_PS_DISABLE BIT(23) +#define AS_CTRL_ALPHA_INVERT BIT(20) +#define AS_CTRL_ALPHA(a) (((a) & 0xff) << 8) +#define AS_CTRL_FORMAT_RGB565 (0xe << 4) +#define AS_CTRL_FORMAT_RGB444 (0xd << 4) +#define AS_CTRL_FORMAT_RGB555 (0xc << 4) +#define AS_CTRL_FORMAT_ARGB4444 (0x9 << 4) +#define AS_CTRL_FORMAT_ARGB1555 (0x8 << 4) +#define AS_CTRL_FORMAT_RGB888 (0x4 << 4) +#define AS_CTRL_FORMAT_ARGB8888 (0x0 << 4) +#define AS_CTRL_ENABLE_COLORKEY BIT(3) +#define AS_CTRL_ALPHA_CTRL_ROP (3 << 1) +#define AS_CTRL_ALPHA_CTRL_MULTIPLY (2 << 1) +#define AS_CTRL_ALPHA_CTRL_OVERRIDE (1 << 1) +#define AS_CTRL_ALPHA_CTRL_EMBEDDED (0 << 1) +#define AS_CTRL_AS_ENABLE BIT(0) + #define MXSFB_MIN_XRES 120 #define MXSFB_MIN_YRES 120 #define MXSFB_MAX_XRES 0xffff
On 2020-03-09 20:52, Laurent Pinchart wrote:
The LCDIF in the i.MX6SX and i.MX7 have a second plane called the alpha plane. Support it.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 3 + drivers/gpu/drm/mxsfb/mxsfb_drv.h | 16 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 129 +++++++++++++++++++++++++---- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 22 +++++ 4 files changed, 149 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index ed8e3f7bc27c..ab3a212375f1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -49,6 +49,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V3_NEXT_BUF, .hs_wdth_mask = 0xff, .hs_wdth_shift = 24,
}, [MXSFB_V4] = { .transfer_count = LCDC_V4_TRANSFER_COUNT,.has_overlay = false,
@@ -56,6 +57,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V4_NEXT_BUF, .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18,
}, [MXSFB_V6] = { .transfer_count = LCDC_V4_TRANSFER_COUNT,.has_overlay = false,
@@ -63,6 +65,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V4_NEXT_BUF, .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18,
},.has_overlay = true,
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 607a6a5e6be3..399d23e91ed1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -16,11 +16,12 @@ struct clk;
struct mxsfb_devdata {
- unsigned int transfer_count;
- unsigned int cur_buf;
- unsigned int next_buf;
- unsigned int hs_wdth_mask;
- unsigned int hs_wdth_shift;
- unsigned int transfer_count;
- unsigned int cur_buf;
- unsigned int next_buf;
- unsigned int hs_wdth_mask;
- unsigned int hs_wdth_shift;
This seems an unrelated change, can you split that out?
- bool has_overlay;
};
struct mxsfb_drm_private { @@ -32,7 +33,10 @@ struct mxsfb_drm_private { struct clk *clk_disp_axi;
struct drm_device *drm;
- struct drm_plane plane;
- struct {
struct drm_plane primary;
struct drm_plane overlay;
- } planes; struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector *connector;
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index f81f8c222c13..c9c394f7cbe2 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -169,9 +169,9 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE); }
-static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb) +static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane) {
- struct drm_framebuffer *fb = mxsfb->plane.state->fb;
struct drm_framebuffer *fb = plane->state->fb; struct drm_gem_cma_object *gem;
if (!fb)
@@ -206,6 +206,9 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) /* Clear the FIFOs */ writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
if (mxsfb->devdata->has_overlay)
writel(0, mxsfb->base + LCDC_AS_CTRL);
mxsfb_set_formats(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
@@ -313,7 +316,7 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */
- paddr = mxsfb_get_fb_paddr(mxsfb);
- paddr = mxsfb_get_fb_paddr(crtc->primary); if (paddr) { writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf); writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
@@ -410,20 +413,85 @@ static int mxsfb_plane_atomic_check(struct drm_plane *plane, false, true); }
-static void mxsfb_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
+static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
{ struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); dma_addr_t paddr;
- paddr = mxsfb_get_fb_paddr(mxsfb);
- paddr = mxsfb_get_fb_paddr(plane); if (paddr) writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
}
-static const struct drm_plane_helper_funcs mxsfb_plane_helper_funcs = { +static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
+{
- struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
- struct drm_plane_state *state = plane->state;
- dma_addr_t paddr;
- u32 ctrl;
- paddr = mxsfb_get_fb_paddr(plane);
- if (!paddr) {
writel(0, mxsfb->base + LCDC_AS_CTRL);
return;
- }
- /*
* HACK: The hardware seems to output 64 bytes of data of unknown
* origin, and then to proceed with the framebuffer. Until the reason
* is understood, live with the 16 initial invalid pixels on the first
* line and start 64 bytes within the framebuffer.
*/
- paddr += 64;
Wow, that is interesting. Did you check/find downstream drivers which support this feature and have some kind of hint/work around?
Also adding NXP folks, maybe you have some idea?
Otherwise I guess we live with this.
-- Stefan
- writel(paddr, mxsfb->base + LCDC_AS_NEXT_BUF);
- /*
* If the plane was previously disabled, write LCDC_AS_BUF as well to
* provide the first buffer.
*/
- if (!old_pstate->fb)
writel(paddr, mxsfb->base + LCDC_AS_BUF);
- ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);
- switch (state->fb->format->format) {
- case DRM_FORMAT_XRGB4444:
ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_ARGB4444:
ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
- case DRM_FORMAT_XRGB1555:
ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_ARGB1555:
ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
- case DRM_FORMAT_RGB565:
ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_XRGB8888:
ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_ARGB8888:
ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
- }
- writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
+}
+static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
- .atomic_check = mxsfb_plane_atomic_check,
- .atomic_update = mxsfb_plane_primary_atomic_update,
+};
+static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = { .atomic_check = mxsfb_plane_atomic_check,
- .atomic_update = mxsfb_plane_atomic_update,
- .atomic_update = mxsfb_plane_overlay_atomic_update,
};
static const struct drm_plane_funcs mxsfb_plane_funcs = { @@ -435,27 +503,58 @@ static const struct drm_plane_funcs mxsfb_plane_funcs = { .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, };
-static const uint32_t mxsfb_formats[] = { +static const uint32_t mxsfb_primary_plane_formats[] = {
- DRM_FORMAT_RGB565,
- DRM_FORMAT_XRGB8888,
+};
+static const uint32_t mxsfb_overlay_plane_formats[] = {
- DRM_FORMAT_XRGB4444,
- DRM_FORMAT_ARGB4444,
- DRM_FORMAT_XRGB1555,
- DRM_FORMAT_ARGB1555,
- DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
- DRM_FORMAT_RGB565
- DRM_FORMAT_ARGB8888,
};
+/*
- Initialization
- */
int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb) { struct drm_encoder *encoder = &mxsfb->encoder;
struct drm_plane *plane = &mxsfb->plane; struct drm_crtc *crtc = &mxsfb->crtc; int ret;
drm_plane_helper_add(plane, &mxsfb_plane_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm, plane, 0, &mxsfb_plane_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats),
drm_plane_helper_add(&mxsfb->planes.primary,
&mxsfb_plane_primary_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
&mxsfb_plane_funcs,
mxsfb_primary_plane_formats,
ARRAY_SIZE(mxsfb_primary_plane_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) return ret;
if (mxsfb->devdata->has_overlay) {
drm_plane_helper_add(&mxsfb->planes.overlay,
&mxsfb_plane_overlay_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm,
&mxsfb->planes.overlay, 1,
&mxsfb_plane_funcs,
mxsfb_overlay_plane_formats,
ARRAY_SIZE(mxsfb_overlay_plane_formats),
NULL, DRM_PLANE_TYPE_OVERLAY,
NULL);
if (ret)
return ret;
}
drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
- ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, plane, NULL,
- ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
if (ret) return ret;&mxsfb->planes.primary, NULL, &mxsfb_crtc_funcs, NULL);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 8ebb52bb1b46..55d28a27f912 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -27,6 +27,11 @@ #define LCDC_VDCTRL4 0xb0 #define LCDC_V4_DEBUG0 0x1d0 #define LCDC_V3_DEBUG0 0x1f0 +#define LCDC_AS_CTRL 0x210 +#define LCDC_AS_BUF 0x220 +#define LCDC_AS_NEXT_BUF 0x230 +#define LCDC_AS_CLRKEYLOW 0x240 +#define LCDC_AS_CLRKEYHIGH 0x250
#define CTRL_SFTRST BIT(31) #define CTRL_CLKGATE BIT(30) @@ -90,6 +95,23 @@ #define DEBUG0_HSYNC BIT(26) #define DEBUG0_VSYNC BIT(25)
+#define AS_CTRL_PS_DISABLE BIT(23) +#define AS_CTRL_ALPHA_INVERT BIT(20) +#define AS_CTRL_ALPHA(a) (((a) & 0xff) << 8) +#define AS_CTRL_FORMAT_RGB565 (0xe << 4) +#define AS_CTRL_FORMAT_RGB444 (0xd << 4) +#define AS_CTRL_FORMAT_RGB555 (0xc << 4) +#define AS_CTRL_FORMAT_ARGB4444 (0x9 << 4) +#define AS_CTRL_FORMAT_ARGB1555 (0x8 << 4) +#define AS_CTRL_FORMAT_RGB888 (0x4 << 4) +#define AS_CTRL_FORMAT_ARGB8888 (0x0 << 4) +#define AS_CTRL_ENABLE_COLORKEY BIT(3) +#define AS_CTRL_ALPHA_CTRL_ROP (3 << 1) +#define AS_CTRL_ALPHA_CTRL_MULTIPLY (2 << 1) +#define AS_CTRL_ALPHA_CTRL_OVERRIDE (1 << 1) +#define AS_CTRL_ALPHA_CTRL_EMBEDDED (0 << 1) +#define AS_CTRL_AS_ENABLE BIT(0)
#define MXSFB_MIN_XRES 120 #define MXSFB_MIN_YRES 120 #define MXSFB_MAX_XRES 0xffff
Hi Stefan,
On Tue, Mar 24, 2020 at 12:48:17AM +0100, Stefan Agner wrote:
On 2020-03-09 20:52, Laurent Pinchart wrote:
The LCDIF in the i.MX6SX and i.MX7 have a second plane called the alpha plane. Support it.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 3 + drivers/gpu/drm/mxsfb/mxsfb_drv.h | 16 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 129 +++++++++++++++++++++++++---- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 22 +++++ 4 files changed, 149 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index ed8e3f7bc27c..ab3a212375f1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -49,6 +49,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V3_NEXT_BUF, .hs_wdth_mask = 0xff, .hs_wdth_shift = 24,
}, [MXSFB_V4] = { .transfer_count = LCDC_V4_TRANSFER_COUNT,.has_overlay = false,
@@ -56,6 +57,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V4_NEXT_BUF, .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18,
}, [MXSFB_V6] = { .transfer_count = LCDC_V4_TRANSFER_COUNT,.has_overlay = false,
@@ -63,6 +65,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = { .next_buf = LCDC_V4_NEXT_BUF, .hs_wdth_mask = 0x3fff, .hs_wdth_shift = 18,
},.has_overlay = true,
};
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 607a6a5e6be3..399d23e91ed1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -16,11 +16,12 @@ struct clk;
struct mxsfb_devdata {
- unsigned int transfer_count;
- unsigned int cur_buf;
- unsigned int next_buf;
- unsigned int hs_wdth_mask;
- unsigned int hs_wdth_shift;
- unsigned int transfer_count;
- unsigned int cur_buf;
- unsigned int next_buf;
- unsigned int hs_wdth_mask;
- unsigned int hs_wdth_shift;
This seems an unrelated change, can you split that out?
OK.
- bool has_overlay;
};
struct mxsfb_drm_private { @@ -32,7 +33,10 @@ struct mxsfb_drm_private { struct clk *clk_disp_axi;
struct drm_device *drm;
- struct drm_plane plane;
- struct {
struct drm_plane primary;
struct drm_plane overlay;
- } planes; struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector *connector;
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index f81f8c222c13..c9c394f7cbe2 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -169,9 +169,9 @@ static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb) return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE); }
-static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb) +static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane) {
- struct drm_framebuffer *fb = mxsfb->plane.state->fb;
struct drm_framebuffer *fb = plane->state->fb; struct drm_gem_cma_object *gem;
if (!fb)
@@ -206,6 +206,9 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) /* Clear the FIFOs */ writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
if (mxsfb->devdata->has_overlay)
writel(0, mxsfb->base + LCDC_AS_CTRL);
mxsfb_set_formats(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
@@ -313,7 +316,7 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */
- paddr = mxsfb_get_fb_paddr(mxsfb);
- paddr = mxsfb_get_fb_paddr(crtc->primary); if (paddr) { writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf); writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
@@ -410,20 +413,85 @@ static int mxsfb_plane_atomic_check(struct drm_plane *plane, false, true); }
-static void mxsfb_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
+static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
{ struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); dma_addr_t paddr;
- paddr = mxsfb_get_fb_paddr(mxsfb);
- paddr = mxsfb_get_fb_paddr(plane); if (paddr) writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
}
-static const struct drm_plane_helper_funcs mxsfb_plane_helper_funcs = { +static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
+{
- struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
- struct drm_plane_state *state = plane->state;
- dma_addr_t paddr;
- u32 ctrl;
- paddr = mxsfb_get_fb_paddr(plane);
- if (!paddr) {
writel(0, mxsfb->base + LCDC_AS_CTRL);
return;
- }
- /*
* HACK: The hardware seems to output 64 bytes of data of unknown
* origin, and then to proceed with the framebuffer. Until the reason
* is understood, live with the 16 initial invalid pixels on the first
* line and start 64 bytes within the framebuffer.
*/
- paddr += 64;
Wow, that is interesting. Did you check/find downstream drivers which support this feature and have some kind of hint/work around?
Also adding NXP folks, maybe you have some idea?
I've had a look at the fbdev driver (drivers/video/fbdev/mxsfb.c) in the 4.14-2.3.x-imx branch of https://github.com/Freescale/linux-fslc.git. When enabling the overlay, the driver first disables the LCDIF completely, clears the FIFOs, then enable the overlay, clears the FIFOs again, and reenables the LCDIF. I really hope that whole sequence isn't needed, as that would reduce the usability of the LCDIF even more, but it looks quite like a FIFO issue. Feedback from NXP would be helpful, the i.MX7D reference manual doesn't decribe how to operate the overlay plane, it only lists the related registers.
Otherwise I guess we live with this.
- writel(paddr, mxsfb->base + LCDC_AS_NEXT_BUF);
- /*
* If the plane was previously disabled, write LCDC_AS_BUF as well to
* provide the first buffer.
*/
- if (!old_pstate->fb)
writel(paddr, mxsfb->base + LCDC_AS_BUF);
- ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);
- switch (state->fb->format->format) {
- case DRM_FORMAT_XRGB4444:
ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_ARGB4444:
ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
- case DRM_FORMAT_XRGB1555:
ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_ARGB1555:
ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
- case DRM_FORMAT_RGB565:
ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_XRGB8888:
ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
- case DRM_FORMAT_ARGB8888:
ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
- }
- writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
+}
+static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
- .atomic_check = mxsfb_plane_atomic_check,
- .atomic_update = mxsfb_plane_primary_atomic_update,
+};
+static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = { .atomic_check = mxsfb_plane_atomic_check,
- .atomic_update = mxsfb_plane_atomic_update,
- .atomic_update = mxsfb_plane_overlay_atomic_update,
};
static const struct drm_plane_funcs mxsfb_plane_funcs = { @@ -435,27 +503,58 @@ static const struct drm_plane_funcs mxsfb_plane_funcs = { .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, };
-static const uint32_t mxsfb_formats[] = { +static const uint32_t mxsfb_primary_plane_formats[] = {
- DRM_FORMAT_RGB565,
- DRM_FORMAT_XRGB8888,
+};
+static const uint32_t mxsfb_overlay_plane_formats[] = {
- DRM_FORMAT_XRGB4444,
- DRM_FORMAT_ARGB4444,
- DRM_FORMAT_XRGB1555,
- DRM_FORMAT_ARGB1555,
- DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
- DRM_FORMAT_RGB565
- DRM_FORMAT_ARGB8888,
};
+/*
- Initialization
- */
int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb) { struct drm_encoder *encoder = &mxsfb->encoder;
struct drm_plane *plane = &mxsfb->plane; struct drm_crtc *crtc = &mxsfb->crtc; int ret;
drm_plane_helper_add(plane, &mxsfb_plane_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm, plane, 0, &mxsfb_plane_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats),
drm_plane_helper_add(&mxsfb->planes.primary,
&mxsfb_plane_primary_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
&mxsfb_plane_funcs,
mxsfb_primary_plane_formats,
ARRAY_SIZE(mxsfb_primary_plane_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) return ret;
if (mxsfb->devdata->has_overlay) {
drm_plane_helper_add(&mxsfb->planes.overlay,
&mxsfb_plane_overlay_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm,
&mxsfb->planes.overlay, 1,
&mxsfb_plane_funcs,
mxsfb_overlay_plane_formats,
ARRAY_SIZE(mxsfb_overlay_plane_formats),
NULL, DRM_PLANE_TYPE_OVERLAY,
NULL);
if (ret)
return ret;
}
drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
- ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, plane, NULL,
- ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
if (ret) return ret;&mxsfb->planes.primary, NULL, &mxsfb_crtc_funcs, NULL);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h index 8ebb52bb1b46..55d28a27f912 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -27,6 +27,11 @@ #define LCDC_VDCTRL4 0xb0 #define LCDC_V4_DEBUG0 0x1d0 #define LCDC_V3_DEBUG0 0x1f0 +#define LCDC_AS_CTRL 0x210 +#define LCDC_AS_BUF 0x220 +#define LCDC_AS_NEXT_BUF 0x230 +#define LCDC_AS_CLRKEYLOW 0x240 +#define LCDC_AS_CLRKEYHIGH 0x250
#define CTRL_SFTRST BIT(31) #define CTRL_CLKGATE BIT(30) @@ -90,6 +95,23 @@ #define DEBUG0_HSYNC BIT(26) #define DEBUG0_VSYNC BIT(25)
+#define AS_CTRL_PS_DISABLE BIT(23) +#define AS_CTRL_ALPHA_INVERT BIT(20) +#define AS_CTRL_ALPHA(a) (((a) & 0xff) << 8) +#define AS_CTRL_FORMAT_RGB565 (0xe << 4) +#define AS_CTRL_FORMAT_RGB444 (0xd << 4) +#define AS_CTRL_FORMAT_RGB555 (0xc << 4) +#define AS_CTRL_FORMAT_ARGB4444 (0x9 << 4) +#define AS_CTRL_FORMAT_ARGB1555 (0x8 << 4) +#define AS_CTRL_FORMAT_RGB888 (0x4 << 4) +#define AS_CTRL_FORMAT_ARGB8888 (0x0 << 4) +#define AS_CTRL_ENABLE_COLORKEY BIT(3) +#define AS_CTRL_ALPHA_CTRL_ROP (3 << 1) +#define AS_CTRL_ALPHA_CTRL_MULTIPLY (2 << 1) +#define AS_CTRL_ALPHA_CTRL_OVERRIDE (1 << 1) +#define AS_CTRL_ALPHA_CTRL_EMBEDDED (0 << 1) +#define AS_CTRL_AS_ENABLE BIT(0)
#define MXSFB_MIN_XRES 120 #define MXSFB_MIN_YRES 120 #define MXSFB_MAX_XRES 0xffff
Hello,
Gentle ping.
On Mon, Mar 09, 2020 at 09:51:55PM +0200, Laurent Pinchart wrote:
Hello,
This patch series adds i.MX7 support to the mxsfb driver. The eLCDIF instance found in the i.MX7 is backward-compatible with the already supported LCDC v4, but has extended features amongst which the most notable one is a second plane.
The first 9 patches (01/21 to 09/21) contain miscellaneous cleanups and refactoring to prepare for what is to come. Patch 10/21 starts the real work with removal of the DRM simple display pipeline helper, as it doesn't support multiple planes. The next patch (11/21) is an additional cleanup.
Patches 12/21 to 14/21 fix vblank handling that I found to be broken when testing on my device. Patch 15/21 then performs an additional small cleanup, and patch 16/21 starts official support for i.MX7 by mentioning it in Kconfig.
Patch 17/21 adds a new device model for the i.MX6SX and i.MX7 eLCDIF. After three additional cleanups in patches 18/21 to 20/21, patch 21/21 finally adds support for the second plane.
The code is based on drm-misc-next and has been tested on an i.MX7D platform with a DPI panel.
Laurent Pinchart (21): drm: mxsfb: Remove fbdev leftovers drm: mxsfb: Use drm_panel_bridge drm: mxsfb: Use BIT() macro to define register bitfields drm: mxsfb: Remove unused macros from mxsfb_regs.h drm: mxsfb: Clarify format and bus width configuration drm: mxsfb: Pass mxsfb_drm_private pointer to mxsfb_reset_block() drm: mxsfb: Use LCDC_CTRL register name explicitly drm: mxsfb: Remove register definitions from mxsfb_crtc.c drm: mxsfb: Remove unneeded includes drm: mxsfb: Stop using DRM simple display pipeline helper drm: mxsfb: Rename mxsfb_crtc.c to mxsfb_kms.c drm: mxsfb: Move vblank event arm to CRTC .atomic_flush() drm: mxsfb: Don't touch AXI clock in IRQ context drm: mxsfb: Enable vblank handling drm: mxsfb: Remove mxsfb_devdata unused fields drm: mxsfb: Add i.MX7 to the list of supported SoCs in Kconfig drm: mxsfb: Update internal IP version number for i.MX6SX drm: mxsfb: Drop non-OF support drm: mxsfb: Turn mxsfb_set_pixel_fmt() into a void function drm: mxsfb: Merge mxsfb_set_pixel_fmt() and mxsfb_set_bus_fmt() drm: mxsfb: Support the alpha plane
drivers/gpu/drm/mxsfb/Kconfig | 4 +- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 343 ----------------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 246 ++++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 42 ++- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 565 +++++++++++++++++++++++++++++ drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ----- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 107 +++--- 8 files changed, 730 insertions(+), 678 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_crtc.c create mode 100644 drivers/gpu/drm/mxsfb/mxsfb_kms.c delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
Hi, On Mon, Mar 09, 2020 at 09:51:55PM +0200, Laurent Pinchart wrote:
Hello,
This patch series adds i.MX7 support to the mxsfb driver. The eLCDIF instance found in the i.MX7 is backward-compatible with the already supported LCDC v4, but has extended features amongst which the most notable one is a second plane.
The first 9 patches (01/21 to 09/21) contain miscellaneous cleanups and refactoring to prepare for what is to come. Patch 10/21 starts the real work with removal of the DRM simple display pipeline helper, as it doesn't support multiple planes. The next patch (11/21) is an additional cleanup.
Patches 12/21 to 14/21 fix vblank handling that I found to be broken when testing on my device. Patch 15/21 then performs an additional small cleanup, and patch 16/21 starts official support for i.MX7 by mentioning it in Kconfig.
Patch 17/21 adds a new device model for the i.MX6SX and i.MX7 eLCDIF. After three additional cleanups in patches 18/21 to 20/21, patch 21/21 finally adds support for the second plane.
The code is based on drm-misc-next and has been tested on an i.MX7D platform with a DPI panel.
I've tested this with an imx8mq using both the current fsl,imx28-lcdif and fsl,imx6sx-lcdif with the nwl DSI bridge and a dsi panel and both worked well. Cheers, -- Guido
Laurent Pinchart (21): drm: mxsfb: Remove fbdev leftovers drm: mxsfb: Use drm_panel_bridge drm: mxsfb: Use BIT() macro to define register bitfields drm: mxsfb: Remove unused macros from mxsfb_regs.h drm: mxsfb: Clarify format and bus width configuration drm: mxsfb: Pass mxsfb_drm_private pointer to mxsfb_reset_block() drm: mxsfb: Use LCDC_CTRL register name explicitly drm: mxsfb: Remove register definitions from mxsfb_crtc.c drm: mxsfb: Remove unneeded includes drm: mxsfb: Stop using DRM simple display pipeline helper drm: mxsfb: Rename mxsfb_crtc.c to mxsfb_kms.c drm: mxsfb: Move vblank event arm to CRTC .atomic_flush() drm: mxsfb: Don't touch AXI clock in IRQ context drm: mxsfb: Enable vblank handling drm: mxsfb: Remove mxsfb_devdata unused fields drm: mxsfb: Add i.MX7 to the list of supported SoCs in Kconfig drm: mxsfb: Update internal IP version number for i.MX6SX drm: mxsfb: Drop non-OF support drm: mxsfb: Turn mxsfb_set_pixel_fmt() into a void function drm: mxsfb: Merge mxsfb_set_pixel_fmt() and mxsfb_set_bus_fmt() drm: mxsfb: Support the alpha plane
drivers/gpu/drm/mxsfb/Kconfig | 4 +- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 343 ----------------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 246 ++++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 42 ++- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 565 +++++++++++++++++++++++++++++ drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ----- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 107 +++--- 8 files changed, 730 insertions(+), 678 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_crtc.c create mode 100644 drivers/gpu/drm/mxsfb/mxsfb_kms.c delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
-- Regards,
Laurent Pinchart
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi Laurent,
On 2020-03-09 20:51, Laurent Pinchart wrote:
Hello,
This patch series adds i.MX7 support to the mxsfb driver. The eLCDIF instance found in the i.MX7 is backward-compatible with the already supported LCDC v4, but has extended features amongst which the most notable one is a second plane.
The first 9 patches (01/21 to 09/21) contain miscellaneous cleanups and refactoring to prepare for what is to come. Patch 10/21 starts the real work with removal of the DRM simple display pipeline helper, as it doesn't support multiple planes. The next patch (11/21) is an additional cleanup.
Patches 12/21 to 14/21 fix vblank handling that I found to be broken when testing on my device. Patch 15/21 then performs an additional small cleanup, and patch 16/21 starts official support for i.MX7 by mentioning it in Kconfig.
Patch 17/21 adds a new device model for the i.MX6SX and i.MX7 eLCDIF. After three additional cleanups in patches 18/21 to 20/21, patch 21/21 finally adds support for the second plane.
The code is based on drm-misc-next and has been tested on an i.MX7D platform with a DPI panel.
Did you plan to update this series? I guess some of the changes I could already merge now.
-- Stefan
Laurent Pinchart (21): drm: mxsfb: Remove fbdev leftovers drm: mxsfb: Use drm_panel_bridge drm: mxsfb: Use BIT() macro to define register bitfields drm: mxsfb: Remove unused macros from mxsfb_regs.h drm: mxsfb: Clarify format and bus width configuration drm: mxsfb: Pass mxsfb_drm_private pointer to mxsfb_reset_block() drm: mxsfb: Use LCDC_CTRL register name explicitly drm: mxsfb: Remove register definitions from mxsfb_crtc.c drm: mxsfb: Remove unneeded includes drm: mxsfb: Stop using DRM simple display pipeline helper drm: mxsfb: Rename mxsfb_crtc.c to mxsfb_kms.c drm: mxsfb: Move vblank event arm to CRTC .atomic_flush() drm: mxsfb: Don't touch AXI clock in IRQ context drm: mxsfb: Enable vblank handling drm: mxsfb: Remove mxsfb_devdata unused fields drm: mxsfb: Add i.MX7 to the list of supported SoCs in Kconfig drm: mxsfb: Update internal IP version number for i.MX6SX drm: mxsfb: Drop non-OF support drm: mxsfb: Turn mxsfb_set_pixel_fmt() into a void function drm: mxsfb: Merge mxsfb_set_pixel_fmt() and mxsfb_set_bus_fmt() drm: mxsfb: Support the alpha plane
drivers/gpu/drm/mxsfb/Kconfig | 4 +- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 343 ----------------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 246 ++++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 42 ++- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 565 +++++++++++++++++++++++++++++ drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ----- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 107 +++--- 8 files changed, 730 insertions(+), 678 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_crtc.c create mode 100644 drivers/gpu/drm/mxsfb/mxsfb_kms.c delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
Hi Stefan,
On Tue, May 26, 2020 at 05:13:42PM +0200, Stefan Agner wrote:
On 2020-03-09 20:51, Laurent Pinchart wrote:
Hello,
This patch series adds i.MX7 support to the mxsfb driver. The eLCDIF instance found in the i.MX7 is backward-compatible with the already supported LCDC v4, but has extended features amongst which the most notable one is a second plane.
The first 9 patches (01/21 to 09/21) contain miscellaneous cleanups and refactoring to prepare for what is to come. Patch 10/21 starts the real work with removal of the DRM simple display pipeline helper, as it doesn't support multiple planes. The next patch (11/21) is an additional cleanup.
Patches 12/21 to 14/21 fix vblank handling that I found to be broken when testing on my device. Patch 15/21 then performs an additional small cleanup, and patch 16/21 starts official support for i.MX7 by mentioning it in Kconfig.
Patch 17/21 adds a new device model for the i.MX6SX and i.MX7 eLCDIF. After three additional cleanups in patches 18/21 to 20/21, patch 21/21 finally adds support for the second plane.
The code is based on drm-misc-next and has been tested on an i.MX7D platform with a DPI panel.
Did you plan to update this series? I guess some of the changes I could already merge now.
I'm resuming work on it now, will incorporate your review comments and send a v2. Feel free to apply some of the changes already if you want to.
Laurent Pinchart (21): drm: mxsfb: Remove fbdev leftovers drm: mxsfb: Use drm_panel_bridge drm: mxsfb: Use BIT() macro to define register bitfields drm: mxsfb: Remove unused macros from mxsfb_regs.h drm: mxsfb: Clarify format and bus width configuration drm: mxsfb: Pass mxsfb_drm_private pointer to mxsfb_reset_block() drm: mxsfb: Use LCDC_CTRL register name explicitly drm: mxsfb: Remove register definitions from mxsfb_crtc.c drm: mxsfb: Remove unneeded includes drm: mxsfb: Stop using DRM simple display pipeline helper drm: mxsfb: Rename mxsfb_crtc.c to mxsfb_kms.c drm: mxsfb: Move vblank event arm to CRTC .atomic_flush() drm: mxsfb: Don't touch AXI clock in IRQ context drm: mxsfb: Enable vblank handling drm: mxsfb: Remove mxsfb_devdata unused fields drm: mxsfb: Add i.MX7 to the list of supported SoCs in Kconfig drm: mxsfb: Update internal IP version number for i.MX6SX drm: mxsfb: Drop non-OF support drm: mxsfb: Turn mxsfb_set_pixel_fmt() into a void function drm: mxsfb: Merge mxsfb_set_pixel_fmt() and mxsfb_set_bus_fmt() drm: mxsfb: Support the alpha plane
drivers/gpu/drm/mxsfb/Kconfig | 4 +- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 343 ----------------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 246 ++++--------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 42 ++- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 565 +++++++++++++++++++++++++++++ drivers/gpu/drm/mxsfb/mxsfb_out.c | 99 ----- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 107 +++--- 8 files changed, 730 insertions(+), 678 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_crtc.c create mode 100644 drivers/gpu/drm/mxsfb/mxsfb_kms.c delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
dri-devel@lists.freedesktop.org