1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> |
4 | * Parts of this file were based on sources as follows: |
5 | * |
6 | * Copyright (C) 2006-2008 Intel Corporation |
7 | * Copyright (C) 2007 Amos Lee <amos_lee@storlinksemi.com> |
8 | * Copyright (C) 2007 Dave Airlie <airlied@linux.ie> |
9 | * Copyright (C) 2011 Texas Instruments |
10 | * Copyright (C) 2017 Eric Anholt |
11 | */ |
12 | |
13 | #include <linux/clk.h> |
14 | #include <linux/dma-buf.h> |
15 | #include <linux/of_graph.h> |
16 | #include <linux/delay.h> |
17 | |
18 | #include <drm/drm_fb_dma_helper.h> |
19 | #include <drm/drm_fourcc.h> |
20 | #include <drm/drm_framebuffer.h> |
21 | #include <drm/drm_gem_atomic_helper.h> |
22 | #include <drm/drm_gem_dma_helper.h> |
23 | #include <drm/drm_panel.h> |
24 | #include <drm/drm_vblank.h> |
25 | |
26 | #include "tve200_drm.h" |
27 | |
28 | irqreturn_t tve200_irq(int irq, void *data) |
29 | { |
30 | struct tve200_drm_dev_private *priv = data; |
31 | u32 stat; |
32 | u32 val; |
33 | |
34 | stat = readl(addr: priv->regs + TVE200_INT_STAT); |
35 | |
36 | if (!stat) |
37 | return IRQ_NONE; |
38 | |
39 | /* |
40 | * Vblank IRQ |
41 | * |
42 | * The hardware is a bit tilted: the line stays high after clearing |
43 | * the vblank IRQ, firing many more interrupts. We counter this |
44 | * by toggling the IRQ back and forth from firing at vblank and |
45 | * firing at start of active image, which works around the problem |
46 | * since those occur strictly in sequence, and we get two IRQs for each |
47 | * frame, one at start of Vblank (that we make call into the CRTC) and |
48 | * another one at the start of the image (that we discard). |
49 | */ |
50 | if (stat & TVE200_INT_V_STATUS) { |
51 | val = readl(addr: priv->regs + TVE200_CTRL); |
52 | /* We have an actual start of vsync */ |
53 | if (!(val & TVE200_VSTSTYPE_BITS)) { |
54 | drm_crtc_handle_vblank(crtc: &priv->pipe.crtc); |
55 | /* Toggle trigger to start of active image */ |
56 | val |= TVE200_VSTSTYPE_VAI; |
57 | } else { |
58 | /* Toggle trigger back to start of vsync */ |
59 | val &= ~TVE200_VSTSTYPE_BITS; |
60 | } |
61 | writel(val, addr: priv->regs + TVE200_CTRL); |
62 | } else |
63 | dev_err(priv->drm->dev, "stray IRQ %08x\n" , stat); |
64 | |
65 | /* Clear the interrupt once done */ |
66 | writel(val: stat, addr: priv->regs + TVE200_INT_CLR); |
67 | |
68 | return IRQ_HANDLED; |
69 | } |
70 | |
71 | static int tve200_display_check(struct drm_simple_display_pipe *pipe, |
72 | struct drm_plane_state *pstate, |
73 | struct drm_crtc_state *cstate) |
74 | { |
75 | const struct drm_display_mode *mode = &cstate->mode; |
76 | struct drm_framebuffer *old_fb = pipe->plane.state->fb; |
77 | struct drm_framebuffer *fb = pstate->fb; |
78 | |
79 | /* |
80 | * We support these specific resolutions and nothing else. |
81 | */ |
82 | if (!(mode->hdisplay == 352 && mode->vdisplay == 240) && /* SIF(525) */ |
83 | !(mode->hdisplay == 352 && mode->vdisplay == 288) && /* CIF(625) */ |
84 | !(mode->hdisplay == 640 && mode->vdisplay == 480) && /* VGA */ |
85 | !(mode->hdisplay == 720 && mode->vdisplay == 480) && /* D1 */ |
86 | !(mode->hdisplay == 720 && mode->vdisplay == 576)) { /* D1 */ |
87 | DRM_DEBUG_KMS("unsupported display mode (%u x %u)\n" , |
88 | mode->hdisplay, mode->vdisplay); |
89 | return -EINVAL; |
90 | } |
91 | |
92 | if (fb) { |
93 | u32 offset = drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 0); |
94 | |
95 | /* FB base address must be dword aligned. */ |
96 | if (offset & 3) { |
97 | DRM_DEBUG_KMS("FB not 32-bit aligned\n" ); |
98 | return -EINVAL; |
99 | } |
100 | |
101 | /* |
102 | * There's no pitch register, the mode's hdisplay |
103 | * controls this. |
104 | */ |
105 | if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) { |
106 | DRM_DEBUG_KMS("can't handle pitches\n" ); |
107 | return -EINVAL; |
108 | } |
109 | |
110 | /* |
111 | * We can't change the FB format in a flicker-free |
112 | * manner (and only update it during CRTC enable). |
113 | */ |
114 | if (old_fb && old_fb->format != fb->format) |
115 | cstate->mode_changed = true; |
116 | } |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static void tve200_display_enable(struct drm_simple_display_pipe *pipe, |
122 | struct drm_crtc_state *cstate, |
123 | struct drm_plane_state *plane_state) |
124 | { |
125 | struct drm_crtc *crtc = &pipe->crtc; |
126 | struct drm_plane *plane = &pipe->plane; |
127 | struct drm_device *drm = crtc->dev; |
128 | struct tve200_drm_dev_private *priv = drm->dev_private; |
129 | const struct drm_display_mode *mode = &cstate->mode; |
130 | struct drm_framebuffer *fb = plane->state->fb; |
131 | struct drm_connector *connector = priv->connector; |
132 | u32 format = fb->format->format; |
133 | u32 ctrl1 = 0; |
134 | int retries; |
135 | |
136 | clk_prepare_enable(clk: priv->clk); |
137 | |
138 | /* Reset the TVE200 and wait for it to come back online */ |
139 | writel(TVE200_CTRL_4_RESET, addr: priv->regs + TVE200_CTRL_4); |
140 | for (retries = 0; retries < 5; retries++) { |
141 | usleep_range(min: 30000, max: 50000); |
142 | if (readl(addr: priv->regs + TVE200_CTRL_4) & TVE200_CTRL_4_RESET) |
143 | continue; |
144 | else |
145 | break; |
146 | } |
147 | if (retries == 5 && |
148 | readl(addr: priv->regs + TVE200_CTRL_4) & TVE200_CTRL_4_RESET) { |
149 | dev_err(drm->dev, "can't get hardware out of reset\n" ); |
150 | return; |
151 | } |
152 | |
153 | /* Function 1 */ |
154 | ctrl1 |= TVE200_CTRL_CSMODE; |
155 | /* Interlace mode for CCIR656: parameterize? */ |
156 | ctrl1 |= TVE200_CTRL_NONINTERLACE; |
157 | /* 32 words per burst */ |
158 | ctrl1 |= TVE200_CTRL_BURST_32_WORDS; |
159 | /* 16 retries */ |
160 | ctrl1 |= TVE200_CTRL_RETRYCNT_16; |
161 | /* NTSC mode: parametrize? */ |
162 | ctrl1 |= TVE200_CTRL_NTSC; |
163 | |
164 | /* Vsync IRQ at start of Vsync at first */ |
165 | ctrl1 |= TVE200_VSTSTYPE_VSYNC; |
166 | |
167 | if (connector->display_info.bus_flags & |
168 | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) |
169 | ctrl1 |= TVE200_CTRL_TVCLKP; |
170 | |
171 | if ((mode->hdisplay == 352 && mode->vdisplay == 240) || /* SIF(525) */ |
172 | (mode->hdisplay == 352 && mode->vdisplay == 288)) { /* CIF(625) */ |
173 | ctrl1 |= TVE200_CTRL_IPRESOL_CIF; |
174 | dev_info(drm->dev, "CIF mode\n" ); |
175 | } else if (mode->hdisplay == 640 && mode->vdisplay == 480) { |
176 | ctrl1 |= TVE200_CTRL_IPRESOL_VGA; |
177 | dev_info(drm->dev, "VGA mode\n" ); |
178 | } else if ((mode->hdisplay == 720 && mode->vdisplay == 480) || |
179 | (mode->hdisplay == 720 && mode->vdisplay == 576)) { |
180 | ctrl1 |= TVE200_CTRL_IPRESOL_D1; |
181 | dev_info(drm->dev, "D1 mode\n" ); |
182 | } |
183 | |
184 | if (format & DRM_FORMAT_BIG_ENDIAN) { |
185 | ctrl1 |= TVE200_CTRL_BBBP; |
186 | format &= ~DRM_FORMAT_BIG_ENDIAN; |
187 | } |
188 | |
189 | switch (format) { |
190 | case DRM_FORMAT_XRGB8888: |
191 | ctrl1 |= TVE200_IPDMOD_RGB888; |
192 | break; |
193 | case DRM_FORMAT_RGB565: |
194 | ctrl1 |= TVE200_IPDMOD_RGB565; |
195 | break; |
196 | case DRM_FORMAT_XRGB1555: |
197 | ctrl1 |= TVE200_IPDMOD_RGB555; |
198 | break; |
199 | case DRM_FORMAT_XBGR8888: |
200 | ctrl1 |= TVE200_IPDMOD_RGB888 | TVE200_BGR; |
201 | break; |
202 | case DRM_FORMAT_BGR565: |
203 | ctrl1 |= TVE200_IPDMOD_RGB565 | TVE200_BGR; |
204 | break; |
205 | case DRM_FORMAT_XBGR1555: |
206 | ctrl1 |= TVE200_IPDMOD_RGB555 | TVE200_BGR; |
207 | break; |
208 | case DRM_FORMAT_YUYV: |
209 | ctrl1 |= TVE200_IPDMOD_YUV422; |
210 | ctrl1 |= TVE200_CTRL_YCBCRODR_CR0Y1CB0Y0; |
211 | break; |
212 | case DRM_FORMAT_YVYU: |
213 | ctrl1 |= TVE200_IPDMOD_YUV422; |
214 | ctrl1 |= TVE200_CTRL_YCBCRODR_CB0Y1CR0Y0; |
215 | break; |
216 | case DRM_FORMAT_UYVY: |
217 | ctrl1 |= TVE200_IPDMOD_YUV422; |
218 | ctrl1 |= TVE200_CTRL_YCBCRODR_Y1CR0Y0CB0; |
219 | break; |
220 | case DRM_FORMAT_VYUY: |
221 | ctrl1 |= TVE200_IPDMOD_YUV422; |
222 | ctrl1 |= TVE200_CTRL_YCBCRODR_Y1CB0Y0CR0; |
223 | break; |
224 | case DRM_FORMAT_YUV420: |
225 | ctrl1 |= TVE200_CTRL_YUV420; |
226 | ctrl1 |= TVE200_IPDMOD_YUV420; |
227 | break; |
228 | default: |
229 | dev_err(drm->dev, "Unknown FB format 0x%08x\n" , |
230 | fb->format->format); |
231 | break; |
232 | } |
233 | |
234 | ctrl1 |= TVE200_TVEEN; |
235 | |
236 | /* Turn it on */ |
237 | writel(val: ctrl1, addr: priv->regs + TVE200_CTRL); |
238 | |
239 | drm_crtc_vblank_on(crtc); |
240 | } |
241 | |
242 | static void tve200_display_disable(struct drm_simple_display_pipe *pipe) |
243 | { |
244 | struct drm_crtc *crtc = &pipe->crtc; |
245 | struct drm_device *drm = crtc->dev; |
246 | struct tve200_drm_dev_private *priv = drm->dev_private; |
247 | |
248 | drm_crtc_vblank_off(crtc); |
249 | |
250 | /* Disable put into reset and Power Down */ |
251 | writel(val: 0, addr: priv->regs + TVE200_CTRL); |
252 | writel(TVE200_CTRL_4_RESET, addr: priv->regs + TVE200_CTRL_4); |
253 | |
254 | clk_disable_unprepare(clk: priv->clk); |
255 | } |
256 | |
257 | static void tve200_display_update(struct drm_simple_display_pipe *pipe, |
258 | struct drm_plane_state *old_pstate) |
259 | { |
260 | struct drm_crtc *crtc = &pipe->crtc; |
261 | struct drm_device *drm = crtc->dev; |
262 | struct tve200_drm_dev_private *priv = drm->dev_private; |
263 | struct drm_pending_vblank_event *event = crtc->state->event; |
264 | struct drm_plane *plane = &pipe->plane; |
265 | struct drm_plane_state *pstate = plane->state; |
266 | struct drm_framebuffer *fb = pstate->fb; |
267 | |
268 | if (fb) { |
269 | /* For RGB, the Y component is used as base address */ |
270 | writel(val: drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 0), |
271 | addr: priv->regs + TVE200_Y_FRAME_BASE_ADDR); |
272 | |
273 | /* For three plane YUV we need two more addresses */ |
274 | if (fb->format->format == DRM_FORMAT_YUV420) { |
275 | writel(val: drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 1), |
276 | addr: priv->regs + TVE200_U_FRAME_BASE_ADDR); |
277 | writel(val: drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 2), |
278 | addr: priv->regs + TVE200_V_FRAME_BASE_ADDR); |
279 | } |
280 | } |
281 | |
282 | if (event) { |
283 | crtc->state->event = NULL; |
284 | |
285 | spin_lock_irq(lock: &crtc->dev->event_lock); |
286 | if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) |
287 | drm_crtc_arm_vblank_event(crtc, e: event); |
288 | else |
289 | drm_crtc_send_vblank_event(crtc, e: event); |
290 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
291 | } |
292 | } |
293 | |
294 | static int tve200_display_enable_vblank(struct drm_simple_display_pipe *pipe) |
295 | { |
296 | struct drm_crtc *crtc = &pipe->crtc; |
297 | struct drm_device *drm = crtc->dev; |
298 | struct tve200_drm_dev_private *priv = drm->dev_private; |
299 | |
300 | /* Clear any IRQs and enable */ |
301 | writel(val: 0xFF, addr: priv->regs + TVE200_INT_CLR); |
302 | writel(TVE200_INT_V_STATUS, addr: priv->regs + TVE200_INT_EN); |
303 | return 0; |
304 | } |
305 | |
306 | static void tve200_display_disable_vblank(struct drm_simple_display_pipe *pipe) |
307 | { |
308 | struct drm_crtc *crtc = &pipe->crtc; |
309 | struct drm_device *drm = crtc->dev; |
310 | struct tve200_drm_dev_private *priv = drm->dev_private; |
311 | |
312 | writel(val: 0, addr: priv->regs + TVE200_INT_EN); |
313 | } |
314 | |
315 | static const struct drm_simple_display_pipe_funcs tve200_display_funcs = { |
316 | .check = tve200_display_check, |
317 | .enable = tve200_display_enable, |
318 | .disable = tve200_display_disable, |
319 | .update = tve200_display_update, |
320 | .enable_vblank = tve200_display_enable_vblank, |
321 | .disable_vblank = tve200_display_disable_vblank, |
322 | }; |
323 | |
324 | int tve200_display_init(struct drm_device *drm) |
325 | { |
326 | struct tve200_drm_dev_private *priv = drm->dev_private; |
327 | int ret; |
328 | static const u32 formats[] = { |
329 | DRM_FORMAT_XRGB8888, |
330 | DRM_FORMAT_XBGR8888, |
331 | DRM_FORMAT_RGB565, |
332 | DRM_FORMAT_BGR565, |
333 | DRM_FORMAT_XRGB1555, |
334 | DRM_FORMAT_XBGR1555, |
335 | /* |
336 | * The controller actually supports any YCbCr ordering, |
337 | * for packed YCbCr. This just lists the orderings that |
338 | * DRM supports. |
339 | */ |
340 | DRM_FORMAT_YUYV, |
341 | DRM_FORMAT_YVYU, |
342 | DRM_FORMAT_UYVY, |
343 | DRM_FORMAT_VYUY, |
344 | /* This uses three planes */ |
345 | DRM_FORMAT_YUV420, |
346 | }; |
347 | |
348 | ret = drm_simple_display_pipe_init(dev: drm, pipe: &priv->pipe, |
349 | funcs: &tve200_display_funcs, |
350 | formats, ARRAY_SIZE(formats), |
351 | NULL, |
352 | connector: priv->connector); |
353 | if (ret) |
354 | return ret; |
355 | |
356 | return 0; |
357 | } |
358 | |