drm/exynos: updated crtc and encoder dpms framework.
[deliverable/linux.git] / drivers / gpu / drm / exynos / exynos_drm_fimd.c
CommitLineData
1c248b7d
ID
1/* exynos_drm_fimd.c
2 *
3 * Copyright (C) 2011 Samsung Electronics Co.Ltd
4 * Authors:
5 * Joonyoung Shim <jy0922.shim@samsung.com>
6 * Inki Dae <inki.dae@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14#include "drmP.h"
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/clk.h>
20
21#include <drm/exynos_drm.h>
22#include <plat/regs-fb-v4.h>
23
24#include "exynos_drm_drv.h"
25#include "exynos_drm_fbdev.h"
26#include "exynos_drm_crtc.h"
27
28/*
29 * FIMD is stand for Fully Interactive Mobile Display and
30 * as a display controller, it transfers contents drawn on memory
31 * to a LCD Panel through Display Interfaces such as RGB or
32 * CPU Interface.
33 */
34
35/* position control register for hardware window 0, 2 ~ 4.*/
36#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
37#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
38/* size control register for hardware window 0. */
39#define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08)
40/* alpha control register for hardware window 1 ~ 4. */
41#define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16)
42/* size control register for hardware window 1 ~ 4. */
43#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
44
45#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
46#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
47#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
48
49/* color key control register for hardware window 1 ~ 4. */
50#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8))
51/* color key value register for hardware window 1 ~ 4. */
52#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8))
53
54/* FIMD has totally five hardware windows. */
55#define WINDOWS_NR 5
56
57#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
58
59struct fimd_win_data {
60 unsigned int offset_x;
61 unsigned int offset_y;
19c8b834
ID
62 unsigned int ovl_width;
63 unsigned int ovl_height;
64 unsigned int fb_width;
65 unsigned int fb_height;
1c248b7d 66 unsigned int bpp;
2c871127 67 dma_addr_t dma_addr;
1c248b7d
ID
68 void __iomem *vaddr;
69 unsigned int buf_offsize;
70 unsigned int line_size; /* bytes */
ec05da95 71 bool enabled;
1c248b7d
ID
72};
73
74struct fimd_context {
75 struct exynos_drm_subdrv subdrv;
76 int irq;
77 struct drm_crtc *crtc;
78 struct clk *bus_clk;
79 struct clk *lcd_clk;
80 struct resource *regs_res;
81 void __iomem *regs;
82 struct fimd_win_data win_data[WINDOWS_NR];
83 unsigned int clkdiv;
84 unsigned int default_win;
85 unsigned long irq_flags;
86 u32 vidcon0;
87 u32 vidcon1;
88
89 struct fb_videomode *timing;
90};
91
92static bool fimd_display_is_connected(struct device *dev)
93{
1c248b7d
ID
94 DRM_DEBUG_KMS("%s\n", __FILE__);
95
96 /* TODO. */
97
98 return true;
99}
100
101static void *fimd_get_timing(struct device *dev)
102{
103 struct fimd_context *ctx = get_fimd_context(dev);
104
105 DRM_DEBUG_KMS("%s\n", __FILE__);
106
107 return ctx->timing;
108}
109
110static int fimd_check_timing(struct device *dev, void *timing)
111{
1c248b7d
ID
112 DRM_DEBUG_KMS("%s\n", __FILE__);
113
114 /* TODO. */
115
116 return 0;
117}
118
119static int fimd_display_power_on(struct device *dev, int mode)
120{
1c248b7d
ID
121 DRM_DEBUG_KMS("%s\n", __FILE__);
122
ec05da95 123 /* TODO */
1c248b7d
ID
124
125 return 0;
126}
127
74ccc539 128static struct exynos_drm_display_ops fimd_display_ops = {
1c248b7d
ID
129 .type = EXYNOS_DISPLAY_TYPE_LCD,
130 .is_connected = fimd_display_is_connected,
131 .get_timing = fimd_get_timing,
132 .check_timing = fimd_check_timing,
133 .power_on = fimd_display_power_on,
134};
135
ec05da95
ID
136static void fimd_dpms(struct device *subdrv_dev, int mode)
137{
138 DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
139
140 /* TODO */
141}
142
143static void fimd_apply(struct device *subdrv_dev)
144{
145 struct fimd_context *ctx = get_fimd_context(subdrv_dev);
146 struct exynos_drm_manager *mgr = &ctx->subdrv.manager;
147 struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
148 struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
149 struct fimd_win_data *win_data;
150
151 DRM_DEBUG_KMS("%s\n", __FILE__);
152
153 win_data = &ctx->win_data[ctx->default_win];
154 if (win_data->enabled && (ovl_ops && ovl_ops->commit))
155 ovl_ops->commit(subdrv_dev);
156
157 if (mgr_ops && mgr_ops->commit)
158 mgr_ops->commit(subdrv_dev);
159}
160
1c248b7d
ID
161static void fimd_commit(struct device *dev)
162{
163 struct fimd_context *ctx = get_fimd_context(dev);
164 struct fb_videomode *timing = ctx->timing;
165 u32 val;
166
167 DRM_DEBUG_KMS("%s\n", __FILE__);
168
169 /* setup polarity values from machine code. */
170 writel(ctx->vidcon1, ctx->regs + VIDCON1);
171
172 /* setup vertical timing values. */
173 val = VIDTCON0_VBPD(timing->upper_margin - 1) |
174 VIDTCON0_VFPD(timing->lower_margin - 1) |
175 VIDTCON0_VSPW(timing->vsync_len - 1);
176 writel(val, ctx->regs + VIDTCON0);
177
178 /* setup horizontal timing values. */
179 val = VIDTCON1_HBPD(timing->left_margin - 1) |
180 VIDTCON1_HFPD(timing->right_margin - 1) |
181 VIDTCON1_HSPW(timing->hsync_len - 1);
182 writel(val, ctx->regs + VIDTCON1);
183
184 /* setup horizontal and vertical display size. */
185 val = VIDTCON2_LINEVAL(timing->yres - 1) |
186 VIDTCON2_HOZVAL(timing->xres - 1);
187 writel(val, ctx->regs + VIDTCON2);
188
189 /* setup clock source, clock divider, enable dma. */
190 val = ctx->vidcon0;
191 val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
192
193 if (ctx->clkdiv > 1)
194 val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
195 else
196 val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
197
198 /*
199 * fields of register with prefix '_F' would be updated
200 * at vsync(same as dma start)
201 */
202 val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
203 writel(val, ctx->regs + VIDCON0);
204}
205
206static int fimd_enable_vblank(struct device *dev)
207{
208 struct fimd_context *ctx = get_fimd_context(dev);
209 u32 val;
210
211 DRM_DEBUG_KMS("%s\n", __FILE__);
212
213 if (!test_and_set_bit(0, &ctx->irq_flags)) {
214 val = readl(ctx->regs + VIDINTCON0);
215
216 val |= VIDINTCON0_INT_ENABLE;
217 val |= VIDINTCON0_INT_FRAME;
218
219 val &= ~VIDINTCON0_FRAMESEL0_MASK;
220 val |= VIDINTCON0_FRAMESEL0_VSYNC;
221 val &= ~VIDINTCON0_FRAMESEL1_MASK;
222 val |= VIDINTCON0_FRAMESEL1_NONE;
223
224 writel(val, ctx->regs + VIDINTCON0);
225 }
226
227 return 0;
228}
229
230static void fimd_disable_vblank(struct device *dev)
231{
232 struct fimd_context *ctx = get_fimd_context(dev);
233 u32 val;
234
235 DRM_DEBUG_KMS("%s\n", __FILE__);
236
237 if (test_and_clear_bit(0, &ctx->irq_flags)) {
238 val = readl(ctx->regs + VIDINTCON0);
239
240 val &= ~VIDINTCON0_INT_FRAME;
241 val &= ~VIDINTCON0_INT_ENABLE;
242
243 writel(val, ctx->regs + VIDINTCON0);
244 }
245}
246
247static struct exynos_drm_manager_ops fimd_manager_ops = {
ec05da95
ID
248 .dpms = fimd_dpms,
249 .apply = fimd_apply,
1c248b7d
ID
250 .commit = fimd_commit,
251 .enable_vblank = fimd_enable_vblank,
252 .disable_vblank = fimd_disable_vblank,
253};
254
255static void fimd_win_mode_set(struct device *dev,
256 struct exynos_drm_overlay *overlay)
257{
258 struct fimd_context *ctx = get_fimd_context(dev);
259 struct fimd_win_data *win_data;
19c8b834 260 unsigned long offset;
1c248b7d
ID
261
262 DRM_DEBUG_KMS("%s\n", __FILE__);
263
264 if (!overlay) {
265 dev_err(dev, "overlay is NULL\n");
266 return;
267 }
268
19c8b834
ID
269 offset = overlay->fb_x * (overlay->bpp >> 3);
270 offset += overlay->fb_y * overlay->pitch;
271
272 DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
273
1c248b7d
ID
274 win_data = &ctx->win_data[ctx->default_win];
275
19c8b834
ID
276 win_data->offset_x = overlay->crtc_x;
277 win_data->offset_y = overlay->crtc_y;
278 win_data->ovl_width = overlay->crtc_width;
279 win_data->ovl_height = overlay->crtc_height;
280 win_data->fb_width = overlay->fb_width;
281 win_data->fb_height = overlay->fb_height;
2c871127 282 win_data->dma_addr = overlay->dma_addr + offset;
19c8b834 283 win_data->vaddr = overlay->vaddr + offset;
1c248b7d 284 win_data->bpp = overlay->bpp;
19c8b834
ID
285 win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
286 (overlay->bpp >> 3);
287 win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
288
289 DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
290 win_data->offset_x, win_data->offset_y);
291 DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
292 win_data->ovl_width, win_data->ovl_height);
293 DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
2c871127 294 (unsigned long)win_data->dma_addr,
19c8b834
ID
295 (unsigned long)win_data->vaddr);
296 DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
297 overlay->fb_width, overlay->crtc_width);
1c248b7d
ID
298}
299
300static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
301{
302 struct fimd_context *ctx = get_fimd_context(dev);
303 struct fimd_win_data *win_data = &ctx->win_data[win];
304 unsigned long val;
305
306 DRM_DEBUG_KMS("%s\n", __FILE__);
307
308 val = WINCONx_ENWIN;
309
310 switch (win_data->bpp) {
311 case 1:
312 val |= WINCON0_BPPMODE_1BPP;
313 val |= WINCONx_BITSWP;
314 val |= WINCONx_BURSTLEN_4WORD;
315 break;
316 case 2:
317 val |= WINCON0_BPPMODE_2BPP;
318 val |= WINCONx_BITSWP;
319 val |= WINCONx_BURSTLEN_8WORD;
320 break;
321 case 4:
322 val |= WINCON0_BPPMODE_4BPP;
323 val |= WINCONx_BITSWP;
324 val |= WINCONx_BURSTLEN_8WORD;
325 break;
326 case 8:
327 val |= WINCON0_BPPMODE_8BPP_PALETTE;
328 val |= WINCONx_BURSTLEN_8WORD;
329 val |= WINCONx_BYTSWP;
330 break;
331 case 16:
332 val |= WINCON0_BPPMODE_16BPP_565;
333 val |= WINCONx_HAWSWP;
334 val |= WINCONx_BURSTLEN_16WORD;
335 break;
336 case 24:
337 val |= WINCON0_BPPMODE_24BPP_888;
338 val |= WINCONx_WSWP;
339 val |= WINCONx_BURSTLEN_16WORD;
340 break;
341 case 32:
342 val |= WINCON1_BPPMODE_28BPP_A4888
343 | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
344 val |= WINCONx_WSWP;
345 val |= WINCONx_BURSTLEN_16WORD;
346 break;
347 default:
348 DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
349
350 val |= WINCON0_BPPMODE_24BPP_888;
351 val |= WINCONx_WSWP;
352 val |= WINCONx_BURSTLEN_16WORD;
353 break;
354 }
355
356 DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
357
358 writel(val, ctx->regs + WINCON(win));
359}
360
361static void fimd_win_set_colkey(struct device *dev, unsigned int win)
362{
363 struct fimd_context *ctx = get_fimd_context(dev);
364 unsigned int keycon0 = 0, keycon1 = 0;
365
366 DRM_DEBUG_KMS("%s\n", __FILE__);
367
368 keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
369 WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
370
371 keycon1 = WxKEYCON1_COLVAL(0xffffffff);
372
373 writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
374 writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
375}
376
377static void fimd_win_commit(struct device *dev)
378{
379 struct fimd_context *ctx = get_fimd_context(dev);
380 struct fimd_win_data *win_data;
381 int win = ctx->default_win;
382 unsigned long val, alpha, size;
383
384 DRM_DEBUG_KMS("%s\n", __FILE__);
385
386 if (win < 0 || win > WINDOWS_NR)
387 return;
388
389 win_data = &ctx->win_data[win];
390
391 /*
392 * SHADOWCON register is used for enabling timing.
393 *
394 * for example, once only width value of a register is set,
395 * if the dma is started then fimd hardware could malfunction so
396 * with protect window setting, the register fields with prefix '_F'
397 * wouldn't be updated at vsync also but updated once unprotect window
398 * is set.
399 */
400
401 /* protect windows */
402 val = readl(ctx->regs + SHADOWCON);
403 val |= SHADOWCON_WINx_PROTECT(win);
404 writel(val, ctx->regs + SHADOWCON);
405
406 /* buffer start address */
2c871127 407 val = (unsigned long)win_data->dma_addr;
1c248b7d
ID
408 writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
409
410 /* buffer end address */
19c8b834 411 size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
2c871127 412 val = (unsigned long)(win_data->dma_addr + size);
1c248b7d
ID
413 writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
414
415 DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
2c871127 416 (unsigned long)win_data->dma_addr, val, size);
19c8b834
ID
417 DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
418 win_data->ovl_width, win_data->ovl_height);
1c248b7d
ID
419
420 /* buffer size */
421 val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
422 VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size);
423 writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
424
425 /* OSD position */
426 val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
427 VIDOSDxA_TOPLEFT_Y(win_data->offset_y);
428 writel(val, ctx->regs + VIDOSD_A(win));
429
19c8b834
ID
430 val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x +
431 win_data->ovl_width - 1) |
432 VIDOSDxB_BOTRIGHT_Y(win_data->offset_y +
433 win_data->ovl_height - 1);
1c248b7d
ID
434 writel(val, ctx->regs + VIDOSD_B(win));
435
19c8b834 436 DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
1c248b7d 437 win_data->offset_x, win_data->offset_y,
19c8b834
ID
438 win_data->offset_x + win_data->ovl_width - 1,
439 win_data->offset_y + win_data->ovl_height - 1);
1c248b7d
ID
440
441 /* hardware window 0 doesn't support alpha channel. */
442 if (win != 0) {
443 /* OSD alpha */
444 alpha = VIDISD14C_ALPHA1_R(0xf) |
445 VIDISD14C_ALPHA1_G(0xf) |
446 VIDISD14C_ALPHA1_B(0xf);
447
448 writel(alpha, ctx->regs + VIDOSD_C(win));
449 }
450
451 /* OSD size */
452 if (win != 3 && win != 4) {
453 u32 offset = VIDOSD_D(win);
454 if (win == 0)
455 offset = VIDOSD_C_SIZE_W0;
19c8b834 456 val = win_data->ovl_width * win_data->ovl_height;
1c248b7d
ID
457 writel(val, ctx->regs + offset);
458
459 DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
460 }
461
462 fimd_win_set_pixfmt(dev, win);
463
464 /* hardware window 0 doesn't support color key. */
465 if (win != 0)
466 fimd_win_set_colkey(dev, win);
467
ec05da95
ID
468 /* wincon */
469 val = readl(ctx->regs + WINCON(win));
470 val |= WINCONx_ENWIN;
471 writel(val, ctx->regs + WINCON(win));
472
1c248b7d
ID
473 /* Enable DMA channel and unprotect windows */
474 val = readl(ctx->regs + SHADOWCON);
475 val |= SHADOWCON_CHx_ENABLE(win);
476 val &= ~SHADOWCON_WINx_PROTECT(win);
477 writel(val, ctx->regs + SHADOWCON);
ec05da95
ID
478
479 win_data->enabled = true;
1c248b7d
ID
480}
481
482static void fimd_win_disable(struct device *dev)
483{
484 struct fimd_context *ctx = get_fimd_context(dev);
ec05da95 485 struct fimd_win_data *win_data;
1c248b7d
ID
486 int win = ctx->default_win;
487 u32 val;
488
489 DRM_DEBUG_KMS("%s\n", __FILE__);
490
491 if (win < 0 || win > WINDOWS_NR)
492 return;
493
ec05da95
ID
494 win_data = &ctx->win_data[win];
495
1c248b7d
ID
496 /* protect windows */
497 val = readl(ctx->regs + SHADOWCON);
498 val |= SHADOWCON_WINx_PROTECT(win);
499 writel(val, ctx->regs + SHADOWCON);
500
501 /* wincon */
502 val = readl(ctx->regs + WINCON(win));
503 val &= ~WINCONx_ENWIN;
504 writel(val, ctx->regs + WINCON(win));
505
506 /* unprotect windows */
507 val = readl(ctx->regs + SHADOWCON);
508 val &= ~SHADOWCON_CHx_ENABLE(win);
509 val &= ~SHADOWCON_WINx_PROTECT(win);
510 writel(val, ctx->regs + SHADOWCON);
ec05da95
ID
511
512 win_data->enabled = false;
1c248b7d
ID
513}
514
515static struct exynos_drm_overlay_ops fimd_overlay_ops = {
516 .mode_set = fimd_win_mode_set,
517 .commit = fimd_win_commit,
518 .disable = fimd_win_disable,
519};
520
1c248b7d
ID
521static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
522{
523 struct exynos_drm_private *dev_priv = drm_dev->dev_private;
524 struct drm_pending_vblank_event *e, *t;
525 struct timeval now;
526 unsigned long flags;
ccf4d883 527 bool is_checked = false;
1c248b7d
ID
528
529 spin_lock_irqsave(&drm_dev->event_lock, flags);
530
1c248b7d
ID
531 list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
532 base.link) {
a88cab2b 533 /* if event's pipe isn't same as crtc then ignore it. */
ccf4d883
ID
534 if (crtc != e->pipe)
535 continue;
536
537 is_checked = true;
538
1c248b7d
ID
539 do_gettimeofday(&now);
540 e->event.sequence = 0;
541 e->event.tv_sec = now.tv_sec;
542 e->event.tv_usec = now.tv_usec;
543
544 list_move_tail(&e->base.link, &e->base.file_priv->event_list);
545 wake_up_interruptible(&e->base.file_priv->event_wait);
546 }
547
ec05da95 548 if (is_checked) {
ccf4d883 549 drm_vblank_put(drm_dev, crtc);
1c248b7d 550
ec05da95
ID
551 /*
552 * don't off vblank if vblank_disable_allowed is 1,
553 * because vblank would be off by timer handler.
554 */
555 if (!drm_dev->vblank_disable_allowed)
556 drm_vblank_off(drm_dev, crtc);
557 }
558
1c248b7d
ID
559 spin_unlock_irqrestore(&drm_dev->event_lock, flags);
560}
561
562static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
563{
564 struct fimd_context *ctx = (struct fimd_context *)dev_id;
565 struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
566 struct drm_device *drm_dev = subdrv->drm_dev;
1c248b7d
ID
567 struct exynos_drm_manager *manager = &subdrv->manager;
568 u32 val;
569
570 val = readl(ctx->regs + VIDINTCON1);
571
572 if (val & VIDINTCON1_INT_FRAME)
573 /* VSYNC interrupt */
574 writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
575
ec05da95
ID
576 /* check the crtc is detached already from encoder */
577 if (manager->pipe < 0)
578 goto out;
483b88f8 579
1c248b7d
ID
580 drm_handle_vblank(drm_dev, manager->pipe);
581 fimd_finish_pageflip(drm_dev, manager->pipe);
582
ec05da95 583out:
1c248b7d
ID
584 return IRQ_HANDLED;
585}
586
41c24346 587static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
1c248b7d 588{
1c248b7d
ID
589 DRM_DEBUG_KMS("%s\n", __FILE__);
590
591 /*
592 * enable drm irq mode.
593 * - with irq_enabled = 1, we can use the vblank feature.
594 *
595 * P.S. note that we wouldn't use drm irq handler but
596 * just specific driver own one instead because
597 * drm framework supports only one irq handler.
598 */
599 drm_dev->irq_enabled = 1;
600
ec05da95
ID
601 /*
602 * with vblank_disable_allowed = 1, vblank interrupt will be disabled
603 * by drm timer once a current process gives up ownership of
604 * vblank event.(after drm_vblank_put function is called)
605 */
606 drm_dev->vblank_disable_allowed = 1;
607
1c248b7d
ID
608 return 0;
609}
610
611static void fimd_subdrv_remove(struct drm_device *drm_dev)
612{
1c248b7d
ID
613 DRM_DEBUG_KMS("%s\n", __FILE__);
614
615 /* TODO. */
616}
617
618static int fimd_calc_clkdiv(struct fimd_context *ctx,
619 struct fb_videomode *timing)
620{
621 unsigned long clk = clk_get_rate(ctx->lcd_clk);
622 u32 retrace;
623 u32 clkdiv;
624 u32 best_framerate = 0;
625 u32 framerate;
626
627 DRM_DEBUG_KMS("%s\n", __FILE__);
628
629 retrace = timing->left_margin + timing->hsync_len +
630 timing->right_margin + timing->xres;
631 retrace *= timing->upper_margin + timing->vsync_len +
632 timing->lower_margin + timing->yres;
633
634 /* default framerate is 60Hz */
635 if (!timing->refresh)
636 timing->refresh = 60;
637
638 clk /= retrace;
639
640 for (clkdiv = 1; clkdiv < 0x100; clkdiv++) {
641 int tmp;
642
643 /* get best framerate */
644 framerate = clk / clkdiv;
645 tmp = timing->refresh - framerate;
646 if (tmp < 0) {
647 best_framerate = framerate;
648 continue;
649 } else {
650 if (!best_framerate)
651 best_framerate = framerate;
652 else if (tmp < (best_framerate - framerate))
653 best_framerate = framerate;
654 break;
655 }
656 }
657
658 return clkdiv;
659}
660
661static void fimd_clear_win(struct fimd_context *ctx, int win)
662{
663 u32 val;
664
665 DRM_DEBUG_KMS("%s\n", __FILE__);
666
667 writel(0, ctx->regs + WINCON(win));
668 writel(0, ctx->regs + VIDOSD_A(win));
669 writel(0, ctx->regs + VIDOSD_B(win));
670 writel(0, ctx->regs + VIDOSD_C(win));
671
672 if (win == 1 || win == 2)
673 writel(0, ctx->regs + VIDOSD_D(win));
674
675 val = readl(ctx->regs + SHADOWCON);
676 val &= ~SHADOWCON_WINx_PROTECT(win);
677 writel(val, ctx->regs + SHADOWCON);
678}
679
680static int __devinit fimd_probe(struct platform_device *pdev)
681{
682 struct device *dev = &pdev->dev;
683 struct fimd_context *ctx;
684 struct exynos_drm_subdrv *subdrv;
685 struct exynos_drm_fimd_pdata *pdata;
686 struct fb_videomode *timing;
687 struct resource *res;
688 int win;
689 int ret = -EINVAL;
690
691 DRM_DEBUG_KMS("%s\n", __FILE__);
692
693 pdata = pdev->dev.platform_data;
694 if (!pdata) {
695 dev_err(dev, "no platform data specified\n");
696 return -EINVAL;
697 }
698
699 timing = &pdata->timing;
700 if (!timing) {
701 dev_err(dev, "timing is null.\n");
702 return -EINVAL;
703 }
704
705 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
706 if (!ctx)
707 return -ENOMEM;
708
709 ctx->bus_clk = clk_get(dev, "fimd");
710 if (IS_ERR(ctx->bus_clk)) {
711 dev_err(dev, "failed to get bus clock\n");
712 ret = PTR_ERR(ctx->bus_clk);
713 goto err_clk_get;
714 }
715
716 clk_enable(ctx->bus_clk);
717
718 ctx->lcd_clk = clk_get(dev, "sclk_fimd");
719 if (IS_ERR(ctx->lcd_clk)) {
720 dev_err(dev, "failed to get lcd clock\n");
721 ret = PTR_ERR(ctx->lcd_clk);
722 goto err_bus_clk;
723 }
724
725 clk_enable(ctx->lcd_clk);
726
727 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
728 if (!res) {
729 dev_err(dev, "failed to find registers\n");
730 ret = -ENOENT;
731 goto err_clk;
732 }
733
734 ctx->regs_res = request_mem_region(res->start, resource_size(res),
735 dev_name(dev));
736 if (!ctx->regs_res) {
737 dev_err(dev, "failed to claim register region\n");
738 ret = -ENOENT;
739 goto err_clk;
740 }
741
742 ctx->regs = ioremap(res->start, resource_size(res));
743 if (!ctx->regs) {
744 dev_err(dev, "failed to map registers\n");
745 ret = -ENXIO;
746 goto err_req_region_io;
747 }
748
749 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
750 if (!res) {
751 dev_err(dev, "irq request failed.\n");
752 goto err_req_region_irq;
753 }
754
755 ctx->irq = res->start;
756
1c248b7d
ID
757 ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
758 if (ret < 0) {
759 dev_err(dev, "irq request failed.\n");
760 goto err_req_irq;
761 }
762
ec05da95
ID
763 for (win = 0; win < WINDOWS_NR; win++)
764 fimd_clear_win(ctx, win);
765
1c248b7d
ID
766 ctx->clkdiv = fimd_calc_clkdiv(ctx, timing);
767 ctx->vidcon0 = pdata->vidcon0;
768 ctx->vidcon1 = pdata->vidcon1;
769 ctx->default_win = pdata->default_win;
770 ctx->timing = timing;
771
772 timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv;
773
774 DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n",
775 timing->pixclock, ctx->clkdiv);
776
777 subdrv = &ctx->subdrv;
778
779 subdrv->probe = fimd_subdrv_probe;
780 subdrv->remove = fimd_subdrv_remove;
781 subdrv->manager.pipe = -1;
782 subdrv->manager.ops = &fimd_manager_ops;
783 subdrv->manager.overlay_ops = &fimd_overlay_ops;
74ccc539 784 subdrv->manager.display_ops = &fimd_display_ops;
1c248b7d
ID
785 subdrv->manager.dev = dev;
786
787 platform_set_drvdata(pdev, ctx);
788 exynos_drm_subdrv_register(subdrv);
789
790 return 0;
791
792err_req_irq:
793err_req_region_irq:
794 iounmap(ctx->regs);
795
796err_req_region_io:
797 release_resource(ctx->regs_res);
798 kfree(ctx->regs_res);
799
800err_clk:
801 clk_disable(ctx->lcd_clk);
802 clk_put(ctx->lcd_clk);
803
804err_bus_clk:
805 clk_disable(ctx->bus_clk);
806 clk_put(ctx->bus_clk);
807
808err_clk_get:
809 kfree(ctx);
810 return ret;
811}
812
813static int __devexit fimd_remove(struct platform_device *pdev)
814{
815 struct fimd_context *ctx = platform_get_drvdata(pdev);
816
817 DRM_DEBUG_KMS("%s\n", __FILE__);
818
819 exynos_drm_subdrv_unregister(&ctx->subdrv);
820
821 clk_disable(ctx->lcd_clk);
822 clk_disable(ctx->bus_clk);
823 clk_put(ctx->lcd_clk);
824 clk_put(ctx->bus_clk);
825
826 iounmap(ctx->regs);
827 release_resource(ctx->regs_res);
828 kfree(ctx->regs_res);
829 free_irq(ctx->irq, ctx);
830
831 kfree(ctx);
832
833 return 0;
834}
835
836static struct platform_driver fimd_driver = {
837 .probe = fimd_probe,
838 .remove = __devexit_p(fimd_remove),
839 .driver = {
840 .name = "exynos4-fb",
841 .owner = THIS_MODULE,
842 },
843};
844
845static int __init fimd_init(void)
846{
847 return platform_driver_register(&fimd_driver);
848}
849
850static void __exit fimd_exit(void)
851{
852 platform_driver_unregister(&fimd_driver);
853}
854
855module_init(fimd_init);
856module_exit(fimd_exit);
857
858MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
859MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
860MODULE_DESCRIPTION("Samsung DRM FIMD Driver");
861MODULE_LICENSE("GPL");
This page took 0.070664 seconds and 5 git commands to generate.