xref: /openbmc/linux/drivers/gpu/drm/imx/lcdc/imx-lcdc.c (revision c87e859cdeb5d106cb861326e3135c606d61f88d)
1*c87e859cSMarian Cichy // SPDX-License-Identifier: GPL-2.0-only
2*c87e859cSMarian Cichy // SPDX-FileCopyrightText: 2020 Marian Cichy <M.Cichy@pengutronix.de>
3*c87e859cSMarian Cichy 
4*c87e859cSMarian Cichy #include <drm/drm_bridge_connector.h>
5*c87e859cSMarian Cichy #include <drm/drm_damage_helper.h>
6*c87e859cSMarian Cichy #include <drm/drm_drv.h>
7*c87e859cSMarian Cichy #include <drm/drm_fbdev_generic.h>
8*c87e859cSMarian Cichy #include <drm/drm_fb_dma_helper.h>
9*c87e859cSMarian Cichy #include <drm/drm_fourcc.h>
10*c87e859cSMarian Cichy #include <drm/drm_framebuffer.h>
11*c87e859cSMarian Cichy #include <drm/drm_gem_atomic_helper.h>
12*c87e859cSMarian Cichy #include <drm/drm_gem_dma_helper.h>
13*c87e859cSMarian Cichy #include <drm/drm_gem_framebuffer_helper.h>
14*c87e859cSMarian Cichy #include <drm/drm_of.h>
15*c87e859cSMarian Cichy #include <drm/drm_probe_helper.h>
16*c87e859cSMarian Cichy #include <drm/drm_simple_kms_helper.h>
17*c87e859cSMarian Cichy #include <drm/drm_vblank.h>
18*c87e859cSMarian Cichy #include <linux/bitfield.h>
19*c87e859cSMarian Cichy #include <linux/clk.h>
20*c87e859cSMarian Cichy #include <linux/dma-mapping.h>
21*c87e859cSMarian Cichy #include <linux/module.h>
22*c87e859cSMarian Cichy #include <linux/of_device.h>
23*c87e859cSMarian Cichy #include <linux/platform_device.h>
24*c87e859cSMarian Cichy 
25*c87e859cSMarian Cichy #define IMX21LCDC_LSSAR         0x0000 /* LCDC Screen Start Address Register */
26*c87e859cSMarian Cichy #define IMX21LCDC_LSR           0x0004 /* LCDC Size Register */
27*c87e859cSMarian Cichy #define IMX21LCDC_LVPWR         0x0008 /* LCDC Virtual Page Width Register */
28*c87e859cSMarian Cichy #define IMX21LCDC_LCPR          0x000C /* LCDC Cursor Position Register */
29*c87e859cSMarian Cichy #define IMX21LCDC_LCWHB         0x0010 /* LCDC Cursor Width Height and Blink Register*/
30*c87e859cSMarian Cichy #define IMX21LCDC_LCCMR         0x0014 /* LCDC Color Cursor Mapping Register */
31*c87e859cSMarian Cichy #define IMX21LCDC_LPCR          0x0018 /* LCDC Panel Configuration Register */
32*c87e859cSMarian Cichy #define IMX21LCDC_LHCR          0x001C /* LCDC Horizontal Configuration Register */
33*c87e859cSMarian Cichy #define IMX21LCDC_LVCR          0x0020 /* LCDC Vertical Configuration Register */
34*c87e859cSMarian Cichy #define IMX21LCDC_LPOR          0x0024 /* LCDC Panning Offset Register */
35*c87e859cSMarian Cichy #define IMX21LCDC_LSCR          0x0028 /* LCDC Sharp Configuration Register */
36*c87e859cSMarian Cichy #define IMX21LCDC_LPCCR         0x002C /* LCDC PWM Contrast Control Register */
37*c87e859cSMarian Cichy #define IMX21LCDC_LDCR          0x0030 /* LCDC DMA Control Register */
38*c87e859cSMarian Cichy #define IMX21LCDC_LRMCR         0x0034 /* LCDC Refresh Mode Control Register */
39*c87e859cSMarian Cichy #define IMX21LCDC_LICR          0x0038 /* LCDC Interrupt Configuration Register */
40*c87e859cSMarian Cichy #define IMX21LCDC_LIER          0x003C /* LCDC Interrupt Enable Register */
41*c87e859cSMarian Cichy #define IMX21LCDC_LISR          0x0040 /* LCDC Interrupt Status Register */
42*c87e859cSMarian Cichy #define IMX21LCDC_LGWSAR        0x0050 /* LCDC Graphic Window Start Address Register */
43*c87e859cSMarian Cichy #define IMX21LCDC_LGWSR         0x0054 /* LCDC Graph Window Size Register */
44*c87e859cSMarian Cichy #define IMX21LCDC_LGWVPWR       0x0058 /* LCDC Graphic Window Virtual Page Width Register */
45*c87e859cSMarian Cichy #define IMX21LCDC_LGWPOR        0x005C /* LCDC Graphic Window Panning Offset Register */
46*c87e859cSMarian Cichy #define IMX21LCDC_LGWPR         0x0060 /* LCDC Graphic Window Position Register */
47*c87e859cSMarian Cichy #define IMX21LCDC_LGWCR         0x0064 /* LCDC Graphic Window Control Register */
48*c87e859cSMarian Cichy #define IMX21LCDC_LGWDCR        0x0068 /* LCDC Graphic Window DMA Control Register */
49*c87e859cSMarian Cichy #define IMX21LCDC_LAUSCR        0x0080 /* LCDC AUS Mode Control Register */
50*c87e859cSMarian Cichy #define IMX21LCDC_LAUSCCR       0x0084 /* LCDC AUS Mode Cursor Control Register */
51*c87e859cSMarian Cichy #define IMX21LCDC_BGLUT         0x0800 /* Background Lookup Table */
52*c87e859cSMarian Cichy #define IMX21LCDC_GWLUT         0x0C00 /* Graphic Window Lookup Table */
53*c87e859cSMarian Cichy 
54*c87e859cSMarian Cichy #define IMX21LCDC_LCPR_CC0 BIT(30) /* Cursor Control Bit 0 */
55*c87e859cSMarian Cichy #define IMX21LCDC_LCPR_CC1 BIT(31) /* Cursor Control Bit 1 */
56*c87e859cSMarian Cichy 
57*c87e859cSMarian Cichy /* Values HSYNC, VSYNC and Framesize Register */
58*c87e859cSMarian Cichy #define IMX21LCDC_LHCR_HWIDTH		GENMASK(31, 26)
59*c87e859cSMarian Cichy #define IMX21LCDC_LHCR_HFPORCH		GENMASK(15, 8)		/* H_WAIT_1 in the i.MX25 Reference manual */
60*c87e859cSMarian Cichy #define IMX21LCDC_LHCR_HBPORCH		GENMASK(7, 0)		/* H_WAIT_2 in the i.MX25 Reference manual */
61*c87e859cSMarian Cichy 
62*c87e859cSMarian Cichy #define IMX21LCDC_LVCR_VWIDTH		GENMASK(31, 26)
63*c87e859cSMarian Cichy #define IMX21LCDC_LVCR_VFPORCH		GENMASK(15, 8)		/* V_WAIT_1 in the i.MX25 Reference manual */
64*c87e859cSMarian Cichy #define IMX21LCDC_LVCR_VBPORCH		GENMASK(7, 0)		/* V_WAIT_2 in the i.MX25 Reference manual */
65*c87e859cSMarian Cichy 
66*c87e859cSMarian Cichy #define IMX21LCDC_LSR_XMAX		GENMASK(25, 20)
67*c87e859cSMarian Cichy #define IMX21LCDC_LSR_YMAX		GENMASK(9, 0)
68*c87e859cSMarian Cichy 
69*c87e859cSMarian Cichy /* Values for LPCR Register */
70*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_PCD		GENMASK(5, 0)
71*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_SHARP		BIT(6)
72*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_SCLKSEL		BIT(7)
73*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_ACD		GENMASK(14, 8)
74*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_ACDSEL		BIT(15)
75*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_REV_VS		BIT(16)
76*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_SWAP_SEL		BIT(17)
77*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_END_SEL		BIT(18)
78*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_SCLKIDLE		BIT(19)
79*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_OEPOL		BIT(20)
80*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_CLKPOL		BIT(21)
81*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_LPPOL		BIT(22)
82*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_FLMPOL		BIT(23)
83*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_PIXPOL		BIT(24)
84*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_BPIX		GENMASK(27, 25)
85*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_PBSIZ		GENMASK(29, 28)
86*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_COLOR		BIT(30)
87*c87e859cSMarian Cichy #define IMX21LCDC_LPCR_TFT		BIT(31)
88*c87e859cSMarian Cichy 
89*c87e859cSMarian Cichy #define INTR_EOF BIT(1) /* VBLANK Interrupt Bit */
90*c87e859cSMarian Cichy 
91*c87e859cSMarian Cichy #define BPP_RGB565	0x05
92*c87e859cSMarian Cichy #define BPP_XRGB8888	0x07
93*c87e859cSMarian Cichy 
94*c87e859cSMarian Cichy #define LCDC_MIN_XRES 64
95*c87e859cSMarian Cichy #define LCDC_MIN_YRES 64
96*c87e859cSMarian Cichy 
97*c87e859cSMarian Cichy #define LCDC_MAX_XRES 1024
98*c87e859cSMarian Cichy #define LCDC_MAX_YRES 1024
99*c87e859cSMarian Cichy 
100*c87e859cSMarian Cichy struct imx_lcdc {
101*c87e859cSMarian Cichy 	struct drm_device drm;
102*c87e859cSMarian Cichy 	struct drm_simple_display_pipe pipe;
103*c87e859cSMarian Cichy 	struct drm_connector *connector;
104*c87e859cSMarian Cichy 	void __iomem *base;
105*c87e859cSMarian Cichy 
106*c87e859cSMarian Cichy 	struct clk *clk_ipg;
107*c87e859cSMarian Cichy 	struct clk *clk_ahb;
108*c87e859cSMarian Cichy 	struct clk *clk_per;
109*c87e859cSMarian Cichy };
110*c87e859cSMarian Cichy 
111*c87e859cSMarian Cichy static const u32 imx_lcdc_formats[] = {
112*c87e859cSMarian Cichy 	DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
113*c87e859cSMarian Cichy };
114*c87e859cSMarian Cichy 
115*c87e859cSMarian Cichy static inline struct imx_lcdc *imx_lcdc_from_drmdev(struct drm_device *drm)
116*c87e859cSMarian Cichy {
117*c87e859cSMarian Cichy 	return container_of(drm, struct imx_lcdc, drm);
118*c87e859cSMarian Cichy }
119*c87e859cSMarian Cichy 
120*c87e859cSMarian Cichy static unsigned int imx_lcdc_get_format(unsigned int drm_format)
121*c87e859cSMarian Cichy {
122*c87e859cSMarian Cichy 	switch (drm_format) {
123*c87e859cSMarian Cichy 	default:
124*c87e859cSMarian Cichy 		DRM_WARN("Format not supported - fallback to XRGB8888\n");
125*c87e859cSMarian Cichy 		fallthrough;
126*c87e859cSMarian Cichy 
127*c87e859cSMarian Cichy 	case DRM_FORMAT_XRGB8888:
128*c87e859cSMarian Cichy 		return BPP_XRGB8888;
129*c87e859cSMarian Cichy 
130*c87e859cSMarian Cichy 	case DRM_FORMAT_RGB565:
131*c87e859cSMarian Cichy 		return BPP_RGB565;
132*c87e859cSMarian Cichy 	}
133*c87e859cSMarian Cichy }
134*c87e859cSMarian Cichy 
135*c87e859cSMarian Cichy static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
136*c87e859cSMarian Cichy 					 struct drm_plane_state *old_state,
137*c87e859cSMarian Cichy 					 bool mode_set)
138*c87e859cSMarian Cichy {
139*c87e859cSMarian Cichy 	struct drm_crtc *crtc = &pipe->crtc;
140*c87e859cSMarian Cichy 	struct drm_plane_state *new_state = pipe->plane.state;
141*c87e859cSMarian Cichy 	struct drm_framebuffer *fb = new_state->fb;
142*c87e859cSMarian Cichy 	struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
143*c87e859cSMarian Cichy 	u32 lpcr, lvcr, lhcr;
144*c87e859cSMarian Cichy 	u32 framesize;
145*c87e859cSMarian Cichy 	dma_addr_t addr;
146*c87e859cSMarian Cichy 
147*c87e859cSMarian Cichy 	addr = drm_fb_dma_get_gem_addr(fb, new_state, 0);
148*c87e859cSMarian Cichy 	/* The LSSAR register specifies the LCD screen start address (SSA). */
149*c87e859cSMarian Cichy 	writel(addr, lcdc->base + IMX21LCDC_LSSAR);
150*c87e859cSMarian Cichy 
151*c87e859cSMarian Cichy 	if (!mode_set)
152*c87e859cSMarian Cichy 		return;
153*c87e859cSMarian Cichy 
154*c87e859cSMarian Cichy 	/* Disable PER clock to make register write possible */
155*c87e859cSMarian Cichy 	if (old_state && old_state->crtc && old_state->crtc->enabled)
156*c87e859cSMarian Cichy 		clk_disable_unprepare(lcdc->clk_per);
157*c87e859cSMarian Cichy 
158*c87e859cSMarian Cichy 	/* Framesize */
159*c87e859cSMarian Cichy 	framesize = FIELD_PREP(IMX21LCDC_LSR_XMAX, crtc->mode.hdisplay >> 4) |
160*c87e859cSMarian Cichy 		FIELD_PREP(IMX21LCDC_LSR_YMAX, crtc->mode.vdisplay);
161*c87e859cSMarian Cichy 	writel(framesize, lcdc->base + IMX21LCDC_LSR);
162*c87e859cSMarian Cichy 
163*c87e859cSMarian Cichy 	/* HSYNC */
164*c87e859cSMarian Cichy 	lhcr = FIELD_PREP(IMX21LCDC_LHCR_HFPORCH, crtc->mode.hsync_start - crtc->mode.hdisplay - 1) |
165*c87e859cSMarian Cichy 		FIELD_PREP(IMX21LCDC_LHCR_HWIDTH, crtc->mode.hsync_end - crtc->mode.hsync_start - 1) |
166*c87e859cSMarian Cichy 		FIELD_PREP(IMX21LCDC_LHCR_HBPORCH, crtc->mode.htotal - crtc->mode.hsync_end - 3);
167*c87e859cSMarian Cichy 	writel(lhcr, lcdc->base + IMX21LCDC_LHCR);
168*c87e859cSMarian Cichy 
169*c87e859cSMarian Cichy 	/* VSYNC */
170*c87e859cSMarian Cichy 	lvcr = FIELD_PREP(IMX21LCDC_LVCR_VFPORCH, crtc->mode.vsync_start - crtc->mode.vdisplay) |
171*c87e859cSMarian Cichy 		FIELD_PREP(IMX21LCDC_LVCR_VWIDTH, crtc->mode.vsync_end - crtc->mode.vsync_start) |
172*c87e859cSMarian Cichy 		FIELD_PREP(IMX21LCDC_LVCR_VBPORCH, crtc->mode.vtotal - crtc->mode.vsync_end);
173*c87e859cSMarian Cichy 	writel(lvcr, lcdc->base + IMX21LCDC_LVCR);
174*c87e859cSMarian Cichy 
175*c87e859cSMarian Cichy 	lpcr = readl(lcdc->base + IMX21LCDC_LPCR);
176*c87e859cSMarian Cichy 	lpcr &= ~IMX21LCDC_LPCR_BPIX;
177*c87e859cSMarian Cichy 	lpcr |= FIELD_PREP(IMX21LCDC_LPCR_BPIX, imx_lcdc_get_format(fb->format->format));
178*c87e859cSMarian Cichy 	writel(lpcr, lcdc->base + IMX21LCDC_LPCR);
179*c87e859cSMarian Cichy 
180*c87e859cSMarian Cichy 	/* Virtual Page Width */
181*c87e859cSMarian Cichy 	writel(new_state->fb->pitches[0] / 4, lcdc->base + IMX21LCDC_LVPWR);
182*c87e859cSMarian Cichy 
183*c87e859cSMarian Cichy 	/* Enable PER clock */
184*c87e859cSMarian Cichy 	if (new_state->crtc->enabled)
185*c87e859cSMarian Cichy 		clk_prepare_enable(lcdc->clk_per);
186*c87e859cSMarian Cichy }
187*c87e859cSMarian Cichy 
188*c87e859cSMarian Cichy static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
189*c87e859cSMarian Cichy 				 struct drm_crtc_state *crtc_state,
190*c87e859cSMarian Cichy 				 struct drm_plane_state *plane_state)
191*c87e859cSMarian Cichy {
192*c87e859cSMarian Cichy 	int ret;
193*c87e859cSMarian Cichy 	int clk_div;
194*c87e859cSMarian Cichy 	int bpp;
195*c87e859cSMarian Cichy 	struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
196*c87e859cSMarian Cichy 	struct drm_display_mode *mode = &pipe->crtc.mode;
197*c87e859cSMarian Cichy 	struct drm_display_info *disp_info = &lcdc->connector->display_info;
198*c87e859cSMarian Cichy 	const int hsync_pol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1;
199*c87e859cSMarian Cichy 	const int vsync_pol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1;
200*c87e859cSMarian Cichy 	const int data_enable_pol =
201*c87e859cSMarian Cichy 		(disp_info->bus_flags & DRM_BUS_FLAG_DE_HIGH) ? 0 : 1;
202*c87e859cSMarian Cichy 	const int clk_pol =
203*c87e859cSMarian Cichy 		(disp_info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) ? 0 : 1;
204*c87e859cSMarian Cichy 
205*c87e859cSMarian Cichy 	clk_div = DIV_ROUND_CLOSEST_ULL(clk_get_rate(lcdc->clk_per),
206*c87e859cSMarian Cichy 					mode->clock * 1000);
207*c87e859cSMarian Cichy 	bpp = imx_lcdc_get_format(plane_state->fb->format->format);
208*c87e859cSMarian Cichy 
209*c87e859cSMarian Cichy 	writel(FIELD_PREP(IMX21LCDC_LPCR_PCD, clk_div - 1) |
210*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_LPPOL, hsync_pol) |
211*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_FLMPOL, vsync_pol) |
212*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_OEPOL, data_enable_pol) |
213*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_TFT, 1) |
214*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_COLOR, 1) |
215*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_PBSIZ, 3) |
216*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_BPIX, bpp) |
217*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_SCLKSEL, 1) |
218*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_PIXPOL, 0) |
219*c87e859cSMarian Cichy 	       FIELD_PREP(IMX21LCDC_LPCR_CLKPOL, clk_pol),
220*c87e859cSMarian Cichy 	       lcdc->base + IMX21LCDC_LPCR);
221*c87e859cSMarian Cichy 
222*c87e859cSMarian Cichy 	/* 0px panning offset */
223*c87e859cSMarian Cichy 	writel(0x00000000, lcdc->base + IMX21LCDC_LPOR);
224*c87e859cSMarian Cichy 
225*c87e859cSMarian Cichy 	/* disable hardware cursor */
226*c87e859cSMarian Cichy 	writel(readl(lcdc->base + IMX21LCDC_LCPR) & ~(IMX21LCDC_LCPR_CC0 | IMX21LCDC_LCPR_CC1),
227*c87e859cSMarian Cichy 	       lcdc->base + IMX21LCDC_LCPR);
228*c87e859cSMarian Cichy 
229*c87e859cSMarian Cichy 	ret = clk_prepare_enable(lcdc->clk_ipg);
230*c87e859cSMarian Cichy 	if (ret) {
231*c87e859cSMarian Cichy 		dev_err(pipe->crtc.dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret));
232*c87e859cSMarian Cichy 		return;
233*c87e859cSMarian Cichy 	}
234*c87e859cSMarian Cichy 	ret = clk_prepare_enable(lcdc->clk_ahb);
235*c87e859cSMarian Cichy 	if (ret) {
236*c87e859cSMarian Cichy 		dev_err(pipe->crtc.dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret));
237*c87e859cSMarian Cichy 
238*c87e859cSMarian Cichy 		clk_disable_unprepare(lcdc->clk_ipg);
239*c87e859cSMarian Cichy 
240*c87e859cSMarian Cichy 		return;
241*c87e859cSMarian Cichy 	}
242*c87e859cSMarian Cichy 
243*c87e859cSMarian Cichy 	imx_lcdc_update_hw_registers(pipe, NULL, true);
244*c87e859cSMarian Cichy 
245*c87e859cSMarian Cichy 	/* Enable VBLANK Interrupt */
246*c87e859cSMarian Cichy 	writel(INTR_EOF, lcdc->base + IMX21LCDC_LIER);
247*c87e859cSMarian Cichy }
248*c87e859cSMarian Cichy 
249*c87e859cSMarian Cichy static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe)
250*c87e859cSMarian Cichy {
251*c87e859cSMarian Cichy 	struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
252*c87e859cSMarian Cichy 	struct drm_crtc *crtc = &lcdc->pipe.crtc;
253*c87e859cSMarian Cichy 	struct drm_pending_vblank_event *event;
254*c87e859cSMarian Cichy 
255*c87e859cSMarian Cichy 	clk_disable_unprepare(lcdc->clk_ahb);
256*c87e859cSMarian Cichy 	clk_disable_unprepare(lcdc->clk_ipg);
257*c87e859cSMarian Cichy 
258*c87e859cSMarian Cichy 	if (pipe->crtc.enabled)
259*c87e859cSMarian Cichy 		clk_disable_unprepare(lcdc->clk_per);
260*c87e859cSMarian Cichy 
261*c87e859cSMarian Cichy 	spin_lock_irq(&lcdc->drm.event_lock);
262*c87e859cSMarian Cichy 	event = crtc->state->event;
263*c87e859cSMarian Cichy 	if (event) {
264*c87e859cSMarian Cichy 		crtc->state->event = NULL;
265*c87e859cSMarian Cichy 		drm_crtc_send_vblank_event(crtc, event);
266*c87e859cSMarian Cichy 	}
267*c87e859cSMarian Cichy 	spin_unlock_irq(&lcdc->drm.event_lock);
268*c87e859cSMarian Cichy 
269*c87e859cSMarian Cichy 	/* Disable VBLANK Interrupt */
270*c87e859cSMarian Cichy 	writel(0, lcdc->base + IMX21LCDC_LIER);
271*c87e859cSMarian Cichy }
272*c87e859cSMarian Cichy 
273*c87e859cSMarian Cichy static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe,
274*c87e859cSMarian Cichy 			       struct drm_plane_state *plane_state,
275*c87e859cSMarian Cichy 			       struct drm_crtc_state *crtc_state)
276*c87e859cSMarian Cichy {
277*c87e859cSMarian Cichy 	const struct drm_display_mode *mode = &crtc_state->mode;
278*c87e859cSMarian Cichy 	const struct drm_display_mode *old_mode = &pipe->crtc.state->mode;
279*c87e859cSMarian Cichy 
280*c87e859cSMarian Cichy 	if (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES ||
281*c87e859cSMarian Cichy 	    mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES ||
282*c87e859cSMarian Cichy 	    mode->hdisplay % 0x10) { /* must be multiple of 16 */
283*c87e859cSMarian Cichy 		drm_err(pipe->crtc.dev, "unsupported display mode (%u x %u)\n",
284*c87e859cSMarian Cichy 			mode->hdisplay, mode->vdisplay);
285*c87e859cSMarian Cichy 		return -EINVAL;
286*c87e859cSMarian Cichy 	}
287*c87e859cSMarian Cichy 
288*c87e859cSMarian Cichy 	crtc_state->mode_changed =
289*c87e859cSMarian Cichy 		old_mode->hdisplay != mode->hdisplay ||
290*c87e859cSMarian Cichy 		old_mode->vdisplay != mode->vdisplay;
291*c87e859cSMarian Cichy 
292*c87e859cSMarian Cichy 	return 0;
293*c87e859cSMarian Cichy }
294*c87e859cSMarian Cichy 
295*c87e859cSMarian Cichy static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe,
296*c87e859cSMarian Cichy 				 struct drm_plane_state *old_state)
297*c87e859cSMarian Cichy {
298*c87e859cSMarian Cichy 	struct drm_crtc *crtc = &pipe->crtc;
299*c87e859cSMarian Cichy 	struct drm_pending_vblank_event *event = crtc->state->event;
300*c87e859cSMarian Cichy 	struct drm_plane_state *new_state = pipe->plane.state;
301*c87e859cSMarian Cichy 	struct drm_framebuffer *fb = new_state->fb;
302*c87e859cSMarian Cichy 	struct drm_framebuffer *old_fb = old_state->fb;
303*c87e859cSMarian Cichy 	struct drm_crtc *old_crtc = old_state->crtc;
304*c87e859cSMarian Cichy 	bool mode_changed = false;
305*c87e859cSMarian Cichy 
306*c87e859cSMarian Cichy 	if (old_fb && old_fb->format != fb->format)
307*c87e859cSMarian Cichy 		mode_changed = true;
308*c87e859cSMarian Cichy 	else if (old_crtc != crtc)
309*c87e859cSMarian Cichy 		mode_changed = true;
310*c87e859cSMarian Cichy 
311*c87e859cSMarian Cichy 	imx_lcdc_update_hw_registers(pipe, old_state, mode_changed);
312*c87e859cSMarian Cichy 
313*c87e859cSMarian Cichy 	if (event) {
314*c87e859cSMarian Cichy 		crtc->state->event = NULL;
315*c87e859cSMarian Cichy 
316*c87e859cSMarian Cichy 		spin_lock_irq(&crtc->dev->event_lock);
317*c87e859cSMarian Cichy 
318*c87e859cSMarian Cichy 		if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
319*c87e859cSMarian Cichy 			drm_crtc_arm_vblank_event(crtc, event);
320*c87e859cSMarian Cichy 		else
321*c87e859cSMarian Cichy 			drm_crtc_send_vblank_event(crtc, event);
322*c87e859cSMarian Cichy 
323*c87e859cSMarian Cichy 		spin_unlock_irq(&crtc->dev->event_lock);
324*c87e859cSMarian Cichy 	}
325*c87e859cSMarian Cichy }
326*c87e859cSMarian Cichy 
327*c87e859cSMarian Cichy static const struct drm_simple_display_pipe_funcs imx_lcdc_pipe_funcs = {
328*c87e859cSMarian Cichy 	.enable = imx_lcdc_pipe_enable,
329*c87e859cSMarian Cichy 	.disable = imx_lcdc_pipe_disable,
330*c87e859cSMarian Cichy 	.check = imx_lcdc_pipe_check,
331*c87e859cSMarian Cichy 	.update = imx_lcdc_pipe_update,
332*c87e859cSMarian Cichy };
333*c87e859cSMarian Cichy 
334*c87e859cSMarian Cichy static const struct drm_mode_config_funcs imx_lcdc_mode_config_funcs = {
335*c87e859cSMarian Cichy 	.fb_create = drm_gem_fb_create_with_dirty,
336*c87e859cSMarian Cichy 	.atomic_check = drm_atomic_helper_check,
337*c87e859cSMarian Cichy 	.atomic_commit = drm_atomic_helper_commit,
338*c87e859cSMarian Cichy };
339*c87e859cSMarian Cichy 
340*c87e859cSMarian Cichy static const struct drm_mode_config_helper_funcs imx_lcdc_mode_config_helpers = {
341*c87e859cSMarian Cichy 	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
342*c87e859cSMarian Cichy };
343*c87e859cSMarian Cichy 
344*c87e859cSMarian Cichy static void imx_lcdc_release(struct drm_device *drm)
345*c87e859cSMarian Cichy {
346*c87e859cSMarian Cichy 	struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(drm);
347*c87e859cSMarian Cichy 
348*c87e859cSMarian Cichy 	drm_kms_helper_poll_fini(drm);
349*c87e859cSMarian Cichy 	kfree(lcdc);
350*c87e859cSMarian Cichy }
351*c87e859cSMarian Cichy 
352*c87e859cSMarian Cichy DEFINE_DRM_GEM_DMA_FOPS(imx_lcdc_drm_fops);
353*c87e859cSMarian Cichy 
354*c87e859cSMarian Cichy static struct drm_driver imx_lcdc_drm_driver = {
355*c87e859cSMarian Cichy 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
356*c87e859cSMarian Cichy 	.fops = &imx_lcdc_drm_fops,
357*c87e859cSMarian Cichy 	DRM_GEM_DMA_DRIVER_OPS_VMAP,
358*c87e859cSMarian Cichy 	.release = imx_lcdc_release,
359*c87e859cSMarian Cichy 	.name = "imx-lcdc",
360*c87e859cSMarian Cichy 	.desc = "i.MX LCDC driver",
361*c87e859cSMarian Cichy 	.date = "20200716",
362*c87e859cSMarian Cichy };
363*c87e859cSMarian Cichy 
364*c87e859cSMarian Cichy static const struct of_device_id imx_lcdc_of_dev_id[] = {
365*c87e859cSMarian Cichy 	{
366*c87e859cSMarian Cichy 		.compatible = "fsl,imx21-lcdc",
367*c87e859cSMarian Cichy 	},
368*c87e859cSMarian Cichy 	{
369*c87e859cSMarian Cichy 		.compatible = "fsl,imx25-lcdc",
370*c87e859cSMarian Cichy 	},
371*c87e859cSMarian Cichy 	{ /* sentinel */ }
372*c87e859cSMarian Cichy };
373*c87e859cSMarian Cichy MODULE_DEVICE_TABLE(of, imx_lcdc_of_dev_id);
374*c87e859cSMarian Cichy 
375*c87e859cSMarian Cichy static irqreturn_t imx_lcdc_irq_handler(int irq, void *arg)
376*c87e859cSMarian Cichy {
377*c87e859cSMarian Cichy 	struct imx_lcdc *lcdc = arg;
378*c87e859cSMarian Cichy 	struct drm_crtc *crtc = &lcdc->pipe.crtc;
379*c87e859cSMarian Cichy 	unsigned int status;
380*c87e859cSMarian Cichy 
381*c87e859cSMarian Cichy 	status = readl(lcdc->base + IMX21LCDC_LISR);
382*c87e859cSMarian Cichy 
383*c87e859cSMarian Cichy 	if (status & INTR_EOF) {
384*c87e859cSMarian Cichy 		drm_crtc_handle_vblank(crtc);
385*c87e859cSMarian Cichy 		return IRQ_HANDLED;
386*c87e859cSMarian Cichy 	}
387*c87e859cSMarian Cichy 
388*c87e859cSMarian Cichy 	return IRQ_NONE;
389*c87e859cSMarian Cichy }
390*c87e859cSMarian Cichy 
391*c87e859cSMarian Cichy static int imx_lcdc_probe(struct platform_device *pdev)
392*c87e859cSMarian Cichy {
393*c87e859cSMarian Cichy 	struct imx_lcdc *lcdc;
394*c87e859cSMarian Cichy 	struct drm_device *drm;
395*c87e859cSMarian Cichy 	struct drm_bridge *bridge;
396*c87e859cSMarian Cichy 	int irq;
397*c87e859cSMarian Cichy 	int ret;
398*c87e859cSMarian Cichy 	struct device *dev = &pdev->dev;
399*c87e859cSMarian Cichy 
400*c87e859cSMarian Cichy 	lcdc = devm_drm_dev_alloc(dev, &imx_lcdc_drm_driver,
401*c87e859cSMarian Cichy 				  struct imx_lcdc, drm);
402*c87e859cSMarian Cichy 	if (!lcdc)
403*c87e859cSMarian Cichy 		return -ENOMEM;
404*c87e859cSMarian Cichy 
405*c87e859cSMarian Cichy 	drm = &lcdc->drm;
406*c87e859cSMarian Cichy 
407*c87e859cSMarian Cichy 	lcdc->base = devm_platform_ioremap_resource(pdev, 0);
408*c87e859cSMarian Cichy 	if (IS_ERR(lcdc->base))
409*c87e859cSMarian Cichy 		return dev_err_probe(dev, PTR_ERR(lcdc->base), "Cannot get IO memory\n");
410*c87e859cSMarian Cichy 
411*c87e859cSMarian Cichy 	bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
412*c87e859cSMarian Cichy 	if (IS_ERR(bridge))
413*c87e859cSMarian Cichy 		return dev_err_probe(dev, PTR_ERR(bridge), "Failed to find bridge\n");
414*c87e859cSMarian Cichy 
415*c87e859cSMarian Cichy 	/* Get Clocks */
416*c87e859cSMarian Cichy 	lcdc->clk_ipg = devm_clk_get(dev, "ipg");
417*c87e859cSMarian Cichy 	if (IS_ERR(lcdc->clk_ipg))
418*c87e859cSMarian Cichy 		return dev_err_probe(dev, PTR_ERR(lcdc->clk_ipg), "Failed to get %s clk\n", "ipg");
419*c87e859cSMarian Cichy 
420*c87e859cSMarian Cichy 	lcdc->clk_ahb = devm_clk_get(dev, "ahb");
421*c87e859cSMarian Cichy 	if (IS_ERR(lcdc->clk_ahb))
422*c87e859cSMarian Cichy 		return dev_err_probe(dev, PTR_ERR(lcdc->clk_ahb), "Failed to get %s clk\n", "ahb");
423*c87e859cSMarian Cichy 
424*c87e859cSMarian Cichy 	lcdc->clk_per = devm_clk_get(dev, "per");
425*c87e859cSMarian Cichy 	if (IS_ERR(lcdc->clk_per))
426*c87e859cSMarian Cichy 		return dev_err_probe(dev, PTR_ERR(lcdc->clk_per), "Failed to get %s clk\n", "per");
427*c87e859cSMarian Cichy 
428*c87e859cSMarian Cichy 	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
429*c87e859cSMarian Cichy 	if (ret)
430*c87e859cSMarian Cichy 		return dev_err_probe(dev, ret, "Cannot set DMA Mask\n");
431*c87e859cSMarian Cichy 
432*c87e859cSMarian Cichy 	/* Modeset init */
433*c87e859cSMarian Cichy 	ret = drmm_mode_config_init(drm);
434*c87e859cSMarian Cichy 	if (ret)
435*c87e859cSMarian Cichy 		return dev_err_probe(dev, ret, "Cannot initialize mode configuration structure\n");
436*c87e859cSMarian Cichy 
437*c87e859cSMarian Cichy 	/* CRTC, Plane, Encoder */
438*c87e859cSMarian Cichy 	ret = drm_simple_display_pipe_init(drm, &lcdc->pipe,
439*c87e859cSMarian Cichy 					   &imx_lcdc_pipe_funcs,
440*c87e859cSMarian Cichy 					   imx_lcdc_formats,
441*c87e859cSMarian Cichy 					   ARRAY_SIZE(imx_lcdc_formats), NULL, NULL);
442*c87e859cSMarian Cichy 	if (ret < 0)
443*c87e859cSMarian Cichy 		return dev_err_probe(drm->dev, ret, "Cannot setup simple display pipe\n");
444*c87e859cSMarian Cichy 
445*c87e859cSMarian Cichy 	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
446*c87e859cSMarian Cichy 	if (ret < 0)
447*c87e859cSMarian Cichy 		return dev_err_probe(drm->dev, ret, "Failed to initialize vblank\n");
448*c87e859cSMarian Cichy 
449*c87e859cSMarian Cichy 	ret = drm_bridge_attach(&lcdc->pipe.encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
450*c87e859cSMarian Cichy 	if (ret)
451*c87e859cSMarian Cichy 		return dev_err_probe(drm->dev, ret, "Cannot attach bridge\n");
452*c87e859cSMarian Cichy 
453*c87e859cSMarian Cichy 	lcdc->connector = drm_bridge_connector_init(drm, &lcdc->pipe.encoder);
454*c87e859cSMarian Cichy 	if (IS_ERR(lcdc->connector))
455*c87e859cSMarian Cichy 		return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n");
456*c87e859cSMarian Cichy 
457*c87e859cSMarian Cichy 	drm_connector_attach_encoder(lcdc->connector, &lcdc->pipe.encoder);
458*c87e859cSMarian Cichy 
459*c87e859cSMarian Cichy 	/*
460*c87e859cSMarian Cichy 	 * The LCDC controller does not have an enable bit. The
461*c87e859cSMarian Cichy 	 * controller starts directly when the clocks are enabled.
462*c87e859cSMarian Cichy 	 * If the clocks are enabled when the controller is not yet
463*c87e859cSMarian Cichy 	 * programmed with proper register values (enabled at the
464*c87e859cSMarian Cichy 	 * bootloader, for example) then it just goes into some undefined
465*c87e859cSMarian Cichy 	 * state.
466*c87e859cSMarian Cichy 	 * To avoid this issue, let's enable and disable LCDC IPG,
467*c87e859cSMarian Cichy 	 * PER and AHB clock so that we force some kind of 'reset'
468*c87e859cSMarian Cichy 	 * to the LCDC block.
469*c87e859cSMarian Cichy 	 */
470*c87e859cSMarian Cichy 
471*c87e859cSMarian Cichy 	ret = clk_prepare_enable(lcdc->clk_ipg);
472*c87e859cSMarian Cichy 	if (ret)
473*c87e859cSMarian Cichy 		return dev_err_probe(dev, ret, "Cannot enable ipg clock\n");
474*c87e859cSMarian Cichy 	clk_disable_unprepare(lcdc->clk_ipg);
475*c87e859cSMarian Cichy 
476*c87e859cSMarian Cichy 	ret = clk_prepare_enable(lcdc->clk_per);
477*c87e859cSMarian Cichy 	if (ret)
478*c87e859cSMarian Cichy 		return dev_err_probe(dev, ret, "Cannot enable per clock\n");
479*c87e859cSMarian Cichy 	clk_disable_unprepare(lcdc->clk_per);
480*c87e859cSMarian Cichy 
481*c87e859cSMarian Cichy 	ret = clk_prepare_enable(lcdc->clk_ahb);
482*c87e859cSMarian Cichy 	if (ret)
483*c87e859cSMarian Cichy 		return dev_err_probe(dev, ret, "Cannot enable ahb clock\n");
484*c87e859cSMarian Cichy 	clk_disable_unprepare(lcdc->clk_ahb);
485*c87e859cSMarian Cichy 
486*c87e859cSMarian Cichy 	drm->mode_config.min_width = LCDC_MIN_XRES;
487*c87e859cSMarian Cichy 	drm->mode_config.max_width = LCDC_MAX_XRES;
488*c87e859cSMarian Cichy 	drm->mode_config.min_height = LCDC_MIN_YRES;
489*c87e859cSMarian Cichy 	drm->mode_config.max_height = LCDC_MAX_YRES;
490*c87e859cSMarian Cichy 	drm->mode_config.preferred_depth = 16;
491*c87e859cSMarian Cichy 	drm->mode_config.funcs = &imx_lcdc_mode_config_funcs;
492*c87e859cSMarian Cichy 	drm->mode_config.helper_private = &imx_lcdc_mode_config_helpers;
493*c87e859cSMarian Cichy 
494*c87e859cSMarian Cichy 	drm_mode_config_reset(drm);
495*c87e859cSMarian Cichy 
496*c87e859cSMarian Cichy 	irq = platform_get_irq(pdev, 0);
497*c87e859cSMarian Cichy 	if (irq < 0) {
498*c87e859cSMarian Cichy 		ret = irq;
499*c87e859cSMarian Cichy 		return ret;
500*c87e859cSMarian Cichy 	}
501*c87e859cSMarian Cichy 
502*c87e859cSMarian Cichy 	ret = devm_request_irq(dev, irq, imx_lcdc_irq_handler, 0, "imx-lcdc", lcdc);
503*c87e859cSMarian Cichy 	if (ret < 0)
504*c87e859cSMarian Cichy 		return dev_err_probe(drm->dev, ret, "Failed to install IRQ handler\n");
505*c87e859cSMarian Cichy 
506*c87e859cSMarian Cichy 	platform_set_drvdata(pdev, drm);
507*c87e859cSMarian Cichy 
508*c87e859cSMarian Cichy 	ret = drm_dev_register(&lcdc->drm, 0);
509*c87e859cSMarian Cichy 	if (ret)
510*c87e859cSMarian Cichy 		return dev_err_probe(dev, ret, "Cannot register device\n");
511*c87e859cSMarian Cichy 
512*c87e859cSMarian Cichy 	drm_fbdev_generic_setup(drm, 0);
513*c87e859cSMarian Cichy 
514*c87e859cSMarian Cichy 	return 0;
515*c87e859cSMarian Cichy }
516*c87e859cSMarian Cichy 
517*c87e859cSMarian Cichy static int imx_lcdc_remove(struct platform_device *pdev)
518*c87e859cSMarian Cichy {
519*c87e859cSMarian Cichy 	struct drm_device *drm = platform_get_drvdata(pdev);
520*c87e859cSMarian Cichy 
521*c87e859cSMarian Cichy 	drm_dev_unregister(drm);
522*c87e859cSMarian Cichy 	drm_atomic_helper_shutdown(drm);
523*c87e859cSMarian Cichy 
524*c87e859cSMarian Cichy 	return 0;
525*c87e859cSMarian Cichy }
526*c87e859cSMarian Cichy 
527*c87e859cSMarian Cichy static void imx_lcdc_shutdown(struct platform_device *pdev)
528*c87e859cSMarian Cichy {
529*c87e859cSMarian Cichy 	drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
530*c87e859cSMarian Cichy }
531*c87e859cSMarian Cichy 
532*c87e859cSMarian Cichy static struct platform_driver imx_lcdc_driver = {
533*c87e859cSMarian Cichy 	.driver = {
534*c87e859cSMarian Cichy 		.name = "imx-lcdc",
535*c87e859cSMarian Cichy 		.of_match_table = imx_lcdc_of_dev_id,
536*c87e859cSMarian Cichy 	},
537*c87e859cSMarian Cichy 	.probe = imx_lcdc_probe,
538*c87e859cSMarian Cichy 	.remove = imx_lcdc_remove,
539*c87e859cSMarian Cichy 	.shutdown = imx_lcdc_shutdown,
540*c87e859cSMarian Cichy };
541*c87e859cSMarian Cichy module_platform_driver(imx_lcdc_driver);
542*c87e859cSMarian Cichy 
543*c87e859cSMarian Cichy MODULE_AUTHOR("Marian Cichy <M.Cichy@pengutronix.de>");
544*c87e859cSMarian Cichy MODULE_DESCRIPTION("Freescale i.MX LCDC driver");
545*c87e859cSMarian Cichy MODULE_LICENSE("GPL");
546