Skip to content

Commit 9db35bb

Browse files
author
Marek Vasut
committed
drm: lcdif: Add support for i.MX8MP LCDIF variant
Add support for i.MX8MP LCDIF variant. This is called LCDIFv3 and is completely different from the LCDIFv3 found in i.MX23 in that it has a completely scrambled register layout compared to all previous LCDIF variants. The new LCDIFv3 also supports 36bit address space. Add a separate driver which is really a fork of MXSFB driver with the i.MX8MP LCDIF variant handling filled in. Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> Tested-by: Martyn Welch <martyn.welch@collabora.com> Signed-off-by: Marek Vasut <marex@denx.de> Cc: Alexander Stein <alexander.stein@ew.tq-group.com> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Peng Fan <peng.fan@nxp.com> Cc: Robby Cai <robby.cai@nxp.com> Cc: Sam Ravnborg <sam@ravnborg.org> Cc: Stefan Agner <stefan@agner.ch> Reviewed-by: Lucas Stach <l.stach@pengutronix.de> Link: https://patchwork.freedesktop.org/patch/msgid/20220628174152.167284-2-marex@denx.de
1 parent f5419cb commit 9db35bb

File tree

7 files changed

+1144
-1
lines changed

7 files changed

+1144
-1
lines changed

drivers/gpu/drm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ obj-y += bridge/
131131
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
132132
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
133133
obj-y += hisilicon/
134-
obj-$(CONFIG_DRM_MXSFB) += mxsfb/
134+
obj-y += mxsfb/
135135
obj-y += tiny/
136136
obj-$(CONFIG_DRM_PL111) += pl111/
137137
obj-$(CONFIG_DRM_TVE200) += tve200/

drivers/gpu/drm/mxsfb/Kconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,19 @@ config DRM_MXSFB
1919
i.MX28, i.MX6SX, i.MX7 and i.MX8M).
2020

2121
If M is selected the module will be called mxsfb.
22+
23+
config DRM_IMX_LCDIF
24+
tristate "i.MX LCDIFv3 LCD controller"
25+
depends on DRM && OF
26+
depends on COMMON_CLK
27+
select DRM_MXS
28+
select DRM_KMS_HELPER
29+
select DRM_GEM_CMA_HELPER
30+
select DRM_PANEL
31+
select DRM_PANEL_BRIDGE
32+
help
33+
Choose this option if you have an LCDIFv3 LCD controller.
34+
Those devices are found in various i.MX SoC (i.MX8MP,
35+
i.MXRT).
36+
37+
If M is selected the module will be called imx-lcdif.

drivers/gpu/drm/mxsfb/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
mxsfb-y := mxsfb_drv.o mxsfb_kms.o
33
obj-$(CONFIG_DRM_MXSFB) += mxsfb.o
4+
imx-lcdif-y := lcdif_drv.o lcdif_kms.o
5+
obj-$(CONFIG_DRM_IMX_LCDIF) += imx-lcdif.o

drivers/gpu/drm/mxsfb/lcdif_drv.c

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
4+
*
5+
* This code is based on drivers/gpu/drm/mxsfb/mxsfb*
6+
*/
7+
8+
#include <linux/clk.h>
9+
#include <linux/dma-mapping.h>
10+
#include <linux/io.h>
11+
#include <linux/iopoll.h>
12+
#include <linux/module.h>
13+
#include <linux/of_device.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/pm_runtime.h>
16+
17+
#include <drm/drm_atomic_helper.h>
18+
#include <drm/drm_bridge.h>
19+
#include <drm/drm_connector.h>
20+
#include <drm/drm_drv.h>
21+
#include <drm/drm_fb_helper.h>
22+
#include <drm/drm_fourcc.h>
23+
#include <drm/drm_gem_cma_helper.h>
24+
#include <drm/drm_gem_framebuffer_helper.h>
25+
#include <drm/drm_mode_config.h>
26+
#include <drm/drm_module.h>
27+
#include <drm/drm_of.h>
28+
#include <drm/drm_probe_helper.h>
29+
#include <drm/drm_vblank.h>
30+
31+
#include "lcdif_drv.h"
32+
#include "lcdif_regs.h"
33+
34+
static const struct drm_mode_config_funcs lcdif_mode_config_funcs = {
35+
.fb_create = drm_gem_fb_create,
36+
.atomic_check = drm_atomic_helper_check,
37+
.atomic_commit = drm_atomic_helper_commit,
38+
};
39+
40+
static const struct drm_mode_config_helper_funcs lcdif_mode_config_helpers = {
41+
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
42+
};
43+
44+
static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif)
45+
{
46+
struct drm_device *drm = lcdif->drm;
47+
struct drm_bridge *bridge;
48+
struct drm_panel *panel;
49+
int ret;
50+
51+
ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
52+
&bridge);
53+
if (ret)
54+
return ret;
55+
56+
if (panel) {
57+
bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
58+
DRM_MODE_CONNECTOR_DPI);
59+
if (IS_ERR(bridge))
60+
return PTR_ERR(bridge);
61+
}
62+
63+
if (!bridge)
64+
return -ENODEV;
65+
66+
ret = drm_bridge_attach(&lcdif->encoder, bridge, NULL, 0);
67+
if (ret)
68+
return dev_err_probe(drm->dev, ret, "Failed to attach bridge\n");
69+
70+
lcdif->bridge = bridge;
71+
72+
return 0;
73+
}
74+
75+
static irqreturn_t lcdif_irq_handler(int irq, void *data)
76+
{
77+
struct drm_device *drm = data;
78+
struct lcdif_drm_private *lcdif = drm->dev_private;
79+
u32 reg, stat;
80+
81+
stat = readl(lcdif->base + LCDC_V8_INT_STATUS_D0);
82+
if (!stat)
83+
return IRQ_NONE;
84+
85+
if (stat & INT_STATUS_D0_VS_BLANK) {
86+
reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
87+
if (!(reg & CTRLDESCL0_5_SHADOW_LOAD_EN))
88+
drm_crtc_handle_vblank(&lcdif->crtc);
89+
}
90+
91+
writel(stat, lcdif->base + LCDC_V8_INT_STATUS_D0);
92+
93+
return IRQ_HANDLED;
94+
}
95+
96+
static int lcdif_load(struct drm_device *drm)
97+
{
98+
struct platform_device *pdev = to_platform_device(drm->dev);
99+
struct lcdif_drm_private *lcdif;
100+
struct resource *res;
101+
int ret;
102+
103+
lcdif = devm_kzalloc(&pdev->dev, sizeof(*lcdif), GFP_KERNEL);
104+
if (!lcdif)
105+
return -ENOMEM;
106+
107+
lcdif->drm = drm;
108+
drm->dev_private = lcdif;
109+
110+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
111+
lcdif->base = devm_ioremap_resource(drm->dev, res);
112+
if (IS_ERR(lcdif->base))
113+
return PTR_ERR(lcdif->base);
114+
115+
lcdif->clk = devm_clk_get(drm->dev, "pix");
116+
if (IS_ERR(lcdif->clk))
117+
return PTR_ERR(lcdif->clk);
118+
119+
lcdif->clk_axi = devm_clk_get(drm->dev, "axi");
120+
if (IS_ERR(lcdif->clk_axi))
121+
return PTR_ERR(lcdif->clk_axi);
122+
123+
lcdif->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi");
124+
if (IS_ERR(lcdif->clk_disp_axi))
125+
return PTR_ERR(lcdif->clk_disp_axi);
126+
127+
platform_set_drvdata(pdev, drm);
128+
129+
ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(36));
130+
if (ret)
131+
return ret;
132+
133+
/* Modeset init */
134+
drm_mode_config_init(drm);
135+
136+
ret = lcdif_kms_init(lcdif);
137+
if (ret < 0) {
138+
dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
139+
return ret;
140+
}
141+
142+
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
143+
if (ret < 0) {
144+
dev_err(drm->dev, "Failed to initialise vblank\n");
145+
return ret;
146+
}
147+
148+
/* Start with vertical blanking interrupt reporting disabled. */
149+
drm_crtc_vblank_off(&lcdif->crtc);
150+
151+
ret = lcdif_attach_bridge(lcdif);
152+
if (ret)
153+
return dev_err_probe(drm->dev, ret, "Cannot connect bridge\n");
154+
155+
drm->mode_config.min_width = LCDIF_MIN_XRES;
156+
drm->mode_config.min_height = LCDIF_MIN_YRES;
157+
drm->mode_config.max_width = LCDIF_MAX_XRES;
158+
drm->mode_config.max_height = LCDIF_MAX_YRES;
159+
drm->mode_config.funcs = &lcdif_mode_config_funcs;
160+
drm->mode_config.helper_private = &lcdif_mode_config_helpers;
161+
162+
drm_mode_config_reset(drm);
163+
164+
ret = platform_get_irq(pdev, 0);
165+
if (ret < 0)
166+
return ret;
167+
lcdif->irq = ret;
168+
169+
ret = devm_request_irq(drm->dev, lcdif->irq, lcdif_irq_handler, 0,
170+
drm->driver->name, drm);
171+
if (ret < 0) {
172+
dev_err(drm->dev, "Failed to install IRQ handler\n");
173+
return ret;
174+
}
175+
176+
drm_kms_helper_poll_init(drm);
177+
178+
drm_helper_hpd_irq_event(drm);
179+
180+
pm_runtime_enable(drm->dev);
181+
182+
return 0;
183+
}
184+
185+
static void lcdif_unload(struct drm_device *drm)
186+
{
187+
struct lcdif_drm_private *lcdif = drm->dev_private;
188+
189+
pm_runtime_get_sync(drm->dev);
190+
191+
drm_crtc_vblank_off(&lcdif->crtc);
192+
193+
drm_kms_helper_poll_fini(drm);
194+
drm_mode_config_cleanup(drm);
195+
196+
pm_runtime_put_sync(drm->dev);
197+
pm_runtime_disable(drm->dev);
198+
199+
drm->dev_private = NULL;
200+
}
201+
202+
DEFINE_DRM_GEM_CMA_FOPS(fops);
203+
204+
static const struct drm_driver lcdif_driver = {
205+
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
206+
DRM_GEM_CMA_DRIVER_OPS,
207+
.fops = &fops,
208+
.name = "imx-lcdif",
209+
.desc = "i.MX LCDIF Controller DRM",
210+
.date = "20220417",
211+
.major = 1,
212+
.minor = 0,
213+
};
214+
215+
static const struct of_device_id lcdif_dt_ids[] = {
216+
{ .compatible = "fsl,imx8mp-lcdif" },
217+
{ /* sentinel */ }
218+
};
219+
MODULE_DEVICE_TABLE(of, lcdif_dt_ids);
220+
221+
static int lcdif_probe(struct platform_device *pdev)
222+
{
223+
struct drm_device *drm;
224+
int ret;
225+
226+
drm = drm_dev_alloc(&lcdif_driver, &pdev->dev);
227+
if (IS_ERR(drm))
228+
return PTR_ERR(drm);
229+
230+
ret = lcdif_load(drm);
231+
if (ret)
232+
goto err_free;
233+
234+
ret = drm_dev_register(drm, 0);
235+
if (ret)
236+
goto err_unload;
237+
238+
drm_fbdev_generic_setup(drm, 32);
239+
240+
return 0;
241+
242+
err_unload:
243+
lcdif_unload(drm);
244+
err_free:
245+
drm_dev_put(drm);
246+
247+
return ret;
248+
}
249+
250+
static int lcdif_remove(struct platform_device *pdev)
251+
{
252+
struct drm_device *drm = platform_get_drvdata(pdev);
253+
254+
drm_dev_unregister(drm);
255+
drm_atomic_helper_shutdown(drm);
256+
lcdif_unload(drm);
257+
drm_dev_put(drm);
258+
259+
return 0;
260+
}
261+
262+
static void lcdif_shutdown(struct platform_device *pdev)
263+
{
264+
struct drm_device *drm = platform_get_drvdata(pdev);
265+
266+
drm_atomic_helper_shutdown(drm);
267+
}
268+
269+
static int __maybe_unused lcdif_rpm_suspend(struct device *dev)
270+
{
271+
struct drm_device *drm = dev_get_drvdata(dev);
272+
struct lcdif_drm_private *lcdif = drm->dev_private;
273+
274+
/* These clock supply the DISPLAY CLOCK Domain */
275+
clk_disable_unprepare(lcdif->clk);
276+
/* These clock supply the System Bus, AXI, Write Path, LFIFO */
277+
clk_disable_unprepare(lcdif->clk_disp_axi);
278+
/* These clock supply the Control Bus, APB, APBH Ctrl Registers */
279+
clk_disable_unprepare(lcdif->clk_axi);
280+
281+
return 0;
282+
}
283+
284+
static int __maybe_unused lcdif_rpm_resume(struct device *dev)
285+
{
286+
struct drm_device *drm = dev_get_drvdata(dev);
287+
struct lcdif_drm_private *lcdif = drm->dev_private;
288+
289+
/* These clock supply the Control Bus, APB, APBH Ctrl Registers */
290+
clk_prepare_enable(lcdif->clk_axi);
291+
/* These clock supply the System Bus, AXI, Write Path, LFIFO */
292+
clk_prepare_enable(lcdif->clk_disp_axi);
293+
/* These clock supply the DISPLAY CLOCK Domain */
294+
clk_prepare_enable(lcdif->clk);
295+
296+
return 0;
297+
}
298+
299+
static int __maybe_unused lcdif_suspend(struct device *dev)
300+
{
301+
struct drm_device *drm = dev_get_drvdata(dev);
302+
int ret;
303+
304+
ret = drm_mode_config_helper_suspend(drm);
305+
if (ret)
306+
return ret;
307+
308+
return lcdif_rpm_suspend(dev);
309+
}
310+
311+
static int __maybe_unused lcdif_resume(struct device *dev)
312+
{
313+
struct drm_device *drm = dev_get_drvdata(dev);
314+
315+
lcdif_rpm_resume(dev);
316+
317+
return drm_mode_config_helper_resume(drm);
318+
}
319+
320+
static const struct dev_pm_ops lcdif_pm_ops = {
321+
SET_SYSTEM_SLEEP_PM_OPS(lcdif_suspend, lcdif_resume)
322+
SET_RUNTIME_PM_OPS(lcdif_rpm_suspend, lcdif_rpm_resume, NULL)
323+
};
324+
325+
static struct platform_driver lcdif_platform_driver = {
326+
.probe = lcdif_probe,
327+
.remove = lcdif_remove,
328+
.shutdown = lcdif_shutdown,
329+
.driver = {
330+
.name = "imx-lcdif",
331+
.of_match_table = lcdif_dt_ids,
332+
.pm = &lcdif_pm_ops,
333+
},
334+
};
335+
336+
drm_module_platform_driver(lcdif_platform_driver);
337+
338+
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
339+
MODULE_DESCRIPTION("Freescale LCDIF DRM/KMS driver");
340+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)