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 10 patches (01/22 to 10/22) contain miscellaneous cleanups and refactoring to prepare for what is to come. Patch 11/22 starts the real work with removal of the DRM simple display pipeline helper, as it doesn't support multiple planes. The next patch (12/22) is an additional cleanup.
Patches 13/22 to 15/22 fix vblank handling that I found to be broken when testing on my device. Patch 16/22 then performs an additional small cleanup, and patch 17/22 starts official support for i.MX7 by mentioning it in Kconfig.
Patch 18/22 adds a new device model for the i.MX6SX and i.MX7 eLCDIF. After three additional cleanups in patches 19/22 to 21/22, patch 22/22 finally adds support for the second plane.
The second plane suffers from an issue whose root cause hasn't been found, which results in the first 64 bytes of the first line to contain data of unknown origin. Help from NXP to diagnose this issue would be useful and appreciated.
Compared to v1, the patches incorporate various review feedback, without major modifications. See individual changelogs for details.
The code is based on v5.7-rc7 and has been tested on an i.MX7D platform with a DPI panel. There is no conflict between v5.7-rc7 and drm-misc-next for the mxsfb driver.
Laurent Pinchart (22): 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: Rename mxsfb_crtc.c to mxsfb_kms.c drm: mxsfb: Stop using DRM simple display pipeline helper 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 and i.MX8M 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: Remove unnecessary spaces after tab drm: mxsfb: Support the alpha plane
drivers/gpu/drm/mxsfb/Kconfig | 8 +- drivers/gpu/drm/mxsfb/Makefile | 2 +- drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 343 ----------------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 248 ++++--------- 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 | 103 +++--- 8 files changed, 732 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 Reviewed-by: Stefan Agner stefan@agner.ch --- 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 --- Changes since v1:
- Select DRM_PANEL_BRIDGE in Kconfig --- drivers/gpu/drm/mxsfb/Kconfig | 1 + 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 ---------------------------- 5 files changed, 54 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig index 0dca8f27169e..e43b326e9147 100644 --- a/drivers/gpu/drm/mxsfb/Kconfig +++ b/drivers/gpu/drm/mxsfb/Kconfig @@ -13,6 +13,7 @@ config DRM_MXSFB select DRM_KMS_FB_HELPER select DRM_KMS_CMA_HELPER select DRM_PANEL + select DRM_PANEL_BRIDGE help Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB LCD controller. 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-05-30 05:09, 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.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
As discussed in the late discussion on the v1 thread, we should add a default type to avoid warnings for some panels.
Other than that, looks good to me:
Reviewed-by: Stefan Agner stefan@agner.ch
Changes since v1:
- Select DRM_PANEL_BRIDGE in Kconfig
drivers/gpu/drm/mxsfb/Kconfig | 1 + 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 ---------------------------- 5 files changed, 54 insertions(+), 158 deletions(-) delete mode 100644 drivers/gpu/drm/mxsfb/mxsfb_out.c
diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig index 0dca8f27169e..e43b326e9147 100644 --- a/drivers/gpu/drm/mxsfb/Kconfig +++ b/drivers/gpu/drm/mxsfb/Kconfig @@ -13,6 +13,7 @@ config DRM_MXSFB select DRM_KMS_FB_HELPER select DRM_KMS_CMA_HELPER select DRM_PANEL
- select DRM_PANEL_BRIDGE help Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB LCD controller.
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);
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;
-}
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 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 Reviewed-by: Stefan Agner stefan@agner.ch --- 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__ */
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 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 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 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 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 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 --- Changes since v1:
- Move after mxsfb_crtc.c rename to mxsfb_kms.c --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 +++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 184 +++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 118 deletions(-)
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__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.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); +}
On 2020-05-30 05:10, 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
Changes since v1:
- Move after mxsfb_crtc.c rename to mxsfb_kms.c
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 +++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 184 +++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 118 deletions(-)
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__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.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;
This actually fails some of the igt subtests of kms_vblank.
I think it has to be: if (state->active && !has_primary)
With that, all kms_vblank tests succeed (or are skipped).
With that fixed: Reviewed-by: Stefan Agner stefan@agner.ch
-- Stefan
- /* 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);
+}
Hi Stefan,
On Thu, Jun 11, 2020 at 09:33:11PM +0200, Stefan Agner wrote:
On 2020-05-30 05:10, 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
Changes since v1:
- Move after mxsfb_crtc.c rename to mxsfb_kms.c
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 +++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 184 +++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 118 deletions(-)
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__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.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;
This actually fails some of the igt subtests of kms_vblank.
I think it has to be: if (state->active && !has_primary)
With that, all kms_vblank tests succeed (or are skipped).
With that fixed: Reviewed-by: Stefan Agner stefan@agner.ch
Good catch, this will be fixed in v3. Speaking of which, I'm still missing your review on 02/22 and 22/22. Do you plan to have a look at them ?
- /* 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);
+}
Hi Laurent,
On 2020-06-16 03:50, Laurent Pinchart wrote:
Hi Stefan,
On Thu, Jun 11, 2020 at 09:33:11PM +0200, Stefan Agner wrote:
On 2020-05-30 05:10, 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
Changes since v1:
- Move after mxsfb_crtc.c rename to mxsfb_kms.c
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 +++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 184 +++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 118 deletions(-)
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__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.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;
This actually fails some of the igt subtests of kms_vblank.
I think it has to be: if (state->active && !has_primary)
With that, all kms_vblank tests succeed (or are skipped).
With that fixed: Reviewed-by: Stefan Agner stefan@agner.ch
Good catch, this will be fixed in v3. Speaking of which, I'm still missing your review on 02/22 and 22/22. Do you plan to have a look at them ?
Yeah sorry, I did initially miss them and sent a review a couple of weeks ago.
Would be good if you can send a v3 soon, I think it should still be early enough to make it into the next merge window.
-- Stefan
- /* 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);
+}
Hi Stefan,
On Thu, Jul 09, 2020 at 12:25:42PM +0200, Stefan Agner wrote:
On 2020-06-16 03:50, Laurent Pinchart wrote:
On Thu, Jun 11, 2020 at 09:33:11PM +0200, Stefan Agner wrote:
On 2020-05-30 05:10, 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
Changes since v1:
- Move after mxsfb_crtc.c rename to mxsfb_kms.c
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 +++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 184 +++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 118 deletions(-)
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__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.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;
This actually fails some of the igt subtests of kms_vblank.
I think it has to be: if (state->active && !has_primary)
With that, all kms_vblank tests succeed (or are skipped).
With that fixed: Reviewed-by: Stefan Agner stefan@agner.ch
Good catch, this will be fixed in v3. Speaking of which, I'm still missing your review on 02/22 and 22/22. Do you plan to have a look at them ?
Yeah sorry, I did initially miss them and sent a review a couple of weeks ago.
Would be good if you can send a v3 soon, I think it should still be early enough to make it into the next merge window.
I'm afraid I'm fairly busy at the moment :-( I'll try to give it a go next week, but it will be too late for v5.9.
- /* 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);
+}
Hi Stefan,
On Fri, Jul 17, 2020 at 05:06:55AM +0300, Laurent Pinchart wrote:
On Thu, Jul 09, 2020 at 12:25:42PM +0200, Stefan Agner wrote:
On 2020-06-16 03:50, Laurent Pinchart wrote:
On Thu, Jun 11, 2020 at 09:33:11PM +0200, Stefan Agner wrote:
On 2020-05-30 05:10, 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
Changes since v1:
- Move after mxsfb_crtc.c rename to mxsfb_kms.c
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 +++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 184 +++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 118 deletions(-)
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__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.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;
This actually fails some of the igt subtests of kms_vblank.
I think it has to be: if (state->active && !has_primary)
With that, all kms_vblank tests succeed (or are skipped).
With that fixed: Reviewed-by: Stefan Agner stefan@agner.ch
Good catch, this will be fixed in v3. Speaking of which, I'm still missing your review on 02/22 and 22/22. Do you plan to have a look at them ?
Yeah sorry, I did initially miss them and sent a review a couple of weeks ago.
Would be good if you can send a v3 soon, I think it should still be early enough to make it into the next merge window.
I'm afraid I'm fairly busy at the moment :-( I'll try to give it a go next week, but it will be too late for v5.9.
Turns out I had already sent a v3 a month ago :-) I'll send a rebased v4 shortly.
- /* 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);
+}
On 2020-07-26 20:28, Laurent Pinchart wrote:
Hi Stefan,
On Fri, Jul 17, 2020 at 05:06:55AM +0300, Laurent Pinchart wrote:
On Thu, Jul 09, 2020 at 12:25:42PM +0200, Stefan Agner wrote:
On 2020-06-16 03:50, Laurent Pinchart wrote:
On Thu, Jun 11, 2020 at 09:33:11PM +0200, Stefan Agner wrote:
On 2020-05-30 05:10, 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
Changes since v1:
- Move after mxsfb_crtc.c rename to mxsfb_kms.c
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 108 +++--------------- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 23 ++-- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 184 +++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 118 deletions(-)
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__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index c4f1575b4210..8f339adb8d04 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.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;
This actually fails some of the igt subtests of kms_vblank.
I think it has to be: if (state->active && !has_primary)
With that, all kms_vblank tests succeed (or are skipped).
With that fixed: Reviewed-by: Stefan Agner stefan@agner.ch
Good catch, this will be fixed in v3. Speaking of which, I'm still missing your review on 02/22 and 22/22. Do you plan to have a look at them ?
Yeah sorry, I did initially miss them and sent a review a couple of weeks ago.
Would be good if you can send a v3 soon, I think it should still be early enough to make it into the next merge window.
I'm afraid I'm fairly busy at the moment :-( I'll try to give it a go next week, but it will be too late for v5.9.
Turns out I had already sent a v3 a month ago :-) I'll send a rebased v4 shortly.
Oh sorry, missed that one. I just checked my inbox and spam folder, but wasn't able to find it. I think you missed my email? https://lwn.net/Articles/823498/
Thanks for v4! Will take care of it today or tomorrow.
-- Stefan
- /* 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);
+}
The vblank event is armed in the plane .atomic_update(). This works fine as we have a single plane, and was the only option when the driver was using the drm_simple_kms_helper helper, but will break as soon as multiple planes are supported. Move it to CRTC .atomic_flush().
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Reviewed-by: Stefan Agner stefan@agner.ch --- Changes since v1:
- Reword commit message --- 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 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-05-30 05:10, 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
Reviewed-by: Stefan Agner stefan@agner.ch
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 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, - .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 {
Extend the Kconfig option description by listing the i.MX7 and i.MX8M 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 --- Changes since v1:
- Make description more explicit by mentioning LCDIF and eLCDIF - Add i.MX8M --- 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 e43b326e9147..0143d539f8f8 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 @@ -15,7 +15,8 @@ config DRM_MXSFB select DRM_PANEL select DRM_PANEL_BRIDGE 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, i.MX7 and i.MX8M).
If M is selected the module will be called mxsfb.
On 2020-05-30 05:10, Laurent Pinchart wrote:
Extend the Kconfig option description by listing the i.MX7 and i.MX8M 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
Reviewed-by: Stefan Agner stefan@agner.ch
Changes since v1:
- Make description more explicit by mentioning LCDIF and eLCDIF
- Add i.MX8M
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 e43b326e9147..0143d539f8f8 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
@@ -15,7 +15,8 @@ config DRM_MXSFB select DRM_PANEL select DRM_PANEL_BRIDGE 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, i.MX7 and i.MX8M).
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 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 Reviewed-by: Stefan Agner stefan@agner.ch --- 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 Reviewed-by: Stefan Agner stefan@agner.ch --- 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 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; + 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);
Hi Laurent,
With the previous disclaimer in mind, the series is: Reviewed-by: Emil Velikov emil.l.velikov@gmail.com
There's a small suggestion inline, for future work.
On Sat, 30 May 2020 at 04:11, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
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;
Off the top of my head, the default case should be handled in the atomic_check() hook. That is, unless I'm missing something and one can change the bus format in between atomic_check() and atomic_enable(). Which would sound like a very odd thing to do.
-Emil
Hi Emil,
On Fri, Jun 05, 2020 at 03:58:53PM +0100, Emil Velikov wrote:
Hi Laurent,
With the previous disclaimer in mind, the series is: Reviewed-by: Emil Velikov emil.l.velikov@gmail.com
Sorry, which disclaimer is that ?
There's a small suggestion inline, for future work.
On Sat, 30 May 2020 at 04:11, Laurent Pinchart wrote:
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;
Off the top of my head, the default case should be handled in the atomic_check() hook. That is, unless I'm missing something and one can change the bus format in between atomic_check() and atomic_enable(). Which would sound like a very odd thing to do.
I agree, this should go to the atomic check. There are still quite a few things to improve in this driver, one step at a time :-)
i Laurent,
On Sun, 7 Jun 2020 at 00:02, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Emil,
On Fri, Jun 05, 2020 at 03:58:53PM +0100, Emil Velikov wrote:
Hi Laurent,
With the previous disclaimer in mind, the series is: Reviewed-by: Emil Velikov emil.l.velikov@gmail.com
Sorry, which disclaimer is that ?
"I don't know much about the hardware" from earlier. Even then, the refactor that is 1-21 is spot on.
There's a small suggestion inline, for future work.
On Sat, 30 May 2020 at 04:11, Laurent Pinchart wrote:
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;
Off the top of my head, the default case should be handled in the atomic_check() hook. That is, unless I'm missing something and one can change the bus format in between atomic_check() and atomic_enable(). Which would sound like a very odd thing to do.
I agree, this should go to the atomic check. There are still quite a few things to improve in this driver, one step at a time :-)
Agreed - simply mentioning for completeness-sake. Doing that at misc later point is perfectly fine.
HTH Emil
This is a cosmetic change only, no code change is included.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/mxsfb/mxsfb_drv.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 607a6a5e6be3..f883b56caed3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -16,11 +16,11 @@ 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; };
struct mxsfb_drm_private {
On 2020-05-30 05:10, Laurent Pinchart wrote:
This is a cosmetic change only, no code change is included.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
Reviewed-by: Stefan Agner stefan@agner.ch
drivers/gpu/drm/mxsfb/mxsfb_drv.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index 607a6a5e6be3..f883b56caed3 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -16,11 +16,11 @@ 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;
};
struct mxsfb_drm_private {
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 --- Changes since v1:
- Split whitespace cleanup to a separate patch --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 3 + drivers/gpu/drm/mxsfb/mxsfb_drv.h | 6 +- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 129 +++++++++++++++++++++++++---- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 22 +++++ 4 files changed, 144 insertions(+), 16 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 f883b56caed3..399d23e91ed1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -21,6 +21,7 @@ struct mxsfb_devdata { 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_atomic_update, + .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_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, - DRM_FORMAT_RGB565 };
+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_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
HI Laurent,
From a quick glance the series looks really good and neat. Then again,
I don't know much about the hardware to provide meaningful review.
A couple of small ideas below.
On Sat, 30 May 2020 at 04:11, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
+#define LCDC_AS_BUF 0x220 +#define LCDC_AS_NEXT_BUF 0x230
s/LCDC_AS_BUF/LCDC_AS_CUR_BUF/ - to stay consistent with the primary plane name scheme.
Would it make sense to store the above registers in mxsfb_devdata, just like we do for the primary planes?
HTH Emil
Hi Emil,
On Sun, May 31, 2020 at 05:54:04PM +0100, Emil Velikov wrote:
HI Laurent,
From a quick glance the series looks really good and neat.
Thank you :-)
Then again, I don't know much about the hardware to provide meaningful review.
A couple of small ideas below.
On Sat, 30 May 2020 at 04:11, Laurent Pinchart wrote:
+#define LCDC_AS_BUF 0x220 +#define LCDC_AS_NEXT_BUF 0x230
s/LCDC_AS_BUF/LCDC_AS_CUR_BUF/ - to stay consistent with the primary plane name scheme.
The register names come directly from the datasheet (and yes, the datasheet uses CUR_BUF and NEXT_BUF for the primary plane, and BUF and NEXT_BUF for the overlay plane :-S). I'd thus rather keep them aligned with the documentation.
Would it make sense to store the above registers in mxsfb_devdata, just like we do for the primary planes?
The reason the register addresses are stored in mxsfb_devdata for the primary plane is because they differ between hardware revisions (don't they teach hardware engineers in school these days *not* to move registers around ? :-)). The overlay plane is only supported in the latest versions of the IP core, and are always located at the same address as far as I can tell. I don't think we need an extra level of indirection.
On Tue, 2 Jun 2020 at 23:43, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
Hi Emil,
On Sun, May 31, 2020 at 05:54:04PM +0100, Emil Velikov wrote:
HI Laurent,
From a quick glance the series looks really good and neat.
Thank you :-)
Then again, I don't know much about the hardware to provide meaningful review.
A couple of small ideas below.
On Sat, 30 May 2020 at 04:11, Laurent Pinchart wrote:
+#define LCDC_AS_BUF 0x220 +#define LCDC_AS_NEXT_BUF 0x230
s/LCDC_AS_BUF/LCDC_AS_CUR_BUF/ - to stay consistent with the primary plane name scheme.
The register names come directly from the datasheet (and yes, the datasheet uses CUR_BUF and NEXT_BUF for the primary plane, and BUF and NEXT_BUF for the overlay plane :-S). I'd thus rather keep them aligned with the documentation.
Would it make sense to store the above registers in mxsfb_devdata, just like we do for the primary planes?
The reason the register addresses are stored in mxsfb_devdata for the primary plane is because they differ between hardware revisions (don't they teach hardware engineers in school these days *not* to move registers around ? :-)). The overlay plane is only supported in the latest versions of the IP core, and are always located at the same address as far as I can tell. I don't think we need an extra level of indirection.
Right, makes sense. Thanks for the information.
-Emil
On 2020-05-30 05:10, 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
Looks good to me.
Reviewed-by: Stefan Agner stefan@agner.ch
-- Stefan
Changes since v1:
- Split whitespace cleanup to a separate patch
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 3 + drivers/gpu/drm/mxsfb/mxsfb_drv.h | 6 +- drivers/gpu/drm/mxsfb/mxsfb_kms.c | 129 +++++++++++++++++++++++++---- drivers/gpu/drm/mxsfb/mxsfb_regs.h | 22 +++++ 4 files changed, 144 insertions(+), 16 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 f883b56caed3..399d23e91ed1 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -21,6 +21,7 @@ struct mxsfb_devdata { 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_atomic_update,
- .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_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,
- DRM_FORMAT_RGB565
};
+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_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
dri-devel@lists.freedesktop.org