blob: 167734529229012eb29d45e14f20670e61121008 [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
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"
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090039
Seung-Woo Kimd8408322011-12-21 17:39:39 +090040#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
41
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090042struct 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;
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +090057 unsigned int src_width;
58 unsigned int src_height;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090059 unsigned int mode_width;
60 unsigned int mode_height;
61 unsigned int scan_flags;
62};
63
64struct mixer_resources {
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090065 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
Rahul Sharma1e123442012-10-04 20:48:51 +053076enum mixer_version_id {
77 MXR_VER_0_0_0_16,
78 MXR_VER_16_0_33_0,
79};
80
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090081struct mixer_context {
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090082 struct device *dev;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090083 int pipe;
84 bool interlace;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090085 bool powered;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053086 bool vp_enabled;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090087 u32 int_en;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090088
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090089 struct mutex mixer_mutex;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090090 struct mixer_resources mixer_res;
Joonyoung Shima634dd52012-04-05 20:49:24 +090091 struct hdmi_win_data win_data[MIXER_WIN_NR];
Rahul Sharma1e123442012-10-04 20:48:51 +053092 enum mixer_version_id mxr_ver;
93};
94
95struct mixer_drv_data {
96 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053097 bool is_vp_enabled;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090098};
99
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900100static 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,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530251 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900252 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530253 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900254 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530255 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900256}
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
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530266 if (ctx->vp_enabled)
267 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900268 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:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530349 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 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900354 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;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900372 unsigned int x_ratio, y_ratio;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900373 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;
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900384 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900385 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
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900399 /* scaling feature: (src << 16) / dst */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900400 x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
401 y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900402
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
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900409 + (win_data->fb_width * win_data->fb_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900410 }
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 {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900418 luma_addr[1] = luma_addr[0] + win_data->fb_width;
419 chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900420 }
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 */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900440 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
441 VP_IMG_VSIZE(win_data->fb_height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900442 /* chroma height has to reduced by 2 to avoid chroma distorions */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900443 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
444 VP_IMG_VSIZE(win_data->fb_height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900445
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900446 vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
447 vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900448 vp_reg_write(res, VP_SRC_H_POSITION,
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900449 VP_SRC_H_POSITION_VAL(win_data->fb_x));
450 vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900451
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900452 vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
453 vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900454 if (ctx->interlace) {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900455 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);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900457 } else {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900458 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
459 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900460 }
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
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900473 mixer_cfg_scan(ctx, win_data->mode_height);
474 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900475 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;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900489 unsigned int x_ratio, y_ratio;
490 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900491 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
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900513 /* 2x scaling feature */
514 x_ratio = 0;
515 y_ratio = 0;
516
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900517 dst_x_offset = win_data->crtc_x;
518 dst_y_offset = win_data->crtc_y;
519
520 /* converting dma address base and source offset */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900521 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);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900524 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 */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900540 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900541
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900542 val = MXR_GRP_WH_WIDTH(win_data->crtc_width);
543 val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900544 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
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900561 mixer_cfg_scan(ctx, win_data->mode_height);
562 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900563 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
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900585static 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);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530611 if (ctx->vp_enabled)
612 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900613 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 */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900621 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
622 val |= MXR_GRP_CFG_WIN_BLEND_EN;
Seung-Woo Kim57366032012-05-15 17:22:08 +0900623 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
624 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900625 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);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900629 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
630
Seung-Woo Kim57366032012-05-15 17:22:08 +0900631 /* setting video layers */
632 val = MXR_GRP_CFG_ALPHA_VAL(0);
633 mixer_reg_write(res, MXR_VIDEO_CFG, val);
634
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530635 if (ctx->vp_enabled) {
636 /* configuration of Video Processor Registers */
637 vp_win_reset(ctx);
638 vp_default_filter(res);
639 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900640
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);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530644 if (ctx->vp_enabled)
645 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900646
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);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530668 if (ctx->vp_enabled) {
669 clk_enable(res->vp);
670 clk_enable(res->sclk_mixer);
671 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900672
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);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530691 if (ctx->vp_enabled) {
692 clk_disable(res->vp);
693 clk_disable(res->sclk_mixer);
694 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900695
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
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900705static 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
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900732static 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
Inki Dae3d058592012-08-17 18:35:47 +0900753static 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
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900765static 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)
Joonyoung Shima2ee1512012-04-05 20:49:25 +0900787 win = MIXER_DEFAULT_WIN;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900788
Joonyoung Shima634dd52012-04-05 20:49:24 +0900789 if (win < 0 || win > MIXER_WIN_NR) {
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900790 DRM_ERROR("mixer window[%d] is wrong\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900791 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;
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900812 win_data->src_width = overlay->src_width;
813 win_data->src_height = overlay->src_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900814
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
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900821static void mixer_win_commit(void *ctx, int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900822{
823 struct mixer_context *mixer_ctx = ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900824
825 DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
826
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530827 if (win > 1 && mixer_ctx->vp_enabled)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900828 vp_video_buffer(mixer_ctx, win);
829 else
830 mixer_graph_buffer(mixer_ctx, win);
831}
832
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900833static void mixer_win_disable(void *ctx, int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900834{
835 struct mixer_context *mixer_ctx = ctx;
836 struct mixer_resources *res = &mixer_ctx->mixer_res;
837 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900838
839 DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
840
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900841 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
Joonyoung Shim578b6062012-04-05 20:49:26 +0900850static struct exynos_mixer_ops mixer_ops = {
851 /* manager */
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900852 .enable_vblank = mixer_enable_vblank,
853 .disable_vblank = mixer_disable_vblank,
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900854 .dpms = mixer_dpms,
Joonyoung Shim578b6062012-04-05 20:49:26 +0900855
856 /* overlay */
Inki Dae3d058592012-08-17 18:35:47 +0900857 .wait_for_vblank = mixer_wait_for_vblank,
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900858 .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)
Inki Daec5614ae2012-02-15 11:25:20 +0900891 /*
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);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900897
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;
Joonyoung Shimf9309d12012-04-05 20:49:22 +0900904 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900905 struct mixer_resources *res = &ctx->mixer_res;
Seung-Woo Kim8379e482012-04-23 20:30:13 +0900906 u32 val, base, shadow;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900907
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) {
Seung-Woo Kim8379e482012-04-23 20:30:13 +0900917 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
918 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
919 if (base != shadow)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900920 goto out;
921
Seung-Woo Kim8379e482012-04-23 20:30:13 +0900922 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
923 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
924 if (base != shadow)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900925 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
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900946static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
947 struct platform_device *pdev)
948{
Joonyoung Shimf9309d12012-04-05 20:49:22 +0900949 struct mixer_context *mixer_ctx = ctx->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900950 struct device *dev = &pdev->dev;
951 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
952 struct resource *res;
953 int ret;
954
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900955 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 }
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530963
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900964 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 }
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530970 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900971 if (res == NULL) {
972 dev_err(dev, "get memory resource failed.\n");
973 ret = -ENXIO;
974 goto fail;
975 }
976
Sachin Kamat9416dfa2012-06-19 11:47:41 +0530977 mixer_res->mixer_regs = devm_ioremap(&pdev->dev, res->start,
978 resource_size(res));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900979 if (mixer_res->mixer_regs == NULL) {
980 dev_err(dev, "register mapping failed.\n");
981 ret = -ENXIO;
982 goto fail;
983 }
984
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530985 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900986 if (res == NULL) {
987 dev_err(dev, "get interrupt resource failed.\n");
988 ret = -ENXIO;
Sachin Kamat9416dfa2012-06-19 11:47:41 +0530989 goto fail;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900990 }
991
Sachin Kamat9416dfa2012-06-19 11:47:41 +0530992 ret = devm_request_irq(&pdev->dev, res->start, mixer_irq_handler,
993 0, "drm_mixer", ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900994 if (ret) {
995 dev_err(dev, "request interrupt failed.\n");
Sachin Kamat9416dfa2012-06-19 11:47:41 +0530996 goto fail;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900997 }
998 mixer_res->irq = res->start;
999
1000 return 0;
1001
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001002fail:
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001003 if (!IS_ERR_OR_NULL(mixer_res->sclk_hdmi))
1004 clk_put(mixer_res->sclk_hdmi);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001005 if (!IS_ERR_OR_NULL(mixer_res->mixer))
1006 clk_put(mixer_res->mixer);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001007 return ret;
1008}
1009
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301010static 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);
1042 if (res == NULL) {
1043 dev_err(dev, "get memory resource failed.\n");
1044 ret = -ENXIO;
1045 goto fail;
1046 }
1047
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;
1053 goto fail;
1054 }
1055
1056 return 0;
1057
1058fail:
1059 if (!IS_ERR_OR_NULL(mixer_res->sclk_dac))
1060 clk_put(mixer_res->sclk_dac);
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);
1065 return ret;
1066}
1067
Rahul Sharma1e123442012-10-04 20:48:51 +05301068static struct mixer_drv_data exynos4_mxr_drv_data = {
1069 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301070 .is_vp_enabled = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301071};
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
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001082static 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;
Rahul Sharma1e123442012-10-04 20:48:51 +05301087 struct mixer_drv_data *drv;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001088 int ret;
1089
1090 dev_info(dev, "probe start\n");
1091
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301092 drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
1093 GFP_KERNEL);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001094 if (!drm_hdmi_ctx) {
1095 DRM_ERROR("failed to allocate common hdmi context.\n");
1096 return -ENOMEM;
1097 }
1098
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301099 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001100 if (!ctx) {
1101 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001102 return -ENOMEM;
1103 }
1104
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001105 mutex_init(&ctx->mixer_mutex);
1106
Rahul Sharma1e123442012-10-04 20:48:51 +05301107 drv = (struct mixer_drv_data *)platform_get_device_id(
1108 pdev)->driver_data;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001109 ctx->dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001110 drm_hdmi_ctx->ctx = (void *)ctx;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301111 ctx->vp_enabled = drv->is_vp_enabled;
Rahul Sharma1e123442012-10-04 20:48:51 +05301112 ctx->mxr_ver = drv->version;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001113
1114 platform_set_drvdata(pdev, drm_hdmi_ctx);
1115
1116 /* acquire resources: regs, irqs, clocks */
1117 ret = mixer_resources_init(drm_hdmi_ctx, pdev);
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301118 if (ret) {
1119 DRM_ERROR("mixer_resources_init failed\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001120 goto fail;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301121 }
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 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001131
1132 /* register specific callback point to common hdmi. */
Joonyoung Shim578b6062012-04-05 20:49:26 +09001133 exynos_mixer_ops_register(&mixer_ops);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001134
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001135 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001136
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{
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301147 dev_info(&pdev->dev, "remove successful\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001148
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001149 pm_runtime_disable(&pdev->dev);
1150
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001151 return 0;
1152}
1153
Joonyoung Shimab27af82012-04-23 19:35:51 +09001154#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
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001168struct platform_driver mixer_driver = {
1169 .driver = {
1170 .name = "s5p-mixer",
1171 .owner = THIS_MODULE,
Joonyoung Shimab27af82012-04-23 19:35:51 +09001172 .pm = &mixer_pm_ops,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001173 },
1174 .probe = mixer_probe,
1175 .remove = __devexit_p(mixer_remove),
Rahul Sharma1e123442012-10-04 20:48:51 +05301176 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001177};