diff options
Diffstat (limited to 'drivers/gpu/drm/tilcdc/tilcdc_crtc.c')
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c new file mode 100644 index 000000000000..5dd3c7d031d5 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments | ||
3 | * Author: Rob Clark <robdclark@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <linux/kfifo.h> | ||
19 | |||
20 | #include "tilcdc_drv.h" | ||
21 | #include "tilcdc_regs.h" | ||
22 | |||
23 | struct tilcdc_crtc { | ||
24 | struct drm_crtc base; | ||
25 | |||
26 | const struct tilcdc_panel_info *info; | ||
27 | uint32_t dirty; | ||
28 | dma_addr_t start, end; | ||
29 | struct drm_pending_vblank_event *event; | ||
30 | int dpms; | ||
31 | wait_queue_head_t frame_done_wq; | ||
32 | bool frame_done; | ||
33 | |||
34 | /* fb currently set to scanout 0/1: */ | ||
35 | struct drm_framebuffer *scanout[2]; | ||
36 | |||
37 | /* for deferred fb unref's: */ | ||
38 | DECLARE_KFIFO_PTR(unref_fifo, struct drm_framebuffer *); | ||
39 | struct work_struct work; | ||
40 | }; | ||
41 | #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) | ||
42 | |||
43 | static void unref_worker(struct work_struct *work) | ||
44 | { | ||
45 | struct tilcdc_crtc *tilcdc_crtc = container_of(work, struct tilcdc_crtc, work); | ||
46 | struct drm_device *dev = tilcdc_crtc->base.dev; | ||
47 | struct drm_framebuffer *fb; | ||
48 | |||
49 | mutex_lock(&dev->mode_config.mutex); | ||
50 | while (kfifo_get(&tilcdc_crtc->unref_fifo, &fb)) | ||
51 | drm_framebuffer_unreference(fb); | ||
52 | mutex_unlock(&dev->mode_config.mutex); | ||
53 | } | ||
54 | |||
55 | static void set_scanout(struct drm_crtc *crtc, int n) | ||
56 | { | ||
57 | static const uint32_t base_reg[] = { | ||
58 | LCDC_DMA_FB_BASE_ADDR_0_REG, LCDC_DMA_FB_BASE_ADDR_1_REG, | ||
59 | }; | ||
60 | static const uint32_t ceil_reg[] = { | ||
61 | LCDC_DMA_FB_CEILING_ADDR_0_REG, LCDC_DMA_FB_CEILING_ADDR_1_REG, | ||
62 | }; | ||
63 | static const uint32_t stat[] = { | ||
64 | LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1, | ||
65 | }; | ||
66 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
67 | struct drm_device *dev = crtc->dev; | ||
68 | |||
69 | pm_runtime_get_sync(dev->dev); | ||
70 | tilcdc_write(dev, base_reg[n], tilcdc_crtc->start); | ||
71 | tilcdc_write(dev, ceil_reg[n], tilcdc_crtc->end); | ||
72 | if (tilcdc_crtc->scanout[n]) { | ||
73 | if (kfifo_put(&tilcdc_crtc->unref_fifo, | ||
74 | (const struct drm_framebuffer **)&tilcdc_crtc->scanout[n])) { | ||
75 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
76 | queue_work(priv->wq, &tilcdc_crtc->work); | ||
77 | } else { | ||
78 | dev_err(dev->dev, "unref fifo full!\n"); | ||
79 | drm_framebuffer_unreference(tilcdc_crtc->scanout[n]); | ||
80 | } | ||
81 | } | ||
82 | tilcdc_crtc->scanout[n] = crtc->fb; | ||
83 | drm_framebuffer_reference(tilcdc_crtc->scanout[n]); | ||
84 | tilcdc_crtc->dirty &= ~stat[n]; | ||
85 | pm_runtime_put_sync(dev->dev); | ||
86 | } | ||
87 | |||
88 | static void update_scanout(struct drm_crtc *crtc) | ||
89 | { | ||
90 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
91 | struct drm_device *dev = crtc->dev; | ||
92 | struct drm_framebuffer *fb = crtc->fb; | ||
93 | struct drm_gem_cma_object *gem; | ||
94 | unsigned int depth, bpp; | ||
95 | |||
96 | drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); | ||
97 | gem = drm_fb_cma_get_gem_obj(fb, 0); | ||
98 | |||
99 | tilcdc_crtc->start = gem->paddr + fb->offsets[0] + | ||
100 | (crtc->y * fb->pitches[0]) + (crtc->x * bpp/8); | ||
101 | |||
102 | tilcdc_crtc->end = tilcdc_crtc->start + | ||
103 | (crtc->mode.vdisplay * fb->pitches[0]); | ||
104 | |||
105 | if (tilcdc_crtc->dpms == DRM_MODE_DPMS_ON) { | ||
106 | /* already enabled, so just mark the frames that need | ||
107 | * updating and they will be updated on vblank: | ||
108 | */ | ||
109 | tilcdc_crtc->dirty |= LCDC_END_OF_FRAME0 | LCDC_END_OF_FRAME1; | ||
110 | drm_vblank_get(dev, 0); | ||
111 | } else { | ||
112 | /* not enabled yet, so update registers immediately: */ | ||
113 | set_scanout(crtc, 0); | ||
114 | set_scanout(crtc, 1); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static void start(struct drm_crtc *crtc) | ||
119 | { | ||
120 | struct drm_device *dev = crtc->dev; | ||
121 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
122 | |||
123 | if (priv->rev == 2) { | ||
124 | tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); | ||
125 | msleep(1); | ||
126 | tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); | ||
127 | msleep(1); | ||
128 | } | ||
129 | |||
130 | tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); | ||
131 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); | ||
132 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); | ||
133 | } | ||
134 | |||
135 | static void stop(struct drm_crtc *crtc) | ||
136 | { | ||
137 | struct drm_device *dev = crtc->dev; | ||
138 | |||
139 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); | ||
140 | } | ||
141 | |||
142 | static void tilcdc_crtc_destroy(struct drm_crtc *crtc) | ||
143 | { | ||
144 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
145 | |||
146 | WARN_ON(tilcdc_crtc->dpms == DRM_MODE_DPMS_ON); | ||
147 | |||
148 | drm_crtc_cleanup(crtc); | ||
149 | WARN_ON(!kfifo_is_empty(&tilcdc_crtc->unref_fifo)); | ||
150 | kfifo_free(&tilcdc_crtc->unref_fifo); | ||
151 | kfree(tilcdc_crtc); | ||
152 | } | ||
153 | |||
154 | static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, | ||
155 | struct drm_framebuffer *fb, | ||
156 | struct drm_pending_vblank_event *event) | ||
157 | { | ||
158 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
159 | struct drm_device *dev = crtc->dev; | ||
160 | |||
161 | if (tilcdc_crtc->event) { | ||
162 | dev_err(dev->dev, "already pending page flip!\n"); | ||
163 | return -EBUSY; | ||
164 | } | ||
165 | |||
166 | crtc->fb = fb; | ||
167 | tilcdc_crtc->event = event; | ||
168 | update_scanout(crtc); | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
174 | { | ||
175 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
176 | struct drm_device *dev = crtc->dev; | ||
177 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
178 | |||
179 | /* we really only care about on or off: */ | ||
180 | if (mode != DRM_MODE_DPMS_ON) | ||
181 | mode = DRM_MODE_DPMS_OFF; | ||
182 | |||
183 | if (tilcdc_crtc->dpms == mode) | ||
184 | return; | ||
185 | |||
186 | tilcdc_crtc->dpms = mode; | ||
187 | |||
188 | pm_runtime_get_sync(dev->dev); | ||
189 | |||
190 | if (mode == DRM_MODE_DPMS_ON) { | ||
191 | pm_runtime_forbid(dev->dev); | ||
192 | start(crtc); | ||
193 | } else { | ||
194 | tilcdc_crtc->frame_done = false; | ||
195 | stop(crtc); | ||
196 | |||
197 | /* if necessary wait for framedone irq which will still come | ||
198 | * before putting things to sleep.. | ||
199 | */ | ||
200 | if (priv->rev == 2) { | ||
201 | int ret = wait_event_timeout( | ||
202 | tilcdc_crtc->frame_done_wq, | ||
203 | tilcdc_crtc->frame_done, | ||
204 | msecs_to_jiffies(50)); | ||
205 | if (ret == 0) | ||
206 | dev_err(dev->dev, "timeout waiting for framedone\n"); | ||
207 | } | ||
208 | pm_runtime_allow(dev->dev); | ||
209 | } | ||
210 | |||
211 | pm_runtime_put_sync(dev->dev); | ||
212 | } | ||
213 | |||
214 | static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, | ||
215 | const struct drm_display_mode *mode, | ||
216 | struct drm_display_mode *adjusted_mode) | ||
217 | { | ||
218 | return true; | ||
219 | } | ||
220 | |||
221 | static void tilcdc_crtc_prepare(struct drm_crtc *crtc) | ||
222 | { | ||
223 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
224 | } | ||
225 | |||
226 | static void tilcdc_crtc_commit(struct drm_crtc *crtc) | ||
227 | { | ||
228 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | ||
229 | } | ||
230 | |||
231 | static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, | ||
232 | struct drm_display_mode *mode, | ||
233 | struct drm_display_mode *adjusted_mode, | ||
234 | int x, int y, | ||
235 | struct drm_framebuffer *old_fb) | ||
236 | { | ||
237 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
238 | struct drm_device *dev = crtc->dev; | ||
239 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
240 | const struct tilcdc_panel_info *info = tilcdc_crtc->info; | ||
241 | uint32_t reg, hbp, hfp, hsw, vbp, vfp, vsw; | ||
242 | int ret; | ||
243 | |||
244 | ret = tilcdc_crtc_mode_valid(crtc, mode); | ||
245 | if (WARN_ON(ret)) | ||
246 | return ret; | ||
247 | |||
248 | if (WARN_ON(!info)) | ||
249 | return -EINVAL; | ||
250 | |||
251 | pm_runtime_get_sync(dev->dev); | ||
252 | |||
253 | /* Configure the Burst Size and fifo threshold of DMA: */ | ||
254 | reg = tilcdc_read(dev, LCDC_DMA_CTRL_REG) & ~0x00000770; | ||
255 | switch (info->dma_burst_sz) { | ||
256 | case 1: | ||
257 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_1); | ||
258 | break; | ||
259 | case 2: | ||
260 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_2); | ||
261 | break; | ||
262 | case 4: | ||
263 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_4); | ||
264 | break; | ||
265 | case 8: | ||
266 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_8); | ||
267 | break; | ||
268 | case 16: | ||
269 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_16); | ||
270 | break; | ||
271 | default: | ||
272 | return -EINVAL; | ||
273 | } | ||
274 | reg |= (info->fifo_th << 8); | ||
275 | tilcdc_write(dev, LCDC_DMA_CTRL_REG, reg); | ||
276 | |||
277 | /* Configure timings: */ | ||
278 | hbp = mode->htotal - mode->hsync_end; | ||
279 | hfp = mode->hsync_start - mode->hdisplay; | ||
280 | hsw = mode->hsync_end - mode->hsync_start; | ||
281 | vbp = mode->vtotal - mode->vsync_end; | ||
282 | vfp = mode->vsync_start - mode->vdisplay; | ||
283 | vsw = mode->vsync_end - mode->vsync_start; | ||
284 | |||
285 | DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u", | ||
286 | mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw); | ||
287 | |||
288 | /* Configure the AC Bias Period and Number of Transitions per Interrupt: */ | ||
289 | reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00; | ||
290 | reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) | | ||
291 | LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt); | ||
292 | if (priv->rev == 2) { | ||
293 | reg |= (hfp & 0x300) >> 8; | ||
294 | reg |= (hbp & 0x300) >> 4; | ||
295 | reg |= (hsw & 0x3c0) << 21; | ||
296 | } | ||
297 | tilcdc_write(dev, LCDC_RASTER_TIMING_2_REG, reg); | ||
298 | |||
299 | reg = (((mode->hdisplay >> 4) - 1) << 4) | | ||
300 | ((hbp & 0xff) << 24) | | ||
301 | ((hfp & 0xff) << 16) | | ||
302 | ((hsw & 0x3f) << 10); | ||
303 | if (priv->rev == 2) | ||
304 | reg |= (((mode->hdisplay >> 4) - 1) & 0x40) >> 3; | ||
305 | tilcdc_write(dev, LCDC_RASTER_TIMING_0_REG, reg); | ||
306 | |||
307 | reg = ((mode->vdisplay - 1) & 0x3ff) | | ||
308 | ((vbp & 0xff) << 24) | | ||
309 | ((vfp & 0xff) << 16) | | ||
310 | ((vsw & 0x3f) << 10); | ||
311 | tilcdc_write(dev, LCDC_RASTER_TIMING_1_REG, reg); | ||
312 | |||
313 | /* Configure display type: */ | ||
314 | reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) & | ||
315 | ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE | | ||
316 | LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | 0x000ff000); | ||
317 | reg |= LCDC_TFT_MODE; /* no monochrome/passive support */ | ||
318 | if (info->tft_alt_mode) | ||
319 | reg |= LCDC_TFT_ALT_ENABLE; | ||
320 | if (priv->rev == 2) { | ||
321 | unsigned int depth, bpp; | ||
322 | |||
323 | drm_fb_get_bpp_depth(crtc->fb->pixel_format, &depth, &bpp); | ||
324 | switch (bpp) { | ||
325 | case 16: | ||
326 | break; | ||
327 | case 32: | ||
328 | reg |= LCDC_V2_TFT_24BPP_UNPACK; | ||
329 | /* fallthrough */ | ||
330 | case 24: | ||
331 | reg |= LCDC_V2_TFT_24BPP_MODE; | ||
332 | break; | ||
333 | default: | ||
334 | dev_err(dev->dev, "invalid pixel format\n"); | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | } | ||
338 | reg |= info->fdd < 12; | ||
339 | tilcdc_write(dev, LCDC_RASTER_CTRL_REG, reg); | ||
340 | |||
341 | if (info->invert_pxl_clk) | ||
342 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); | ||
343 | else | ||
344 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); | ||
345 | |||
346 | if (info->sync_ctrl) | ||
347 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); | ||
348 | else | ||
349 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); | ||
350 | |||
351 | if (info->sync_edge) | ||
352 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); | ||
353 | else | ||
354 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); | ||
355 | |||
356 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
357 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); | ||
358 | else | ||
359 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); | ||
360 | |||
361 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
362 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); | ||
363 | else | ||
364 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); | ||
365 | |||
366 | if (info->raster_order) | ||
367 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); | ||
368 | else | ||
369 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); | ||
370 | |||
371 | |||
372 | update_scanout(crtc); | ||
373 | tilcdc_crtc_update_clk(crtc); | ||
374 | |||
375 | pm_runtime_put_sync(dev->dev); | ||
376 | |||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
381 | struct drm_framebuffer *old_fb) | ||
382 | { | ||
383 | update_scanout(crtc); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static void tilcdc_crtc_load_lut(struct drm_crtc *crtc) | ||
388 | { | ||
389 | } | ||
390 | |||
391 | static const struct drm_crtc_funcs tilcdc_crtc_funcs = { | ||
392 | .destroy = tilcdc_crtc_destroy, | ||
393 | .set_config = drm_crtc_helper_set_config, | ||
394 | .page_flip = tilcdc_crtc_page_flip, | ||
395 | }; | ||
396 | |||
397 | static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = { | ||
398 | .dpms = tilcdc_crtc_dpms, | ||
399 | .mode_fixup = tilcdc_crtc_mode_fixup, | ||
400 | .prepare = tilcdc_crtc_prepare, | ||
401 | .commit = tilcdc_crtc_commit, | ||
402 | .mode_set = tilcdc_crtc_mode_set, | ||
403 | .mode_set_base = tilcdc_crtc_mode_set_base, | ||
404 | .load_lut = tilcdc_crtc_load_lut, | ||
405 | }; | ||
406 | |||
407 | int tilcdc_crtc_max_width(struct drm_crtc *crtc) | ||
408 | { | ||
409 | struct drm_device *dev = crtc->dev; | ||
410 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
411 | int max_width = 0; | ||
412 | |||
413 | if (priv->rev == 1) | ||
414 | max_width = 1024; | ||
415 | else if (priv->rev == 2) | ||
416 | max_width = 2048; | ||
417 | |||
418 | return max_width; | ||
419 | } | ||
420 | |||
421 | int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode) | ||
422 | { | ||
423 | struct tilcdc_drm_private *priv = crtc->dev->dev_private; | ||
424 | unsigned int bandwidth; | ||
425 | |||
426 | if (mode->hdisplay > tilcdc_crtc_max_width(crtc)) | ||
427 | return MODE_VIRTUAL_X; | ||
428 | |||
429 | /* width must be multiple of 16 */ | ||
430 | if (mode->hdisplay & 0xf) | ||
431 | return MODE_VIRTUAL_X; | ||
432 | |||
433 | if (mode->vdisplay > 2048) | ||
434 | return MODE_VIRTUAL_Y; | ||
435 | |||
436 | /* filter out modes that would require too much memory bandwidth: */ | ||
437 | bandwidth = mode->hdisplay * mode->vdisplay * drm_mode_vrefresh(mode); | ||
438 | if (bandwidth > priv->max_bandwidth) | ||
439 | return MODE_BAD; | ||
440 | |||
441 | return MODE_OK; | ||
442 | } | ||
443 | |||
444 | void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, | ||
445 | const struct tilcdc_panel_info *info) | ||
446 | { | ||
447 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
448 | tilcdc_crtc->info = info; | ||
449 | } | ||
450 | |||
451 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc) | ||
452 | { | ||
453 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
454 | struct drm_device *dev = crtc->dev; | ||
455 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
456 | int dpms = tilcdc_crtc->dpms; | ||
457 | unsigned int lcd_clk, div; | ||
458 | int ret; | ||
459 | |||
460 | pm_runtime_get_sync(dev->dev); | ||
461 | |||
462 | if (dpms == DRM_MODE_DPMS_ON) | ||
463 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
464 | |||
465 | /* in raster mode, minimum divisor is 2: */ | ||
466 | ret = clk_set_rate(priv->disp_clk, crtc->mode.clock * 1000 * 2); | ||
467 | if (ret) { | ||
468 | dev_err(dev->dev, "failed to set display clock rate to: %d\n", | ||
469 | crtc->mode.clock); | ||
470 | goto out; | ||
471 | } | ||
472 | |||
473 | lcd_clk = clk_get_rate(priv->clk); | ||
474 | div = lcd_clk / (crtc->mode.clock * 1000); | ||
475 | |||
476 | DBG("lcd_clk=%u, mode clock=%d, div=%u", lcd_clk, crtc->mode.clock, div); | ||
477 | DBG("fck=%lu, dpll_disp_ck=%lu", clk_get_rate(priv->clk), clk_get_rate(priv->disp_clk)); | ||
478 | |||
479 | /* Configure the LCD clock divisor. */ | ||
480 | tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(div) | | ||
481 | LCDC_RASTER_MODE); | ||
482 | |||
483 | if (priv->rev == 2) | ||
484 | tilcdc_set(dev, LCDC_CLK_ENABLE_REG, | ||
485 | LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN | | ||
486 | LCDC_V2_CORE_CLK_EN); | ||
487 | |||
488 | if (dpms == DRM_MODE_DPMS_ON) | ||
489 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | ||
490 | |||
491 | out: | ||
492 | pm_runtime_put_sync(dev->dev); | ||
493 | } | ||
494 | |||
495 | irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) | ||
496 | { | ||
497 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
498 | struct drm_device *dev = crtc->dev; | ||
499 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
500 | uint32_t stat = tilcdc_read_irqstatus(dev); | ||
501 | |||
502 | if ((stat & LCDC_SYNC_LOST) && (stat & LCDC_FIFO_UNDERFLOW)) { | ||
503 | stop(crtc); | ||
504 | dev_err(dev->dev, "error: %08x\n", stat); | ||
505 | tilcdc_clear_irqstatus(dev, stat); | ||
506 | start(crtc); | ||
507 | } else if (stat & LCDC_PL_LOAD_DONE) { | ||
508 | tilcdc_clear_irqstatus(dev, stat); | ||
509 | } else { | ||
510 | struct drm_pending_vblank_event *event; | ||
511 | unsigned long flags; | ||
512 | uint32_t dirty = tilcdc_crtc->dirty & stat; | ||
513 | |||
514 | tilcdc_clear_irqstatus(dev, stat); | ||
515 | |||
516 | if (dirty & LCDC_END_OF_FRAME0) | ||
517 | set_scanout(crtc, 0); | ||
518 | |||
519 | if (dirty & LCDC_END_OF_FRAME1) | ||
520 | set_scanout(crtc, 1); | ||
521 | |||
522 | drm_handle_vblank(dev, 0); | ||
523 | |||
524 | spin_lock_irqsave(&dev->event_lock, flags); | ||
525 | event = tilcdc_crtc->event; | ||
526 | tilcdc_crtc->event = NULL; | ||
527 | if (event) | ||
528 | drm_send_vblank_event(dev, 0, event); | ||
529 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
530 | |||
531 | if (dirty && !tilcdc_crtc->dirty) | ||
532 | drm_vblank_put(dev, 0); | ||
533 | } | ||
534 | |||
535 | if (priv->rev == 2) { | ||
536 | if (stat & LCDC_FRAME_DONE) { | ||
537 | tilcdc_crtc->frame_done = true; | ||
538 | wake_up(&tilcdc_crtc->frame_done_wq); | ||
539 | } | ||
540 | tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); | ||
541 | } | ||
542 | |||
543 | return IRQ_HANDLED; | ||
544 | } | ||
545 | |||
546 | void tilcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) | ||
547 | { | ||
548 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
549 | struct drm_pending_vblank_event *event; | ||
550 | struct drm_device *dev = crtc->dev; | ||
551 | unsigned long flags; | ||
552 | |||
553 | /* Destroy the pending vertical blanking event associated with the | ||
554 | * pending page flip, if any, and disable vertical blanking interrupts. | ||
555 | */ | ||
556 | spin_lock_irqsave(&dev->event_lock, flags); | ||
557 | event = tilcdc_crtc->event; | ||
558 | if (event && event->base.file_priv == file) { | ||
559 | tilcdc_crtc->event = NULL; | ||
560 | event->base.destroy(&event->base); | ||
561 | drm_vblank_put(dev, 0); | ||
562 | } | ||
563 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
564 | } | ||
565 | |||
566 | struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) | ||
567 | { | ||
568 | struct tilcdc_crtc *tilcdc_crtc; | ||
569 | struct drm_crtc *crtc; | ||
570 | int ret; | ||
571 | |||
572 | tilcdc_crtc = kzalloc(sizeof(*tilcdc_crtc), GFP_KERNEL); | ||
573 | if (!tilcdc_crtc) { | ||
574 | dev_err(dev->dev, "allocation failed\n"); | ||
575 | return NULL; | ||
576 | } | ||
577 | |||
578 | crtc = &tilcdc_crtc->base; | ||
579 | |||
580 | tilcdc_crtc->dpms = DRM_MODE_DPMS_OFF; | ||
581 | init_waitqueue_head(&tilcdc_crtc->frame_done_wq); | ||
582 | |||
583 | ret = kfifo_alloc(&tilcdc_crtc->unref_fifo, 16, GFP_KERNEL); | ||
584 | if (ret) { | ||
585 | dev_err(dev->dev, "could not allocate unref FIFO\n"); | ||
586 | goto fail; | ||
587 | } | ||
588 | |||
589 | INIT_WORK(&tilcdc_crtc->work, unref_worker); | ||
590 | |||
591 | ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs); | ||
592 | if (ret < 0) | ||
593 | goto fail; | ||
594 | |||
595 | drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); | ||
596 | |||
597 | return crtc; | ||
598 | |||
599 | fail: | ||
600 | tilcdc_crtc_destroy(crtc); | ||
601 | return NULL; | ||
602 | } | ||