1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <drm/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/platform_device.h>
27#include <linux/interrupt.h>
28#include <linux/irq.h>
29#include <linux/delay.h>
30#include <linux/pm_runtime.h>
31#include <linux/clk.h>
32#include <linux/regulator/consumer.h>
33#include <linux/of.h>
34#include <linux/component.h>
35
36#include <drm/exynos_drm.h>
37
38#include "exynos_drm_drv.h"
39#include "exynos_drm_crtc.h"
40#include "exynos_drm_iommu.h"
41#include "exynos_mixer.h"
42
43#define get_mixer_manager(dev) platform_get_drvdata(to_platform_device(dev))
44
45#define MIXER_WIN_NR 3
46#define MIXER_DEFAULT_WIN 0
47
48struct hdmi_win_data {
49 dma_addr_t dma_addr;
50 dma_addr_t chroma_dma_addr;
51 uint32_t pixel_format;
52 unsigned int bpp;
53 unsigned int crtc_x;
54 unsigned int crtc_y;
55 unsigned int crtc_width;
56 unsigned int crtc_height;
57 unsigned int fb_x;
58 unsigned int fb_y;
59 unsigned int fb_width;
60 unsigned int fb_height;
61 unsigned int src_width;
62 unsigned int src_height;
63 unsigned int mode_width;
64 unsigned int mode_height;
65 unsigned int scan_flags;
66 bool enabled;
67 bool resume;
68};
69
70struct mixer_resources {
71 int irq;
72 void __iomem *mixer_regs;
73 void __iomem *vp_regs;
74 spinlock_t reg_slock;
75 struct clk *mixer;
76 struct clk *vp;
77 struct clk *sclk_mixer;
78 struct clk *sclk_hdmi;
79 struct clk *mout_mixer;
80};
81
82enum mixer_version_id {
83 MXR_VER_0_0_0_16,
84 MXR_VER_16_0_33_0,
85 MXR_VER_128_0_0_184,
86};
87
88struct mixer_context {
89 struct platform_device *pdev;
90 struct device *dev;
91 struct drm_device *drm_dev;
92 int pipe;
93 bool interlace;
94 bool powered;
95 bool vp_enabled;
96 bool has_sclk;
97 u32 int_en;
98
99 struct mutex mixer_mutex;
100 struct mixer_resources mixer_res;
101 struct hdmi_win_data win_data[MIXER_WIN_NR];
102 enum mixer_version_id mxr_ver;
103 wait_queue_head_t wait_vsync_queue;
104 atomic_t wait_vsync_event;
105};
106
107struct mixer_drv_data {
108 enum mixer_version_id version;
109 bool is_vp_enabled;
110 bool has_sclk;
111};
112
113static const u8 filter_y_horiz_tap8[] = {
114 0, -1, -1, -1, -1, -1, -1, -1,
115 -1, -1, -1, -1, -1, 0, 0, 0,
116 0, 2, 4, 5, 6, 6, 6, 6,
117 6, 5, 5, 4, 3, 2, 1, 1,
118 0, -6, -12, -16, -18, -20, -21, -20,
119 -20, -18, -16, -13, -10, -8, -5, -2,
120 127, 126, 125, 121, 114, 107, 99, 89,
121 79, 68, 57, 46, 35, 25, 16, 8,
122};
123
124static const u8 filter_y_vert_tap4[] = {
125 0, -3, -6, -8, -8, -8, -8, -7,
126 -6, -5, -4, -3, -2, -1, -1, 0,
127 127, 126, 124, 118, 111, 102, 92, 81,
128 70, 59, 48, 37, 27, 19, 11, 5,
129 0, 5, 11, 19, 27, 37, 48, 59,
130 70, 81, 92, 102, 111, 118, 124, 126,
131 0, 0, -1, -1, -2, -3, -4, -5,
132 -6, -7, -8, -8, -8, -8, -6, -3,
133};
134
135static const u8 filter_cr_horiz_tap4[] = {
136 0, -3, -6, -8, -8, -8, -8, -7,
137 -6, -5, -4, -3, -2, -1, -1, 0,
138 127, 126, 124, 118, 111, 102, 92, 81,
139 70, 59, 48, 37, 27, 19, 11, 5,
140};
141
142static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
143{
144 return readl(res->vp_regs + reg_id);
145}
146
147static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
148 u32 val)
149{
150 writel(val, res->vp_regs + reg_id);
151}
152
153static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
154 u32 val, u32 mask)
155{
156 u32 old = vp_reg_read(res, reg_id);
157
158 val = (val & mask) | (old & ~mask);
159 writel(val, res->vp_regs + reg_id);
160}
161
162static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
163{
164 return readl(res->mixer_regs + reg_id);
165}
166
167static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
168 u32 val)
169{
170 writel(val, res->mixer_regs + reg_id);
171}
172
173static inline void mixer_reg_writemask(struct mixer_resources *res,
174 u32 reg_id, u32 val, u32 mask)
175{
176 u32 old = mixer_reg_read(res, reg_id);
177
178 val = (val & mask) | (old & ~mask);
179 writel(val, res->mixer_regs + reg_id);
180}
181
182static void mixer_regs_dump(struct mixer_context *ctx)
183{
184#define DUMPREG(reg_id) \
185do { \
186 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
187 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
188} while (0)
189
190 DUMPREG(MXR_STATUS);
191 DUMPREG(MXR_CFG);
192 DUMPREG(MXR_INT_EN);
193 DUMPREG(MXR_INT_STATUS);
194
195 DUMPREG(MXR_LAYER_CFG);
196 DUMPREG(MXR_VIDEO_CFG);
197
198 DUMPREG(MXR_GRAPHIC0_CFG);
199 DUMPREG(MXR_GRAPHIC0_BASE);
200 DUMPREG(MXR_GRAPHIC0_SPAN);
201 DUMPREG(MXR_GRAPHIC0_WH);
202 DUMPREG(MXR_GRAPHIC0_SXY);
203 DUMPREG(MXR_GRAPHIC0_DXY);
204
205 DUMPREG(MXR_GRAPHIC1_CFG);
206 DUMPREG(MXR_GRAPHIC1_BASE);
207 DUMPREG(MXR_GRAPHIC1_SPAN);
208 DUMPREG(MXR_GRAPHIC1_WH);
209 DUMPREG(MXR_GRAPHIC1_SXY);
210 DUMPREG(MXR_GRAPHIC1_DXY);
211#undef DUMPREG
212}
213
214static void vp_regs_dump(struct mixer_context *ctx)
215{
216#define DUMPREG(reg_id) \
217do { \
218 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
219 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
220} while (0)
221
222 DUMPREG(VP_ENABLE);
223 DUMPREG(VP_SRESET);
224 DUMPREG(VP_SHADOW_UPDATE);
225 DUMPREG(VP_FIELD_ID);
226 DUMPREG(VP_MODE);
227 DUMPREG(VP_IMG_SIZE_Y);
228 DUMPREG(VP_IMG_SIZE_C);
229 DUMPREG(VP_PER_RATE_CTRL);
230 DUMPREG(VP_TOP_Y_PTR);
231 DUMPREG(VP_BOT_Y_PTR);
232 DUMPREG(VP_TOP_C_PTR);
233 DUMPREG(VP_BOT_C_PTR);
234 DUMPREG(VP_ENDIAN_MODE);
235 DUMPREG(VP_SRC_H_POSITION);
236 DUMPREG(VP_SRC_V_POSITION);
237 DUMPREG(VP_SRC_WIDTH);
238 DUMPREG(VP_SRC_HEIGHT);
239 DUMPREG(VP_DST_H_POSITION);
240 DUMPREG(VP_DST_V_POSITION);
241 DUMPREG(VP_DST_WIDTH);
242 DUMPREG(VP_DST_HEIGHT);
243 DUMPREG(VP_H_RATIO);
244 DUMPREG(VP_V_RATIO);
245
246#undef DUMPREG
247}
248
249static inline void vp_filter_set(struct mixer_resources *res,
250 int reg_id, const u8 *data, unsigned int size)
251{
252
253 BUG_ON(size & 3);
254 for (; size; size -= 4, reg_id += 4, data += 4) {
255 u32 val = (data[0] << 24) | (data[1] << 16) |
256 (data[2] << 8) | data[3];
257 vp_reg_write(res, reg_id, val);
258 }
259}
260
261static void vp_default_filter(struct mixer_resources *res)
262{
263 vp_filter_set(res, VP_POLY8_Y0_LL,
264 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
265 vp_filter_set(res, VP_POLY4_Y0_LL,
266 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
267 vp_filter_set(res, VP_POLY4_C0_LL,
268 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
269}
270
271static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
272{
273 struct mixer_resources *res = &ctx->mixer_res;
274
275
276 mixer_reg_writemask(res, MXR_STATUS, enable ?
277 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
278
279 if (ctx->vp_enabled)
280 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
281 VP_SHADOW_UPDATE_ENABLE : 0);
282}
283
284static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
285{
286 struct mixer_resources *res = &ctx->mixer_res;
287 u32 val;
288
289
290 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
291 MXR_CFG_SCAN_PROGRASSIVE);
292
293 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
294
295 if (height <= 480)
296 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
297 else if (height <= 576)
298 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
299 else if (height <= 720)
300 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
301 else if (height <= 1080)
302 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
303 else
304 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
305 }
306
307 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
308}
309
310static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
311{
312 struct mixer_resources *res = &ctx->mixer_res;
313 u32 val;
314
315 if (height == 480) {
316 val = MXR_CFG_RGB601_0_255;
317 } else if (height == 576) {
318 val = MXR_CFG_RGB601_0_255;
319 } else if (height == 720) {
320 val = MXR_CFG_RGB709_16_235;
321 mixer_reg_write(res, MXR_CM_COEFF_Y,
322 (1 << 30) | (94 << 20) | (314 << 10) |
323 (32 << 0));
324 mixer_reg_write(res, MXR_CM_COEFF_CB,
325 (972 << 20) | (851 << 10) | (225 << 0));
326 mixer_reg_write(res, MXR_CM_COEFF_CR,
327 (225 << 20) | (820 << 10) | (1004 << 0));
328 } else if (height == 1080) {
329 val = MXR_CFG_RGB709_16_235;
330 mixer_reg_write(res, MXR_CM_COEFF_Y,
331 (1 << 30) | (94 << 20) | (314 << 10) |
332 (32 << 0));
333 mixer_reg_write(res, MXR_CM_COEFF_CB,
334 (972 << 20) | (851 << 10) | (225 << 0));
335 mixer_reg_write(res, MXR_CM_COEFF_CR,
336 (225 << 20) | (820 << 10) | (1004 << 0));
337 } else {
338 val = MXR_CFG_RGB709_16_235;
339 mixer_reg_write(res, MXR_CM_COEFF_Y,
340 (1 << 30) | (94 << 20) | (314 << 10) |
341 (32 << 0));
342 mixer_reg_write(res, MXR_CM_COEFF_CB,
343 (972 << 20) | (851 << 10) | (225 << 0));
344 mixer_reg_write(res, MXR_CM_COEFF_CR,
345 (225 << 20) | (820 << 10) | (1004 << 0));
346 }
347
348 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
349}
350
351static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
352{
353 struct mixer_resources *res = &ctx->mixer_res;
354 u32 val = enable ? ~0 : 0;
355
356 switch (win) {
357 case 0:
358 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
359 break;
360 case 1:
361 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
362 break;
363 case 2:
364 if (ctx->vp_enabled) {
365 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
366 mixer_reg_writemask(res, MXR_CFG, val,
367 MXR_CFG_VP_ENABLE);
368
369
370 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
371 MXR_GRP_CFG_BLEND_PRE_MUL |
372 MXR_GRP_CFG_PIXEL_BLEND_EN);
373 }
374 break;
375 }
376}
377
378static void mixer_run(struct mixer_context *ctx)
379{
380 struct mixer_resources *res = &ctx->mixer_res;
381
382 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
383
384 mixer_regs_dump(ctx);
385}
386
387static void mixer_stop(struct mixer_context *ctx)
388{
389 struct mixer_resources *res = &ctx->mixer_res;
390 int timeout = 20;
391
392 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
393
394 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
395 --timeout)
396 usleep_range(10000, 12000);
397
398 mixer_regs_dump(ctx);
399}
400
401static void vp_video_buffer(struct mixer_context *ctx, int win)
402{
403 struct mixer_resources *res = &ctx->mixer_res;
404 unsigned long flags;
405 struct hdmi_win_data *win_data;
406 unsigned int x_ratio, y_ratio;
407 unsigned int buf_num = 1;
408 dma_addr_t luma_addr[2], chroma_addr[2];
409 bool tiled_mode = false;
410 bool crcb_mode = false;
411 u32 val;
412
413 win_data = &ctx->win_data[win];
414
415 switch (win_data->pixel_format) {
416 case DRM_FORMAT_NV12MT:
417 tiled_mode = true;
418 case DRM_FORMAT_NV12:
419 crcb_mode = false;
420 buf_num = 2;
421 break;
422
423 default:
424
425 if (!win_data->dma_addr)
426 break;
427
428 DRM_ERROR("pixel format for vp is wrong [%d].\n",
429 win_data->pixel_format);
430 return;
431 }
432
433
434 x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
435 y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
436
437 if (buf_num == 2) {
438 luma_addr[0] = win_data->dma_addr;
439 chroma_addr[0] = win_data->chroma_dma_addr;
440 } else {
441 luma_addr[0] = win_data->dma_addr;
442 chroma_addr[0] = win_data->dma_addr
443 + (win_data->fb_width * win_data->fb_height);
444 }
445
446 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
447 ctx->interlace = true;
448 if (tiled_mode) {
449 luma_addr[1] = luma_addr[0] + 0x40;
450 chroma_addr[1] = chroma_addr[0] + 0x40;
451 } else {
452 luma_addr[1] = luma_addr[0] + win_data->fb_width;
453 chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
454 }
455 } else {
456 ctx->interlace = false;
457 luma_addr[1] = 0;
458 chroma_addr[1] = 0;
459 }
460
461 spin_lock_irqsave(&res->reg_slock, flags);
462 mixer_vsync_set_update(ctx, false);
463
464
465 val = (ctx->interlace ? ~0 : 0);
466 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
467
468
469 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
470 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
471 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
472
473
474 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
475 VP_IMG_VSIZE(win_data->fb_height));
476
477 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
478 VP_IMG_VSIZE(win_data->fb_height / 2));
479
480 vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
481 vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
482 vp_reg_write(res, VP_SRC_H_POSITION,
483 VP_SRC_H_POSITION_VAL(win_data->fb_x));
484 vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
485
486 vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
487 vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
488 if (ctx->interlace) {
489 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2);
490 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2);
491 } else {
492 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
493 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
494 }
495
496 vp_reg_write(res, VP_H_RATIO, x_ratio);
497 vp_reg_write(res, VP_V_RATIO, y_ratio);
498
499 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
500
501
502 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
503 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
504 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
505 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
506
507 mixer_cfg_scan(ctx, win_data->mode_height);
508 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
509 mixer_cfg_layer(ctx, win, true);
510 mixer_run(ctx);
511
512 mixer_vsync_set_update(ctx, true);
513 spin_unlock_irqrestore(&res->reg_slock, flags);
514
515 vp_regs_dump(ctx);
516}
517
518static void mixer_layer_update(struct mixer_context *ctx)
519{
520 struct mixer_resources *res = &ctx->mixer_res;
521
522 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
523}
524
525static void mixer_graph_buffer(struct mixer_context *ctx, int win)
526{
527 struct mixer_resources *res = &ctx->mixer_res;
528 unsigned long flags;
529 struct hdmi_win_data *win_data;
530 unsigned int x_ratio, y_ratio;
531 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
532 dma_addr_t dma_addr;
533 unsigned int fmt;
534 u32 val;
535
536 win_data = &ctx->win_data[win];
537
538 #define RGB565 4
539 #define ARGB1555 5
540 #define ARGB4444 6
541 #define ARGB8888 7
542
543 switch (win_data->bpp) {
544 case 16:
545 fmt = ARGB4444;
546 break;
547 case 32:
548 fmt = ARGB8888;
549 break;
550 default:
551 fmt = ARGB8888;
552 }
553
554
555 x_ratio = 0;
556 y_ratio = 0;
557
558 dst_x_offset = win_data->crtc_x;
559 dst_y_offset = win_data->crtc_y;
560
561
562 dma_addr = win_data->dma_addr
563 + (win_data->fb_x * win_data->bpp >> 3)
564 + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
565 src_x_offset = 0;
566 src_y_offset = 0;
567
568 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
569 ctx->interlace = true;
570 else
571 ctx->interlace = false;
572
573 spin_lock_irqsave(&res->reg_slock, flags);
574 mixer_vsync_set_update(ctx, false);
575
576
577 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
578 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
579
580
581 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
582
583
584 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
585 win == MIXER_DEFAULT_WIN) {
586 val = MXR_MXR_RES_HEIGHT(win_data->fb_height);
587 val |= MXR_MXR_RES_WIDTH(win_data->fb_width);
588 mixer_reg_write(res, MXR_RESOLUTION, val);
589 }
590
591 val = MXR_GRP_WH_WIDTH(win_data->crtc_width);
592 val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
593 val |= MXR_GRP_WH_H_SCALE(x_ratio);
594 val |= MXR_GRP_WH_V_SCALE(y_ratio);
595 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
596
597
598 val = MXR_GRP_SXY_SX(src_x_offset);
599 val |= MXR_GRP_SXY_SY(src_y_offset);
600 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
601
602
603 val = MXR_GRP_DXY_DX(dst_x_offset);
604 val |= MXR_GRP_DXY_DY(dst_y_offset);
605 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
606
607
608 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
609
610 mixer_cfg_scan(ctx, win_data->mode_height);
611 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
612 mixer_cfg_layer(ctx, win, true);
613
614
615 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
616 ctx->mxr_ver == MXR_VER_128_0_0_184)
617 mixer_layer_update(ctx);
618
619 mixer_run(ctx);
620
621 mixer_vsync_set_update(ctx, true);
622 spin_unlock_irqrestore(&res->reg_slock, flags);
623}
624
625static void vp_win_reset(struct mixer_context *ctx)
626{
627 struct mixer_resources *res = &ctx->mixer_res;
628 int tries = 100;
629
630 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
631 for (tries = 100; tries; --tries) {
632
633 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
634 break;
635 usleep_range(10000, 12000);
636 }
637 WARN(tries == 0, "failed to reset Video Processor\n");
638}
639
640static void mixer_win_reset(struct mixer_context *ctx)
641{
642 struct mixer_resources *res = &ctx->mixer_res;
643 unsigned long flags;
644 u32 val;
645
646 spin_lock_irqsave(&res->reg_slock, flags);
647 mixer_vsync_set_update(ctx, false);
648
649 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
650
651
652 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
653
654
655 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
656 MXR_STATUS_BURST_MASK);
657
658
659
660
661
662
663
664 val = MXR_LAYER_CFG_GRP1_VAL(3);
665 val |= MXR_LAYER_CFG_GRP0_VAL(2);
666 if (ctx->vp_enabled)
667 val |= MXR_LAYER_CFG_VP_VAL(1);
668 mixer_reg_write(res, MXR_LAYER_CFG, val);
669
670
671 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
672 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
673 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
674
675
676 val = MXR_GRP_CFG_COLOR_KEY_DISABLE;
677 val |= MXR_GRP_CFG_WIN_BLEND_EN;
678 val |= MXR_GRP_CFG_ALPHA_VAL(0xff);
679
680
681 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
682
683
684 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
685 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
686 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
687
688
689 val = MXR_GRP_CFG_ALPHA_VAL(0);
690 mixer_reg_write(res, MXR_VIDEO_CFG, val);
691
692 if (ctx->vp_enabled) {
693
694 vp_win_reset(ctx);
695 vp_default_filter(res);
696 }
697
698
699 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
700 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
701 if (ctx->vp_enabled)
702 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
703
704 mixer_vsync_set_update(ctx, true);
705 spin_unlock_irqrestore(&res->reg_slock, flags);
706}
707
708static irqreturn_t mixer_irq_handler(int irq, void *arg)
709{
710 struct mixer_context *ctx = arg;
711 struct mixer_resources *res = &ctx->mixer_res;
712 u32 val, base, shadow;
713
714 spin_lock(&res->reg_slock);
715
716
717 val = mixer_reg_read(res, MXR_INT_STATUS);
718
719
720 if (val & MXR_INT_STATUS_VSYNC) {
721
722 if (ctx->interlace) {
723 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
724 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
725 if (base != shadow)
726 goto out;
727
728 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
729 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
730 if (base != shadow)
731 goto out;
732 }
733
734 drm_handle_vblank(ctx->drm_dev, ctx->pipe);
735 exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
736
737
738 if (atomic_read(&ctx->wait_vsync_event)) {
739 atomic_set(&ctx->wait_vsync_event, 0);
740 wake_up(&ctx->wait_vsync_queue);
741 }
742 }
743
744out:
745
746 if (~val & MXR_INT_EN_VSYNC) {
747
748 val &= ~MXR_INT_EN_VSYNC;
749 val |= MXR_INT_CLEAR_VSYNC;
750 }
751 mixer_reg_write(res, MXR_INT_STATUS, val);
752
753 spin_unlock(&res->reg_slock);
754
755 return IRQ_HANDLED;
756}
757
758static int mixer_resources_init(struct mixer_context *mixer_ctx)
759{
760 struct device *dev = &mixer_ctx->pdev->dev;
761 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
762 struct resource *res;
763 int ret;
764
765 spin_lock_init(&mixer_res->reg_slock);
766
767 mixer_res->mixer = devm_clk_get(dev, "mixer");
768 if (IS_ERR(mixer_res->mixer)) {
769 dev_err(dev, "failed to get clock 'mixer'\n");
770 return -ENODEV;
771 }
772
773 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
774 if (IS_ERR(mixer_res->sclk_hdmi)) {
775 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
776 return -ENODEV;
777 }
778 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
779 if (res == NULL) {
780 dev_err(dev, "get memory resource failed.\n");
781 return -ENXIO;
782 }
783
784 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
785 resource_size(res));
786 if (mixer_res->mixer_regs == NULL) {
787 dev_err(dev, "register mapping failed.\n");
788 return -ENXIO;
789 }
790
791 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
792 if (res == NULL) {
793 dev_err(dev, "get interrupt resource failed.\n");
794 return -ENXIO;
795 }
796
797 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
798 0, "drm_mixer", mixer_ctx);
799 if (ret) {
800 dev_err(dev, "request interrupt failed.\n");
801 return ret;
802 }
803 mixer_res->irq = res->start;
804
805 return 0;
806}
807
808static int vp_resources_init(struct mixer_context *mixer_ctx)
809{
810 struct device *dev = &mixer_ctx->pdev->dev;
811 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
812 struct resource *res;
813
814 mixer_res->vp = devm_clk_get(dev, "vp");
815 if (IS_ERR(mixer_res->vp)) {
816 dev_err(dev, "failed to get clock 'vp'\n");
817 return -ENODEV;
818 }
819
820 if (mixer_ctx->has_sclk) {
821 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
822 if (IS_ERR(mixer_res->sclk_mixer)) {
823 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
824 return -ENODEV;
825 }
826 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
827 if (IS_ERR(mixer_res->mout_mixer)) {
828 dev_err(dev, "failed to get clock 'mout_mixer'\n");
829 return -ENODEV;
830 }
831
832 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
833 clk_set_parent(mixer_res->mout_mixer,
834 mixer_res->sclk_hdmi);
835 }
836
837 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
838 if (res == NULL) {
839 dev_err(dev, "get memory resource failed.\n");
840 return -ENXIO;
841 }
842
843 mixer_res->vp_regs = devm_ioremap(dev, res->start,
844 resource_size(res));
845 if (mixer_res->vp_regs == NULL) {
846 dev_err(dev, "register mapping failed.\n");
847 return -ENXIO;
848 }
849
850 return 0;
851}
852
853static int mixer_initialize(struct exynos_drm_manager *mgr,
854 struct drm_device *drm_dev)
855{
856 int ret;
857 struct mixer_context *mixer_ctx = mgr->ctx;
858 struct exynos_drm_private *priv;
859 priv = drm_dev->dev_private;
860
861 mgr->drm_dev = mixer_ctx->drm_dev = drm_dev;
862 mgr->pipe = mixer_ctx->pipe = priv->pipe++;
863
864
865 ret = mixer_resources_init(mixer_ctx);
866 if (ret) {
867 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
868 return ret;
869 }
870
871 if (mixer_ctx->vp_enabled) {
872
873 ret = vp_resources_init(mixer_ctx);
874 if (ret) {
875 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
876 return ret;
877 }
878 }
879
880 if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
881 return 0;
882
883 return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
884}
885
886static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
887{
888 struct mixer_context *mixer_ctx = mgr->ctx;
889
890 if (is_drm_iommu_supported(mixer_ctx->drm_dev))
891 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
892}
893
894static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
895{
896 struct mixer_context *mixer_ctx = mgr->ctx;
897 struct mixer_resources *res = &mixer_ctx->mixer_res;
898
899 if (!mixer_ctx->powered) {
900 mixer_ctx->int_en |= MXR_INT_EN_VSYNC;
901 return 0;
902 }
903
904
905 mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
906 MXR_INT_EN_VSYNC);
907
908 return 0;
909}
910
911static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
912{
913 struct mixer_context *mixer_ctx = mgr->ctx;
914 struct mixer_resources *res = &mixer_ctx->mixer_res;
915
916
917 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
918}
919
920static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
921 struct exynos_drm_overlay *overlay)
922{
923 struct mixer_context *mixer_ctx = mgr->ctx;
924 struct hdmi_win_data *win_data;
925 int win;
926
927 if (!overlay) {
928 DRM_ERROR("overlay is NULL\n");
929 return;
930 }
931
932 DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
933 overlay->fb_width, overlay->fb_height,
934 overlay->fb_x, overlay->fb_y,
935 overlay->crtc_width, overlay->crtc_height,
936 overlay->crtc_x, overlay->crtc_y);
937
938 win = overlay->zpos;
939 if (win == DEFAULT_ZPOS)
940 win = MIXER_DEFAULT_WIN;
941
942 if (win < 0 || win >= MIXER_WIN_NR) {
943 DRM_ERROR("mixer window[%d] is wrong\n", win);
944 return;
945 }
946
947 win_data = &mixer_ctx->win_data[win];
948
949 win_data->dma_addr = overlay->dma_addr[0];
950 win_data->chroma_dma_addr = overlay->dma_addr[1];
951 win_data->pixel_format = overlay->pixel_format;
952 win_data->bpp = overlay->bpp;
953
954 win_data->crtc_x = overlay->crtc_x;
955 win_data->crtc_y = overlay->crtc_y;
956 win_data->crtc_width = overlay->crtc_width;
957 win_data->crtc_height = overlay->crtc_height;
958
959 win_data->fb_x = overlay->fb_x;
960 win_data->fb_y = overlay->fb_y;
961 win_data->fb_width = overlay->fb_width;
962 win_data->fb_height = overlay->fb_height;
963 win_data->src_width = overlay->src_width;
964 win_data->src_height = overlay->src_height;
965
966 win_data->mode_width = overlay->mode_width;
967 win_data->mode_height = overlay->mode_height;
968
969 win_data->scan_flags = overlay->scan_flag;
970}
971
972static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
973{
974 struct mixer_context *mixer_ctx = mgr->ctx;
975 int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
976
977 DRM_DEBUG_KMS("win: %d\n", win);
978
979 mutex_lock(&mixer_ctx->mixer_mutex);
980 if (!mixer_ctx->powered) {
981 mutex_unlock(&mixer_ctx->mixer_mutex);
982 return;
983 }
984 mutex_unlock(&mixer_ctx->mixer_mutex);
985
986 if (win > 1 && mixer_ctx->vp_enabled)
987 vp_video_buffer(mixer_ctx, win);
988 else
989 mixer_graph_buffer(mixer_ctx, win);
990
991 mixer_ctx->win_data[win].enabled = true;
992}
993
994static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
995{
996 struct mixer_context *mixer_ctx = mgr->ctx;
997 struct mixer_resources *res = &mixer_ctx->mixer_res;
998 int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
999 unsigned long flags;
1000
1001 DRM_DEBUG_KMS("win: %d\n", win);
1002
1003 mutex_lock(&mixer_ctx->mixer_mutex);
1004 if (!mixer_ctx->powered) {
1005 mutex_unlock(&mixer_ctx->mixer_mutex);
1006 mixer_ctx->win_data[win].resume = false;
1007 return;
1008 }
1009 mutex_unlock(&mixer_ctx->mixer_mutex);
1010
1011 spin_lock_irqsave(&res->reg_slock, flags);
1012 mixer_vsync_set_update(mixer_ctx, false);
1013
1014 mixer_cfg_layer(mixer_ctx, win, false);
1015
1016 mixer_vsync_set_update(mixer_ctx, true);
1017 spin_unlock_irqrestore(&res->reg_slock, flags);
1018
1019 mixer_ctx->win_data[win].enabled = false;
1020}
1021
1022static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
1023{
1024 struct mixer_context *mixer_ctx = mgr->ctx;
1025
1026 mutex_lock(&mixer_ctx->mixer_mutex);
1027 if (!mixer_ctx->powered) {
1028 mutex_unlock(&mixer_ctx->mixer_mutex);
1029 return;
1030 }
1031 mutex_unlock(&mixer_ctx->mixer_mutex);
1032
1033 drm_vblank_get(mgr->crtc->dev, mixer_ctx->pipe);
1034
1035 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1036
1037
1038
1039
1040
1041 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1042 !atomic_read(&mixer_ctx->wait_vsync_event),
1043 HZ/20))
1044 DRM_DEBUG_KMS("vblank wait timed out.\n");
1045
1046 drm_vblank_put(mgr->crtc->dev, mixer_ctx->pipe);
1047}
1048
1049static void mixer_window_suspend(struct exynos_drm_manager *mgr)
1050{
1051 struct mixer_context *ctx = mgr->ctx;
1052 struct hdmi_win_data *win_data;
1053 int i;
1054
1055 for (i = 0; i < MIXER_WIN_NR; i++) {
1056 win_data = &ctx->win_data[i];
1057 win_data->resume = win_data->enabled;
1058 mixer_win_disable(mgr, i);
1059 }
1060 mixer_wait_for_vblank(mgr);
1061}
1062
1063static void mixer_window_resume(struct exynos_drm_manager *mgr)
1064{
1065 struct mixer_context *ctx = mgr->ctx;
1066 struct hdmi_win_data *win_data;
1067 int i;
1068
1069 for (i = 0; i < MIXER_WIN_NR; i++) {
1070 win_data = &ctx->win_data[i];
1071 win_data->enabled = win_data->resume;
1072 win_data->resume = false;
1073 if (win_data->enabled)
1074 mixer_win_commit(mgr, i);
1075 }
1076}
1077
1078static void mixer_poweron(struct exynos_drm_manager *mgr)
1079{
1080 struct mixer_context *ctx = mgr->ctx;
1081 struct mixer_resources *res = &ctx->mixer_res;
1082
1083 mutex_lock(&ctx->mixer_mutex);
1084 if (ctx->powered) {
1085 mutex_unlock(&ctx->mixer_mutex);
1086 return;
1087 }
1088
1089 mutex_unlock(&ctx->mixer_mutex);
1090
1091 pm_runtime_get_sync(ctx->dev);
1092
1093 clk_prepare_enable(res->mixer);
1094 if (ctx->vp_enabled) {
1095 clk_prepare_enable(res->vp);
1096 if (ctx->has_sclk)
1097 clk_prepare_enable(res->sclk_mixer);
1098 }
1099
1100 mutex_lock(&ctx->mixer_mutex);
1101 ctx->powered = true;
1102 mutex_unlock(&ctx->mixer_mutex);
1103
1104 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1105
1106 mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
1107 mixer_win_reset(ctx);
1108
1109 mixer_window_resume(mgr);
1110}
1111
1112static void mixer_poweroff(struct exynos_drm_manager *mgr)
1113{
1114 struct mixer_context *ctx = mgr->ctx;
1115 struct mixer_resources *res = &ctx->mixer_res;
1116
1117 mutex_lock(&ctx->mixer_mutex);
1118 if (!ctx->powered) {
1119 mutex_unlock(&ctx->mixer_mutex);
1120 return;
1121 }
1122 mutex_unlock(&ctx->mixer_mutex);
1123
1124 mixer_stop(ctx);
1125 mixer_window_suspend(mgr);
1126
1127 ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
1128
1129 mutex_lock(&ctx->mixer_mutex);
1130 ctx->powered = false;
1131 mutex_unlock(&ctx->mixer_mutex);
1132
1133 clk_disable_unprepare(res->mixer);
1134 if (ctx->vp_enabled) {
1135 clk_disable_unprepare(res->vp);
1136 if (ctx->has_sclk)
1137 clk_disable_unprepare(res->sclk_mixer);
1138 }
1139
1140 pm_runtime_put_sync(ctx->dev);
1141}
1142
1143static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
1144{
1145 switch (mode) {
1146 case DRM_MODE_DPMS_ON:
1147 mixer_poweron(mgr);
1148 break;
1149 case DRM_MODE_DPMS_STANDBY:
1150 case DRM_MODE_DPMS_SUSPEND:
1151 case DRM_MODE_DPMS_OFF:
1152 mixer_poweroff(mgr);
1153 break;
1154 default:
1155 DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
1156 break;
1157 }
1158}
1159
1160
1161int mixer_check_mode(struct drm_display_mode *mode)
1162{
1163 u32 w, h;
1164
1165 w = mode->hdisplay;
1166 h = mode->vdisplay;
1167
1168 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1169 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1170 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1171
1172 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1173 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1174 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1175 return 0;
1176
1177 return -EINVAL;
1178}
1179
1180static struct exynos_drm_manager_ops mixer_manager_ops = {
1181 .dpms = mixer_dpms,
1182 .enable_vblank = mixer_enable_vblank,
1183 .disable_vblank = mixer_disable_vblank,
1184 .wait_for_vblank = mixer_wait_for_vblank,
1185 .win_mode_set = mixer_win_mode_set,
1186 .win_commit = mixer_win_commit,
1187 .win_disable = mixer_win_disable,
1188};
1189
1190static struct exynos_drm_manager mixer_manager = {
1191 .type = EXYNOS_DISPLAY_TYPE_HDMI,
1192 .ops = &mixer_manager_ops,
1193};
1194
1195static struct mixer_drv_data exynos5420_mxr_drv_data = {
1196 .version = MXR_VER_128_0_0_184,
1197 .is_vp_enabled = 0,
1198};
1199
1200static struct mixer_drv_data exynos5250_mxr_drv_data = {
1201 .version = MXR_VER_16_0_33_0,
1202 .is_vp_enabled = 0,
1203};
1204
1205static struct mixer_drv_data exynos4212_mxr_drv_data = {
1206 .version = MXR_VER_0_0_0_16,
1207 .is_vp_enabled = 1,
1208};
1209
1210static struct mixer_drv_data exynos4210_mxr_drv_data = {
1211 .version = MXR_VER_0_0_0_16,
1212 .is_vp_enabled = 1,
1213 .has_sclk = 1,
1214};
1215
1216static struct platform_device_id mixer_driver_types[] = {
1217 {
1218 .name = "s5p-mixer",
1219 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
1220 }, {
1221 .name = "exynos5-mixer",
1222 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
1223 }, {
1224
1225 }
1226};
1227
1228static struct of_device_id mixer_match_types[] = {
1229 {
1230 .compatible = "samsung,exynos4210-mixer",
1231 .data = &exynos4210_mxr_drv_data,
1232 }, {
1233 .compatible = "samsung,exynos4212-mixer",
1234 .data = &exynos4212_mxr_drv_data,
1235 }, {
1236 .compatible = "samsung,exynos5-mixer",
1237 .data = &exynos5250_mxr_drv_data,
1238 }, {
1239 .compatible = "samsung,exynos5250-mixer",
1240 .data = &exynos5250_mxr_drv_data,
1241 }, {
1242 .compatible = "samsung,exynos5420-mixer",
1243 .data = &exynos5420_mxr_drv_data,
1244 }, {
1245
1246 }
1247};
1248MODULE_DEVICE_TABLE(of, mixer_match_types);
1249
1250static int mixer_bind(struct device *dev, struct device *manager, void *data)
1251{
1252 struct platform_device *pdev = to_platform_device(dev);
1253 struct drm_device *drm_dev = data;
1254 struct mixer_context *ctx;
1255 struct mixer_drv_data *drv;
1256 int ret;
1257
1258 dev_info(dev, "probe start\n");
1259
1260 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1261 if (!ctx) {
1262 DRM_ERROR("failed to alloc mixer context.\n");
1263 return -ENOMEM;
1264 }
1265
1266 mutex_init(&ctx->mixer_mutex);
1267
1268 if (dev->of_node) {
1269 const struct of_device_id *match;
1270 match = of_match_node(mixer_match_types, dev->of_node);
1271 drv = (struct mixer_drv_data *)match->data;
1272 } else {
1273 drv = (struct mixer_drv_data *)
1274 platform_get_device_id(pdev)->driver_data;
1275 }
1276
1277 ctx->pdev = pdev;
1278 ctx->dev = dev;
1279 ctx->vp_enabled = drv->is_vp_enabled;
1280 ctx->has_sclk = drv->has_sclk;
1281 ctx->mxr_ver = drv->version;
1282 init_waitqueue_head(&ctx->wait_vsync_queue);
1283 atomic_set(&ctx->wait_vsync_event, 0);
1284
1285 mixer_manager.ctx = ctx;
1286 ret = mixer_initialize(&mixer_manager, drm_dev);
1287 if (ret)
1288 return ret;
1289
1290 platform_set_drvdata(pdev, &mixer_manager);
1291 ret = exynos_drm_crtc_create(&mixer_manager);
1292 if (ret) {
1293 mixer_mgr_remove(&mixer_manager);
1294 return ret;
1295 }
1296
1297 pm_runtime_enable(dev);
1298
1299 return 0;
1300}
1301
1302static void mixer_unbind(struct device *dev, struct device *master, void *data)
1303{
1304 struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
1305 struct drm_crtc *crtc = mgr->crtc;
1306
1307 dev_info(dev, "remove successful\n");
1308
1309 mixer_mgr_remove(mgr);
1310
1311 pm_runtime_disable(dev);
1312
1313 crtc->funcs->destroy(crtc);
1314}
1315
1316static const struct component_ops mixer_component_ops = {
1317 .bind = mixer_bind,
1318 .unbind = mixer_unbind,
1319};
1320
1321static int mixer_probe(struct platform_device *pdev)
1322{
1323 int ret;
1324
1325 ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
1326 mixer_manager.type);
1327 if (ret)
1328 return ret;
1329
1330 ret = component_add(&pdev->dev, &mixer_component_ops);
1331 if (ret)
1332 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1333
1334 return ret;
1335}
1336
1337static int mixer_remove(struct platform_device *pdev)
1338{
1339 component_del(&pdev->dev, &mixer_component_ops);
1340 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1341
1342 return 0;
1343}
1344
1345struct platform_driver mixer_driver = {
1346 .driver = {
1347 .name = "exynos-mixer",
1348 .owner = THIS_MODULE,
1349 .of_match_table = mixer_match_types,
1350 },
1351 .probe = mixer_probe,
1352 .remove = mixer_remove,
1353 .id_table = mixer_driver_types,
1354};
1355