]>
Commit | Line | Data |
---|---|---|
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 | */ | |
760285e7 | 14 | #include <drm/drmP.h> |
1c248b7d ID |
15 | |
16 | #include <linux/kernel.h> | |
1c248b7d ID |
17 | #include <linux/platform_device.h> |
18 | #include <linux/clk.h> | |
3f1c781d | 19 | #include <linux/of.h> |
d636ead8 | 20 | #include <linux/of_device.h> |
cb91f6a0 | 21 | #include <linux/pm_runtime.h> |
f37cd5e8 | 22 | #include <linux/component.h> |
1c248b7d | 23 | |
7f4596f4 | 24 | #include <video/of_display_timing.h> |
111e6055 | 25 | #include <video/of_videomode.h> |
5a213a55 | 26 | #include <video/samsung_fimd.h> |
1c248b7d | 27 | #include <drm/exynos_drm.h> |
1c248b7d ID |
28 | |
29 | #include "exynos_drm_drv.h" | |
30 | #include "exynos_drm_fbdev.h" | |
31 | #include "exynos_drm_crtc.h" | |
bcc5cd1c | 32 | #include "exynos_drm_iommu.h" |
1c248b7d ID |
33 | |
34 | /* | |
b8654b37 | 35 | * FIMD stands for Fully Interactive Mobile Display and |
1c248b7d ID |
36 | * as a display controller, it transfers contents drawn on memory |
37 | * to a LCD Panel through Display Interfaces such as RGB or | |
38 | * CPU Interface. | |
39 | */ | |
40 | ||
111e6055 | 41 | #define FIMD_DEFAULT_FRAMERATE 60 |
66367461 | 42 | #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 |
111e6055 | 43 | |
1c248b7d ID |
44 | /* position control register for hardware window 0, 2 ~ 4.*/ |
45 | #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) | |
46 | #define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) | |
0f10cf14 LKA |
47 | /* |
48 | * size control register for hardware windows 0 and alpha control register | |
49 | * for hardware windows 1 ~ 4 | |
50 | */ | |
51 | #define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16) | |
52 | /* size control register for hardware windows 1 ~ 2. */ | |
1c248b7d ID |
53 | #define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) |
54 | ||
55 | #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) | |
56 | #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) | |
57 | #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) | |
58 | ||
59 | /* color key control register for hardware window 1 ~ 4. */ | |
0f10cf14 | 60 | #define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8)) |
1c248b7d | 61 | /* color key value register for hardware window 1 ~ 4. */ |
0f10cf14 | 62 | #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8)) |
1c248b7d ID |
63 | |
64 | /* FIMD has totally five hardware windows. */ | |
65 | #define WINDOWS_NR 5 | |
66 | ||
bb7704d6 | 67 | #define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev)) |
1c248b7d | 68 | |
e2e13389 LKA |
69 | struct fimd_driver_data { |
70 | unsigned int timing_base; | |
de7af100 TF |
71 | |
72 | unsigned int has_shadowcon:1; | |
411d9ed4 | 73 | unsigned int has_clksel:1; |
5cc4621a | 74 | unsigned int has_limited_fmt:1; |
e2e13389 LKA |
75 | }; |
76 | ||
725ddead TF |
77 | static struct fimd_driver_data s3c64xx_fimd_driver_data = { |
78 | .timing_base = 0x0, | |
79 | .has_clksel = 1, | |
5cc4621a | 80 | .has_limited_fmt = 1, |
725ddead TF |
81 | }; |
82 | ||
6ecf18f9 | 83 | static struct fimd_driver_data exynos4_fimd_driver_data = { |
e2e13389 | 84 | .timing_base = 0x0, |
de7af100 | 85 | .has_shadowcon = 1, |
e2e13389 LKA |
86 | }; |
87 | ||
6ecf18f9 | 88 | static struct fimd_driver_data exynos5_fimd_driver_data = { |
e2e13389 | 89 | .timing_base = 0x20000, |
de7af100 | 90 | .has_shadowcon = 1, |
e2e13389 LKA |
91 | }; |
92 | ||
1c248b7d ID |
93 | struct fimd_win_data { |
94 | unsigned int offset_x; | |
95 | unsigned int offset_y; | |
19c8b834 ID |
96 | unsigned int ovl_width; |
97 | unsigned int ovl_height; | |
98 | unsigned int fb_width; | |
99 | unsigned int fb_height; | |
1c248b7d | 100 | unsigned int bpp; |
a4f38a80 | 101 | unsigned int pixel_format; |
2c871127 | 102 | dma_addr_t dma_addr; |
1c248b7d ID |
103 | unsigned int buf_offsize; |
104 | unsigned int line_size; /* bytes */ | |
ec05da95 | 105 | bool enabled; |
db7e55ae | 106 | bool resume; |
1c248b7d ID |
107 | }; |
108 | ||
109 | struct fimd_context { | |
bb7704d6 | 110 | struct device *dev; |
40c8ab4b | 111 | struct drm_device *drm_dev; |
1c248b7d ID |
112 | struct clk *bus_clk; |
113 | struct clk *lcd_clk; | |
1c248b7d | 114 | void __iomem *regs; |
a968e727 | 115 | struct drm_display_mode mode; |
1c248b7d | 116 | struct fimd_win_data win_data[WINDOWS_NR]; |
1c248b7d ID |
117 | unsigned int default_win; |
118 | unsigned long irq_flags; | |
1c248b7d | 119 | u32 vidcon1; |
cb91f6a0 | 120 | bool suspended; |
080be03d | 121 | int pipe; |
01ce113c P |
122 | wait_queue_head_t wait_vsync_queue; |
123 | atomic_t wait_vsync_event; | |
1c248b7d | 124 | |
562ad9f4 | 125 | struct exynos_drm_panel_info panel; |
18873465 | 126 | struct fimd_driver_data *driver_data; |
000cc920 | 127 | struct exynos_drm_display *display; |
1c248b7d ID |
128 | }; |
129 | ||
d636ead8 | 130 | static const struct of_device_id fimd_driver_dt_match[] = { |
725ddead TF |
131 | { .compatible = "samsung,s3c6400-fimd", |
132 | .data = &s3c64xx_fimd_driver_data }, | |
5830daf8 | 133 | { .compatible = "samsung,exynos4210-fimd", |
d636ead8 | 134 | .data = &exynos4_fimd_driver_data }, |
5830daf8 | 135 | { .compatible = "samsung,exynos5250-fimd", |
d636ead8 JS |
136 | .data = &exynos5_fimd_driver_data }, |
137 | {}, | |
138 | }; | |
d636ead8 | 139 | |
e2e13389 LKA |
140 | static inline struct fimd_driver_data *drm_fimd_get_driver_data( |
141 | struct platform_device *pdev) | |
142 | { | |
d636ead8 JS |
143 | const struct of_device_id *of_id = |
144 | of_match_device(fimd_driver_dt_match, &pdev->dev); | |
145 | ||
2d3f173c | 146 | return (struct fimd_driver_data *)of_id->data; |
e2e13389 LKA |
147 | } |
148 | ||
f13bdbd1 AA |
149 | static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) |
150 | { | |
151 | struct fimd_context *ctx = mgr->ctx; | |
152 | ||
153 | if (ctx->suspended) | |
154 | return; | |
155 | ||
156 | atomic_set(&ctx->wait_vsync_event, 1); | |
157 | ||
158 | /* | |
159 | * wait for FIMD to signal VSYNC interrupt or return after | |
160 | * timeout which is set to 50ms (refresh rate of 20). | |
161 | */ | |
162 | if (!wait_event_timeout(ctx->wait_vsync_queue, | |
163 | !atomic_read(&ctx->wait_vsync_event), | |
164 | HZ/20)) | |
165 | DRM_DEBUG_KMS("vblank wait timed out.\n"); | |
166 | } | |
167 | ||
168 | ||
169 | static void fimd_clear_channel(struct exynos_drm_manager *mgr) | |
170 | { | |
171 | struct fimd_context *ctx = mgr->ctx; | |
172 | int win, ch_enabled = 0; | |
173 | ||
174 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
175 | ||
176 | /* Check if any channel is enabled. */ | |
177 | for (win = 0; win < WINDOWS_NR; win++) { | |
178 | u32 val = readl(ctx->regs + SHADOWCON); | |
179 | if (val & SHADOWCON_CHx_ENABLE(win)) { | |
180 | val &= ~SHADOWCON_CHx_ENABLE(win); | |
181 | writel(val, ctx->regs + SHADOWCON); | |
182 | ch_enabled = 1; | |
183 | } | |
184 | } | |
185 | ||
186 | /* Wait for vsync, as disable channel takes effect at next vsync */ | |
187 | if (ch_enabled) | |
188 | fimd_wait_for_vblank(mgr); | |
189 | } | |
190 | ||
bb7704d6 | 191 | static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, |
f37cd5e8 | 192 | struct drm_device *drm_dev) |
40c8ab4b | 193 | { |
bb7704d6 | 194 | struct fimd_context *ctx = mgr->ctx; |
f37cd5e8 ID |
195 | struct exynos_drm_private *priv; |
196 | priv = drm_dev->dev_private; | |
40c8ab4b | 197 | |
f37cd5e8 ID |
198 | mgr->drm_dev = ctx->drm_dev = drm_dev; |
199 | mgr->pipe = ctx->pipe = priv->pipe++; | |
40c8ab4b | 200 | |
080be03d SP |
201 | /* |
202 | * enable drm irq mode. | |
203 | * - with irq_enabled = true, we can use the vblank feature. | |
204 | * | |
205 | * P.S. note that we wouldn't use drm irq handler but | |
206 | * just specific driver own one instead because | |
207 | * drm framework supports only one irq handler. | |
208 | */ | |
209 | drm_dev->irq_enabled = true; | |
ec05da95 | 210 | |
080be03d SP |
211 | /* |
212 | * with vblank_disable_allowed = true, vblank interrupt will be disabled | |
213 | * by drm timer once a current process gives up ownership of | |
214 | * vblank event.(after drm_vblank_put function is called) | |
215 | */ | |
216 | drm_dev->vblank_disable_allowed = true; | |
c32b06ef | 217 | |
080be03d | 218 | /* attach this sub driver to iommu mapping if supported. */ |
f13bdbd1 AA |
219 | if (is_drm_iommu_supported(ctx->drm_dev)) { |
220 | /* | |
221 | * If any channel is already active, iommu will throw | |
222 | * a PAGE FAULT when enabled. So clear any channel if enabled. | |
223 | */ | |
224 | fimd_clear_channel(mgr); | |
080be03d | 225 | drm_iommu_attach_device(ctx->drm_dev, ctx->dev); |
f13bdbd1 | 226 | } |
c32b06ef | 227 | |
080be03d | 228 | return 0; |
ec05da95 ID |
229 | } |
230 | ||
080be03d | 231 | static void fimd_mgr_remove(struct exynos_drm_manager *mgr) |
ec05da95 | 232 | { |
bb7704d6 | 233 | struct fimd_context *ctx = mgr->ctx; |
ec05da95 | 234 | |
080be03d SP |
235 | /* detach this sub driver from iommu mapping if supported. */ |
236 | if (is_drm_iommu_supported(ctx->drm_dev)) | |
237 | drm_iommu_detach_device(ctx->drm_dev, ctx->dev); | |
ec05da95 ID |
238 | } |
239 | ||
a968e727 SP |
240 | static u32 fimd_calc_clkdiv(struct fimd_context *ctx, |
241 | const struct drm_display_mode *mode) | |
242 | { | |
243 | unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; | |
244 | u32 clkdiv; | |
245 | ||
246 | /* Find the clock divider value that gets us closest to ideal_clk */ | |
247 | clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); | |
248 | ||
249 | return (clkdiv < 0x100) ? clkdiv : 0xff; | |
250 | } | |
251 | ||
252 | static bool fimd_mode_fixup(struct exynos_drm_manager *mgr, | |
253 | const struct drm_display_mode *mode, | |
254 | struct drm_display_mode *adjusted_mode) | |
255 | { | |
256 | if (adjusted_mode->vrefresh == 0) | |
257 | adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE; | |
258 | ||
259 | return true; | |
260 | } | |
261 | ||
262 | static void fimd_mode_set(struct exynos_drm_manager *mgr, | |
263 | const struct drm_display_mode *in_mode) | |
264 | { | |
265 | struct fimd_context *ctx = mgr->ctx; | |
266 | ||
267 | drm_mode_copy(&ctx->mode, in_mode); | |
268 | } | |
269 | ||
bb7704d6 | 270 | static void fimd_commit(struct exynos_drm_manager *mgr) |
1c248b7d | 271 | { |
bb7704d6 | 272 | struct fimd_context *ctx = mgr->ctx; |
a968e727 | 273 | struct drm_display_mode *mode = &ctx->mode; |
e2e13389 | 274 | struct fimd_driver_data *driver_data; |
1417f109 | 275 | u32 val, clkdiv, vidcon1; |
8b4cad23 | 276 | int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; |
1c248b7d | 277 | |
18873465 | 278 | driver_data = ctx->driver_data; |
e30d4bcf ID |
279 | if (ctx->suspended) |
280 | return; | |
281 | ||
a968e727 SP |
282 | /* nothing to do if we haven't set the mode yet */ |
283 | if (mode->htotal == 0 || mode->vtotal == 0) | |
284 | return; | |
285 | ||
1417f109 SP |
286 | /* setup polarity values */ |
287 | vidcon1 = ctx->vidcon1; | |
288 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
289 | vidcon1 |= VIDCON1_INV_VSYNC; | |
290 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | |
291 | vidcon1 |= VIDCON1_INV_HSYNC; | |
292 | writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); | |
1c248b7d ID |
293 | |
294 | /* setup vertical timing values. */ | |
a968e727 | 295 | vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; |
8b4cad23 AH |
296 | vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; |
297 | vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; | |
a968e727 SP |
298 | |
299 | val = VIDTCON0_VBPD(vbpd - 1) | | |
300 | VIDTCON0_VFPD(vfpd - 1) | | |
301 | VIDTCON0_VSPW(vsync_len - 1); | |
e2e13389 | 302 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); |
1c248b7d ID |
303 | |
304 | /* setup horizontal timing values. */ | |
a968e727 | 305 | hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; |
8b4cad23 AH |
306 | hbpd = mode->crtc_htotal - mode->crtc_hsync_end; |
307 | hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; | |
a968e727 SP |
308 | |
309 | val = VIDTCON1_HBPD(hbpd - 1) | | |
310 | VIDTCON1_HFPD(hfpd - 1) | | |
311 | VIDTCON1_HSPW(hsync_len - 1); | |
e2e13389 | 312 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); |
1c248b7d ID |
313 | |
314 | /* setup horizontal and vertical display size. */ | |
a968e727 SP |
315 | val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | |
316 | VIDTCON2_HOZVAL(mode->hdisplay - 1) | | |
317 | VIDTCON2_LINEVAL_E(mode->vdisplay - 1) | | |
318 | VIDTCON2_HOZVAL_E(mode->hdisplay - 1); | |
e2e13389 | 319 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); |
1c248b7d | 320 | |
1d531062 AH |
321 | /* |
322 | * fields of register with prefix '_F' would be updated | |
323 | * at vsync(same as dma start) | |
324 | */ | |
325 | val = VIDCON0_ENVID | VIDCON0_ENVID_F; | |
1c248b7d | 326 | |
1d531062 | 327 | if (ctx->driver_data->has_clksel) |
411d9ed4 | 328 | val |= VIDCON0_CLKSEL_LCD; |
411d9ed4 | 329 | |
a968e727 SP |
330 | clkdiv = fimd_calc_clkdiv(ctx, mode); |
331 | if (clkdiv > 1) | |
332 | val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR; | |
1c248b7d | 333 | |
1c248b7d ID |
334 | writel(val, ctx->regs + VIDCON0); |
335 | } | |
336 | ||
bb7704d6 | 337 | static int fimd_enable_vblank(struct exynos_drm_manager *mgr) |
1c248b7d | 338 | { |
bb7704d6 | 339 | struct fimd_context *ctx = mgr->ctx; |
1c248b7d ID |
340 | u32 val; |
341 | ||
cb91f6a0 JS |
342 | if (ctx->suspended) |
343 | return -EPERM; | |
344 | ||
1c248b7d ID |
345 | if (!test_and_set_bit(0, &ctx->irq_flags)) { |
346 | val = readl(ctx->regs + VIDINTCON0); | |
347 | ||
348 | val |= VIDINTCON0_INT_ENABLE; | |
349 | val |= VIDINTCON0_INT_FRAME; | |
350 | ||
351 | val &= ~VIDINTCON0_FRAMESEL0_MASK; | |
352 | val |= VIDINTCON0_FRAMESEL0_VSYNC; | |
353 | val &= ~VIDINTCON0_FRAMESEL1_MASK; | |
354 | val |= VIDINTCON0_FRAMESEL1_NONE; | |
355 | ||
356 | writel(val, ctx->regs + VIDINTCON0); | |
357 | } | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
bb7704d6 | 362 | static void fimd_disable_vblank(struct exynos_drm_manager *mgr) |
1c248b7d | 363 | { |
bb7704d6 | 364 | struct fimd_context *ctx = mgr->ctx; |
1c248b7d ID |
365 | u32 val; |
366 | ||
cb91f6a0 JS |
367 | if (ctx->suspended) |
368 | return; | |
369 | ||
1c248b7d ID |
370 | if (test_and_clear_bit(0, &ctx->irq_flags)) { |
371 | val = readl(ctx->regs + VIDINTCON0); | |
372 | ||
373 | val &= ~VIDINTCON0_INT_FRAME; | |
374 | val &= ~VIDINTCON0_INT_ENABLE; | |
375 | ||
376 | writel(val, ctx->regs + VIDINTCON0); | |
377 | } | |
378 | } | |
379 | ||
bb7704d6 SP |
380 | static void fimd_win_mode_set(struct exynos_drm_manager *mgr, |
381 | struct exynos_drm_overlay *overlay) | |
1c248b7d | 382 | { |
bb7704d6 | 383 | struct fimd_context *ctx = mgr->ctx; |
1c248b7d | 384 | struct fimd_win_data *win_data; |
864ee9e6 | 385 | int win; |
19c8b834 | 386 | unsigned long offset; |
1c248b7d | 387 | |
1c248b7d | 388 | if (!overlay) { |
bb7704d6 | 389 | DRM_ERROR("overlay is NULL\n"); |
1c248b7d ID |
390 | return; |
391 | } | |
392 | ||
864ee9e6 JS |
393 | win = overlay->zpos; |
394 | if (win == DEFAULT_ZPOS) | |
395 | win = ctx->default_win; | |
396 | ||
37b006e8 | 397 | if (win < 0 || win >= WINDOWS_NR) |
864ee9e6 JS |
398 | return; |
399 | ||
19c8b834 ID |
400 | offset = overlay->fb_x * (overlay->bpp >> 3); |
401 | offset += overlay->fb_y * overlay->pitch; | |
402 | ||
403 | DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); | |
404 | ||
864ee9e6 | 405 | win_data = &ctx->win_data[win]; |
1c248b7d | 406 | |
19c8b834 ID |
407 | win_data->offset_x = overlay->crtc_x; |
408 | win_data->offset_y = overlay->crtc_y; | |
409 | win_data->ovl_width = overlay->crtc_width; | |
410 | win_data->ovl_height = overlay->crtc_height; | |
411 | win_data->fb_width = overlay->fb_width; | |
412 | win_data->fb_height = overlay->fb_height; | |
229d3534 | 413 | win_data->dma_addr = overlay->dma_addr[0] + offset; |
1c248b7d | 414 | win_data->bpp = overlay->bpp; |
a4f38a80 | 415 | win_data->pixel_format = overlay->pixel_format; |
19c8b834 ID |
416 | win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * |
417 | (overlay->bpp >> 3); | |
418 | win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); | |
419 | ||
420 | DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", | |
421 | win_data->offset_x, win_data->offset_y); | |
422 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", | |
423 | win_data->ovl_width, win_data->ovl_height); | |
ddd8e959 | 424 | DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr); |
19c8b834 ID |
425 | DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", |
426 | overlay->fb_width, overlay->crtc_width); | |
1c248b7d ID |
427 | } |
428 | ||
bb7704d6 | 429 | static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win) |
1c248b7d | 430 | { |
1c248b7d ID |
431 | struct fimd_win_data *win_data = &ctx->win_data[win]; |
432 | unsigned long val; | |
433 | ||
1c248b7d ID |
434 | val = WINCONx_ENWIN; |
435 | ||
5cc4621a ID |
436 | /* |
437 | * In case of s3c64xx, window 0 doesn't support alpha channel. | |
438 | * So the request format is ARGB8888 then change it to XRGB8888. | |
439 | */ | |
440 | if (ctx->driver_data->has_limited_fmt && !win) { | |
441 | if (win_data->pixel_format == DRM_FORMAT_ARGB8888) | |
442 | win_data->pixel_format = DRM_FORMAT_XRGB8888; | |
443 | } | |
444 | ||
a4f38a80 ID |
445 | switch (win_data->pixel_format) { |
446 | case DRM_FORMAT_C8: | |
1c248b7d ID |
447 | val |= WINCON0_BPPMODE_8BPP_PALETTE; |
448 | val |= WINCONx_BURSTLEN_8WORD; | |
449 | val |= WINCONx_BYTSWP; | |
450 | break; | |
a4f38a80 ID |
451 | case DRM_FORMAT_XRGB1555: |
452 | val |= WINCON0_BPPMODE_16BPP_1555; | |
453 | val |= WINCONx_HAWSWP; | |
454 | val |= WINCONx_BURSTLEN_16WORD; | |
455 | break; | |
456 | case DRM_FORMAT_RGB565: | |
1c248b7d ID |
457 | val |= WINCON0_BPPMODE_16BPP_565; |
458 | val |= WINCONx_HAWSWP; | |
459 | val |= WINCONx_BURSTLEN_16WORD; | |
460 | break; | |
a4f38a80 | 461 | case DRM_FORMAT_XRGB8888: |
1c248b7d ID |
462 | val |= WINCON0_BPPMODE_24BPP_888; |
463 | val |= WINCONx_WSWP; | |
464 | val |= WINCONx_BURSTLEN_16WORD; | |
465 | break; | |
a4f38a80 ID |
466 | case DRM_FORMAT_ARGB8888: |
467 | val |= WINCON1_BPPMODE_25BPP_A1888 | |
1c248b7d ID |
468 | | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; |
469 | val |= WINCONx_WSWP; | |
470 | val |= WINCONx_BURSTLEN_16WORD; | |
471 | break; | |
472 | default: | |
473 | DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); | |
474 | ||
475 | val |= WINCON0_BPPMODE_24BPP_888; | |
476 | val |= WINCONx_WSWP; | |
477 | val |= WINCONx_BURSTLEN_16WORD; | |
478 | break; | |
479 | } | |
480 | ||
481 | DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); | |
482 | ||
66367461 RS |
483 | /* |
484 | * In case of exynos, setting dma-burst to 16Word causes permanent | |
485 | * tearing for very small buffers, e.g. cursor buffer. Burst Mode | |
486 | * switching which is based on overlay size is not recommended as | |
487 | * overlay size varies alot towards the end of the screen and rapid | |
488 | * movement causes unstable DMA which results into iommu crash/tear. | |
489 | */ | |
490 | ||
491 | if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) { | |
492 | val &= ~WINCONx_BURSTLEN_MASK; | |
493 | val |= WINCONx_BURSTLEN_4WORD; | |
494 | } | |
495 | ||
1c248b7d ID |
496 | writel(val, ctx->regs + WINCON(win)); |
497 | } | |
498 | ||
bb7704d6 | 499 | static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win) |
1c248b7d | 500 | { |
1c248b7d ID |
501 | unsigned int keycon0 = 0, keycon1 = 0; |
502 | ||
1c248b7d ID |
503 | keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | |
504 | WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); | |
505 | ||
506 | keycon1 = WxKEYCON1_COLVAL(0xffffffff); | |
507 | ||
508 | writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); | |
509 | writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); | |
510 | } | |
511 | ||
de7af100 TF |
512 | /** |
513 | * shadow_protect_win() - disable updating values from shadow registers at vsync | |
514 | * | |
515 | * @win: window to protect registers for | |
516 | * @protect: 1 to protect (disable updates) | |
517 | */ | |
518 | static void fimd_shadow_protect_win(struct fimd_context *ctx, | |
519 | int win, bool protect) | |
520 | { | |
521 | u32 reg, bits, val; | |
522 | ||
523 | if (ctx->driver_data->has_shadowcon) { | |
524 | reg = SHADOWCON; | |
525 | bits = SHADOWCON_WINx_PROTECT(win); | |
526 | } else { | |
527 | reg = PRTCON; | |
528 | bits = PRTCON_PROTECT; | |
529 | } | |
530 | ||
531 | val = readl(ctx->regs + reg); | |
532 | if (protect) | |
533 | val |= bits; | |
534 | else | |
535 | val &= ~bits; | |
536 | writel(val, ctx->regs + reg); | |
537 | } | |
538 | ||
bb7704d6 | 539 | static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) |
1c248b7d | 540 | { |
bb7704d6 | 541 | struct fimd_context *ctx = mgr->ctx; |
1c248b7d | 542 | struct fimd_win_data *win_data; |
864ee9e6 | 543 | int win = zpos; |
1c248b7d | 544 | unsigned long val, alpha, size; |
f56aad3a JS |
545 | unsigned int last_x; |
546 | unsigned int last_y; | |
1c248b7d | 547 | |
e30d4bcf ID |
548 | if (ctx->suspended) |
549 | return; | |
550 | ||
864ee9e6 JS |
551 | if (win == DEFAULT_ZPOS) |
552 | win = ctx->default_win; | |
553 | ||
37b006e8 | 554 | if (win < 0 || win >= WINDOWS_NR) |
1c248b7d ID |
555 | return; |
556 | ||
557 | win_data = &ctx->win_data[win]; | |
558 | ||
a43b933b SP |
559 | /* If suspended, enable this on resume */ |
560 | if (ctx->suspended) { | |
561 | win_data->resume = true; | |
562 | return; | |
563 | } | |
564 | ||
1c248b7d | 565 | /* |
de7af100 | 566 | * SHADOWCON/PRTCON register is used for enabling timing. |
1c248b7d ID |
567 | * |
568 | * for example, once only width value of a register is set, | |
569 | * if the dma is started then fimd hardware could malfunction so | |
570 | * with protect window setting, the register fields with prefix '_F' | |
571 | * wouldn't be updated at vsync also but updated once unprotect window | |
572 | * is set. | |
573 | */ | |
574 | ||
575 | /* protect windows */ | |
de7af100 | 576 | fimd_shadow_protect_win(ctx, win, true); |
1c248b7d ID |
577 | |
578 | /* buffer start address */ | |
2c871127 | 579 | val = (unsigned long)win_data->dma_addr; |
1c248b7d ID |
580 | writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); |
581 | ||
582 | /* buffer end address */ | |
19c8b834 | 583 | size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); |
2c871127 | 584 | val = (unsigned long)(win_data->dma_addr + size); |
1c248b7d ID |
585 | writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); |
586 | ||
587 | DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", | |
2c871127 | 588 | (unsigned long)win_data->dma_addr, val, size); |
19c8b834 ID |
589 | DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", |
590 | win_data->ovl_width, win_data->ovl_height); | |
1c248b7d ID |
591 | |
592 | /* buffer size */ | |
593 | val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | | |
ca555e5a JS |
594 | VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) | |
595 | VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) | | |
596 | VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size); | |
1c248b7d ID |
597 | writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); |
598 | ||
599 | /* OSD position */ | |
600 | val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | | |
ca555e5a JS |
601 | VIDOSDxA_TOPLEFT_Y(win_data->offset_y) | |
602 | VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) | | |
603 | VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y); | |
1c248b7d ID |
604 | writel(val, ctx->regs + VIDOSD_A(win)); |
605 | ||
f56aad3a JS |
606 | last_x = win_data->offset_x + win_data->ovl_width; |
607 | if (last_x) | |
608 | last_x--; | |
609 | last_y = win_data->offset_y + win_data->ovl_height; | |
610 | if (last_y) | |
611 | last_y--; | |
612 | ||
ca555e5a JS |
613 | val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) | |
614 | VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y); | |
615 | ||
1c248b7d ID |
616 | writel(val, ctx->regs + VIDOSD_B(win)); |
617 | ||
19c8b834 | 618 | DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", |
f56aad3a | 619 | win_data->offset_x, win_data->offset_y, last_x, last_y); |
1c248b7d ID |
620 | |
621 | /* hardware window 0 doesn't support alpha channel. */ | |
622 | if (win != 0) { | |
623 | /* OSD alpha */ | |
624 | alpha = VIDISD14C_ALPHA1_R(0xf) | | |
625 | VIDISD14C_ALPHA1_G(0xf) | | |
626 | VIDISD14C_ALPHA1_B(0xf); | |
627 | ||
628 | writel(alpha, ctx->regs + VIDOSD_C(win)); | |
629 | } | |
630 | ||
631 | /* OSD size */ | |
632 | if (win != 3 && win != 4) { | |
633 | u32 offset = VIDOSD_D(win); | |
634 | if (win == 0) | |
0f10cf14 | 635 | offset = VIDOSD_C(win); |
19c8b834 | 636 | val = win_data->ovl_width * win_data->ovl_height; |
1c248b7d ID |
637 | writel(val, ctx->regs + offset); |
638 | ||
639 | DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); | |
640 | } | |
641 | ||
bb7704d6 | 642 | fimd_win_set_pixfmt(ctx, win); |
1c248b7d ID |
643 | |
644 | /* hardware window 0 doesn't support color key. */ | |
645 | if (win != 0) | |
bb7704d6 | 646 | fimd_win_set_colkey(ctx, win); |
1c248b7d | 647 | |
ec05da95 ID |
648 | /* wincon */ |
649 | val = readl(ctx->regs + WINCON(win)); | |
650 | val |= WINCONx_ENWIN; | |
651 | writel(val, ctx->regs + WINCON(win)); | |
652 | ||
1c248b7d | 653 | /* Enable DMA channel and unprotect windows */ |
de7af100 TF |
654 | fimd_shadow_protect_win(ctx, win, false); |
655 | ||
656 | if (ctx->driver_data->has_shadowcon) { | |
657 | val = readl(ctx->regs + SHADOWCON); | |
658 | val |= SHADOWCON_CHx_ENABLE(win); | |
659 | writel(val, ctx->regs + SHADOWCON); | |
660 | } | |
ec05da95 ID |
661 | |
662 | win_data->enabled = true; | |
1c248b7d ID |
663 | } |
664 | ||
bb7704d6 | 665 | static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) |
1c248b7d | 666 | { |
bb7704d6 | 667 | struct fimd_context *ctx = mgr->ctx; |
ec05da95 | 668 | struct fimd_win_data *win_data; |
864ee9e6 | 669 | int win = zpos; |
1c248b7d ID |
670 | u32 val; |
671 | ||
864ee9e6 JS |
672 | if (win == DEFAULT_ZPOS) |
673 | win = ctx->default_win; | |
674 | ||
37b006e8 | 675 | if (win < 0 || win >= WINDOWS_NR) |
1c248b7d ID |
676 | return; |
677 | ||
ec05da95 ID |
678 | win_data = &ctx->win_data[win]; |
679 | ||
db7e55ae P |
680 | if (ctx->suspended) { |
681 | /* do not resume this window*/ | |
682 | win_data->resume = false; | |
683 | return; | |
684 | } | |
685 | ||
1c248b7d | 686 | /* protect windows */ |
de7af100 | 687 | fimd_shadow_protect_win(ctx, win, true); |
1c248b7d ID |
688 | |
689 | /* wincon */ | |
690 | val = readl(ctx->regs + WINCON(win)); | |
691 | val &= ~WINCONx_ENWIN; | |
692 | writel(val, ctx->regs + WINCON(win)); | |
693 | ||
694 | /* unprotect windows */ | |
de7af100 TF |
695 | if (ctx->driver_data->has_shadowcon) { |
696 | val = readl(ctx->regs + SHADOWCON); | |
697 | val &= ~SHADOWCON_CHx_ENABLE(win); | |
698 | writel(val, ctx->regs + SHADOWCON); | |
699 | } | |
700 | ||
701 | fimd_shadow_protect_win(ctx, win, false); | |
ec05da95 ID |
702 | |
703 | win_data->enabled = false; | |
1c248b7d ID |
704 | } |
705 | ||
a43b933b SP |
706 | static void fimd_clear_win(struct fimd_context *ctx, int win) |
707 | { | |
708 | writel(0, ctx->regs + WINCON(win)); | |
709 | writel(0, ctx->regs + VIDOSD_A(win)); | |
710 | writel(0, ctx->regs + VIDOSD_B(win)); | |
711 | writel(0, ctx->regs + VIDOSD_C(win)); | |
712 | ||
713 | if (win == 1 || win == 2) | |
714 | writel(0, ctx->regs + VIDOSD_D(win)); | |
715 | ||
716 | fimd_shadow_protect_win(ctx, win, false); | |
717 | } | |
718 | ||
719 | static void fimd_window_suspend(struct exynos_drm_manager *mgr) | |
720 | { | |
721 | struct fimd_context *ctx = mgr->ctx; | |
722 | struct fimd_win_data *win_data; | |
723 | int i; | |
724 | ||
725 | for (i = 0; i < WINDOWS_NR; i++) { | |
726 | win_data = &ctx->win_data[i]; | |
727 | win_data->resume = win_data->enabled; | |
728 | if (win_data->enabled) | |
729 | fimd_win_disable(mgr, i); | |
730 | } | |
731 | fimd_wait_for_vblank(mgr); | |
732 | } | |
733 | ||
734 | static void fimd_window_resume(struct exynos_drm_manager *mgr) | |
735 | { | |
736 | struct fimd_context *ctx = mgr->ctx; | |
737 | struct fimd_win_data *win_data; | |
738 | int i; | |
739 | ||
740 | for (i = 0; i < WINDOWS_NR; i++) { | |
741 | win_data = &ctx->win_data[i]; | |
742 | win_data->enabled = win_data->resume; | |
743 | win_data->resume = false; | |
744 | } | |
745 | } | |
746 | ||
747 | static void fimd_apply(struct exynos_drm_manager *mgr) | |
748 | { | |
749 | struct fimd_context *ctx = mgr->ctx; | |
750 | struct fimd_win_data *win_data; | |
751 | int i; | |
752 | ||
753 | for (i = 0; i < WINDOWS_NR; i++) { | |
754 | win_data = &ctx->win_data[i]; | |
755 | if (win_data->enabled) | |
756 | fimd_win_commit(mgr, i); | |
757 | } | |
758 | ||
759 | fimd_commit(mgr); | |
760 | } | |
761 | ||
762 | static int fimd_poweron(struct exynos_drm_manager *mgr) | |
763 | { | |
764 | struct fimd_context *ctx = mgr->ctx; | |
765 | int ret; | |
766 | ||
767 | if (!ctx->suspended) | |
768 | return 0; | |
769 | ||
770 | ctx->suspended = false; | |
771 | ||
af65c804 SP |
772 | pm_runtime_get_sync(ctx->dev); |
773 | ||
a43b933b SP |
774 | ret = clk_prepare_enable(ctx->bus_clk); |
775 | if (ret < 0) { | |
776 | DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); | |
777 | goto bus_clk_err; | |
778 | } | |
779 | ||
780 | ret = clk_prepare_enable(ctx->lcd_clk); | |
781 | if (ret < 0) { | |
782 | DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); | |
783 | goto lcd_clk_err; | |
784 | } | |
785 | ||
786 | /* if vblank was enabled status, enable it again. */ | |
787 | if (test_and_clear_bit(0, &ctx->irq_flags)) { | |
788 | ret = fimd_enable_vblank(mgr); | |
789 | if (ret) { | |
790 | DRM_ERROR("Failed to re-enable vblank [%d]\n", ret); | |
791 | goto enable_vblank_err; | |
792 | } | |
793 | } | |
794 | ||
795 | fimd_window_resume(mgr); | |
796 | ||
797 | fimd_apply(mgr); | |
798 | ||
799 | return 0; | |
800 | ||
801 | enable_vblank_err: | |
802 | clk_disable_unprepare(ctx->lcd_clk); | |
803 | lcd_clk_err: | |
804 | clk_disable_unprepare(ctx->bus_clk); | |
805 | bus_clk_err: | |
806 | ctx->suspended = true; | |
807 | return ret; | |
808 | } | |
809 | ||
810 | static int fimd_poweroff(struct exynos_drm_manager *mgr) | |
811 | { | |
812 | struct fimd_context *ctx = mgr->ctx; | |
813 | ||
814 | if (ctx->suspended) | |
815 | return 0; | |
816 | ||
817 | /* | |
818 | * We need to make sure that all windows are disabled before we | |
819 | * suspend that connector. Otherwise we might try to scan from | |
820 | * a destroyed buffer later. | |
821 | */ | |
822 | fimd_window_suspend(mgr); | |
823 | ||
824 | clk_disable_unprepare(ctx->lcd_clk); | |
825 | clk_disable_unprepare(ctx->bus_clk); | |
826 | ||
af65c804 SP |
827 | pm_runtime_put_sync(ctx->dev); |
828 | ||
a43b933b SP |
829 | ctx->suspended = true; |
830 | return 0; | |
831 | } | |
832 | ||
080be03d SP |
833 | static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) |
834 | { | |
af65c804 | 835 | DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); |
080be03d | 836 | |
080be03d SP |
837 | switch (mode) { |
838 | case DRM_MODE_DPMS_ON: | |
af65c804 | 839 | fimd_poweron(mgr); |
080be03d SP |
840 | break; |
841 | case DRM_MODE_DPMS_STANDBY: | |
842 | case DRM_MODE_DPMS_SUSPEND: | |
843 | case DRM_MODE_DPMS_OFF: | |
af65c804 | 844 | fimd_poweroff(mgr); |
080be03d SP |
845 | break; |
846 | default: | |
847 | DRM_DEBUG_KMS("unspecified mode %d\n", mode); | |
848 | break; | |
849 | } | |
080be03d SP |
850 | } |
851 | ||
1c6244c3 SP |
852 | static struct exynos_drm_manager_ops fimd_manager_ops = { |
853 | .dpms = fimd_dpms, | |
a968e727 SP |
854 | .mode_fixup = fimd_mode_fixup, |
855 | .mode_set = fimd_mode_set, | |
1c6244c3 SP |
856 | .commit = fimd_commit, |
857 | .enable_vblank = fimd_enable_vblank, | |
858 | .disable_vblank = fimd_disable_vblank, | |
859 | .wait_for_vblank = fimd_wait_for_vblank, | |
860 | .win_mode_set = fimd_win_mode_set, | |
861 | .win_commit = fimd_win_commit, | |
862 | .win_disable = fimd_win_disable, | |
1c248b7d ID |
863 | }; |
864 | ||
677e84c1 | 865 | static struct exynos_drm_manager fimd_manager = { |
080be03d SP |
866 | .type = EXYNOS_DISPLAY_TYPE_LCD, |
867 | .ops = &fimd_manager_ops, | |
677e84c1 JS |
868 | }; |
869 | ||
1c248b7d ID |
870 | static irqreturn_t fimd_irq_handler(int irq, void *dev_id) |
871 | { | |
872 | struct fimd_context *ctx = (struct fimd_context *)dev_id; | |
1c248b7d ID |
873 | u32 val; |
874 | ||
875 | val = readl(ctx->regs + VIDINTCON1); | |
876 | ||
877 | if (val & VIDINTCON1_INT_FRAME) | |
878 | /* VSYNC interrupt */ | |
879 | writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); | |
880 | ||
ec05da95 | 881 | /* check the crtc is detached already from encoder */ |
080be03d | 882 | if (ctx->pipe < 0 || !ctx->drm_dev) |
ec05da95 | 883 | goto out; |
483b88f8 | 884 | |
080be03d SP |
885 | drm_handle_vblank(ctx->drm_dev, ctx->pipe); |
886 | exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); | |
1c248b7d | 887 | |
01ce113c P |
888 | /* set wait vsync event to zero and wake up queue. */ |
889 | if (atomic_read(&ctx->wait_vsync_event)) { | |
890 | atomic_set(&ctx->wait_vsync_event, 0); | |
8dd9ad5d | 891 | wake_up(&ctx->wait_vsync_queue); |
01ce113c | 892 | } |
ec05da95 | 893 | out: |
1c248b7d ID |
894 | return IRQ_HANDLED; |
895 | } | |
896 | ||
f37cd5e8 | 897 | static int fimd_bind(struct device *dev, struct device *master, void *data) |
562ad9f4 | 898 | { |
000cc920 | 899 | struct fimd_context *ctx = fimd_manager.ctx; |
f37cd5e8 | 900 | struct drm_device *drm_dev = data; |
000cc920 AH |
901 | int win; |
902 | ||
903 | fimd_mgr_initialize(&fimd_manager, drm_dev); | |
904 | exynos_drm_crtc_create(&fimd_manager); | |
905 | if (ctx->display) | |
906 | exynos_drm_create_enc_conn(drm_dev, ctx->display); | |
907 | ||
908 | for (win = 0; win < WINDOWS_NR; win++) | |
909 | fimd_clear_win(ctx, win); | |
910 | ||
911 | return 0; | |
912 | ||
913 | } | |
914 | ||
915 | static void fimd_unbind(struct device *dev, struct device *master, | |
916 | void *data) | |
917 | { | |
918 | struct exynos_drm_manager *mgr = dev_get_drvdata(dev); | |
919 | struct fimd_context *ctx = fimd_manager.ctx; | |
920 | struct drm_crtc *crtc = mgr->crtc; | |
921 | ||
922 | fimd_dpms(mgr, DRM_MODE_DPMS_OFF); | |
923 | ||
924 | if (ctx->display) | |
925 | exynos_dpi_remove(dev); | |
926 | ||
927 | fimd_mgr_remove(mgr); | |
928 | ||
929 | crtc->funcs->destroy(crtc); | |
930 | } | |
931 | ||
932 | static const struct component_ops fimd_component_ops = { | |
933 | .bind = fimd_bind, | |
934 | .unbind = fimd_unbind, | |
935 | }; | |
936 | ||
937 | static int fimd_probe(struct platform_device *pdev) | |
938 | { | |
939 | struct device *dev = &pdev->dev; | |
562ad9f4 | 940 | struct fimd_context *ctx; |
562ad9f4 | 941 | struct resource *res; |
562ad9f4 | 942 | int ret = -EINVAL; |
1c248b7d | 943 | |
2d3f173c SK |
944 | if (!dev->of_node) |
945 | return -ENODEV; | |
946 | ||
d873ab99 | 947 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); |
1c248b7d ID |
948 | if (!ctx) |
949 | return -ENOMEM; | |
950 | ||
bb7704d6 | 951 | ctx->dev = dev; |
a43b933b | 952 | ctx->suspended = true; |
bb7704d6 | 953 | |
1417f109 SP |
954 | if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) |
955 | ctx->vidcon1 |= VIDCON1_INV_VDEN; | |
956 | if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) | |
957 | ctx->vidcon1 |= VIDCON1_INV_VCLK; | |
562ad9f4 | 958 | |
a968e727 SP |
959 | ctx->bus_clk = devm_clk_get(dev, "fimd"); |
960 | if (IS_ERR(ctx->bus_clk)) { | |
961 | dev_err(dev, "failed to get bus clock\n"); | |
962 | return PTR_ERR(ctx->bus_clk); | |
963 | } | |
964 | ||
965 | ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); | |
966 | if (IS_ERR(ctx->lcd_clk)) { | |
967 | dev_err(dev, "failed to get lcd clock\n"); | |
968 | return PTR_ERR(ctx->lcd_clk); | |
969 | } | |
1c248b7d | 970 | |
1c248b7d | 971 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1c248b7d | 972 | |
d873ab99 | 973 | ctx->regs = devm_ioremap_resource(dev, res); |
d4ed6025 TR |
974 | if (IS_ERR(ctx->regs)) |
975 | return PTR_ERR(ctx->regs); | |
1c248b7d | 976 | |
1977e6d8 | 977 | res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync"); |
1c248b7d ID |
978 | if (!res) { |
979 | dev_err(dev, "irq request failed.\n"); | |
a4d8de5f | 980 | return -ENXIO; |
1c248b7d ID |
981 | } |
982 | ||
055e0c06 | 983 | ret = devm_request_irq(dev, res->start, fimd_irq_handler, |
edc57266 SK |
984 | 0, "drm_fimd", ctx); |
985 | if (ret) { | |
1c248b7d | 986 | dev_err(dev, "irq request failed.\n"); |
a4d8de5f | 987 | return ret; |
1c248b7d ID |
988 | } |
989 | ||
18873465 | 990 | ctx->driver_data = drm_fimd_get_driver_data(pdev); |
57ed0f7b | 991 | init_waitqueue_head(&ctx->wait_vsync_queue); |
01ce113c | 992 | atomic_set(&ctx->wait_vsync_event, 0); |
1c248b7d | 993 | |
bb7704d6 | 994 | platform_set_drvdata(pdev, &fimd_manager); |
c32b06ef | 995 | |
080be03d | 996 | fimd_manager.ctx = ctx; |
14b6873a | 997 | |
000cc920 AH |
998 | ctx->display = exynos_dpi_probe(dev); |
999 | if (IS_ERR(ctx->display)) | |
1000 | return PTR_ERR(ctx->display); | |
f37cd5e8 ID |
1001 | |
1002 | pm_runtime_enable(&pdev->dev); | |
1003 | ||
1004 | return exynos_drm_component_add(&pdev->dev, &fimd_component_ops); | |
1005 | } | |
cb91f6a0 | 1006 | |
f37cd5e8 ID |
1007 | static int fimd_remove(struct platform_device *pdev) |
1008 | { | |
af65c804 | 1009 | pm_runtime_disable(&pdev->dev); |
5d55393a | 1010 | |
f37cd5e8 | 1011 | exynos_drm_component_del(&pdev->dev, &fimd_component_ops); |
5d55393a | 1012 | return 0; |
e30d4bcf ID |
1013 | } |
1014 | ||
132a5b91 | 1015 | struct platform_driver fimd_driver = { |
1c248b7d | 1016 | .probe = fimd_probe, |
56550d94 | 1017 | .remove = fimd_remove, |
1c248b7d ID |
1018 | .driver = { |
1019 | .name = "exynos4-fb", | |
1020 | .owner = THIS_MODULE, | |
2d3f173c | 1021 | .of_match_table = fimd_driver_dt_match, |
1c248b7d ID |
1022 | }, |
1023 | }; |