blob: f79d70a3855dca0a81a1bbf2bbb40b381bd854e8 [file] [log] [blame]
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001/*
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
David Howells760285e2012-10-02 18:01:07 +010017#include <drm/drmP.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090018
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>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090026#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>
Sachin Kamat3f1c7812013-08-14 16:38:01 +053033#include <linux/of.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090034#include <linux/component.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090035
36#include <drm/exynos_drm.h>
37
38#include "exynos_drm_drv.h"
Rahul Sharma663d8762013-01-03 05:44:04 -050039#include "exynos_drm_crtc.h"
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +090040#include "exynos_drm_plane.h"
Inki Dae1055b392012-10-19 17:37:35 +090041#include "exynos_drm_iommu.h"
Sean Paulf041b252014-01-30 16:19:15 -050042#include "exynos_mixer.h"
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090043
Sean Paulf041b252014-01-30 16:19:15 -050044#define MIXER_WIN_NR 3
45#define MIXER_DEFAULT_WIN 0
Seung-Woo Kimd8408322011-12-21 17:39:39 +090046
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090047struct mixer_resources {
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090048 int irq;
49 void __iomem *mixer_regs;
50 void __iomem *vp_regs;
51 spinlock_t reg_slock;
52 struct clk *mixer;
53 struct clk *vp;
Marek Szyprowski04427ec2015-02-02 14:20:28 +010054 struct clk *hdmi;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090055 struct clk *sclk_mixer;
56 struct clk *sclk_hdmi;
Marek Szyprowskiff830c92014-07-01 10:10:07 +020057 struct clk *mout_mixer;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090058};
59
Rahul Sharma1e123442012-10-04 20:48:51 +053060enum mixer_version_id {
61 MXR_VER_0_0_0_16,
62 MXR_VER_16_0_33_0,
Rahul Sharmadef5e092013-06-19 18:21:08 +053063 MXR_VER_128_0_0_184,
Rahul Sharma1e123442012-10-04 20:48:51 +053064};
65
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090066struct mixer_context {
Sean Paul45517892014-01-30 16:19:05 -050067 struct platform_device *pdev;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090068 struct device *dev;
Inki Dae1055b392012-10-19 17:37:35 +090069 struct drm_device *drm_dev;
Gustavo Padovan93bca242015-01-18 18:16:23 +090070 struct exynos_drm_crtc *crtc;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +090071 struct exynos_drm_plane planes[MIXER_WIN_NR];
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090072 int pipe;
73 bool interlace;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090074 bool powered;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053075 bool vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +020076 bool has_sclk;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090077 u32 int_en;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090078
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090079 struct mutex mixer_mutex;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090080 struct mixer_resources mixer_res;
Rahul Sharma1e123442012-10-04 20:48:51 +053081 enum mixer_version_id mxr_ver;
Prathyush K6e95d5e2012-12-06 20:16:03 +053082 wait_queue_head_t wait_vsync_queue;
83 atomic_t wait_vsync_event;
Rahul Sharma1e123442012-10-04 20:48:51 +053084};
85
86struct mixer_drv_data {
87 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053088 bool is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +020089 bool has_sclk;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090090};
91
Seung-Woo Kimd8408322011-12-21 17:39:39 +090092static const u8 filter_y_horiz_tap8[] = {
93 0, -1, -1, -1, -1, -1, -1, -1,
94 -1, -1, -1, -1, -1, 0, 0, 0,
95 0, 2, 4, 5, 6, 6, 6, 6,
96 6, 5, 5, 4, 3, 2, 1, 1,
97 0, -6, -12, -16, -18, -20, -21, -20,
98 -20, -18, -16, -13, -10, -8, -5, -2,
99 127, 126, 125, 121, 114, 107, 99, 89,
100 79, 68, 57, 46, 35, 25, 16, 8,
101};
102
103static const u8 filter_y_vert_tap4[] = {
104 0, -3, -6, -8, -8, -8, -8, -7,
105 -6, -5, -4, -3, -2, -1, -1, 0,
106 127, 126, 124, 118, 111, 102, 92, 81,
107 70, 59, 48, 37, 27, 19, 11, 5,
108 0, 5, 11, 19, 27, 37, 48, 59,
109 70, 81, 92, 102, 111, 118, 124, 126,
110 0, 0, -1, -1, -2, -3, -4, -5,
111 -6, -7, -8, -8, -8, -8, -6, -3,
112};
113
114static const u8 filter_cr_horiz_tap4[] = {
115 0, -3, -6, -8, -8, -8, -8, -7,
116 -6, -5, -4, -3, -2, -1, -1, 0,
117 127, 126, 124, 118, 111, 102, 92, 81,
118 70, 59, 48, 37, 27, 19, 11, 5,
119};
120
121static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
122{
123 return readl(res->vp_regs + reg_id);
124}
125
126static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
127 u32 val)
128{
129 writel(val, res->vp_regs + reg_id);
130}
131
132static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
133 u32 val, u32 mask)
134{
135 u32 old = vp_reg_read(res, reg_id);
136
137 val = (val & mask) | (old & ~mask);
138 writel(val, res->vp_regs + reg_id);
139}
140
141static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
142{
143 return readl(res->mixer_regs + reg_id);
144}
145
146static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
147 u32 val)
148{
149 writel(val, res->mixer_regs + reg_id);
150}
151
152static inline void mixer_reg_writemask(struct mixer_resources *res,
153 u32 reg_id, u32 val, u32 mask)
154{
155 u32 old = mixer_reg_read(res, reg_id);
156
157 val = (val & mask) | (old & ~mask);
158 writel(val, res->mixer_regs + reg_id);
159}
160
161static void mixer_regs_dump(struct mixer_context *ctx)
162{
163#define DUMPREG(reg_id) \
164do { \
165 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
166 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
167} while (0)
168
169 DUMPREG(MXR_STATUS);
170 DUMPREG(MXR_CFG);
171 DUMPREG(MXR_INT_EN);
172 DUMPREG(MXR_INT_STATUS);
173
174 DUMPREG(MXR_LAYER_CFG);
175 DUMPREG(MXR_VIDEO_CFG);
176
177 DUMPREG(MXR_GRAPHIC0_CFG);
178 DUMPREG(MXR_GRAPHIC0_BASE);
179 DUMPREG(MXR_GRAPHIC0_SPAN);
180 DUMPREG(MXR_GRAPHIC0_WH);
181 DUMPREG(MXR_GRAPHIC0_SXY);
182 DUMPREG(MXR_GRAPHIC0_DXY);
183
184 DUMPREG(MXR_GRAPHIC1_CFG);
185 DUMPREG(MXR_GRAPHIC1_BASE);
186 DUMPREG(MXR_GRAPHIC1_SPAN);
187 DUMPREG(MXR_GRAPHIC1_WH);
188 DUMPREG(MXR_GRAPHIC1_SXY);
189 DUMPREG(MXR_GRAPHIC1_DXY);
190#undef DUMPREG
191}
192
193static void vp_regs_dump(struct mixer_context *ctx)
194{
195#define DUMPREG(reg_id) \
196do { \
197 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
198 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
199} while (0)
200
201 DUMPREG(VP_ENABLE);
202 DUMPREG(VP_SRESET);
203 DUMPREG(VP_SHADOW_UPDATE);
204 DUMPREG(VP_FIELD_ID);
205 DUMPREG(VP_MODE);
206 DUMPREG(VP_IMG_SIZE_Y);
207 DUMPREG(VP_IMG_SIZE_C);
208 DUMPREG(VP_PER_RATE_CTRL);
209 DUMPREG(VP_TOP_Y_PTR);
210 DUMPREG(VP_BOT_Y_PTR);
211 DUMPREG(VP_TOP_C_PTR);
212 DUMPREG(VP_BOT_C_PTR);
213 DUMPREG(VP_ENDIAN_MODE);
214 DUMPREG(VP_SRC_H_POSITION);
215 DUMPREG(VP_SRC_V_POSITION);
216 DUMPREG(VP_SRC_WIDTH);
217 DUMPREG(VP_SRC_HEIGHT);
218 DUMPREG(VP_DST_H_POSITION);
219 DUMPREG(VP_DST_V_POSITION);
220 DUMPREG(VP_DST_WIDTH);
221 DUMPREG(VP_DST_HEIGHT);
222 DUMPREG(VP_H_RATIO);
223 DUMPREG(VP_V_RATIO);
224
225#undef DUMPREG
226}
227
228static inline void vp_filter_set(struct mixer_resources *res,
229 int reg_id, const u8 *data, unsigned int size)
230{
231 /* assure 4-byte align */
232 BUG_ON(size & 3);
233 for (; size; size -= 4, reg_id += 4, data += 4) {
234 u32 val = (data[0] << 24) | (data[1] << 16) |
235 (data[2] << 8) | data[3];
236 vp_reg_write(res, reg_id, val);
237 }
238}
239
240static void vp_default_filter(struct mixer_resources *res)
241{
242 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530243 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900244 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530245 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900246 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530247 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900248}
249
250static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
251{
252 struct mixer_resources *res = &ctx->mixer_res;
253
254 /* block update on vsync */
255 mixer_reg_writemask(res, MXR_STATUS, enable ?
256 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
257
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530258 if (ctx->vp_enabled)
259 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900260 VP_SHADOW_UPDATE_ENABLE : 0);
261}
262
263static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
264{
265 struct mixer_resources *res = &ctx->mixer_res;
266 u32 val;
267
268 /* choosing between interlace and progressive mode */
269 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
270 MXR_CFG_SCAN_PROGRASSIVE);
271
Rahul Sharmadef5e092013-06-19 18:21:08 +0530272 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
273 /* choosing between proper HD and SD mode */
274 if (height <= 480)
275 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
276 else if (height <= 576)
277 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
278 else if (height <= 720)
279 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
280 else if (height <= 1080)
281 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
282 else
283 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
284 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900285
286 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
287}
288
289static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
290{
291 struct mixer_resources *res = &ctx->mixer_res;
292 u32 val;
293
294 if (height == 480) {
295 val = MXR_CFG_RGB601_0_255;
296 } else if (height == 576) {
297 val = MXR_CFG_RGB601_0_255;
298 } else if (height == 720) {
299 val = MXR_CFG_RGB709_16_235;
300 mixer_reg_write(res, MXR_CM_COEFF_Y,
301 (1 << 30) | (94 << 20) | (314 << 10) |
302 (32 << 0));
303 mixer_reg_write(res, MXR_CM_COEFF_CB,
304 (972 << 20) | (851 << 10) | (225 << 0));
305 mixer_reg_write(res, MXR_CM_COEFF_CR,
306 (225 << 20) | (820 << 10) | (1004 << 0));
307 } else if (height == 1080) {
308 val = MXR_CFG_RGB709_16_235;
309 mixer_reg_write(res, MXR_CM_COEFF_Y,
310 (1 << 30) | (94 << 20) | (314 << 10) |
311 (32 << 0));
312 mixer_reg_write(res, MXR_CM_COEFF_CB,
313 (972 << 20) | (851 << 10) | (225 << 0));
314 mixer_reg_write(res, MXR_CM_COEFF_CR,
315 (225 << 20) | (820 << 10) | (1004 << 0));
316 } else {
317 val = MXR_CFG_RGB709_16_235;
318 mixer_reg_write(res, MXR_CM_COEFF_Y,
319 (1 << 30) | (94 << 20) | (314 << 10) |
320 (32 << 0));
321 mixer_reg_write(res, MXR_CM_COEFF_CB,
322 (972 << 20) | (851 << 10) | (225 << 0));
323 mixer_reg_write(res, MXR_CM_COEFF_CR,
324 (225 << 20) | (820 << 10) | (1004 << 0));
325 }
326
327 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
328}
329
330static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
331{
332 struct mixer_resources *res = &ctx->mixer_res;
333 u32 val = enable ? ~0 : 0;
334
335 switch (win) {
336 case 0:
337 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
338 break;
339 case 1:
340 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
341 break;
342 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530343 if (ctx->vp_enabled) {
344 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
345 mixer_reg_writemask(res, MXR_CFG, val,
346 MXR_CFG_VP_ENABLE);
Joonyoung Shimf1e716d2014-07-25 19:59:10 +0900347
348 /* control blending of graphic layer 0 */
349 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
350 MXR_GRP_CFG_BLEND_PRE_MUL |
351 MXR_GRP_CFG_PIXEL_BLEND_EN);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530352 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900353 break;
354 }
355}
356
357static void mixer_run(struct mixer_context *ctx)
358{
359 struct mixer_resources *res = &ctx->mixer_res;
360
361 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
362
363 mixer_regs_dump(ctx);
364}
365
Rahul Sharma381be022014-06-23 11:02:22 +0530366static void mixer_stop(struct mixer_context *ctx)
367{
368 struct mixer_resources *res = &ctx->mixer_res;
369 int timeout = 20;
370
371 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
372
373 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
374 --timeout)
375 usleep_range(10000, 12000);
376
377 mixer_regs_dump(ctx);
378}
379
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900380static void vp_video_buffer(struct mixer_context *ctx, int win)
381{
382 struct mixer_resources *res = &ctx->mixer_res;
383 unsigned long flags;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900384 struct exynos_drm_plane *plane;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900385 unsigned int x_ratio, y_ratio;
YoungJun Cho782953e2013-07-01 13:04:12 +0900386 unsigned int buf_num = 1;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900387 dma_addr_t luma_addr[2], chroma_addr[2];
388 bool tiled_mode = false;
389 bool crcb_mode = false;
390 u32 val;
391
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900392 plane = &ctx->planes[win];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900393
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900394 switch (plane->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900395 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900396 crcb_mode = false;
397 buf_num = 2;
398 break;
399 /* TODO: single buffer format NV12, NV21 */
400 default:
401 /* ignore pixel format at disable time */
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900402 if (!plane->dma_addr[0])
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900403 break;
404
405 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900406 plane->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900407 return;
408 }
409
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900410 /* scaling feature: (src << 16) / dst */
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900411 x_ratio = (plane->src_width << 16) / plane->crtc_width;
412 y_ratio = (plane->src_height << 16) / plane->crtc_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900413
414 if (buf_num == 2) {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900415 luma_addr[0] = plane->dma_addr[0];
416 chroma_addr[0] = plane->dma_addr[1];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900417 } else {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900418 luma_addr[0] = plane->dma_addr[0];
419 chroma_addr[0] = plane->dma_addr[0]
420 + (plane->pitch * plane->fb_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900421 }
422
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900423 if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900424 ctx->interlace = true;
425 if (tiled_mode) {
426 luma_addr[1] = luma_addr[0] + 0x40;
427 chroma_addr[1] = chroma_addr[0] + 0x40;
428 } else {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900429 luma_addr[1] = luma_addr[0] + plane->pitch;
430 chroma_addr[1] = chroma_addr[0] + plane->pitch;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900431 }
432 } else {
433 ctx->interlace = false;
434 luma_addr[1] = 0;
435 chroma_addr[1] = 0;
436 }
437
438 spin_lock_irqsave(&res->reg_slock, flags);
439 mixer_vsync_set_update(ctx, false);
440
441 /* interlace or progressive scan mode */
442 val = (ctx->interlace ? ~0 : 0);
443 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
444
445 /* setup format */
446 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
447 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
448 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
449
450 /* setting size of input image */
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900451 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(plane->pitch) |
452 VP_IMG_VSIZE(plane->fb_height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900453 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900454 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(plane->pitch) |
455 VP_IMG_VSIZE(plane->fb_height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900456
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900457 vp_reg_write(res, VP_SRC_WIDTH, plane->src_width);
458 vp_reg_write(res, VP_SRC_HEIGHT, plane->src_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900459 vp_reg_write(res, VP_SRC_H_POSITION,
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900460 VP_SRC_H_POSITION_VAL(plane->fb_x));
461 vp_reg_write(res, VP_SRC_V_POSITION, plane->fb_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900462
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900463 vp_reg_write(res, VP_DST_WIDTH, plane->crtc_width);
464 vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900465 if (ctx->interlace) {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900466 vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height / 2);
467 vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900468 } else {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900469 vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height);
470 vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900471 }
472
473 vp_reg_write(res, VP_H_RATIO, x_ratio);
474 vp_reg_write(res, VP_V_RATIO, y_ratio);
475
476 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
477
478 /* set buffer address to vp */
479 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
480 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
481 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
482 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
483
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900484 mixer_cfg_scan(ctx, plane->mode_height);
485 mixer_cfg_rgb_fmt(ctx, plane->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900486 mixer_cfg_layer(ctx, win, true);
487 mixer_run(ctx);
488
489 mixer_vsync_set_update(ctx, true);
490 spin_unlock_irqrestore(&res->reg_slock, flags);
491
492 vp_regs_dump(ctx);
493}
494
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530495static void mixer_layer_update(struct mixer_context *ctx)
496{
497 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530498
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530499 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530500}
501
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900502static void mixer_graph_buffer(struct mixer_context *ctx, int win)
503{
504 struct mixer_resources *res = &ctx->mixer_res;
505 unsigned long flags;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900506 struct exynos_drm_plane *plane;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900507 unsigned int x_ratio, y_ratio;
508 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900509 dma_addr_t dma_addr;
510 unsigned int fmt;
511 u32 val;
512
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900513 plane = &ctx->planes[win];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900514
515 #define RGB565 4
516 #define ARGB1555 5
517 #define ARGB4444 6
518 #define ARGB8888 7
519
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900520 switch (plane->bpp) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900521 case 16:
522 fmt = ARGB4444;
523 break;
524 case 32:
525 fmt = ARGB8888;
526 break;
527 default:
528 fmt = ARGB8888;
529 }
530
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900531 /* 2x scaling feature */
532 x_ratio = 0;
533 y_ratio = 0;
534
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900535 dst_x_offset = plane->crtc_x;
536 dst_y_offset = plane->crtc_y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900537
538 /* converting dma address base and source offset */
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900539 dma_addr = plane->dma_addr[0]
540 + (plane->fb_x * plane->bpp >> 3)
541 + (plane->fb_y * plane->pitch);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900542 src_x_offset = 0;
543 src_y_offset = 0;
544
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900545 if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900546 ctx->interlace = true;
547 else
548 ctx->interlace = false;
549
550 spin_lock_irqsave(&res->reg_slock, flags);
551 mixer_vsync_set_update(ctx, false);
552
553 /* setup format */
554 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
555 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
556
557 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000558 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900559 plane->pitch / (plane->bpp >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900560
Rahul Sharmadef5e092013-06-19 18:21:08 +0530561 /* setup display size */
562 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
563 win == MIXER_DEFAULT_WIN) {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900564 val = MXR_MXR_RES_HEIGHT(plane->mode_height);
565 val |= MXR_MXR_RES_WIDTH(plane->mode_width);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530566 mixer_reg_write(res, MXR_RESOLUTION, val);
567 }
568
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900569 val = MXR_GRP_WH_WIDTH(plane->crtc_width);
570 val |= MXR_GRP_WH_HEIGHT(plane->crtc_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900571 val |= MXR_GRP_WH_H_SCALE(x_ratio);
572 val |= MXR_GRP_WH_V_SCALE(y_ratio);
573 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
574
575 /* setup offsets in source image */
576 val = MXR_GRP_SXY_SX(src_x_offset);
577 val |= MXR_GRP_SXY_SY(src_y_offset);
578 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
579
580 /* setup offsets in display image */
581 val = MXR_GRP_DXY_DX(dst_x_offset);
582 val |= MXR_GRP_DXY_DY(dst_y_offset);
583 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
584
585 /* set buffer address to mixer */
586 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
587
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900588 mixer_cfg_scan(ctx, plane->mode_height);
589 mixer_cfg_rgb_fmt(ctx, plane->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900590 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530591
592 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530593 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
594 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530595 mixer_layer_update(ctx);
596
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900597 mixer_run(ctx);
598
599 mixer_vsync_set_update(ctx, true);
600 spin_unlock_irqrestore(&res->reg_slock, flags);
601}
602
603static void vp_win_reset(struct mixer_context *ctx)
604{
605 struct mixer_resources *res = &ctx->mixer_res;
606 int tries = 100;
607
608 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
609 for (tries = 100; tries; --tries) {
610 /* waiting until VP_SRESET_PROCESSING is 0 */
611 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
612 break;
Sean Paul09760ea2013-01-14 17:03:20 -0500613 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900614 }
615 WARN(tries == 0, "failed to reset Video Processor\n");
616}
617
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900618static void mixer_win_reset(struct mixer_context *ctx)
619{
620 struct mixer_resources *res = &ctx->mixer_res;
621 unsigned long flags;
622 u32 val; /* value stored to register */
623
624 spin_lock_irqsave(&res->reg_slock, flags);
625 mixer_vsync_set_update(ctx, false);
626
627 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
628
629 /* set output in RGB888 mode */
630 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
631
632 /* 16 beat burst in DMA */
633 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
634 MXR_STATUS_BURST_MASK);
635
636 /* setting default layer priority: layer1 > layer0 > video
637 * because typical usage scenario would be
638 * layer1 - OSD
639 * layer0 - framebuffer
640 * video - video overlay
641 */
642 val = MXR_LAYER_CFG_GRP1_VAL(3);
643 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530644 if (ctx->vp_enabled)
645 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900646 mixer_reg_write(res, MXR_LAYER_CFG, val);
647
648 /* setting background color */
649 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
650 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
651 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
652
653 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900654 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
655 val |= MXR_GRP_CFG_WIN_BLEND_EN;
656 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
657
Sean Paul0377f4e2013-04-25 15:13:26 -0400658 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900659 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400660
661 /* Blend layer 1 into layer 0 */
662 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
663 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900664 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
665
Seung-Woo Kim57366032012-05-15 17:22:08 +0900666 /* setting video layers */
667 val = MXR_GRP_CFG_ALPHA_VAL(0);
668 mixer_reg_write(res, MXR_VIDEO_CFG, val);
669
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530670 if (ctx->vp_enabled) {
671 /* configuration of Video Processor Registers */
672 vp_win_reset(ctx);
673 vp_default_filter(res);
674 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900675
676 /* disable all layers */
677 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
678 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530679 if (ctx->vp_enabled)
680 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900681
682 mixer_vsync_set_update(ctx, true);
683 spin_unlock_irqrestore(&res->reg_slock, flags);
684}
685
Sean Paul45517892014-01-30 16:19:05 -0500686static irqreturn_t mixer_irq_handler(int irq, void *arg)
687{
688 struct mixer_context *ctx = arg;
689 struct mixer_resources *res = &ctx->mixer_res;
690 u32 val, base, shadow;
691
692 spin_lock(&res->reg_slock);
693
694 /* read interrupt status for handling and clearing flags for VSYNC */
695 val = mixer_reg_read(res, MXR_INT_STATUS);
696
697 /* handling VSYNC */
698 if (val & MXR_INT_STATUS_VSYNC) {
699 /* interlace scan need to check shadow register */
700 if (ctx->interlace) {
701 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
702 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
703 if (base != shadow)
704 goto out;
705
706 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
707 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
708 if (base != shadow)
709 goto out;
710 }
711
712 drm_handle_vblank(ctx->drm_dev, ctx->pipe);
713 exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
714
715 /* set wait vsync event to zero and wake up queue. */
716 if (atomic_read(&ctx->wait_vsync_event)) {
717 atomic_set(&ctx->wait_vsync_event, 0);
718 wake_up(&ctx->wait_vsync_queue);
719 }
720 }
721
722out:
723 /* clear interrupts */
724 if (~val & MXR_INT_EN_VSYNC) {
725 /* vsync interrupt use different bit for read and clear */
726 val &= ~MXR_INT_EN_VSYNC;
727 val |= MXR_INT_CLEAR_VSYNC;
728 }
729 mixer_reg_write(res, MXR_INT_STATUS, val);
730
731 spin_unlock(&res->reg_slock);
732
733 return IRQ_HANDLED;
734}
735
736static int mixer_resources_init(struct mixer_context *mixer_ctx)
737{
738 struct device *dev = &mixer_ctx->pdev->dev;
739 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
740 struct resource *res;
741 int ret;
742
743 spin_lock_init(&mixer_res->reg_slock);
744
745 mixer_res->mixer = devm_clk_get(dev, "mixer");
746 if (IS_ERR(mixer_res->mixer)) {
747 dev_err(dev, "failed to get clock 'mixer'\n");
748 return -ENODEV;
749 }
750
Marek Szyprowski04427ec2015-02-02 14:20:28 +0100751 mixer_res->hdmi = devm_clk_get(dev, "hdmi");
752 if (IS_ERR(mixer_res->hdmi)) {
753 dev_err(dev, "failed to get clock 'hdmi'\n");
754 return PTR_ERR(mixer_res->hdmi);
755 }
756
Sean Paul45517892014-01-30 16:19:05 -0500757 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
758 if (IS_ERR(mixer_res->sclk_hdmi)) {
759 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
760 return -ENODEV;
761 }
762 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
763 if (res == NULL) {
764 dev_err(dev, "get memory resource failed.\n");
765 return -ENXIO;
766 }
767
768 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
769 resource_size(res));
770 if (mixer_res->mixer_regs == NULL) {
771 dev_err(dev, "register mapping failed.\n");
772 return -ENXIO;
773 }
774
775 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
776 if (res == NULL) {
777 dev_err(dev, "get interrupt resource failed.\n");
778 return -ENXIO;
779 }
780
781 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
782 0, "drm_mixer", mixer_ctx);
783 if (ret) {
784 dev_err(dev, "request interrupt failed.\n");
785 return ret;
786 }
787 mixer_res->irq = res->start;
788
789 return 0;
790}
791
792static int vp_resources_init(struct mixer_context *mixer_ctx)
793{
794 struct device *dev = &mixer_ctx->pdev->dev;
795 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
796 struct resource *res;
797
798 mixer_res->vp = devm_clk_get(dev, "vp");
799 if (IS_ERR(mixer_res->vp)) {
800 dev_err(dev, "failed to get clock 'vp'\n");
801 return -ENODEV;
802 }
Sean Paul45517892014-01-30 16:19:05 -0500803
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200804 if (mixer_ctx->has_sclk) {
805 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
806 if (IS_ERR(mixer_res->sclk_mixer)) {
807 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
808 return -ENODEV;
809 }
810 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
811 if (IS_ERR(mixer_res->mout_mixer)) {
812 dev_err(dev, "failed to get clock 'mout_mixer'\n");
813 return -ENODEV;
814 }
815
816 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
817 clk_set_parent(mixer_res->mout_mixer,
818 mixer_res->sclk_hdmi);
819 }
Sean Paul45517892014-01-30 16:19:05 -0500820
821 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
822 if (res == NULL) {
823 dev_err(dev, "get memory resource failed.\n");
824 return -ENXIO;
825 }
826
827 mixer_res->vp_regs = devm_ioremap(dev, res->start,
828 resource_size(res));
829 if (mixer_res->vp_regs == NULL) {
830 dev_err(dev, "register mapping failed.\n");
831 return -ENXIO;
832 }
833
834 return 0;
835}
836
Gustavo Padovan93bca242015-01-18 18:16:23 +0900837static int mixer_initialize(struct mixer_context *mixer_ctx,
Inki Daef37cd5e2014-05-09 14:25:20 +0900838 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500839{
840 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +0900841 struct exynos_drm_private *priv;
842 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500843
Gustavo Padovaneb88e422014-11-26 16:43:27 -0200844 mixer_ctx->drm_dev = drm_dev;
Gustavo Padovan8a326ed2014-11-04 18:44:47 -0200845 mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500846
847 /* acquire resources: regs, irqs, clocks */
848 ret = mixer_resources_init(mixer_ctx);
849 if (ret) {
850 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
851 return ret;
852 }
853
854 if (mixer_ctx->vp_enabled) {
855 /* acquire vp resources: regs, irqs, clocks */
856 ret = vp_resources_init(mixer_ctx);
857 if (ret) {
858 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
859 return ret;
860 }
861 }
862
Sean Paulf041b252014-01-30 16:19:15 -0500863 if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
864 return 0;
865
866 return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Sean Paul45517892014-01-30 16:19:05 -0500867}
868
Gustavo Padovan93bca242015-01-18 18:16:23 +0900869static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
Inki Dae1055b392012-10-19 17:37:35 +0900870{
Sean Paulf041b252014-01-30 16:19:15 -0500871 if (is_drm_iommu_supported(mixer_ctx->drm_dev))
872 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900873}
874
Gustavo Padovan93bca242015-01-18 18:16:23 +0900875static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900876{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900877 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900878 struct mixer_resources *res = &mixer_ctx->mixer_res;
879
Sean Paulf041b252014-01-30 16:19:15 -0500880 if (!mixer_ctx->powered) {
881 mixer_ctx->int_en |= MXR_INT_EN_VSYNC;
882 return 0;
883 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900884
885 /* enable vsync interrupt */
886 mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
887 MXR_INT_EN_VSYNC);
888
889 return 0;
890}
891
Gustavo Padovan93bca242015-01-18 18:16:23 +0900892static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900893{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900894 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900895 struct mixer_resources *res = &mixer_ctx->mixer_res;
896
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900897 /* disable vsync interrupt */
898 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
899}
900
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +0900901static void mixer_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900902{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900903 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900904
YoungJun Chocbc4c332013-06-12 10:44:40 +0900905 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900906
Shirish Sdda90122013-01-23 22:03:18 -0500907 mutex_lock(&mixer_ctx->mixer_mutex);
908 if (!mixer_ctx->powered) {
909 mutex_unlock(&mixer_ctx->mixer_mutex);
910 return;
911 }
912 mutex_unlock(&mixer_ctx->mixer_mutex);
913
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530914 if (win > 1 && mixer_ctx->vp_enabled)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900915 vp_video_buffer(mixer_ctx, win);
916 else
917 mixer_graph_buffer(mixer_ctx, win);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530918
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900919 mixer_ctx->planes[win].enabled = true;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900920}
921
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +0900922static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900923{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900924 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900925 struct mixer_resources *res = &mixer_ctx->mixer_res;
926 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900927
YoungJun Chocbc4c332013-06-12 10:44:40 +0900928 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900929
Prathyush Kdb43fd12012-12-06 20:16:05 +0530930 mutex_lock(&mixer_ctx->mixer_mutex);
931 if (!mixer_ctx->powered) {
932 mutex_unlock(&mixer_ctx->mixer_mutex);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900933 mixer_ctx->planes[win].resume = false;
Prathyush Kdb43fd12012-12-06 20:16:05 +0530934 return;
935 }
936 mutex_unlock(&mixer_ctx->mixer_mutex);
937
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900938 spin_lock_irqsave(&res->reg_slock, flags);
939 mixer_vsync_set_update(mixer_ctx, false);
940
941 mixer_cfg_layer(mixer_ctx, win, false);
942
943 mixer_vsync_set_update(mixer_ctx, true);
944 spin_unlock_irqrestore(&res->reg_slock, flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530945
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900946 mixer_ctx->planes[win].enabled = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900947}
948
Gustavo Padovan93bca242015-01-18 18:16:23 +0900949static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -0500950{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900951 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +0900952 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +0530953
Prathyush K6e95d5e2012-12-06 20:16:03 +0530954 mutex_lock(&mixer_ctx->mixer_mutex);
955 if (!mixer_ctx->powered) {
956 mutex_unlock(&mixer_ctx->mixer_mutex);
957 return;
958 }
959 mutex_unlock(&mixer_ctx->mixer_mutex);
960
Gustavo Padovan93bca242015-01-18 18:16:23 +0900961 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +0900962 if (err < 0) {
963 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
964 return;
965 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +0530966
Prathyush K6e95d5e2012-12-06 20:16:03 +0530967 atomic_set(&mixer_ctx->wait_vsync_event, 1);
968
969 /*
970 * wait for MIXER to signal VSYNC interrupt or return after
971 * timeout which is set to 50ms (refresh rate of 20).
972 */
973 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
974 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +0100975 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +0530976 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +0530977
Gustavo Padovan93bca242015-01-18 18:16:23 +0900978 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +0530979}
980
Joonyoung Shim92dc7a02015-01-30 16:43:02 +0900981static void mixer_window_suspend(struct mixer_context *ctx)
Prathyush Kdb43fd12012-12-06 20:16:05 +0530982{
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900983 struct exynos_drm_plane *plane;
Prathyush Kdb43fd12012-12-06 20:16:05 +0530984 int i;
985
986 for (i = 0; i < MIXER_WIN_NR; i++) {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900987 plane = &ctx->planes[i];
988 plane->resume = plane->enabled;
Joonyoung Shim92dc7a02015-01-30 16:43:02 +0900989 mixer_win_disable(ctx->crtc, i);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530990 }
Joonyoung Shim92dc7a02015-01-30 16:43:02 +0900991 mixer_wait_for_vblank(ctx->crtc);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530992}
993
Joonyoung Shim92dc7a02015-01-30 16:43:02 +0900994static void mixer_window_resume(struct mixer_context *ctx)
Prathyush Kdb43fd12012-12-06 20:16:05 +0530995{
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900996 struct exynos_drm_plane *plane;
Prathyush Kdb43fd12012-12-06 20:16:05 +0530997 int i;
998
999 for (i = 0; i < MIXER_WIN_NR; i++) {
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001000 plane = &ctx->planes[i];
1001 plane->enabled = plane->resume;
1002 plane->resume = false;
1003 if (plane->enabled)
Joonyoung Shim92dc7a02015-01-30 16:43:02 +09001004 mixer_win_commit(ctx->crtc, i);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301005 }
1006}
1007
Joonyoung Shim92dc7a02015-01-30 16:43:02 +09001008static void mixer_poweron(struct mixer_context *ctx)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301009{
1010 struct mixer_resources *res = &ctx->mixer_res;
1011
Prathyush Kdb43fd12012-12-06 20:16:05 +05301012 mutex_lock(&ctx->mixer_mutex);
1013 if (ctx->powered) {
1014 mutex_unlock(&ctx->mixer_mutex);
1015 return;
1016 }
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301017
Prathyush Kdb43fd12012-12-06 20:16:05 +05301018 mutex_unlock(&ctx->mixer_mutex);
1019
Sean Paulaf65c802014-01-30 16:19:27 -05001020 pm_runtime_get_sync(ctx->dev);
1021
Sean Paul0bfb1f82013-06-11 12:24:02 +05301022 clk_prepare_enable(res->mixer);
Marek Szyprowski04427ec2015-02-02 14:20:28 +01001023 clk_prepare_enable(res->hdmi);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301024 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +05301025 clk_prepare_enable(res->vp);
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001026 if (ctx->has_sclk)
1027 clk_prepare_enable(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301028 }
1029
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301030 mutex_lock(&ctx->mixer_mutex);
1031 ctx->powered = true;
1032 mutex_unlock(&ctx->mixer_mutex);
1033
Rahul Sharmad74ed932014-06-23 11:02:24 +05301034 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1035
Prathyush Kdb43fd12012-12-06 20:16:05 +05301036 mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
1037 mixer_win_reset(ctx);
1038
Joonyoung Shim92dc7a02015-01-30 16:43:02 +09001039 mixer_window_resume(ctx);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301040}
1041
Joonyoung Shim92dc7a02015-01-30 16:43:02 +09001042static void mixer_poweroff(struct mixer_context *ctx)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301043{
1044 struct mixer_resources *res = &ctx->mixer_res;
1045
Prathyush Kdb43fd12012-12-06 20:16:05 +05301046 mutex_lock(&ctx->mixer_mutex);
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301047 if (!ctx->powered) {
1048 mutex_unlock(&ctx->mixer_mutex);
1049 return;
1050 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301051 mutex_unlock(&ctx->mixer_mutex);
1052
Rahul Sharma381be022014-06-23 11:02:22 +05301053 mixer_stop(ctx);
Joonyoung Shim92dc7a02015-01-30 16:43:02 +09001054 mixer_window_suspend(ctx);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301055
1056 ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
1057
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301058 mutex_lock(&ctx->mixer_mutex);
1059 ctx->powered = false;
1060 mutex_unlock(&ctx->mixer_mutex);
1061
Marek Szyprowski04427ec2015-02-02 14:20:28 +01001062 clk_disable_unprepare(res->hdmi);
Sean Paul0bfb1f82013-06-11 12:24:02 +05301063 clk_disable_unprepare(res->mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301064 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +05301065 clk_disable_unprepare(res->vp);
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001066 if (ctx->has_sclk)
1067 clk_disable_unprepare(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301068 }
1069
Sean Paulaf65c802014-01-30 16:19:27 -05001070 pm_runtime_put_sync(ctx->dev);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301071}
1072
Gustavo Padovan93bca242015-01-18 18:16:23 +09001073static void mixer_dpms(struct exynos_drm_crtc *crtc, int mode)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301074{
Prathyush Kdb43fd12012-12-06 20:16:05 +05301075 switch (mode) {
1076 case DRM_MODE_DPMS_ON:
Joonyoung Shim92dc7a02015-01-30 16:43:02 +09001077 mixer_poweron(crtc->ctx);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301078 break;
1079 case DRM_MODE_DPMS_STANDBY:
1080 case DRM_MODE_DPMS_SUSPEND:
1081 case DRM_MODE_DPMS_OFF:
Joonyoung Shim92dc7a02015-01-30 16:43:02 +09001082 mixer_poweroff(crtc->ctx);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301083 break;
1084 default:
1085 DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
1086 break;
1087 }
1088}
1089
Sean Paulf041b252014-01-30 16:19:15 -05001090/* Only valid for Mixer version 16.0.33.0 */
1091int mixer_check_mode(struct drm_display_mode *mode)
1092{
1093 u32 w, h;
1094
1095 w = mode->hdisplay;
1096 h = mode->vdisplay;
1097
1098 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1099 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1100 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1101
1102 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1103 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1104 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1105 return 0;
1106
1107 return -EINVAL;
1108}
1109
Gustavo Padovan93bca242015-01-18 18:16:23 +09001110static struct exynos_drm_crtc_ops mixer_crtc_ops = {
Sean Paulf041b252014-01-30 16:19:15 -05001111 .dpms = mixer_dpms,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001112 .enable_vblank = mixer_enable_vblank,
1113 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301114 .wait_for_vblank = mixer_wait_for_vblank,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001115 .win_commit = mixer_win_commit,
1116 .win_disable = mixer_win_disable,
Sean Paulf041b252014-01-30 16:19:15 -05001117};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001118
Rahul Sharmadef5e092013-06-19 18:21:08 +05301119static struct mixer_drv_data exynos5420_mxr_drv_data = {
1120 .version = MXR_VER_128_0_0_184,
1121 .is_vp_enabled = 0,
1122};
1123
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301124static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301125 .version = MXR_VER_16_0_33_0,
1126 .is_vp_enabled = 0,
1127};
1128
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001129static struct mixer_drv_data exynos4212_mxr_drv_data = {
1130 .version = MXR_VER_0_0_0_16,
1131 .is_vp_enabled = 1,
1132};
1133
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301134static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301135 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301136 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001137 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301138};
1139
1140static struct platform_device_id mixer_driver_types[] = {
1141 {
1142 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301143 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301144 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301145 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301146 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301147 }, {
1148 /* end node */
1149 }
1150};
1151
1152static struct of_device_id mixer_match_types[] = {
1153 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001154 .compatible = "samsung,exynos4210-mixer",
1155 .data = &exynos4210_mxr_drv_data,
1156 }, {
1157 .compatible = "samsung,exynos4212-mixer",
1158 .data = &exynos4212_mxr_drv_data,
1159 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301160 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301161 .data = &exynos5250_mxr_drv_data,
1162 }, {
1163 .compatible = "samsung,exynos5250-mixer",
1164 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301165 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301166 .compatible = "samsung,exynos5420-mixer",
1167 .data = &exynos5420_mxr_drv_data,
1168 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301169 /* end node */
1170 }
1171};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001172MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301173
Inki Daef37cd5e2014-05-09 14:25:20 +09001174static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001175{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001176 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001177 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001178 struct exynos_drm_plane *exynos_plane;
1179 enum drm_plane_type type;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001180 unsigned int zpos;
1181 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001182
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001183 ret = mixer_initialize(ctx, drm_dev);
1184 if (ret)
1185 return ret;
1186
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001187 for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) {
1188 type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY :
1189 DRM_PLANE_TYPE_OVERLAY;
1190 ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001191 1 << ctx->pipe, type, zpos);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001192 if (ret)
1193 return ret;
1194 }
1195
1196 exynos_plane = &ctx->planes[MIXER_DEFAULT_WIN];
1197 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1198 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1199 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001200 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001201 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001202 ret = PTR_ERR(ctx->crtc);
1203 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001204 }
1205
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001206 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001207
1208free_ctx:
1209 devm_kfree(dev, ctx);
1210 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001211}
1212
1213static void mixer_unbind(struct device *dev, struct device *master, void *data)
1214{
1215 struct mixer_context *ctx = dev_get_drvdata(dev);
1216
Gustavo Padovan93bca242015-01-18 18:16:23 +09001217 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001218}
1219
1220static const struct component_ops mixer_component_ops = {
1221 .bind = mixer_bind,
1222 .unbind = mixer_unbind,
1223};
1224
1225static int mixer_probe(struct platform_device *pdev)
1226{
1227 struct device *dev = &pdev->dev;
1228 struct mixer_drv_data *drv;
1229 struct mixer_context *ctx;
1230 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001231
Sean Paulf041b252014-01-30 16:19:15 -05001232 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1233 if (!ctx) {
1234 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001235 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001236 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001237
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001238 mutex_init(&ctx->mixer_mutex);
1239
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301240 if (dev->of_node) {
1241 const struct of_device_id *match;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001242
Sachin Kamate436b092013-06-05 16:00:23 +09001243 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301244 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301245 } else {
1246 drv = (struct mixer_drv_data *)
1247 platform_get_device_id(pdev)->driver_data;
1248 }
1249
Sean Paul45517892014-01-30 16:19:05 -05001250 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001251 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301252 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001253 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301254 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001255 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301256 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001257
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001258 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001259
1260 ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
Gustavo Padovan5d1741a2014-11-05 19:51:35 -02001261 EXYNOS_DISPLAY_TYPE_HDMI);
Inki Daedf5225b2014-05-29 18:28:02 +09001262 if (ret)
1263 return ret;
1264
1265 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001266 if (ret) {
Inki Daedf5225b2014-05-29 18:28:02 +09001267 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001268 return ret;
1269 }
1270
1271 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001272
1273 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001274}
1275
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001276static int mixer_remove(struct platform_device *pdev)
1277{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001278 pm_runtime_disable(&pdev->dev);
1279
Inki Daedf5225b2014-05-29 18:28:02 +09001280 component_del(&pdev->dev, &mixer_component_ops);
1281 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1282
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001283 return 0;
1284}
1285
1286struct platform_driver mixer_driver = {
1287 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301288 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001289 .owner = THIS_MODULE,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301290 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001291 },
1292 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001293 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301294 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001295};