blob: 62255011c9d60208fe6875e4c43c7b7648abe30e [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>
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"
Rahul Sharma663d8762013-01-03 05:44:04 -050038#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090039#include "exynos_drm_hdmi.h"
Inki Dae1055b392012-10-19 17:37:35 +090040#include "exynos_drm_iommu.h"
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090041
Seung-Woo Kimd8408322011-12-21 17:39:39 +090042#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
43
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090044struct hdmi_win_data {
45 dma_addr_t dma_addr;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090046 dma_addr_t chroma_dma_addr;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090047 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;
Prathyush Kdb43fd12012-12-06 20:16:05 +053062 bool enabled;
63 bool resume;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090064};
65
66struct mixer_resources {
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090067 int irq;
68 void __iomem *mixer_regs;
69 void __iomem *vp_regs;
70 spinlock_t reg_slock;
71 struct clk *mixer;
72 struct clk *vp;
73 struct clk *sclk_mixer;
74 struct clk *sclk_hdmi;
75 struct clk *sclk_dac;
76};
77
Rahul Sharma1e123442012-10-04 20:48:51 +053078enum mixer_version_id {
79 MXR_VER_0_0_0_16,
80 MXR_VER_16_0_33_0,
81};
82
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090083struct mixer_context {
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090084 struct device *dev;
Inki Dae1055b392012-10-19 17:37:35 +090085 struct drm_device *drm_dev;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090086 int pipe;
87 bool interlace;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090088 bool powered;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053089 bool vp_enabled;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090090 u32 int_en;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090091
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090092 struct mutex mixer_mutex;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090093 struct mixer_resources mixer_res;
Joonyoung Shima634dd52012-04-05 20:49:24 +090094 struct hdmi_win_data win_data[MIXER_WIN_NR];
Rahul Sharma1e123442012-10-04 20:48:51 +053095 enum mixer_version_id mxr_ver;
Inki Dae1055b392012-10-19 17:37:35 +090096 void *parent_ctx;
Prathyush K6e95d5e2012-12-06 20:16:03 +053097 wait_queue_head_t wait_vsync_queue;
98 atomic_t wait_vsync_event;
Rahul Sharma1e123442012-10-04 20:48:51 +053099};
100
101struct mixer_drv_data {
102 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530103 bool is_vp_enabled;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900104};
105
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900106static const u8 filter_y_horiz_tap8[] = {
107 0, -1, -1, -1, -1, -1, -1, -1,
108 -1, -1, -1, -1, -1, 0, 0, 0,
109 0, 2, 4, 5, 6, 6, 6, 6,
110 6, 5, 5, 4, 3, 2, 1, 1,
111 0, -6, -12, -16, -18, -20, -21, -20,
112 -20, -18, -16, -13, -10, -8, -5, -2,
113 127, 126, 125, 121, 114, 107, 99, 89,
114 79, 68, 57, 46, 35, 25, 16, 8,
115};
116
117static const u8 filter_y_vert_tap4[] = {
118 0, -3, -6, -8, -8, -8, -8, -7,
119 -6, -5, -4, -3, -2, -1, -1, 0,
120 127, 126, 124, 118, 111, 102, 92, 81,
121 70, 59, 48, 37, 27, 19, 11, 5,
122 0, 5, 11, 19, 27, 37, 48, 59,
123 70, 81, 92, 102, 111, 118, 124, 126,
124 0, 0, -1, -1, -2, -3, -4, -5,
125 -6, -7, -8, -8, -8, -8, -6, -3,
126};
127
128static const u8 filter_cr_horiz_tap4[] = {
129 0, -3, -6, -8, -8, -8, -8, -7,
130 -6, -5, -4, -3, -2, -1, -1, 0,
131 127, 126, 124, 118, 111, 102, 92, 81,
132 70, 59, 48, 37, 27, 19, 11, 5,
133};
134
135static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
136{
137 return readl(res->vp_regs + reg_id);
138}
139
140static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
141 u32 val)
142{
143 writel(val, res->vp_regs + reg_id);
144}
145
146static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
147 u32 val, u32 mask)
148{
149 u32 old = vp_reg_read(res, reg_id);
150
151 val = (val & mask) | (old & ~mask);
152 writel(val, res->vp_regs + reg_id);
153}
154
155static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
156{
157 return readl(res->mixer_regs + reg_id);
158}
159
160static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
161 u32 val)
162{
163 writel(val, res->mixer_regs + reg_id);
164}
165
166static inline void mixer_reg_writemask(struct mixer_resources *res,
167 u32 reg_id, u32 val, u32 mask)
168{
169 u32 old = mixer_reg_read(res, reg_id);
170
171 val = (val & mask) | (old & ~mask);
172 writel(val, res->mixer_regs + reg_id);
173}
174
175static void mixer_regs_dump(struct mixer_context *ctx)
176{
177#define DUMPREG(reg_id) \
178do { \
179 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
180 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
181} while (0)
182
183 DUMPREG(MXR_STATUS);
184 DUMPREG(MXR_CFG);
185 DUMPREG(MXR_INT_EN);
186 DUMPREG(MXR_INT_STATUS);
187
188 DUMPREG(MXR_LAYER_CFG);
189 DUMPREG(MXR_VIDEO_CFG);
190
191 DUMPREG(MXR_GRAPHIC0_CFG);
192 DUMPREG(MXR_GRAPHIC0_BASE);
193 DUMPREG(MXR_GRAPHIC0_SPAN);
194 DUMPREG(MXR_GRAPHIC0_WH);
195 DUMPREG(MXR_GRAPHIC0_SXY);
196 DUMPREG(MXR_GRAPHIC0_DXY);
197
198 DUMPREG(MXR_GRAPHIC1_CFG);
199 DUMPREG(MXR_GRAPHIC1_BASE);
200 DUMPREG(MXR_GRAPHIC1_SPAN);
201 DUMPREG(MXR_GRAPHIC1_WH);
202 DUMPREG(MXR_GRAPHIC1_SXY);
203 DUMPREG(MXR_GRAPHIC1_DXY);
204#undef DUMPREG
205}
206
207static void vp_regs_dump(struct mixer_context *ctx)
208{
209#define DUMPREG(reg_id) \
210do { \
211 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
212 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
213} while (0)
214
215 DUMPREG(VP_ENABLE);
216 DUMPREG(VP_SRESET);
217 DUMPREG(VP_SHADOW_UPDATE);
218 DUMPREG(VP_FIELD_ID);
219 DUMPREG(VP_MODE);
220 DUMPREG(VP_IMG_SIZE_Y);
221 DUMPREG(VP_IMG_SIZE_C);
222 DUMPREG(VP_PER_RATE_CTRL);
223 DUMPREG(VP_TOP_Y_PTR);
224 DUMPREG(VP_BOT_Y_PTR);
225 DUMPREG(VP_TOP_C_PTR);
226 DUMPREG(VP_BOT_C_PTR);
227 DUMPREG(VP_ENDIAN_MODE);
228 DUMPREG(VP_SRC_H_POSITION);
229 DUMPREG(VP_SRC_V_POSITION);
230 DUMPREG(VP_SRC_WIDTH);
231 DUMPREG(VP_SRC_HEIGHT);
232 DUMPREG(VP_DST_H_POSITION);
233 DUMPREG(VP_DST_V_POSITION);
234 DUMPREG(VP_DST_WIDTH);
235 DUMPREG(VP_DST_HEIGHT);
236 DUMPREG(VP_H_RATIO);
237 DUMPREG(VP_V_RATIO);
238
239#undef DUMPREG
240}
241
242static inline void vp_filter_set(struct mixer_resources *res,
243 int reg_id, const u8 *data, unsigned int size)
244{
245 /* assure 4-byte align */
246 BUG_ON(size & 3);
247 for (; size; size -= 4, reg_id += 4, data += 4) {
248 u32 val = (data[0] << 24) | (data[1] << 16) |
249 (data[2] << 8) | data[3];
250 vp_reg_write(res, reg_id, val);
251 }
252}
253
254static void vp_default_filter(struct mixer_resources *res)
255{
256 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530257 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900258 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530259 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900260 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530261 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900262}
263
264static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
265{
266 struct mixer_resources *res = &ctx->mixer_res;
267
268 /* block update on vsync */
269 mixer_reg_writemask(res, MXR_STATUS, enable ?
270 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
271
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530272 if (ctx->vp_enabled)
273 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900274 VP_SHADOW_UPDATE_ENABLE : 0);
275}
276
277static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
278{
279 struct mixer_resources *res = &ctx->mixer_res;
280 u32 val;
281
282 /* choosing between interlace and progressive mode */
283 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
284 MXR_CFG_SCAN_PROGRASSIVE);
285
286 /* choosing between porper HD and SD mode */
Rahul Sharma29630742013-01-15 08:11:07 -0500287 if (height <= 480)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900288 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
Rahul Sharma29630742013-01-15 08:11:07 -0500289 else if (height <= 576)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900290 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
Rahul Sharma29630742013-01-15 08:11:07 -0500291 else if (height <= 720)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900292 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
Rahul Sharma29630742013-01-15 08:11:07 -0500293 else if (height <= 1080)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900294 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
295 else
296 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
297
298 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
299}
300
301static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
302{
303 struct mixer_resources *res = &ctx->mixer_res;
304 u32 val;
305
306 if (height == 480) {
307 val = MXR_CFG_RGB601_0_255;
308 } else if (height == 576) {
309 val = MXR_CFG_RGB601_0_255;
310 } else if (height == 720) {
311 val = MXR_CFG_RGB709_16_235;
312 mixer_reg_write(res, MXR_CM_COEFF_Y,
313 (1 << 30) | (94 << 20) | (314 << 10) |
314 (32 << 0));
315 mixer_reg_write(res, MXR_CM_COEFF_CB,
316 (972 << 20) | (851 << 10) | (225 << 0));
317 mixer_reg_write(res, MXR_CM_COEFF_CR,
318 (225 << 20) | (820 << 10) | (1004 << 0));
319 } else if (height == 1080) {
320 val = MXR_CFG_RGB709_16_235;
321 mixer_reg_write(res, MXR_CM_COEFF_Y,
322 (1 << 30) | (94 << 20) | (314 << 10) |
323 (32 << 0));
324 mixer_reg_write(res, MXR_CM_COEFF_CB,
325 (972 << 20) | (851 << 10) | (225 << 0));
326 mixer_reg_write(res, MXR_CM_COEFF_CR,
327 (225 << 20) | (820 << 10) | (1004 << 0));
328 } else {
329 val = MXR_CFG_RGB709_16_235;
330 mixer_reg_write(res, MXR_CM_COEFF_Y,
331 (1 << 30) | (94 << 20) | (314 << 10) |
332 (32 << 0));
333 mixer_reg_write(res, MXR_CM_COEFF_CB,
334 (972 << 20) | (851 << 10) | (225 << 0));
335 mixer_reg_write(res, MXR_CM_COEFF_CR,
336 (225 << 20) | (820 << 10) | (1004 << 0));
337 }
338
339 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
340}
341
342static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
343{
344 struct mixer_resources *res = &ctx->mixer_res;
345 u32 val = enable ? ~0 : 0;
346
347 switch (win) {
348 case 0:
349 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
350 break;
351 case 1:
352 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
353 break;
354 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530355 if (ctx->vp_enabled) {
356 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
357 mixer_reg_writemask(res, MXR_CFG, val,
358 MXR_CFG_VP_ENABLE);
359 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900360 break;
361 }
362}
363
364static void mixer_run(struct mixer_context *ctx)
365{
366 struct mixer_resources *res = &ctx->mixer_res;
367
368 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
369
370 mixer_regs_dump(ctx);
371}
372
373static void vp_video_buffer(struct mixer_context *ctx, int win)
374{
375 struct mixer_resources *res = &ctx->mixer_res;
376 unsigned long flags;
377 struct hdmi_win_data *win_data;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900378 unsigned int x_ratio, y_ratio;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900379 unsigned int buf_num;
380 dma_addr_t luma_addr[2], chroma_addr[2];
381 bool tiled_mode = false;
382 bool crcb_mode = false;
383 u32 val;
384
385 win_data = &ctx->win_data[win];
386
387 switch (win_data->pixel_format) {
388 case DRM_FORMAT_NV12MT:
389 tiled_mode = true;
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900390 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900391 crcb_mode = false;
392 buf_num = 2;
393 break;
394 /* TODO: single buffer format NV12, NV21 */
395 default:
396 /* ignore pixel format at disable time */
397 if (!win_data->dma_addr)
398 break;
399
400 DRM_ERROR("pixel format for vp is wrong [%d].\n",
401 win_data->pixel_format);
402 return;
403 }
404
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900405 /* scaling feature: (src << 16) / dst */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900406 x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
407 y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900408
409 if (buf_num == 2) {
410 luma_addr[0] = win_data->dma_addr;
411 chroma_addr[0] = win_data->chroma_dma_addr;
412 } else {
413 luma_addr[0] = win_data->dma_addr;
414 chroma_addr[0] = win_data->dma_addr
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900415 + (win_data->fb_width * win_data->fb_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900416 }
417
418 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
419 ctx->interlace = true;
420 if (tiled_mode) {
421 luma_addr[1] = luma_addr[0] + 0x40;
422 chroma_addr[1] = chroma_addr[0] + 0x40;
423 } else {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900424 luma_addr[1] = luma_addr[0] + win_data->fb_width;
425 chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900426 }
427 } else {
428 ctx->interlace = false;
429 luma_addr[1] = 0;
430 chroma_addr[1] = 0;
431 }
432
433 spin_lock_irqsave(&res->reg_slock, flags);
434 mixer_vsync_set_update(ctx, false);
435
436 /* interlace or progressive scan mode */
437 val = (ctx->interlace ? ~0 : 0);
438 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
439
440 /* setup format */
441 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
442 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
443 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
444
445 /* setting size of input image */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900446 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
447 VP_IMG_VSIZE(win_data->fb_height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900448 /* chroma height has to reduced by 2 to avoid chroma distorions */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900449 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
450 VP_IMG_VSIZE(win_data->fb_height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900451
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900452 vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
453 vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900454 vp_reg_write(res, VP_SRC_H_POSITION,
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900455 VP_SRC_H_POSITION_VAL(win_data->fb_x));
456 vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900457
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900458 vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
459 vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900460 if (ctx->interlace) {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900461 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2);
462 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900463 } else {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900464 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
465 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900466 }
467
468 vp_reg_write(res, VP_H_RATIO, x_ratio);
469 vp_reg_write(res, VP_V_RATIO, y_ratio);
470
471 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
472
473 /* set buffer address to vp */
474 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
475 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
476 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
477 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
478
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900479 mixer_cfg_scan(ctx, win_data->mode_height);
480 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900481 mixer_cfg_layer(ctx, win, true);
482 mixer_run(ctx);
483
484 mixer_vsync_set_update(ctx, true);
485 spin_unlock_irqrestore(&res->reg_slock, flags);
486
487 vp_regs_dump(ctx);
488}
489
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530490static void mixer_layer_update(struct mixer_context *ctx)
491{
492 struct mixer_resources *res = &ctx->mixer_res;
493 u32 val;
494
495 val = mixer_reg_read(res, MXR_CFG);
496
497 /* allow one update per vsync only */
498 if (!(val & MXR_CFG_LAYER_UPDATE_COUNT_MASK))
499 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
500}
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;
506 struct hdmi_win_data *win_data;
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
513 win_data = &ctx->win_data[win];
514
515 #define RGB565 4
516 #define ARGB1555 5
517 #define ARGB4444 6
518 #define ARGB8888 7
519
520 switch (win_data->bpp) {
521 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
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900535 dst_x_offset = win_data->crtc_x;
536 dst_y_offset = win_data->crtc_y;
537
538 /* converting dma address base and source offset */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900539 dma_addr = win_data->dma_addr
540 + (win_data->fb_x * win_data->bpp >> 3)
541 + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900542 src_x_offset = 0;
543 src_y_offset = 0;
544
545 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
546 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 */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900558 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900559
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900560 val = MXR_GRP_WH_WIDTH(win_data->crtc_width);
561 val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900562 val |= MXR_GRP_WH_H_SCALE(x_ratio);
563 val |= MXR_GRP_WH_V_SCALE(y_ratio);
564 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
565
566 /* setup offsets in source image */
567 val = MXR_GRP_SXY_SX(src_x_offset);
568 val |= MXR_GRP_SXY_SY(src_y_offset);
569 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
570
571 /* setup offsets in display image */
572 val = MXR_GRP_DXY_DX(dst_x_offset);
573 val |= MXR_GRP_DXY_DY(dst_y_offset);
574 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
575
576 /* set buffer address to mixer */
577 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
578
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900579 mixer_cfg_scan(ctx, win_data->mode_height);
580 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900581 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530582
583 /* layer update mandatory for mixer 16.0.33.0 */
584 if (ctx->mxr_ver == MXR_VER_16_0_33_0)
585 mixer_layer_update(ctx);
586
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900587 mixer_run(ctx);
588
589 mixer_vsync_set_update(ctx, true);
590 spin_unlock_irqrestore(&res->reg_slock, flags);
591}
592
593static void vp_win_reset(struct mixer_context *ctx)
594{
595 struct mixer_resources *res = &ctx->mixer_res;
596 int tries = 100;
597
598 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
599 for (tries = 100; tries; --tries) {
600 /* waiting until VP_SRESET_PROCESSING is 0 */
601 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
602 break;
Sean Paul09760ea2013-01-14 17:03:20 -0500603 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900604 }
605 WARN(tries == 0, "failed to reset Video Processor\n");
606}
607
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900608static void mixer_win_reset(struct mixer_context *ctx)
609{
610 struct mixer_resources *res = &ctx->mixer_res;
611 unsigned long flags;
612 u32 val; /* value stored to register */
613
614 spin_lock_irqsave(&res->reg_slock, flags);
615 mixer_vsync_set_update(ctx, false);
616
617 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
618
619 /* set output in RGB888 mode */
620 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
621
622 /* 16 beat burst in DMA */
623 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
624 MXR_STATUS_BURST_MASK);
625
626 /* setting default layer priority: layer1 > layer0 > video
627 * because typical usage scenario would be
628 * layer1 - OSD
629 * layer0 - framebuffer
630 * video - video overlay
631 */
632 val = MXR_LAYER_CFG_GRP1_VAL(3);
633 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530634 if (ctx->vp_enabled)
635 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900636 mixer_reg_write(res, MXR_LAYER_CFG, val);
637
638 /* setting background color */
639 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
640 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
641 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
642
643 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900644 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
645 val |= MXR_GRP_CFG_WIN_BLEND_EN;
646 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
647
Sean Paul0377f4e2013-04-25 15:13:26 -0400648 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900649 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400650
651 /* Blend layer 1 into layer 0 */
652 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
653 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900654 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
655
Seung-Woo Kim57366032012-05-15 17:22:08 +0900656 /* setting video layers */
657 val = MXR_GRP_CFG_ALPHA_VAL(0);
658 mixer_reg_write(res, MXR_VIDEO_CFG, val);
659
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530660 if (ctx->vp_enabled) {
661 /* configuration of Video Processor Registers */
662 vp_win_reset(ctx);
663 vp_default_filter(res);
664 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900665
666 /* disable all layers */
667 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
668 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530669 if (ctx->vp_enabled)
670 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900671
672 mixer_vsync_set_update(ctx, true);
673 spin_unlock_irqrestore(&res->reg_slock, flags);
674}
675
Inki Dae1055b392012-10-19 17:37:35 +0900676static int mixer_iommu_on(void *ctx, bool enable)
677{
678 struct exynos_drm_hdmi_context *drm_hdmi_ctx;
679 struct mixer_context *mdata = ctx;
680 struct drm_device *drm_dev;
681
682 drm_hdmi_ctx = mdata->parent_ctx;
683 drm_dev = drm_hdmi_ctx->drm_dev;
684
685 if (is_drm_iommu_supported(drm_dev)) {
686 if (enable)
687 return drm_iommu_attach_device(drm_dev, mdata->dev);
688
689 drm_iommu_detach_device(drm_dev, mdata->dev);
690 }
691 return 0;
692}
693
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900694static int mixer_enable_vblank(void *ctx, int pipe)
695{
696 struct mixer_context *mixer_ctx = ctx;
697 struct mixer_resources *res = &mixer_ctx->mixer_res;
698
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900699 mixer_ctx->pipe = pipe;
700
701 /* enable vsync interrupt */
702 mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
703 MXR_INT_EN_VSYNC);
704
705 return 0;
706}
707
708static void mixer_disable_vblank(void *ctx)
709{
710 struct mixer_context *mixer_ctx = ctx;
711 struct mixer_resources *res = &mixer_ctx->mixer_res;
712
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900713 /* disable vsync interrupt */
714 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
715}
716
717static void mixer_win_mode_set(void *ctx,
718 struct exynos_drm_overlay *overlay)
719{
720 struct mixer_context *mixer_ctx = ctx;
721 struct hdmi_win_data *win_data;
722 int win;
723
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900724 if (!overlay) {
725 DRM_ERROR("overlay is NULL\n");
726 return;
727 }
728
729 DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
730 overlay->fb_width, overlay->fb_height,
731 overlay->fb_x, overlay->fb_y,
732 overlay->crtc_width, overlay->crtc_height,
733 overlay->crtc_x, overlay->crtc_y);
734
735 win = overlay->zpos;
736 if (win == DEFAULT_ZPOS)
Joonyoung Shima2ee1512012-04-05 20:49:25 +0900737 win = MIXER_DEFAULT_WIN;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900738
Krzysztof Kozlowski1586d802013-05-27 15:00:43 +0900739 if (win < 0 || win >= MIXER_WIN_NR) {
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900740 DRM_ERROR("mixer window[%d] is wrong\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900741 return;
742 }
743
744 win_data = &mixer_ctx->win_data[win];
745
746 win_data->dma_addr = overlay->dma_addr[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900747 win_data->chroma_dma_addr = overlay->dma_addr[1];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900748 win_data->pixel_format = overlay->pixel_format;
749 win_data->bpp = overlay->bpp;
750
751 win_data->crtc_x = overlay->crtc_x;
752 win_data->crtc_y = overlay->crtc_y;
753 win_data->crtc_width = overlay->crtc_width;
754 win_data->crtc_height = overlay->crtc_height;
755
756 win_data->fb_x = overlay->fb_x;
757 win_data->fb_y = overlay->fb_y;
758 win_data->fb_width = overlay->fb_width;
759 win_data->fb_height = overlay->fb_height;
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900760 win_data->src_width = overlay->src_width;
761 win_data->src_height = overlay->src_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900762
763 win_data->mode_width = overlay->mode_width;
764 win_data->mode_height = overlay->mode_height;
765
766 win_data->scan_flags = overlay->scan_flag;
767}
768
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900769static void mixer_win_commit(void *ctx, int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900770{
771 struct mixer_context *mixer_ctx = ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900772
YoungJun Chocbc4c332013-06-12 10:44:40 +0900773 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900774
Shirish Sdda90122013-01-23 22:03:18 -0500775 mutex_lock(&mixer_ctx->mixer_mutex);
776 if (!mixer_ctx->powered) {
777 mutex_unlock(&mixer_ctx->mixer_mutex);
778 return;
779 }
780 mutex_unlock(&mixer_ctx->mixer_mutex);
781
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530782 if (win > 1 && mixer_ctx->vp_enabled)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900783 vp_video_buffer(mixer_ctx, win);
784 else
785 mixer_graph_buffer(mixer_ctx, win);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530786
787 mixer_ctx->win_data[win].enabled = true;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900788}
789
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900790static void mixer_win_disable(void *ctx, int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900791{
792 struct mixer_context *mixer_ctx = ctx;
793 struct mixer_resources *res = &mixer_ctx->mixer_res;
794 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900795
YoungJun Chocbc4c332013-06-12 10:44:40 +0900796 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900797
Prathyush Kdb43fd12012-12-06 20:16:05 +0530798 mutex_lock(&mixer_ctx->mixer_mutex);
799 if (!mixer_ctx->powered) {
800 mutex_unlock(&mixer_ctx->mixer_mutex);
801 mixer_ctx->win_data[win].resume = false;
802 return;
803 }
804 mutex_unlock(&mixer_ctx->mixer_mutex);
805
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900806 spin_lock_irqsave(&res->reg_slock, flags);
807 mixer_vsync_set_update(mixer_ctx, false);
808
809 mixer_cfg_layer(mixer_ctx, win, false);
810
811 mixer_vsync_set_update(mixer_ctx, true);
812 spin_unlock_irqrestore(&res->reg_slock, flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530813
814 mixer_ctx->win_data[win].enabled = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900815}
816
Rahul Sharma16844fb2013-06-10 14:50:00 +0530817static int mixer_check_mode(void *ctx, struct drm_display_mode *mode)
Rahul Sharma0ea68222013-01-15 08:11:06 -0500818{
Rahul Sharma0ea68222013-01-15 08:11:06 -0500819 u32 w, h;
820
Rahul Sharma16844fb2013-06-10 14:50:00 +0530821 w = mode->hdisplay;
822 h = mode->vdisplay;
Rahul Sharma0ea68222013-01-15 08:11:06 -0500823
Rahul Sharma16844fb2013-06-10 14:50:00 +0530824 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
825 mode->hdisplay, mode->vdisplay, mode->vrefresh,
826 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
Rahul Sharma0ea68222013-01-15 08:11:06 -0500827
Rahul Sharma0ea68222013-01-15 08:11:06 -0500828 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
829 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
830 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
831 return 0;
832
833 return -EINVAL;
834}
Prathyush K8137a2e2012-12-06 20:16:01 +0530835static void mixer_wait_for_vblank(void *ctx)
836{
837 struct mixer_context *mixer_ctx = ctx;
Prathyush K8137a2e2012-12-06 20:16:01 +0530838
Prathyush K6e95d5e2012-12-06 20:16:03 +0530839 mutex_lock(&mixer_ctx->mixer_mutex);
840 if (!mixer_ctx->powered) {
841 mutex_unlock(&mixer_ctx->mixer_mutex);
842 return;
843 }
844 mutex_unlock(&mixer_ctx->mixer_mutex);
845
846 atomic_set(&mixer_ctx->wait_vsync_event, 1);
847
848 /*
849 * wait for MIXER to signal VSYNC interrupt or return after
850 * timeout which is set to 50ms (refresh rate of 20).
851 */
852 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
853 !atomic_read(&mixer_ctx->wait_vsync_event),
854 DRM_HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +0530855 DRM_DEBUG_KMS("vblank wait timed out.\n");
856}
857
Prathyush Kdb43fd12012-12-06 20:16:05 +0530858static void mixer_window_suspend(struct mixer_context *ctx)
859{
860 struct hdmi_win_data *win_data;
861 int i;
862
863 for (i = 0; i < MIXER_WIN_NR; i++) {
864 win_data = &ctx->win_data[i];
865 win_data->resume = win_data->enabled;
866 mixer_win_disable(ctx, i);
867 }
868 mixer_wait_for_vblank(ctx);
869}
870
871static void mixer_window_resume(struct mixer_context *ctx)
872{
873 struct hdmi_win_data *win_data;
874 int i;
875
876 for (i = 0; i < MIXER_WIN_NR; i++) {
877 win_data = &ctx->win_data[i];
878 win_data->enabled = win_data->resume;
879 win_data->resume = false;
880 }
881}
882
883static void mixer_poweron(struct mixer_context *ctx)
884{
885 struct mixer_resources *res = &ctx->mixer_res;
886
Prathyush Kdb43fd12012-12-06 20:16:05 +0530887 mutex_lock(&ctx->mixer_mutex);
888 if (ctx->powered) {
889 mutex_unlock(&ctx->mixer_mutex);
890 return;
891 }
892 ctx->powered = true;
893 mutex_unlock(&ctx->mixer_mutex);
894
Sean Paul0bfb1f82013-06-11 12:24:02 +0530895 clk_prepare_enable(res->mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530896 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +0530897 clk_prepare_enable(res->vp);
898 clk_prepare_enable(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530899 }
900
901 mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
902 mixer_win_reset(ctx);
903
904 mixer_window_resume(ctx);
905}
906
907static void mixer_poweroff(struct mixer_context *ctx)
908{
909 struct mixer_resources *res = &ctx->mixer_res;
910
Prathyush Kdb43fd12012-12-06 20:16:05 +0530911 mutex_lock(&ctx->mixer_mutex);
912 if (!ctx->powered)
913 goto out;
914 mutex_unlock(&ctx->mixer_mutex);
915
916 mixer_window_suspend(ctx);
917
918 ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
919
Sean Paul0bfb1f82013-06-11 12:24:02 +0530920 clk_disable_unprepare(res->mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530921 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +0530922 clk_disable_unprepare(res->vp);
923 clk_disable_unprepare(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530924 }
925
Prathyush Kdb43fd12012-12-06 20:16:05 +0530926 mutex_lock(&ctx->mixer_mutex);
927 ctx->powered = false;
928
929out:
930 mutex_unlock(&ctx->mixer_mutex);
931}
932
933static void mixer_dpms(void *ctx, int mode)
934{
935 struct mixer_context *mixer_ctx = ctx;
936
Prathyush Kdb43fd12012-12-06 20:16:05 +0530937 switch (mode) {
938 case DRM_MODE_DPMS_ON:
Rahul Sharma000f1302012-11-28 11:30:24 +0530939 if (pm_runtime_suspended(mixer_ctx->dev))
940 pm_runtime_get_sync(mixer_ctx->dev);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530941 break;
942 case DRM_MODE_DPMS_STANDBY:
943 case DRM_MODE_DPMS_SUSPEND:
944 case DRM_MODE_DPMS_OFF:
Rahul Sharma000f1302012-11-28 11:30:24 +0530945 if (!pm_runtime_suspended(mixer_ctx->dev))
946 pm_runtime_put_sync(mixer_ctx->dev);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530947 break;
948 default:
949 DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
950 break;
951 }
952}
953
Joonyoung Shim578b6062012-04-05 20:49:26 +0900954static struct exynos_mixer_ops mixer_ops = {
955 /* manager */
Inki Dae1055b392012-10-19 17:37:35 +0900956 .iommu_on = mixer_iommu_on,
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900957 .enable_vblank = mixer_enable_vblank,
958 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +0530959 .wait_for_vblank = mixer_wait_for_vblank,
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900960 .dpms = mixer_dpms,
Joonyoung Shim578b6062012-04-05 20:49:26 +0900961
962 /* overlay */
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900963 .win_mode_set = mixer_win_mode_set,
964 .win_commit = mixer_win_commit,
965 .win_disable = mixer_win_disable,
Rahul Sharma0ea68222013-01-15 08:11:06 -0500966
967 /* display */
Rahul Sharma16844fb2013-06-10 14:50:00 +0530968 .check_mode = mixer_check_mode,
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900969};
970
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900971static irqreturn_t mixer_irq_handler(int irq, void *arg)
972{
973 struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg;
Joonyoung Shimf9309d12012-04-05 20:49:22 +0900974 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900975 struct mixer_resources *res = &ctx->mixer_res;
Seung-Woo Kim8379e482012-04-23 20:30:13 +0900976 u32 val, base, shadow;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900977
978 spin_lock(&res->reg_slock);
979
980 /* read interrupt status for handling and clearing flags for VSYNC */
981 val = mixer_reg_read(res, MXR_INT_STATUS);
982
983 /* handling VSYNC */
984 if (val & MXR_INT_STATUS_VSYNC) {
985 /* interlace scan need to check shadow register */
986 if (ctx->interlace) {
Seung-Woo Kim8379e482012-04-23 20:30:13 +0900987 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
988 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
989 if (base != shadow)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900990 goto out;
991
Seung-Woo Kim8379e482012-04-23 20:30:13 +0900992 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
993 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
994 if (base != shadow)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900995 goto out;
996 }
997
998 drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe);
Rahul Sharma663d8762013-01-03 05:44:04 -0500999 exynos_drm_crtc_finish_pageflip(drm_hdmi_ctx->drm_dev,
1000 ctx->pipe);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301001
1002 /* set wait vsync event to zero and wake up queue. */
1003 if (atomic_read(&ctx->wait_vsync_event)) {
1004 atomic_set(&ctx->wait_vsync_event, 0);
1005 DRM_WAKEUP(&ctx->wait_vsync_queue);
1006 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001007 }
1008
1009out:
1010 /* clear interrupts */
1011 if (~val & MXR_INT_EN_VSYNC) {
1012 /* vsync interrupt use different bit for read and clear */
1013 val &= ~MXR_INT_EN_VSYNC;
1014 val |= MXR_INT_CLEAR_VSYNC;
1015 }
1016 mixer_reg_write(res, MXR_INT_STATUS, val);
1017
1018 spin_unlock(&res->reg_slock);
1019
1020 return IRQ_HANDLED;
1021}
1022
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001023static int mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
1024 struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001025{
Joonyoung Shimf9309d12012-04-05 20:49:22 +09001026 struct mixer_context *mixer_ctx = ctx->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001027 struct device *dev = &pdev->dev;
1028 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
1029 struct resource *res;
1030 int ret;
1031
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001032 spin_lock_init(&mixer_res->reg_slock);
1033
Sachin Kamat37f50862012-11-23 14:13:26 +05301034 mixer_res->mixer = devm_clk_get(dev, "mixer");
Sachin Kamatc11182d2013-03-21 15:33:58 +05301035 if (IS_ERR(mixer_res->mixer)) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001036 dev_err(dev, "failed to get clock 'mixer'\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301037 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001038 }
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301039
Sachin Kamat37f50862012-11-23 14:13:26 +05301040 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
Sachin Kamatc11182d2013-03-21 15:33:58 +05301041 if (IS_ERR(mixer_res->sclk_hdmi)) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001042 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301043 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001044 }
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301045 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001046 if (res == NULL) {
1047 dev_err(dev, "get memory resource failed.\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301048 return -ENXIO;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001049 }
1050
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001051 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301052 resource_size(res));
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001053 if (mixer_res->mixer_regs == NULL) {
1054 dev_err(dev, "register mapping failed.\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301055 return -ENXIO;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001056 }
1057
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301058 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001059 if (res == NULL) {
1060 dev_err(dev, "get interrupt resource failed.\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301061 return -ENXIO;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001062 }
1063
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001064 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301065 0, "drm_mixer", ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001066 if (ret) {
1067 dev_err(dev, "request interrupt failed.\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301068 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001069 }
1070 mixer_res->irq = res->start;
1071
1072 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001073}
1074
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001075static int vp_resources_init(struct exynos_drm_hdmi_context *ctx,
1076 struct platform_device *pdev)
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301077{
1078 struct mixer_context *mixer_ctx = ctx->ctx;
1079 struct device *dev = &pdev->dev;
1080 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
1081 struct resource *res;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301082
Sachin Kamat37f50862012-11-23 14:13:26 +05301083 mixer_res->vp = devm_clk_get(dev, "vp");
Sachin Kamatc11182d2013-03-21 15:33:58 +05301084 if (IS_ERR(mixer_res->vp)) {
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301085 dev_err(dev, "failed to get clock 'vp'\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301086 return -ENODEV;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301087 }
Sachin Kamat37f50862012-11-23 14:13:26 +05301088 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
Sachin Kamatc11182d2013-03-21 15:33:58 +05301089 if (IS_ERR(mixer_res->sclk_mixer)) {
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301090 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301091 return -ENODEV;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301092 }
Sachin Kamat37f50862012-11-23 14:13:26 +05301093 mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
Sachin Kamatc11182d2013-03-21 15:33:58 +05301094 if (IS_ERR(mixer_res->sclk_dac)) {
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301095 dev_err(dev, "failed to get clock 'sclk_dac'\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301096 return -ENODEV;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301097 }
1098
1099 if (mixer_res->sclk_hdmi)
1100 clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
1101
1102 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
1103 if (res == NULL) {
1104 dev_err(dev, "get memory resource failed.\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301105 return -ENXIO;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301106 }
1107
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001108 mixer_res->vp_regs = devm_ioremap(dev, res->start,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301109 resource_size(res));
1110 if (mixer_res->vp_regs == NULL) {
1111 dev_err(dev, "register mapping failed.\n");
Sachin Kamat37f50862012-11-23 14:13:26 +05301112 return -ENXIO;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301113 }
1114
1115 return 0;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301116}
1117
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301118static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301119 .version = MXR_VER_16_0_33_0,
1120 .is_vp_enabled = 0,
1121};
1122
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301123static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301124 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301125 .is_vp_enabled = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301126};
1127
1128static struct platform_device_id mixer_driver_types[] = {
1129 {
1130 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301131 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301132 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301133 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301134 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301135 }, {
1136 /* end node */
1137 }
1138};
1139
1140static struct of_device_id mixer_match_types[] = {
1141 {
1142 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301143 .data = &exynos5250_mxr_drv_data,
1144 }, {
1145 .compatible = "samsung,exynos5250-mixer",
1146 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301147 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301148 /* end node */
1149 }
1150};
1151
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001152static int mixer_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001153{
1154 struct device *dev = &pdev->dev;
1155 struct exynos_drm_hdmi_context *drm_hdmi_ctx;
1156 struct mixer_context *ctx;
Rahul Sharma1e123442012-10-04 20:48:51 +05301157 struct mixer_drv_data *drv;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001158 int ret;
1159
1160 dev_info(dev, "probe start\n");
1161
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001162 drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301163 GFP_KERNEL);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001164 if (!drm_hdmi_ctx) {
1165 DRM_ERROR("failed to allocate common hdmi context.\n");
1166 return -ENOMEM;
1167 }
1168
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001169 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001170 if (!ctx) {
1171 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001172 return -ENOMEM;
1173 }
1174
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001175 mutex_init(&ctx->mixer_mutex);
1176
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301177 if (dev->of_node) {
1178 const struct of_device_id *match;
Sachin Kamate436b092013-06-05 16:00:23 +09001179 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301180 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301181 } else {
1182 drv = (struct mixer_drv_data *)
1183 platform_get_device_id(pdev)->driver_data;
1184 }
1185
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001186 ctx->dev = dev;
Inki Dae1055b392012-10-19 17:37:35 +09001187 ctx->parent_ctx = (void *)drm_hdmi_ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001188 drm_hdmi_ctx->ctx = (void *)ctx;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301189 ctx->vp_enabled = drv->is_vp_enabled;
Rahul Sharma1e123442012-10-04 20:48:51 +05301190 ctx->mxr_ver = drv->version;
Prathyush K6e95d5e2012-12-06 20:16:03 +05301191 DRM_INIT_WAITQUEUE(&ctx->wait_vsync_queue);
1192 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001193
1194 platform_set_drvdata(pdev, drm_hdmi_ctx);
1195
1196 /* acquire resources: regs, irqs, clocks */
1197 ret = mixer_resources_init(drm_hdmi_ctx, pdev);
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301198 if (ret) {
1199 DRM_ERROR("mixer_resources_init failed\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001200 goto fail;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301201 }
1202
1203 if (ctx->vp_enabled) {
1204 /* acquire vp resources: regs, irqs, clocks */
1205 ret = vp_resources_init(drm_hdmi_ctx, pdev);
1206 if (ret) {
1207 DRM_ERROR("vp_resources_init failed\n");
1208 goto fail;
1209 }
1210 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001211
Rahul Sharma768c3052012-10-04 20:48:56 +05301212 /* attach mixer driver to common hdmi. */
1213 exynos_mixer_drv_attach(drm_hdmi_ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001214
1215 /* register specific callback point to common hdmi. */
Joonyoung Shim578b6062012-04-05 20:49:26 +09001216 exynos_mixer_ops_register(&mixer_ops);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001217
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001218 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001219
1220 return 0;
1221
1222
1223fail:
1224 dev_info(dev, "probe failed\n");
1225 return ret;
1226}
1227
1228static int mixer_remove(struct platform_device *pdev)
1229{
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301230 dev_info(&pdev->dev, "remove successful\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001231
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001232 pm_runtime_disable(&pdev->dev);
1233
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001234 return 0;
1235}
1236
Joonyoung Shimab27af82012-04-23 19:35:51 +09001237#ifdef CONFIG_PM_SLEEP
1238static int mixer_suspend(struct device *dev)
1239{
1240 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1241 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1242
Rahul Sharma000f1302012-11-28 11:30:24 +05301243 if (pm_runtime_suspended(dev)) {
YoungJun Chocbc4c332013-06-12 10:44:40 +09001244 DRM_DEBUG_KMS("Already suspended\n");
Rahul Sharma000f1302012-11-28 11:30:24 +05301245 return 0;
1246 }
1247
Joonyoung Shimab27af82012-04-23 19:35:51 +09001248 mixer_poweroff(ctx);
1249
1250 return 0;
1251}
Rahul Sharma000f1302012-11-28 11:30:24 +05301252
1253static int mixer_resume(struct device *dev)
1254{
1255 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1256 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1257
Rahul Sharma000f1302012-11-28 11:30:24 +05301258 if (!pm_runtime_suspended(dev)) {
YoungJun Chocbc4c332013-06-12 10:44:40 +09001259 DRM_DEBUG_KMS("Already resumed\n");
Rahul Sharma000f1302012-11-28 11:30:24 +05301260 return 0;
1261 }
1262
1263 mixer_poweron(ctx);
1264
1265 return 0;
1266}
Joonyoung Shimab27af82012-04-23 19:35:51 +09001267#endif
1268
Rahul Sharma000f1302012-11-28 11:30:24 +05301269#ifdef CONFIG_PM_RUNTIME
1270static int mixer_runtime_suspend(struct device *dev)
1271{
1272 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1273 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1274
Rahul Sharma000f1302012-11-28 11:30:24 +05301275 mixer_poweroff(ctx);
1276
1277 return 0;
1278}
1279
1280static int mixer_runtime_resume(struct device *dev)
1281{
1282 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1283 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1284
Rahul Sharma000f1302012-11-28 11:30:24 +05301285 mixer_poweron(ctx);
1286
1287 return 0;
1288}
1289#endif
1290
1291static const struct dev_pm_ops mixer_pm_ops = {
1292 SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume)
1293 SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL)
1294};
Joonyoung Shimab27af82012-04-23 19:35:51 +09001295
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001296struct platform_driver mixer_driver = {
1297 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301298 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001299 .owner = THIS_MODULE,
Joonyoung Shimab27af82012-04-23 19:35:51 +09001300 .pm = &mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301301 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001302 },
1303 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001304 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301305 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001306};