drm: exynos: hdmi: add support to disable video processor in mixer
[linux-2.6-block.git] / drivers / gpu / drm / exynos / exynos_mixer.c
CommitLineData
d8408326
SWK
1/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/mixer_reg.c
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
15 */
16
17#include "drmP.h"
18
19#include "regs-mixer.h"
20#include "regs-vp.h"
21
22#include <linux/kernel.h>
23#include <linux/spinlock.h>
24#include <linux/wait.h>
25#include <linux/i2c.h>
26#include <linux/module.h>
27#include <linux/platform_device.h>
28#include <linux/interrupt.h>
29#include <linux/irq.h>
30#include <linux/delay.h>
31#include <linux/pm_runtime.h>
32#include <linux/clk.h>
33#include <linux/regulator/consumer.h>
34
35#include <drm/exynos_drm.h>
36
37#include "exynos_drm_drv.h"
38#include "exynos_drm_hdmi.h"
22b21ae6 39
d8408326
SWK
40#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
41
22b21ae6
JS
42struct hdmi_win_data {
43 dma_addr_t dma_addr;
44 void __iomem *vaddr;
45 dma_addr_t chroma_dma_addr;
46 void __iomem *chroma_vaddr;
47 uint32_t pixel_format;
48 unsigned int bpp;
49 unsigned int crtc_x;
50 unsigned int crtc_y;
51 unsigned int crtc_width;
52 unsigned int crtc_height;
53 unsigned int fb_x;
54 unsigned int fb_y;
55 unsigned int fb_width;
56 unsigned int fb_height;
8dcb96b6
SWK
57 unsigned int src_width;
58 unsigned int src_height;
22b21ae6
JS
59 unsigned int mode_width;
60 unsigned int mode_height;
61 unsigned int scan_flags;
62};
63
64struct mixer_resources {
22b21ae6
JS
65 int irq;
66 void __iomem *mixer_regs;
67 void __iomem *vp_regs;
68 spinlock_t reg_slock;
69 struct clk *mixer;
70 struct clk *vp;
71 struct clk *sclk_mixer;
72 struct clk *sclk_hdmi;
73 struct clk *sclk_dac;
74};
75
1e123441
RS
76enum mixer_version_id {
77 MXR_VER_0_0_0_16,
78 MXR_VER_16_0_33_0,
79};
80
22b21ae6 81struct mixer_context {
cf8fc4f1 82 struct device *dev;
22b21ae6
JS
83 int pipe;
84 bool interlace;
cf8fc4f1 85 bool powered;
1b8e5747 86 bool vp_enabled;
cf8fc4f1 87 u32 int_en;
22b21ae6 88
cf8fc4f1 89 struct mutex mixer_mutex;
22b21ae6 90 struct mixer_resources mixer_res;
a634dd54 91 struct hdmi_win_data win_data[MIXER_WIN_NR];
1e123441
RS
92 enum mixer_version_id mxr_ver;
93};
94
95struct mixer_drv_data {
96 enum mixer_version_id version;
1b8e5747 97 bool is_vp_enabled;
22b21ae6
JS
98};
99
d8408326
SWK
100static const u8 filter_y_horiz_tap8[] = {
101 0, -1, -1, -1, -1, -1, -1, -1,
102 -1, -1, -1, -1, -1, 0, 0, 0,
103 0, 2, 4, 5, 6, 6, 6, 6,
104 6, 5, 5, 4, 3, 2, 1, 1,
105 0, -6, -12, -16, -18, -20, -21, -20,
106 -20, -18, -16, -13, -10, -8, -5, -2,
107 127, 126, 125, 121, 114, 107, 99, 89,
108 79, 68, 57, 46, 35, 25, 16, 8,
109};
110
111static const u8 filter_y_vert_tap4[] = {
112 0, -3, -6, -8, -8, -8, -8, -7,
113 -6, -5, -4, -3, -2, -1, -1, 0,
114 127, 126, 124, 118, 111, 102, 92, 81,
115 70, 59, 48, 37, 27, 19, 11, 5,
116 0, 5, 11, 19, 27, 37, 48, 59,
117 70, 81, 92, 102, 111, 118, 124, 126,
118 0, 0, -1, -1, -2, -3, -4, -5,
119 -6, -7, -8, -8, -8, -8, -6, -3,
120};
121
122static const u8 filter_cr_horiz_tap4[] = {
123 0, -3, -6, -8, -8, -8, -8, -7,
124 -6, -5, -4, -3, -2, -1, -1, 0,
125 127, 126, 124, 118, 111, 102, 92, 81,
126 70, 59, 48, 37, 27, 19, 11, 5,
127};
128
129static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
130{
131 return readl(res->vp_regs + reg_id);
132}
133
134static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
135 u32 val)
136{
137 writel(val, res->vp_regs + reg_id);
138}
139
140static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
141 u32 val, u32 mask)
142{
143 u32 old = vp_reg_read(res, reg_id);
144
145 val = (val & mask) | (old & ~mask);
146 writel(val, res->vp_regs + reg_id);
147}
148
149static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
150{
151 return readl(res->mixer_regs + reg_id);
152}
153
154static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
155 u32 val)
156{
157 writel(val, res->mixer_regs + reg_id);
158}
159
160static inline void mixer_reg_writemask(struct mixer_resources *res,
161 u32 reg_id, u32 val, u32 mask)
162{
163 u32 old = mixer_reg_read(res, reg_id);
164
165 val = (val & mask) | (old & ~mask);
166 writel(val, res->mixer_regs + reg_id);
167}
168
169static void mixer_regs_dump(struct mixer_context *ctx)
170{
171#define DUMPREG(reg_id) \
172do { \
173 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
174 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
175} while (0)
176
177 DUMPREG(MXR_STATUS);
178 DUMPREG(MXR_CFG);
179 DUMPREG(MXR_INT_EN);
180 DUMPREG(MXR_INT_STATUS);
181
182 DUMPREG(MXR_LAYER_CFG);
183 DUMPREG(MXR_VIDEO_CFG);
184
185 DUMPREG(MXR_GRAPHIC0_CFG);
186 DUMPREG(MXR_GRAPHIC0_BASE);
187 DUMPREG(MXR_GRAPHIC0_SPAN);
188 DUMPREG(MXR_GRAPHIC0_WH);
189 DUMPREG(MXR_GRAPHIC0_SXY);
190 DUMPREG(MXR_GRAPHIC0_DXY);
191
192 DUMPREG(MXR_GRAPHIC1_CFG);
193 DUMPREG(MXR_GRAPHIC1_BASE);
194 DUMPREG(MXR_GRAPHIC1_SPAN);
195 DUMPREG(MXR_GRAPHIC1_WH);
196 DUMPREG(MXR_GRAPHIC1_SXY);
197 DUMPREG(MXR_GRAPHIC1_DXY);
198#undef DUMPREG
199}
200
201static void vp_regs_dump(struct mixer_context *ctx)
202{
203#define DUMPREG(reg_id) \
204do { \
205 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
206 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
207} while (0)
208
209 DUMPREG(VP_ENABLE);
210 DUMPREG(VP_SRESET);
211 DUMPREG(VP_SHADOW_UPDATE);
212 DUMPREG(VP_FIELD_ID);
213 DUMPREG(VP_MODE);
214 DUMPREG(VP_IMG_SIZE_Y);
215 DUMPREG(VP_IMG_SIZE_C);
216 DUMPREG(VP_PER_RATE_CTRL);
217 DUMPREG(VP_TOP_Y_PTR);
218 DUMPREG(VP_BOT_Y_PTR);
219 DUMPREG(VP_TOP_C_PTR);
220 DUMPREG(VP_BOT_C_PTR);
221 DUMPREG(VP_ENDIAN_MODE);
222 DUMPREG(VP_SRC_H_POSITION);
223 DUMPREG(VP_SRC_V_POSITION);
224 DUMPREG(VP_SRC_WIDTH);
225 DUMPREG(VP_SRC_HEIGHT);
226 DUMPREG(VP_DST_H_POSITION);
227 DUMPREG(VP_DST_V_POSITION);
228 DUMPREG(VP_DST_WIDTH);
229 DUMPREG(VP_DST_HEIGHT);
230 DUMPREG(VP_H_RATIO);
231 DUMPREG(VP_V_RATIO);
232
233#undef DUMPREG
234}
235
236static inline void vp_filter_set(struct mixer_resources *res,
237 int reg_id, const u8 *data, unsigned int size)
238{
239 /* assure 4-byte align */
240 BUG_ON(size & 3);
241 for (; size; size -= 4, reg_id += 4, data += 4) {
242 u32 val = (data[0] << 24) | (data[1] << 16) |
243 (data[2] << 8) | data[3];
244 vp_reg_write(res, reg_id, val);
245 }
246}
247
248static void vp_default_filter(struct mixer_resources *res)
249{
250 vp_filter_set(res, VP_POLY8_Y0_LL,
e25e1b66 251 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
d8408326 252 vp_filter_set(res, VP_POLY4_Y0_LL,
e25e1b66 253 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
d8408326 254 vp_filter_set(res, VP_POLY4_C0_LL,
e25e1b66 255 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
d8408326
SWK
256}
257
258static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
259{
260 struct mixer_resources *res = &ctx->mixer_res;
261
262 /* block update on vsync */
263 mixer_reg_writemask(res, MXR_STATUS, enable ?
264 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
265
1b8e5747
RS
266 if (ctx->vp_enabled)
267 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
d8408326
SWK
268 VP_SHADOW_UPDATE_ENABLE : 0);
269}
270
271static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
272{
273 struct mixer_resources *res = &ctx->mixer_res;
274 u32 val;
275
276 /* choosing between interlace and progressive mode */
277 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
278 MXR_CFG_SCAN_PROGRASSIVE);
279
280 /* choosing between porper HD and SD mode */
281 if (height == 480)
282 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
283 else if (height == 576)
284 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
285 else if (height == 720)
286 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
287 else if (height == 1080)
288 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
289 else
290 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
291
292 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
293}
294
295static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
296{
297 struct mixer_resources *res = &ctx->mixer_res;
298 u32 val;
299
300 if (height == 480) {
301 val = MXR_CFG_RGB601_0_255;
302 } else if (height == 576) {
303 val = MXR_CFG_RGB601_0_255;
304 } else if (height == 720) {
305 val = MXR_CFG_RGB709_16_235;
306 mixer_reg_write(res, MXR_CM_COEFF_Y,
307 (1 << 30) | (94 << 20) | (314 << 10) |
308 (32 << 0));
309 mixer_reg_write(res, MXR_CM_COEFF_CB,
310 (972 << 20) | (851 << 10) | (225 << 0));
311 mixer_reg_write(res, MXR_CM_COEFF_CR,
312 (225 << 20) | (820 << 10) | (1004 << 0));
313 } else if (height == 1080) {
314 val = MXR_CFG_RGB709_16_235;
315 mixer_reg_write(res, MXR_CM_COEFF_Y,
316 (1 << 30) | (94 << 20) | (314 << 10) |
317 (32 << 0));
318 mixer_reg_write(res, MXR_CM_COEFF_CB,
319 (972 << 20) | (851 << 10) | (225 << 0));
320 mixer_reg_write(res, MXR_CM_COEFF_CR,
321 (225 << 20) | (820 << 10) | (1004 << 0));
322 } else {
323 val = MXR_CFG_RGB709_16_235;
324 mixer_reg_write(res, MXR_CM_COEFF_Y,
325 (1 << 30) | (94 << 20) | (314 << 10) |
326 (32 << 0));
327 mixer_reg_write(res, MXR_CM_COEFF_CB,
328 (972 << 20) | (851 << 10) | (225 << 0));
329 mixer_reg_write(res, MXR_CM_COEFF_CR,
330 (225 << 20) | (820 << 10) | (1004 << 0));
331 }
332
333 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
334}
335
336static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
337{
338 struct mixer_resources *res = &ctx->mixer_res;
339 u32 val = enable ? ~0 : 0;
340
341 switch (win) {
342 case 0:
343 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
344 break;
345 case 1:
346 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
347 break;
348 case 2:
1b8e5747
RS
349 if (ctx->vp_enabled) {
350 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
351 mixer_reg_writemask(res, MXR_CFG, val,
352 MXR_CFG_VP_ENABLE);
353 }
d8408326
SWK
354 break;
355 }
356}
357
358static void mixer_run(struct mixer_context *ctx)
359{
360 struct mixer_resources *res = &ctx->mixer_res;
361
362 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
363
364 mixer_regs_dump(ctx);
365}
366
367static void vp_video_buffer(struct mixer_context *ctx, int win)
368{
369 struct mixer_resources *res = &ctx->mixer_res;
370 unsigned long flags;
371 struct hdmi_win_data *win_data;
d8408326 372 unsigned int x_ratio, y_ratio;
d8408326
SWK
373 unsigned int buf_num;
374 dma_addr_t luma_addr[2], chroma_addr[2];
375 bool tiled_mode = false;
376 bool crcb_mode = false;
377 u32 val;
378
379 win_data = &ctx->win_data[win];
380
381 switch (win_data->pixel_format) {
382 case DRM_FORMAT_NV12MT:
383 tiled_mode = true;
363b06aa 384 case DRM_FORMAT_NV12:
d8408326
SWK
385 crcb_mode = false;
386 buf_num = 2;
387 break;
388 /* TODO: single buffer format NV12, NV21 */
389 default:
390 /* ignore pixel format at disable time */
391 if (!win_data->dma_addr)
392 break;
393
394 DRM_ERROR("pixel format for vp is wrong [%d].\n",
395 win_data->pixel_format);
396 return;
397 }
398
d8408326 399 /* scaling feature: (src << 16) / dst */
8dcb96b6
SWK
400 x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
401 y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
d8408326
SWK
402
403 if (buf_num == 2) {
404 luma_addr[0] = win_data->dma_addr;
405 chroma_addr[0] = win_data->chroma_dma_addr;
406 } else {
407 luma_addr[0] = win_data->dma_addr;
408 chroma_addr[0] = win_data->dma_addr
8dcb96b6 409 + (win_data->fb_width * win_data->fb_height);
d8408326
SWK
410 }
411
412 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
413 ctx->interlace = true;
414 if (tiled_mode) {
415 luma_addr[1] = luma_addr[0] + 0x40;
416 chroma_addr[1] = chroma_addr[0] + 0x40;
417 } else {
8dcb96b6
SWK
418 luma_addr[1] = luma_addr[0] + win_data->fb_width;
419 chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
d8408326
SWK
420 }
421 } else {
422 ctx->interlace = false;
423 luma_addr[1] = 0;
424 chroma_addr[1] = 0;
425 }
426
427 spin_lock_irqsave(&res->reg_slock, flags);
428 mixer_vsync_set_update(ctx, false);
429
430 /* interlace or progressive scan mode */
431 val = (ctx->interlace ? ~0 : 0);
432 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
433
434 /* setup format */
435 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
436 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
437 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
438
439 /* setting size of input image */
8dcb96b6
SWK
440 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
441 VP_IMG_VSIZE(win_data->fb_height));
d8408326 442 /* chroma height has to reduced by 2 to avoid chroma distorions */
8dcb96b6
SWK
443 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
444 VP_IMG_VSIZE(win_data->fb_height / 2));
d8408326 445
8dcb96b6
SWK
446 vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
447 vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
d8408326 448 vp_reg_write(res, VP_SRC_H_POSITION,
8dcb96b6
SWK
449 VP_SRC_H_POSITION_VAL(win_data->fb_x));
450 vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
d8408326 451
8dcb96b6
SWK
452 vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
453 vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
d8408326 454 if (ctx->interlace) {
8dcb96b6
SWK
455 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2);
456 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2);
d8408326 457 } else {
8dcb96b6
SWK
458 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
459 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
d8408326
SWK
460 }
461
462 vp_reg_write(res, VP_H_RATIO, x_ratio);
463 vp_reg_write(res, VP_V_RATIO, y_ratio);
464
465 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
466
467 /* set buffer address to vp */
468 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
469 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
470 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
471 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
472
8dcb96b6
SWK
473 mixer_cfg_scan(ctx, win_data->mode_height);
474 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
d8408326
SWK
475 mixer_cfg_layer(ctx, win, true);
476 mixer_run(ctx);
477
478 mixer_vsync_set_update(ctx, true);
479 spin_unlock_irqrestore(&res->reg_slock, flags);
480
481 vp_regs_dump(ctx);
482}
483
484static void mixer_graph_buffer(struct mixer_context *ctx, int win)
485{
486 struct mixer_resources *res = &ctx->mixer_res;
487 unsigned long flags;
488 struct hdmi_win_data *win_data;
d8408326
SWK
489 unsigned int x_ratio, y_ratio;
490 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
d8408326
SWK
491 dma_addr_t dma_addr;
492 unsigned int fmt;
493 u32 val;
494
495 win_data = &ctx->win_data[win];
496
497 #define RGB565 4
498 #define ARGB1555 5
499 #define ARGB4444 6
500 #define ARGB8888 7
501
502 switch (win_data->bpp) {
503 case 16:
504 fmt = ARGB4444;
505 break;
506 case 32:
507 fmt = ARGB8888;
508 break;
509 default:
510 fmt = ARGB8888;
511 }
512
d8408326
SWK
513 /* 2x scaling feature */
514 x_ratio = 0;
515 y_ratio = 0;
516
d8408326
SWK
517 dst_x_offset = win_data->crtc_x;
518 dst_y_offset = win_data->crtc_y;
519
520 /* converting dma address base and source offset */
8dcb96b6
SWK
521 dma_addr = win_data->dma_addr
522 + (win_data->fb_x * win_data->bpp >> 3)
523 + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
d8408326
SWK
524 src_x_offset = 0;
525 src_y_offset = 0;
526
527 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
528 ctx->interlace = true;
529 else
530 ctx->interlace = false;
531
532 spin_lock_irqsave(&res->reg_slock, flags);
533 mixer_vsync_set_update(ctx, false);
534
535 /* setup format */
536 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
537 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
538
539 /* setup geometry */
8dcb96b6 540 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
d8408326 541
8dcb96b6
SWK
542 val = MXR_GRP_WH_WIDTH(win_data->crtc_width);
543 val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
d8408326
SWK
544 val |= MXR_GRP_WH_H_SCALE(x_ratio);
545 val |= MXR_GRP_WH_V_SCALE(y_ratio);
546 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
547
548 /* setup offsets in source image */
549 val = MXR_GRP_SXY_SX(src_x_offset);
550 val |= MXR_GRP_SXY_SY(src_y_offset);
551 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
552
553 /* setup offsets in display image */
554 val = MXR_GRP_DXY_DX(dst_x_offset);
555 val |= MXR_GRP_DXY_DY(dst_y_offset);
556 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
557
558 /* set buffer address to mixer */
559 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
560
8dcb96b6
SWK
561 mixer_cfg_scan(ctx, win_data->mode_height);
562 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
d8408326
SWK
563 mixer_cfg_layer(ctx, win, true);
564 mixer_run(ctx);
565
566 mixer_vsync_set_update(ctx, true);
567 spin_unlock_irqrestore(&res->reg_slock, flags);
568}
569
570static void vp_win_reset(struct mixer_context *ctx)
571{
572 struct mixer_resources *res = &ctx->mixer_res;
573 int tries = 100;
574
575 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
576 for (tries = 100; tries; --tries) {
577 /* waiting until VP_SRESET_PROCESSING is 0 */
578 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
579 break;
580 mdelay(10);
581 }
582 WARN(tries == 0, "failed to reset Video Processor\n");
583}
584
cf8fc4f1
JS
585static void mixer_win_reset(struct mixer_context *ctx)
586{
587 struct mixer_resources *res = &ctx->mixer_res;
588 unsigned long flags;
589 u32 val; /* value stored to register */
590
591 spin_lock_irqsave(&res->reg_slock, flags);
592 mixer_vsync_set_update(ctx, false);
593
594 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
595
596 /* set output in RGB888 mode */
597 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
598
599 /* 16 beat burst in DMA */
600 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
601 MXR_STATUS_BURST_MASK);
602
603 /* setting default layer priority: layer1 > layer0 > video
604 * because typical usage scenario would be
605 * layer1 - OSD
606 * layer0 - framebuffer
607 * video - video overlay
608 */
609 val = MXR_LAYER_CFG_GRP1_VAL(3);
610 val |= MXR_LAYER_CFG_GRP0_VAL(2);
1b8e5747
RS
611 if (ctx->vp_enabled)
612 val |= MXR_LAYER_CFG_VP_VAL(1);
cf8fc4f1
JS
613 mixer_reg_write(res, MXR_LAYER_CFG, val);
614
615 /* setting background color */
616 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
617 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
618 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
619
620 /* setting graphical layers */
cf8fc4f1
JS
621 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
622 val |= MXR_GRP_CFG_WIN_BLEND_EN;
5736603b
SWK
623 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
624 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
cf8fc4f1
JS
625 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
626
627 /* the same configuration for both layers */
628 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
cf8fc4f1
JS
629 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
630
5736603b
SWK
631 /* setting video layers */
632 val = MXR_GRP_CFG_ALPHA_VAL(0);
633 mixer_reg_write(res, MXR_VIDEO_CFG, val);
634
1b8e5747
RS
635 if (ctx->vp_enabled) {
636 /* configuration of Video Processor Registers */
637 vp_win_reset(ctx);
638 vp_default_filter(res);
639 }
cf8fc4f1
JS
640
641 /* disable all layers */
642 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
643 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
1b8e5747
RS
644 if (ctx->vp_enabled)
645 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
cf8fc4f1
JS
646
647 mixer_vsync_set_update(ctx, true);
648 spin_unlock_irqrestore(&res->reg_slock, flags);
649}
650
651static void mixer_poweron(struct mixer_context *ctx)
652{
653 struct mixer_resources *res = &ctx->mixer_res;
654
655 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
656
657 mutex_lock(&ctx->mixer_mutex);
658 if (ctx->powered) {
659 mutex_unlock(&ctx->mixer_mutex);
660 return;
661 }
662 ctx->powered = true;
663 mutex_unlock(&ctx->mixer_mutex);
664
665 pm_runtime_get_sync(ctx->dev);
666
667 clk_enable(res->mixer);
1b8e5747
RS
668 if (ctx->vp_enabled) {
669 clk_enable(res->vp);
670 clk_enable(res->sclk_mixer);
671 }
cf8fc4f1
JS
672
673 mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
674 mixer_win_reset(ctx);
675}
676
677static void mixer_poweroff(struct mixer_context *ctx)
678{
679 struct mixer_resources *res = &ctx->mixer_res;
680
681 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
682
683 mutex_lock(&ctx->mixer_mutex);
684 if (!ctx->powered)
685 goto out;
686 mutex_unlock(&ctx->mixer_mutex);
687
688 ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
689
690 clk_disable(res->mixer);
1b8e5747
RS
691 if (ctx->vp_enabled) {
692 clk_disable(res->vp);
693 clk_disable(res->sclk_mixer);
694 }
cf8fc4f1
JS
695
696 pm_runtime_put_sync(ctx->dev);
697
698 mutex_lock(&ctx->mixer_mutex);
699 ctx->powered = false;
700
701out:
702 mutex_unlock(&ctx->mixer_mutex);
703}
704
d8408326
SWK
705static int mixer_enable_vblank(void *ctx, int pipe)
706{
707 struct mixer_context *mixer_ctx = ctx;
708 struct mixer_resources *res = &mixer_ctx->mixer_res;
709
710 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
711
712 mixer_ctx->pipe = pipe;
713
714 /* enable vsync interrupt */
715 mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
716 MXR_INT_EN_VSYNC);
717
718 return 0;
719}
720
721static void mixer_disable_vblank(void *ctx)
722{
723 struct mixer_context *mixer_ctx = ctx;
724 struct mixer_resources *res = &mixer_ctx->mixer_res;
725
726 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
727
728 /* disable vsync interrupt */
729 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
730}
731
cf8fc4f1
JS
732static void mixer_dpms(void *ctx, int mode)
733{
734 struct mixer_context *mixer_ctx = ctx;
735
736 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
737
738 switch (mode) {
739 case DRM_MODE_DPMS_ON:
740 mixer_poweron(mixer_ctx);
741 break;
742 case DRM_MODE_DPMS_STANDBY:
743 case DRM_MODE_DPMS_SUSPEND:
744 case DRM_MODE_DPMS_OFF:
745 mixer_poweroff(mixer_ctx);
746 break;
747 default:
748 DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
749 break;
750 }
751}
752
3d05859f
ID
753static void mixer_wait_for_vblank(void *ctx)
754{
755 struct mixer_context *mixer_ctx = ctx;
756 struct mixer_resources *res = &mixer_ctx->mixer_res;
757 int ret;
758
759 ret = wait_for((mixer_reg_read(res, MXR_INT_STATUS) &
760 MXR_INT_STATUS_VSYNC), 50);
761 if (ret < 0)
762 DRM_DEBUG_KMS("vblank wait timed out.\n");
763}
764
d8408326
SWK
765static void mixer_win_mode_set(void *ctx,
766 struct exynos_drm_overlay *overlay)
767{
768 struct mixer_context *mixer_ctx = ctx;
769 struct hdmi_win_data *win_data;
770 int win;
771
772 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
773
774 if (!overlay) {
775 DRM_ERROR("overlay is NULL\n");
776 return;
777 }
778
779 DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
780 overlay->fb_width, overlay->fb_height,
781 overlay->fb_x, overlay->fb_y,
782 overlay->crtc_width, overlay->crtc_height,
783 overlay->crtc_x, overlay->crtc_y);
784
785 win = overlay->zpos;
786 if (win == DEFAULT_ZPOS)
a2ee151b 787 win = MIXER_DEFAULT_WIN;
d8408326 788
a634dd54 789 if (win < 0 || win > MIXER_WIN_NR) {
cf8fc4f1 790 DRM_ERROR("mixer window[%d] is wrong\n", win);
d8408326
SWK
791 return;
792 }
793
794 win_data = &mixer_ctx->win_data[win];
795
796 win_data->dma_addr = overlay->dma_addr[0];
797 win_data->vaddr = overlay->vaddr[0];
798 win_data->chroma_dma_addr = overlay->dma_addr[1];
799 win_data->chroma_vaddr = overlay->vaddr[1];
800 win_data->pixel_format = overlay->pixel_format;
801 win_data->bpp = overlay->bpp;
802
803 win_data->crtc_x = overlay->crtc_x;
804 win_data->crtc_y = overlay->crtc_y;
805 win_data->crtc_width = overlay->crtc_width;
806 win_data->crtc_height = overlay->crtc_height;
807
808 win_data->fb_x = overlay->fb_x;
809 win_data->fb_y = overlay->fb_y;
810 win_data->fb_width = overlay->fb_width;
811 win_data->fb_height = overlay->fb_height;
8dcb96b6
SWK
812 win_data->src_width = overlay->src_width;
813 win_data->src_height = overlay->src_height;
d8408326
SWK
814
815 win_data->mode_width = overlay->mode_width;
816 win_data->mode_height = overlay->mode_height;
817
818 win_data->scan_flags = overlay->scan_flag;
819}
820
cf8fc4f1 821static void mixer_win_commit(void *ctx, int win)
d8408326
SWK
822{
823 struct mixer_context *mixer_ctx = ctx;
d8408326
SWK
824
825 DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
826
1b8e5747 827 if (win > 1 && mixer_ctx->vp_enabled)
d8408326
SWK
828 vp_video_buffer(mixer_ctx, win);
829 else
830 mixer_graph_buffer(mixer_ctx, win);
831}
832
cf8fc4f1 833static void mixer_win_disable(void *ctx, int win)
d8408326
SWK
834{
835 struct mixer_context *mixer_ctx = ctx;
836 struct mixer_resources *res = &mixer_ctx->mixer_res;
837 unsigned long flags;
d8408326
SWK
838
839 DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
840
d8408326
SWK
841 spin_lock_irqsave(&res->reg_slock, flags);
842 mixer_vsync_set_update(mixer_ctx, false);
843
844 mixer_cfg_layer(mixer_ctx, win, false);
845
846 mixer_vsync_set_update(mixer_ctx, true);
847 spin_unlock_irqrestore(&res->reg_slock, flags);
848}
849
578b6065
JS
850static struct exynos_mixer_ops mixer_ops = {
851 /* manager */
d8408326
SWK
852 .enable_vblank = mixer_enable_vblank,
853 .disable_vblank = mixer_disable_vblank,
cf8fc4f1 854 .dpms = mixer_dpms,
578b6065
JS
855
856 /* overlay */
3d05859f 857 .wait_for_vblank = mixer_wait_for_vblank,
d8408326
SWK
858 .win_mode_set = mixer_win_mode_set,
859 .win_commit = mixer_win_commit,
860 .win_disable = mixer_win_disable,
861};
862
863/* for pageflip event */
864static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc)
865{
866 struct exynos_drm_private *dev_priv = drm_dev->dev_private;
867 struct drm_pending_vblank_event *e, *t;
868 struct timeval now;
869 unsigned long flags;
870 bool is_checked = false;
871
872 spin_lock_irqsave(&drm_dev->event_lock, flags);
873
874 list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
875 base.link) {
876 /* if event's pipe isn't same as crtc then ignore it. */
877 if (crtc != e->pipe)
878 continue;
879
880 is_checked = true;
881 do_gettimeofday(&now);
882 e->event.sequence = 0;
883 e->event.tv_sec = now.tv_sec;
884 e->event.tv_usec = now.tv_usec;
885
886 list_move_tail(&e->base.link, &e->base.file_priv->event_list);
887 wake_up_interruptible(&e->base.file_priv->event_wait);
888 }
889
890 if (is_checked)
c5614ae3
ID
891 /*
892 * call drm_vblank_put only in case that drm_vblank_get was
893 * called.
894 */
895 if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
896 drm_vblank_put(drm_dev, crtc);
d8408326
SWK
897
898 spin_unlock_irqrestore(&drm_dev->event_lock, flags);
899}
900
901static irqreturn_t mixer_irq_handler(int irq, void *arg)
902{
903 struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg;
f9309d1b 904 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
d8408326 905 struct mixer_resources *res = &ctx->mixer_res;
8379e482 906 u32 val, base, shadow;
d8408326
SWK
907
908 spin_lock(&res->reg_slock);
909
910 /* read interrupt status for handling and clearing flags for VSYNC */
911 val = mixer_reg_read(res, MXR_INT_STATUS);
912
913 /* handling VSYNC */
914 if (val & MXR_INT_STATUS_VSYNC) {
915 /* interlace scan need to check shadow register */
916 if (ctx->interlace) {
8379e482
SWK
917 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
918 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
919 if (base != shadow)
d8408326
SWK
920 goto out;
921
8379e482
SWK
922 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
923 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
924 if (base != shadow)
d8408326
SWK
925 goto out;
926 }
927
928 drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe);
929 mixer_finish_pageflip(drm_hdmi_ctx->drm_dev, ctx->pipe);
930 }
931
932out:
933 /* clear interrupts */
934 if (~val & MXR_INT_EN_VSYNC) {
935 /* vsync interrupt use different bit for read and clear */
936 val &= ~MXR_INT_EN_VSYNC;
937 val |= MXR_INT_CLEAR_VSYNC;
938 }
939 mixer_reg_write(res, MXR_INT_STATUS, val);
940
941 spin_unlock(&res->reg_slock);
942
943 return IRQ_HANDLED;
944}
945
d8408326
SWK
946static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
947 struct platform_device *pdev)
948{
f9309d1b 949 struct mixer_context *mixer_ctx = ctx->ctx;
d8408326
SWK
950 struct device *dev = &pdev->dev;
951 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
952 struct resource *res;
953 int ret;
954
d8408326
SWK
955 spin_lock_init(&mixer_res->reg_slock);
956
957 mixer_res->mixer = clk_get(dev, "mixer");
958 if (IS_ERR_OR_NULL(mixer_res->mixer)) {
959 dev_err(dev, "failed to get clock 'mixer'\n");
960 ret = -ENODEV;
961 goto fail;
962 }
1b8e5747 963
d8408326
SWK
964 mixer_res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
965 if (IS_ERR_OR_NULL(mixer_res->sclk_hdmi)) {
966 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
967 ret = -ENODEV;
968 goto fail;
969 }
1b8e5747 970 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
d8408326
SWK
971 if (res == NULL) {
972 dev_err(dev, "get memory resource failed.\n");
973 ret = -ENXIO;
974 goto fail;
975 }
976
9416dfa7
SK
977 mixer_res->mixer_regs = devm_ioremap(&pdev->dev, res->start,
978 resource_size(res));
d8408326
SWK
979 if (mixer_res->mixer_regs == NULL) {
980 dev_err(dev, "register mapping failed.\n");
981 ret = -ENXIO;
982 goto fail;
983 }
984
1b8e5747 985 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
d8408326 986 if (res == NULL) {
1b8e5747 987 dev_err(dev, "get interrupt resource failed.\n");
d8408326 988 ret = -ENXIO;
9416dfa7 989 goto fail;
d8408326
SWK
990 }
991
1b8e5747
RS
992 ret = devm_request_irq(&pdev->dev, res->start, mixer_irq_handler,
993 0, "drm_mixer", ctx);
994 if (ret) {
995 dev_err(dev, "request interrupt failed.\n");
9416dfa7 996 goto fail;
d8408326 997 }
1b8e5747 998 mixer_res->irq = res->start;
d8408326 999
1b8e5747
RS
1000 return 0;
1001
1002fail:
1003 if (!IS_ERR_OR_NULL(mixer_res->sclk_hdmi))
1004 clk_put(mixer_res->sclk_hdmi);
1005 if (!IS_ERR_OR_NULL(mixer_res->mixer))
1006 clk_put(mixer_res->mixer);
1007 return ret;
1008}
1009
1010static int __devinit vp_resources_init(struct exynos_drm_hdmi_context *ctx,
1011 struct platform_device *pdev)
1012{
1013 struct mixer_context *mixer_ctx = ctx->ctx;
1014 struct device *dev = &pdev->dev;
1015 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
1016 struct resource *res;
1017 int ret;
1018
1019 mixer_res->vp = clk_get(dev, "vp");
1020 if (IS_ERR_OR_NULL(mixer_res->vp)) {
1021 dev_err(dev, "failed to get clock 'vp'\n");
1022 ret = -ENODEV;
1023 goto fail;
1024 }
1025 mixer_res->sclk_mixer = clk_get(dev, "sclk_mixer");
1026 if (IS_ERR_OR_NULL(mixer_res->sclk_mixer)) {
1027 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
1028 ret = -ENODEV;
1029 goto fail;
1030 }
1031 mixer_res->sclk_dac = clk_get(dev, "sclk_dac");
1032 if (IS_ERR_OR_NULL(mixer_res->sclk_dac)) {
1033 dev_err(dev, "failed to get clock 'sclk_dac'\n");
1034 ret = -ENODEV;
1035 goto fail;
1036 }
1037
1038 if (mixer_res->sclk_hdmi)
1039 clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
1040
1041 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
d8408326 1042 if (res == NULL) {
1b8e5747 1043 dev_err(dev, "get memory resource failed.\n");
d8408326 1044 ret = -ENXIO;
9416dfa7 1045 goto fail;
d8408326
SWK
1046 }
1047
1b8e5747
RS
1048 mixer_res->vp_regs = devm_ioremap(&pdev->dev, res->start,
1049 resource_size(res));
1050 if (mixer_res->vp_regs == NULL) {
1051 dev_err(dev, "register mapping failed.\n");
1052 ret = -ENXIO;
9416dfa7 1053 goto fail;
d8408326 1054 }
d8408326
SWK
1055
1056 return 0;
1057
d8408326
SWK
1058fail:
1059 if (!IS_ERR_OR_NULL(mixer_res->sclk_dac))
1060 clk_put(mixer_res->sclk_dac);
d8408326
SWK
1061 if (!IS_ERR_OR_NULL(mixer_res->sclk_mixer))
1062 clk_put(mixer_res->sclk_mixer);
1063 if (!IS_ERR_OR_NULL(mixer_res->vp))
1064 clk_put(mixer_res->vp);
d8408326
SWK
1065 return ret;
1066}
1067
1e123441
RS
1068static struct mixer_drv_data exynos4_mxr_drv_data = {
1069 .version = MXR_VER_0_0_0_16,
1b8e5747 1070 .is_vp_enabled = 1,
1e123441
RS
1071};
1072
1073static struct platform_device_id mixer_driver_types[] = {
1074 {
1075 .name = "s5p-mixer",
1076 .driver_data = (unsigned long)&exynos4_mxr_drv_data,
1077 }, {
1078 /* end node */
1079 }
1080};
1081
d8408326
SWK
1082static int __devinit mixer_probe(struct platform_device *pdev)
1083{
1084 struct device *dev = &pdev->dev;
1085 struct exynos_drm_hdmi_context *drm_hdmi_ctx;
1086 struct mixer_context *ctx;
1e123441 1087 struct mixer_drv_data *drv;
d8408326
SWK
1088 int ret;
1089
1090 dev_info(dev, "probe start\n");
1091
9416dfa7
SK
1092 drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
1093 GFP_KERNEL);
d8408326
SWK
1094 if (!drm_hdmi_ctx) {
1095 DRM_ERROR("failed to allocate common hdmi context.\n");
1096 return -ENOMEM;
1097 }
1098
9416dfa7 1099 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
d8408326
SWK
1100 if (!ctx) {
1101 DRM_ERROR("failed to alloc mixer context.\n");
d8408326
SWK
1102 return -ENOMEM;
1103 }
1104
cf8fc4f1
JS
1105 mutex_init(&ctx->mixer_mutex);
1106
1e123441
RS
1107 drv = (struct mixer_drv_data *)platform_get_device_id(
1108 pdev)->driver_data;
cf8fc4f1 1109 ctx->dev = &pdev->dev;
d8408326 1110 drm_hdmi_ctx->ctx = (void *)ctx;
1b8e5747 1111 ctx->vp_enabled = drv->is_vp_enabled;
1e123441 1112 ctx->mxr_ver = drv->version;
d8408326
SWK
1113
1114 platform_set_drvdata(pdev, drm_hdmi_ctx);
1115
1116 /* acquire resources: regs, irqs, clocks */
1117 ret = mixer_resources_init(drm_hdmi_ctx, pdev);
1b8e5747
RS
1118 if (ret) {
1119 DRM_ERROR("mixer_resources_init failed\n");
d8408326 1120 goto fail;
1b8e5747
RS
1121 }
1122
1123 if (ctx->vp_enabled) {
1124 /* acquire vp resources: regs, irqs, clocks */
1125 ret = vp_resources_init(drm_hdmi_ctx, pdev);
1126 if (ret) {
1127 DRM_ERROR("vp_resources_init failed\n");
1128 goto fail;
1129 }
1130 }
d8408326
SWK
1131
1132 /* register specific callback point to common hdmi. */
578b6065 1133 exynos_mixer_ops_register(&mixer_ops);
d8408326 1134
cf8fc4f1 1135 pm_runtime_enable(dev);
d8408326
SWK
1136
1137 return 0;
1138
1139
1140fail:
1141 dev_info(dev, "probe failed\n");
1142 return ret;
1143}
1144
1145static int mixer_remove(struct platform_device *pdev)
1146{
9416dfa7 1147 dev_info(&pdev->dev, "remove successful\n");
d8408326 1148
cf8fc4f1
JS
1149 pm_runtime_disable(&pdev->dev);
1150
d8408326
SWK
1151 return 0;
1152}
1153
ab27af85
JS
1154#ifdef CONFIG_PM_SLEEP
1155static int mixer_suspend(struct device *dev)
1156{
1157 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1158 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1159
1160 mixer_poweroff(ctx);
1161
1162 return 0;
1163}
1164#endif
1165
1166static SIMPLE_DEV_PM_OPS(mixer_pm_ops, mixer_suspend, NULL);
1167
d8408326
SWK
1168struct platform_driver mixer_driver = {
1169 .driver = {
1170 .name = "s5p-mixer",
1171 .owner = THIS_MODULE,
ab27af85 1172 .pm = &mixer_pm_ops,
d8408326
SWK
1173 },
1174 .probe = mixer_probe,
1175 .remove = __devexit_p(mixer_remove),
1e123441 1176 .id_table = mixer_driver_types,
d8408326 1177};