blob: 4c5aed7e54c8ed909e1a608102b959831807a466 [file] [log] [blame]
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/mixer_reg.c
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
15 */
16
David Howells760285e2012-10-02 18:01:07 +010017#include <drm/drmP.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090018
19#include "regs-mixer.h"
20#include "regs-vp.h"
21
22#include <linux/kernel.h>
23#include <linux/spinlock.h>
24#include <linux/wait.h>
25#include <linux/i2c.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090026#include <linux/platform_device.h>
27#include <linux/interrupt.h>
28#include <linux/irq.h>
29#include <linux/delay.h>
30#include <linux/pm_runtime.h>
31#include <linux/clk.h>
32#include <linux/regulator/consumer.h>
Sachin Kamat3f1c7812013-08-14 16:38:01 +053033#include <linux/of.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090034#include <linux/component.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090035
36#include <drm/exynos_drm.h>
37
38#include "exynos_drm_drv.h"
Rahul Sharma663d8762013-01-03 05:44:04 -050039#include "exynos_drm_crtc.h"
Inki Dae1055b392012-10-19 17:37:35 +090040#include "exynos_drm_iommu.h"
Sean Paulf041b252014-01-30 16:19:15 -050041#include "exynos_mixer.h"
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090042
Sean Paulf041b252014-01-30 16:19:15 -050043#define get_mixer_manager(dev) platform_get_drvdata(to_platform_device(dev))
44
45#define MIXER_WIN_NR 3
46#define MIXER_DEFAULT_WIN 0
Seung-Woo Kimd8408322011-12-21 17:39:39 +090047
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090048struct hdmi_win_data {
49 dma_addr_t dma_addr;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090050 dma_addr_t chroma_dma_addr;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090051 uint32_t pixel_format;
52 unsigned int bpp;
53 unsigned int crtc_x;
54 unsigned int crtc_y;
55 unsigned int crtc_width;
56 unsigned int crtc_height;
57 unsigned int fb_x;
58 unsigned int fb_y;
59 unsigned int fb_width;
60 unsigned int fb_height;
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +090061 unsigned int src_width;
62 unsigned int src_height;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090063 unsigned int mode_width;
64 unsigned int mode_height;
65 unsigned int scan_flags;
Prathyush Kdb43fd12012-12-06 20:16:05 +053066 bool enabled;
67 bool resume;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090068};
69
70struct mixer_resources {
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090071 int irq;
72 void __iomem *mixer_regs;
73 void __iomem *vp_regs;
74 spinlock_t reg_slock;
75 struct clk *mixer;
76 struct clk *vp;
77 struct clk *sclk_mixer;
78 struct clk *sclk_hdmi;
79 struct clk *sclk_dac;
80};
81
Rahul Sharma1e123442012-10-04 20:48:51 +053082enum mixer_version_id {
83 MXR_VER_0_0_0_16,
84 MXR_VER_16_0_33_0,
Rahul Sharmadef5e092013-06-19 18:21:08 +053085 MXR_VER_128_0_0_184,
Rahul Sharma1e123442012-10-04 20:48:51 +053086};
87
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090088struct mixer_context {
Sean Paul45517892014-01-30 16:19:05 -050089 struct platform_device *pdev;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090090 struct device *dev;
Inki Dae1055b392012-10-19 17:37:35 +090091 struct drm_device *drm_dev;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090092 int pipe;
93 bool interlace;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090094 bool powered;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053095 bool vp_enabled;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090096 u32 int_en;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090097
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090098 struct mutex mixer_mutex;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090099 struct mixer_resources mixer_res;
Joonyoung Shima634dd52012-04-05 20:49:24 +0900100 struct hdmi_win_data win_data[MIXER_WIN_NR];
Rahul Sharma1e123442012-10-04 20:48:51 +0530101 enum mixer_version_id mxr_ver;
Prathyush K6e95d5e2012-12-06 20:16:03 +0530102 wait_queue_head_t wait_vsync_queue;
103 atomic_t wait_vsync_event;
Rahul Sharma1e123442012-10-04 20:48:51 +0530104};
105
106struct mixer_drv_data {
107 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530108 bool is_vp_enabled;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900109};
110
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900111static const u8 filter_y_horiz_tap8[] = {
112 0, -1, -1, -1, -1, -1, -1, -1,
113 -1, -1, -1, -1, -1, 0, 0, 0,
114 0, 2, 4, 5, 6, 6, 6, 6,
115 6, 5, 5, 4, 3, 2, 1, 1,
116 0, -6, -12, -16, -18, -20, -21, -20,
117 -20, -18, -16, -13, -10, -8, -5, -2,
118 127, 126, 125, 121, 114, 107, 99, 89,
119 79, 68, 57, 46, 35, 25, 16, 8,
120};
121
122static const u8 filter_y_vert_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 0, 5, 11, 19, 27, 37, 48, 59,
128 70, 81, 92, 102, 111, 118, 124, 126,
129 0, 0, -1, -1, -2, -3, -4, -5,
130 -6, -7, -8, -8, -8, -8, -6, -3,
131};
132
133static const u8 filter_cr_horiz_tap4[] = {
134 0, -3, -6, -8, -8, -8, -8, -7,
135 -6, -5, -4, -3, -2, -1, -1, 0,
136 127, 126, 124, 118, 111, 102, 92, 81,
137 70, 59, 48, 37, 27, 19, 11, 5,
138};
139
140static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
141{
142 return readl(res->vp_regs + reg_id);
143}
144
145static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
146 u32 val)
147{
148 writel(val, res->vp_regs + reg_id);
149}
150
151static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
152 u32 val, u32 mask)
153{
154 u32 old = vp_reg_read(res, reg_id);
155
156 val = (val & mask) | (old & ~mask);
157 writel(val, res->vp_regs + reg_id);
158}
159
160static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
161{
162 return readl(res->mixer_regs + reg_id);
163}
164
165static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
166 u32 val)
167{
168 writel(val, res->mixer_regs + reg_id);
169}
170
171static inline void mixer_reg_writemask(struct mixer_resources *res,
172 u32 reg_id, u32 val, u32 mask)
173{
174 u32 old = mixer_reg_read(res, reg_id);
175
176 val = (val & mask) | (old & ~mask);
177 writel(val, res->mixer_regs + reg_id);
178}
179
180static void mixer_regs_dump(struct mixer_context *ctx)
181{
182#define DUMPREG(reg_id) \
183do { \
184 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
185 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
186} while (0)
187
188 DUMPREG(MXR_STATUS);
189 DUMPREG(MXR_CFG);
190 DUMPREG(MXR_INT_EN);
191 DUMPREG(MXR_INT_STATUS);
192
193 DUMPREG(MXR_LAYER_CFG);
194 DUMPREG(MXR_VIDEO_CFG);
195
196 DUMPREG(MXR_GRAPHIC0_CFG);
197 DUMPREG(MXR_GRAPHIC0_BASE);
198 DUMPREG(MXR_GRAPHIC0_SPAN);
199 DUMPREG(MXR_GRAPHIC0_WH);
200 DUMPREG(MXR_GRAPHIC0_SXY);
201 DUMPREG(MXR_GRAPHIC0_DXY);
202
203 DUMPREG(MXR_GRAPHIC1_CFG);
204 DUMPREG(MXR_GRAPHIC1_BASE);
205 DUMPREG(MXR_GRAPHIC1_SPAN);
206 DUMPREG(MXR_GRAPHIC1_WH);
207 DUMPREG(MXR_GRAPHIC1_SXY);
208 DUMPREG(MXR_GRAPHIC1_DXY);
209#undef DUMPREG
210}
211
212static void vp_regs_dump(struct mixer_context *ctx)
213{
214#define DUMPREG(reg_id) \
215do { \
216 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
217 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
218} while (0)
219
220 DUMPREG(VP_ENABLE);
221 DUMPREG(VP_SRESET);
222 DUMPREG(VP_SHADOW_UPDATE);
223 DUMPREG(VP_FIELD_ID);
224 DUMPREG(VP_MODE);
225 DUMPREG(VP_IMG_SIZE_Y);
226 DUMPREG(VP_IMG_SIZE_C);
227 DUMPREG(VP_PER_RATE_CTRL);
228 DUMPREG(VP_TOP_Y_PTR);
229 DUMPREG(VP_BOT_Y_PTR);
230 DUMPREG(VP_TOP_C_PTR);
231 DUMPREG(VP_BOT_C_PTR);
232 DUMPREG(VP_ENDIAN_MODE);
233 DUMPREG(VP_SRC_H_POSITION);
234 DUMPREG(VP_SRC_V_POSITION);
235 DUMPREG(VP_SRC_WIDTH);
236 DUMPREG(VP_SRC_HEIGHT);
237 DUMPREG(VP_DST_H_POSITION);
238 DUMPREG(VP_DST_V_POSITION);
239 DUMPREG(VP_DST_WIDTH);
240 DUMPREG(VP_DST_HEIGHT);
241 DUMPREG(VP_H_RATIO);
242 DUMPREG(VP_V_RATIO);
243
244#undef DUMPREG
245}
246
247static inline void vp_filter_set(struct mixer_resources *res,
248 int reg_id, const u8 *data, unsigned int size)
249{
250 /* assure 4-byte align */
251 BUG_ON(size & 3);
252 for (; size; size -= 4, reg_id += 4, data += 4) {
253 u32 val = (data[0] << 24) | (data[1] << 16) |
254 (data[2] << 8) | data[3];
255 vp_reg_write(res, reg_id, val);
256 }
257}
258
259static void vp_default_filter(struct mixer_resources *res)
260{
261 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530262 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900263 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530264 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900265 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530266 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900267}
268
269static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
270{
271 struct mixer_resources *res = &ctx->mixer_res;
272
273 /* block update on vsync */
274 mixer_reg_writemask(res, MXR_STATUS, enable ?
275 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
276
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530277 if (ctx->vp_enabled)
278 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900279 VP_SHADOW_UPDATE_ENABLE : 0);
280}
281
282static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
283{
284 struct mixer_resources *res = &ctx->mixer_res;
285 u32 val;
286
287 /* choosing between interlace and progressive mode */
288 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
289 MXR_CFG_SCAN_PROGRASSIVE);
290
Rahul Sharmadef5e092013-06-19 18:21:08 +0530291 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
292 /* choosing between proper HD and SD mode */
293 if (height <= 480)
294 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
295 else if (height <= 576)
296 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
297 else if (height <= 720)
298 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
299 else if (height <= 1080)
300 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
301 else
302 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
303 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900304
305 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
306}
307
308static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
309{
310 struct mixer_resources *res = &ctx->mixer_res;
311 u32 val;
312
313 if (height == 480) {
314 val = MXR_CFG_RGB601_0_255;
315 } else if (height == 576) {
316 val = MXR_CFG_RGB601_0_255;
317 } else if (height == 720) {
318 val = MXR_CFG_RGB709_16_235;
319 mixer_reg_write(res, MXR_CM_COEFF_Y,
320 (1 << 30) | (94 << 20) | (314 << 10) |
321 (32 << 0));
322 mixer_reg_write(res, MXR_CM_COEFF_CB,
323 (972 << 20) | (851 << 10) | (225 << 0));
324 mixer_reg_write(res, MXR_CM_COEFF_CR,
325 (225 << 20) | (820 << 10) | (1004 << 0));
326 } else if (height == 1080) {
327 val = MXR_CFG_RGB709_16_235;
328 mixer_reg_write(res, MXR_CM_COEFF_Y,
329 (1 << 30) | (94 << 20) | (314 << 10) |
330 (32 << 0));
331 mixer_reg_write(res, MXR_CM_COEFF_CB,
332 (972 << 20) | (851 << 10) | (225 << 0));
333 mixer_reg_write(res, MXR_CM_COEFF_CR,
334 (225 << 20) | (820 << 10) | (1004 << 0));
335 } else {
336 val = MXR_CFG_RGB709_16_235;
337 mixer_reg_write(res, MXR_CM_COEFF_Y,
338 (1 << 30) | (94 << 20) | (314 << 10) |
339 (32 << 0));
340 mixer_reg_write(res, MXR_CM_COEFF_CB,
341 (972 << 20) | (851 << 10) | (225 << 0));
342 mixer_reg_write(res, MXR_CM_COEFF_CR,
343 (225 << 20) | (820 << 10) | (1004 << 0));
344 }
345
346 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
347}
348
349static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
350{
351 struct mixer_resources *res = &ctx->mixer_res;
352 u32 val = enable ? ~0 : 0;
353
354 switch (win) {
355 case 0:
356 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
357 break;
358 case 1:
359 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
360 break;
361 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530362 if (ctx->vp_enabled) {
363 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
364 mixer_reg_writemask(res, MXR_CFG, val,
365 MXR_CFG_VP_ENABLE);
366 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900367 break;
368 }
369}
370
371static void mixer_run(struct mixer_context *ctx)
372{
373 struct mixer_resources *res = &ctx->mixer_res;
374
375 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
376
377 mixer_regs_dump(ctx);
378}
379
380static void vp_video_buffer(struct mixer_context *ctx, int win)
381{
382 struct mixer_resources *res = &ctx->mixer_res;
383 unsigned long flags;
384 struct hdmi_win_data *win_data;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900385 unsigned int x_ratio, y_ratio;
YoungJun Cho782953e2013-07-01 13:04:12 +0900386 unsigned int buf_num = 1;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900387 dma_addr_t luma_addr[2], chroma_addr[2];
388 bool tiled_mode = false;
389 bool crcb_mode = false;
390 u32 val;
391
392 win_data = &ctx->win_data[win];
393
394 switch (win_data->pixel_format) {
395 case DRM_FORMAT_NV12MT:
396 tiled_mode = true;
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900397 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900398 crcb_mode = false;
399 buf_num = 2;
400 break;
401 /* TODO: single buffer format NV12, NV21 */
402 default:
403 /* ignore pixel format at disable time */
404 if (!win_data->dma_addr)
405 break;
406
407 DRM_ERROR("pixel format for vp is wrong [%d].\n",
408 win_data->pixel_format);
409 return;
410 }
411
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900412 /* scaling feature: (src << 16) / dst */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900413 x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
414 y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900415
416 if (buf_num == 2) {
417 luma_addr[0] = win_data->dma_addr;
418 chroma_addr[0] = win_data->chroma_dma_addr;
419 } else {
420 luma_addr[0] = win_data->dma_addr;
421 chroma_addr[0] = win_data->dma_addr
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900422 + (win_data->fb_width * win_data->fb_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900423 }
424
425 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
426 ctx->interlace = true;
427 if (tiled_mode) {
428 luma_addr[1] = luma_addr[0] + 0x40;
429 chroma_addr[1] = chroma_addr[0] + 0x40;
430 } else {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900431 luma_addr[1] = luma_addr[0] + win_data->fb_width;
432 chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900433 }
434 } else {
435 ctx->interlace = false;
436 luma_addr[1] = 0;
437 chroma_addr[1] = 0;
438 }
439
440 spin_lock_irqsave(&res->reg_slock, flags);
441 mixer_vsync_set_update(ctx, false);
442
443 /* interlace or progressive scan mode */
444 val = (ctx->interlace ? ~0 : 0);
445 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
446
447 /* setup format */
448 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
449 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
450 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
451
452 /* setting size of input image */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900453 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
454 VP_IMG_VSIZE(win_data->fb_height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900455 /* chroma height has to reduced by 2 to avoid chroma distorions */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900456 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
457 VP_IMG_VSIZE(win_data->fb_height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900458
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900459 vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
460 vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900461 vp_reg_write(res, VP_SRC_H_POSITION,
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900462 VP_SRC_H_POSITION_VAL(win_data->fb_x));
463 vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900464
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900465 vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
466 vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900467 if (ctx->interlace) {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900468 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2);
469 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900470 } else {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900471 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
472 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900473 }
474
475 vp_reg_write(res, VP_H_RATIO, x_ratio);
476 vp_reg_write(res, VP_V_RATIO, y_ratio);
477
478 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
479
480 /* set buffer address to vp */
481 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
482 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
483 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
484 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
485
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900486 mixer_cfg_scan(ctx, win_data->mode_height);
487 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900488 mixer_cfg_layer(ctx, win, true);
489 mixer_run(ctx);
490
491 mixer_vsync_set_update(ctx, true);
492 spin_unlock_irqrestore(&res->reg_slock, flags);
493
494 vp_regs_dump(ctx);
495}
496
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530497static void mixer_layer_update(struct mixer_context *ctx)
498{
499 struct mixer_resources *res = &ctx->mixer_res;
500 u32 val;
501
502 val = mixer_reg_read(res, MXR_CFG);
503
504 /* allow one update per vsync only */
505 if (!(val & MXR_CFG_LAYER_UPDATE_COUNT_MASK))
506 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
507}
508
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900509static void mixer_graph_buffer(struct mixer_context *ctx, int win)
510{
511 struct mixer_resources *res = &ctx->mixer_res;
512 unsigned long flags;
513 struct hdmi_win_data *win_data;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900514 unsigned int x_ratio, y_ratio;
515 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900516 dma_addr_t dma_addr;
517 unsigned int fmt;
518 u32 val;
519
520 win_data = &ctx->win_data[win];
521
522 #define RGB565 4
523 #define ARGB1555 5
524 #define ARGB4444 6
525 #define ARGB8888 7
526
527 switch (win_data->bpp) {
528 case 16:
529 fmt = ARGB4444;
530 break;
531 case 32:
532 fmt = ARGB8888;
533 break;
534 default:
535 fmt = ARGB8888;
536 }
537
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900538 /* 2x scaling feature */
539 x_ratio = 0;
540 y_ratio = 0;
541
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900542 dst_x_offset = win_data->crtc_x;
543 dst_y_offset = win_data->crtc_y;
544
545 /* converting dma address base and source offset */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900546 dma_addr = win_data->dma_addr
547 + (win_data->fb_x * win_data->bpp >> 3)
548 + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900549 src_x_offset = 0;
550 src_y_offset = 0;
551
552 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
553 ctx->interlace = true;
554 else
555 ctx->interlace = false;
556
557 spin_lock_irqsave(&res->reg_slock, flags);
558 mixer_vsync_set_update(ctx, false);
559
560 /* setup format */
561 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
562 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
563
564 /* setup geometry */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900565 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900566
Rahul Sharmadef5e092013-06-19 18:21:08 +0530567 /* setup display size */
568 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
569 win == MIXER_DEFAULT_WIN) {
570 val = MXR_MXR_RES_HEIGHT(win_data->fb_height);
571 val |= MXR_MXR_RES_WIDTH(win_data->fb_width);
572 mixer_reg_write(res, MXR_RESOLUTION, val);
573 }
574
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900575 val = MXR_GRP_WH_WIDTH(win_data->crtc_width);
576 val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900577 val |= MXR_GRP_WH_H_SCALE(x_ratio);
578 val |= MXR_GRP_WH_V_SCALE(y_ratio);
579 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
580
581 /* setup offsets in source image */
582 val = MXR_GRP_SXY_SX(src_x_offset);
583 val |= MXR_GRP_SXY_SY(src_y_offset);
584 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
585
586 /* setup offsets in display image */
587 val = MXR_GRP_DXY_DX(dst_x_offset);
588 val |= MXR_GRP_DXY_DY(dst_y_offset);
589 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
590
591 /* set buffer address to mixer */
592 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
593
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900594 mixer_cfg_scan(ctx, win_data->mode_height);
595 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900596 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530597
598 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530599 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
600 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530601 mixer_layer_update(ctx);
602
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900603 mixer_run(ctx);
604
605 mixer_vsync_set_update(ctx, true);
606 spin_unlock_irqrestore(&res->reg_slock, flags);
607}
608
609static void vp_win_reset(struct mixer_context *ctx)
610{
611 struct mixer_resources *res = &ctx->mixer_res;
612 int tries = 100;
613
614 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
615 for (tries = 100; tries; --tries) {
616 /* waiting until VP_SRESET_PROCESSING is 0 */
617 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
618 break;
Sean Paul09760ea2013-01-14 17:03:20 -0500619 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900620 }
621 WARN(tries == 0, "failed to reset Video Processor\n");
622}
623
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900624static void mixer_win_reset(struct mixer_context *ctx)
625{
626 struct mixer_resources *res = &ctx->mixer_res;
627 unsigned long flags;
628 u32 val; /* value stored to register */
629
630 spin_lock_irqsave(&res->reg_slock, flags);
631 mixer_vsync_set_update(ctx, false);
632
633 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
634
635 /* set output in RGB888 mode */
636 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
637
638 /* 16 beat burst in DMA */
639 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
640 MXR_STATUS_BURST_MASK);
641
642 /* setting default layer priority: layer1 > layer0 > video
643 * because typical usage scenario would be
644 * layer1 - OSD
645 * layer0 - framebuffer
646 * video - video overlay
647 */
648 val = MXR_LAYER_CFG_GRP1_VAL(3);
649 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530650 if (ctx->vp_enabled)
651 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900652 mixer_reg_write(res, MXR_LAYER_CFG, val);
653
654 /* setting background color */
655 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
656 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
657 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
658
659 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900660 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
661 val |= MXR_GRP_CFG_WIN_BLEND_EN;
662 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
663
Sean Paul0377f4e2013-04-25 15:13:26 -0400664 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900665 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400666
667 /* Blend layer 1 into layer 0 */
668 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
669 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900670 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
671
Seung-Woo Kim57366032012-05-15 17:22:08 +0900672 /* setting video layers */
673 val = MXR_GRP_CFG_ALPHA_VAL(0);
674 mixer_reg_write(res, MXR_VIDEO_CFG, val);
675
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530676 if (ctx->vp_enabled) {
677 /* configuration of Video Processor Registers */
678 vp_win_reset(ctx);
679 vp_default_filter(res);
680 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900681
682 /* disable all layers */
683 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
684 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530685 if (ctx->vp_enabled)
686 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900687
688 mixer_vsync_set_update(ctx, true);
689 spin_unlock_irqrestore(&res->reg_slock, flags);
690}
691
Sean Paul45517892014-01-30 16:19:05 -0500692static irqreturn_t mixer_irq_handler(int irq, void *arg)
693{
694 struct mixer_context *ctx = arg;
695 struct mixer_resources *res = &ctx->mixer_res;
696 u32 val, base, shadow;
697
698 spin_lock(&res->reg_slock);
699
700 /* read interrupt status for handling and clearing flags for VSYNC */
701 val = mixer_reg_read(res, MXR_INT_STATUS);
702
703 /* handling VSYNC */
704 if (val & MXR_INT_STATUS_VSYNC) {
705 /* interlace scan need to check shadow register */
706 if (ctx->interlace) {
707 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
708 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
709 if (base != shadow)
710 goto out;
711
712 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
713 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
714 if (base != shadow)
715 goto out;
716 }
717
718 drm_handle_vblank(ctx->drm_dev, ctx->pipe);
719 exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
720
721 /* set wait vsync event to zero and wake up queue. */
722 if (atomic_read(&ctx->wait_vsync_event)) {
723 atomic_set(&ctx->wait_vsync_event, 0);
724 wake_up(&ctx->wait_vsync_queue);
725 }
726 }
727
728out:
729 /* clear interrupts */
730 if (~val & MXR_INT_EN_VSYNC) {
731 /* vsync interrupt use different bit for read and clear */
732 val &= ~MXR_INT_EN_VSYNC;
733 val |= MXR_INT_CLEAR_VSYNC;
734 }
735 mixer_reg_write(res, MXR_INT_STATUS, val);
736
737 spin_unlock(&res->reg_slock);
738
739 return IRQ_HANDLED;
740}
741
742static int mixer_resources_init(struct mixer_context *mixer_ctx)
743{
744 struct device *dev = &mixer_ctx->pdev->dev;
745 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
746 struct resource *res;
747 int ret;
748
749 spin_lock_init(&mixer_res->reg_slock);
750
751 mixer_res->mixer = devm_clk_get(dev, "mixer");
752 if (IS_ERR(mixer_res->mixer)) {
753 dev_err(dev, "failed to get clock 'mixer'\n");
754 return -ENODEV;
755 }
756
757 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
758 if (IS_ERR(mixer_res->sclk_hdmi)) {
759 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
760 return -ENODEV;
761 }
762 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
763 if (res == NULL) {
764 dev_err(dev, "get memory resource failed.\n");
765 return -ENXIO;
766 }
767
768 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
769 resource_size(res));
770 if (mixer_res->mixer_regs == NULL) {
771 dev_err(dev, "register mapping failed.\n");
772 return -ENXIO;
773 }
774
775 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
776 if (res == NULL) {
777 dev_err(dev, "get interrupt resource failed.\n");
778 return -ENXIO;
779 }
780
781 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
782 0, "drm_mixer", mixer_ctx);
783 if (ret) {
784 dev_err(dev, "request interrupt failed.\n");
785 return ret;
786 }
787 mixer_res->irq = res->start;
788
789 return 0;
790}
791
792static int vp_resources_init(struct mixer_context *mixer_ctx)
793{
794 struct device *dev = &mixer_ctx->pdev->dev;
795 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
796 struct resource *res;
797
798 mixer_res->vp = devm_clk_get(dev, "vp");
799 if (IS_ERR(mixer_res->vp)) {
800 dev_err(dev, "failed to get clock 'vp'\n");
801 return -ENODEV;
802 }
803 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
804 if (IS_ERR(mixer_res->sclk_mixer)) {
805 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
806 return -ENODEV;
807 }
808 mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
809 if (IS_ERR(mixer_res->sclk_dac)) {
810 dev_err(dev, "failed to get clock 'sclk_dac'\n");
811 return -ENODEV;
812 }
813
814 if (mixer_res->sclk_hdmi)
815 clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
816
817 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
818 if (res == NULL) {
819 dev_err(dev, "get memory resource failed.\n");
820 return -ENXIO;
821 }
822
823 mixer_res->vp_regs = devm_ioremap(dev, res->start,
824 resource_size(res));
825 if (mixer_res->vp_regs == NULL) {
826 dev_err(dev, "register mapping failed.\n");
827 return -ENXIO;
828 }
829
830 return 0;
831}
832
Sean Paulf041b252014-01-30 16:19:15 -0500833static int mixer_initialize(struct exynos_drm_manager *mgr,
Inki Daef37cd5e2014-05-09 14:25:20 +0900834 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500835{
836 int ret;
Sean Paulf041b252014-01-30 16:19:15 -0500837 struct mixer_context *mixer_ctx = mgr->ctx;
Inki Daef37cd5e2014-05-09 14:25:20 +0900838 struct exynos_drm_private *priv;
839 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500840
Inki Daef37cd5e2014-05-09 14:25:20 +0900841 mgr->drm_dev = mixer_ctx->drm_dev = drm_dev;
842 mgr->pipe = mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500843
844 /* acquire resources: regs, irqs, clocks */
845 ret = mixer_resources_init(mixer_ctx);
846 if (ret) {
847 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
848 return ret;
849 }
850
851 if (mixer_ctx->vp_enabled) {
852 /* acquire vp resources: regs, irqs, clocks */
853 ret = vp_resources_init(mixer_ctx);
854 if (ret) {
855 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
856 return ret;
857 }
858 }
859
Sean Paulf041b252014-01-30 16:19:15 -0500860 if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
861 return 0;
862
863 return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Sean Paul45517892014-01-30 16:19:05 -0500864}
865
Sean Paulf041b252014-01-30 16:19:15 -0500866static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
Inki Dae1055b392012-10-19 17:37:35 +0900867{
Sean Paulf041b252014-01-30 16:19:15 -0500868 struct mixer_context *mixer_ctx = mgr->ctx;
Inki Dae1055b392012-10-19 17:37:35 +0900869
Sean Paulf041b252014-01-30 16:19:15 -0500870 if (is_drm_iommu_supported(mixer_ctx->drm_dev))
871 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900872}
873
Sean Paulf041b252014-01-30 16:19:15 -0500874static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900875{
Sean Paulf041b252014-01-30 16:19:15 -0500876 struct mixer_context *mixer_ctx = mgr->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900877 struct mixer_resources *res = &mixer_ctx->mixer_res;
878
Sean Paulf041b252014-01-30 16:19:15 -0500879 if (!mixer_ctx->powered) {
880 mixer_ctx->int_en |= MXR_INT_EN_VSYNC;
881 return 0;
882 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900883
884 /* enable vsync interrupt */
885 mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
886 MXR_INT_EN_VSYNC);
887
888 return 0;
889}
890
Sean Paulf041b252014-01-30 16:19:15 -0500891static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900892{
Sean Paulf041b252014-01-30 16:19:15 -0500893 struct mixer_context *mixer_ctx = mgr->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900894 struct mixer_resources *res = &mixer_ctx->mixer_res;
895
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900896 /* disable vsync interrupt */
897 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
898}
899
Sean Paulf041b252014-01-30 16:19:15 -0500900static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
901 struct exynos_drm_overlay *overlay)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900902{
Sean Paulf041b252014-01-30 16:19:15 -0500903 struct mixer_context *mixer_ctx = mgr->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900904 struct hdmi_win_data *win_data;
905 int win;
906
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900907 if (!overlay) {
908 DRM_ERROR("overlay is NULL\n");
909 return;
910 }
911
912 DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
913 overlay->fb_width, overlay->fb_height,
914 overlay->fb_x, overlay->fb_y,
915 overlay->crtc_width, overlay->crtc_height,
916 overlay->crtc_x, overlay->crtc_y);
917
918 win = overlay->zpos;
919 if (win == DEFAULT_ZPOS)
Joonyoung Shima2ee1512012-04-05 20:49:25 +0900920 win = MIXER_DEFAULT_WIN;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900921
Krzysztof Kozlowski1586d802013-05-27 15:00:43 +0900922 if (win < 0 || win >= MIXER_WIN_NR) {
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900923 DRM_ERROR("mixer window[%d] is wrong\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900924 return;
925 }
926
927 win_data = &mixer_ctx->win_data[win];
928
929 win_data->dma_addr = overlay->dma_addr[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900930 win_data->chroma_dma_addr = overlay->dma_addr[1];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900931 win_data->pixel_format = overlay->pixel_format;
932 win_data->bpp = overlay->bpp;
933
934 win_data->crtc_x = overlay->crtc_x;
935 win_data->crtc_y = overlay->crtc_y;
936 win_data->crtc_width = overlay->crtc_width;
937 win_data->crtc_height = overlay->crtc_height;
938
939 win_data->fb_x = overlay->fb_x;
940 win_data->fb_y = overlay->fb_y;
941 win_data->fb_width = overlay->fb_width;
942 win_data->fb_height = overlay->fb_height;
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900943 win_data->src_width = overlay->src_width;
944 win_data->src_height = overlay->src_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900945
946 win_data->mode_width = overlay->mode_width;
947 win_data->mode_height = overlay->mode_height;
948
949 win_data->scan_flags = overlay->scan_flag;
950}
951
Sean Paulf041b252014-01-30 16:19:15 -0500952static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900953{
Sean Paulf041b252014-01-30 16:19:15 -0500954 struct mixer_context *mixer_ctx = mgr->ctx;
955 int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900956
YoungJun Chocbc4c332013-06-12 10:44:40 +0900957 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900958
Shirish Sdda90122013-01-23 22:03:18 -0500959 mutex_lock(&mixer_ctx->mixer_mutex);
960 if (!mixer_ctx->powered) {
961 mutex_unlock(&mixer_ctx->mixer_mutex);
962 return;
963 }
964 mutex_unlock(&mixer_ctx->mixer_mutex);
965
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530966 if (win > 1 && mixer_ctx->vp_enabled)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900967 vp_video_buffer(mixer_ctx, win);
968 else
969 mixer_graph_buffer(mixer_ctx, win);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530970
971 mixer_ctx->win_data[win].enabled = true;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900972}
973
Sean Paulf041b252014-01-30 16:19:15 -0500974static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900975{
Sean Paulf041b252014-01-30 16:19:15 -0500976 struct mixer_context *mixer_ctx = mgr->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900977 struct mixer_resources *res = &mixer_ctx->mixer_res;
Sean Paulf041b252014-01-30 16:19:15 -0500978 int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900979 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900980
YoungJun Chocbc4c332013-06-12 10:44:40 +0900981 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900982
Prathyush Kdb43fd12012-12-06 20:16:05 +0530983 mutex_lock(&mixer_ctx->mixer_mutex);
984 if (!mixer_ctx->powered) {
985 mutex_unlock(&mixer_ctx->mixer_mutex);
986 mixer_ctx->win_data[win].resume = false;
987 return;
988 }
989 mutex_unlock(&mixer_ctx->mixer_mutex);
990
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900991 spin_lock_irqsave(&res->reg_slock, flags);
992 mixer_vsync_set_update(mixer_ctx, false);
993
994 mixer_cfg_layer(mixer_ctx, win, false);
995
996 mixer_vsync_set_update(mixer_ctx, true);
997 spin_unlock_irqrestore(&res->reg_slock, flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530998
999 mixer_ctx->win_data[win].enabled = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001000}
1001
Sean Paulf041b252014-01-30 16:19:15 -05001002static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
Rahul Sharma0ea68222013-01-15 08:11:06 -05001003{
Sean Paulf041b252014-01-30 16:19:15 -05001004 struct mixer_context *mixer_ctx = mgr->ctx;
Prathyush K8137a2e2012-12-06 20:16:01 +05301005
Prathyush K6e95d5e2012-12-06 20:16:03 +05301006 mutex_lock(&mixer_ctx->mixer_mutex);
1007 if (!mixer_ctx->powered) {
1008 mutex_unlock(&mixer_ctx->mixer_mutex);
1009 return;
1010 }
1011 mutex_unlock(&mixer_ctx->mixer_mutex);
1012
1013 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1014
1015 /*
1016 * wait for MIXER to signal VSYNC interrupt or return after
1017 * timeout which is set to 50ms (refresh rate of 20).
1018 */
1019 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1020 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001021 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301022 DRM_DEBUG_KMS("vblank wait timed out.\n");
1023}
1024
Sean Paulf041b252014-01-30 16:19:15 -05001025static void mixer_window_suspend(struct exynos_drm_manager *mgr)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301026{
Sean Paulf041b252014-01-30 16:19:15 -05001027 struct mixer_context *ctx = mgr->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301028 struct hdmi_win_data *win_data;
1029 int i;
1030
1031 for (i = 0; i < MIXER_WIN_NR; i++) {
1032 win_data = &ctx->win_data[i];
1033 win_data->resume = win_data->enabled;
Sean Paulf041b252014-01-30 16:19:15 -05001034 mixer_win_disable(mgr, i);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301035 }
Sean Paulf041b252014-01-30 16:19:15 -05001036 mixer_wait_for_vblank(mgr);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301037}
1038
Sean Paulf041b252014-01-30 16:19:15 -05001039static void mixer_window_resume(struct exynos_drm_manager *mgr)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301040{
Sean Paulf041b252014-01-30 16:19:15 -05001041 struct mixer_context *ctx = mgr->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301042 struct hdmi_win_data *win_data;
1043 int i;
1044
1045 for (i = 0; i < MIXER_WIN_NR; i++) {
1046 win_data = &ctx->win_data[i];
1047 win_data->enabled = win_data->resume;
1048 win_data->resume = false;
Sean Paul87244fa2014-01-30 16:19:07 -05001049 if (win_data->enabled)
Sean Paulf041b252014-01-30 16:19:15 -05001050 mixer_win_commit(mgr, i);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301051 }
1052}
1053
Sean Paulf041b252014-01-30 16:19:15 -05001054static void mixer_poweron(struct exynos_drm_manager *mgr)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301055{
Sean Paulf041b252014-01-30 16:19:15 -05001056 struct mixer_context *ctx = mgr->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301057 struct mixer_resources *res = &ctx->mixer_res;
1058
Prathyush Kdb43fd12012-12-06 20:16:05 +05301059 mutex_lock(&ctx->mixer_mutex);
1060 if (ctx->powered) {
1061 mutex_unlock(&ctx->mixer_mutex);
1062 return;
1063 }
1064 ctx->powered = true;
1065 mutex_unlock(&ctx->mixer_mutex);
1066
Sean Paulaf65c802014-01-30 16:19:27 -05001067 pm_runtime_get_sync(ctx->dev);
1068
Sean Paul0bfb1f82013-06-11 12:24:02 +05301069 clk_prepare_enable(res->mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301070 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +05301071 clk_prepare_enable(res->vp);
1072 clk_prepare_enable(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301073 }
1074
1075 mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
1076 mixer_win_reset(ctx);
1077
Sean Paulf041b252014-01-30 16:19:15 -05001078 mixer_window_resume(mgr);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301079}
1080
Sean Paulf041b252014-01-30 16:19:15 -05001081static void mixer_poweroff(struct exynos_drm_manager *mgr)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301082{
Sean Paulf041b252014-01-30 16:19:15 -05001083 struct mixer_context *ctx = mgr->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301084 struct mixer_resources *res = &ctx->mixer_res;
1085
Prathyush Kdb43fd12012-12-06 20:16:05 +05301086 mutex_lock(&ctx->mixer_mutex);
1087 if (!ctx->powered)
1088 goto out;
1089 mutex_unlock(&ctx->mixer_mutex);
1090
Sean Paulf041b252014-01-30 16:19:15 -05001091 mixer_window_suspend(mgr);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301092
1093 ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
1094
Sean Paul0bfb1f82013-06-11 12:24:02 +05301095 clk_disable_unprepare(res->mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301096 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +05301097 clk_disable_unprepare(res->vp);
1098 clk_disable_unprepare(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301099 }
1100
Sean Paulaf65c802014-01-30 16:19:27 -05001101 pm_runtime_put_sync(ctx->dev);
1102
Prathyush Kdb43fd12012-12-06 20:16:05 +05301103 mutex_lock(&ctx->mixer_mutex);
1104 ctx->powered = false;
1105
1106out:
1107 mutex_unlock(&ctx->mixer_mutex);
1108}
1109
Sean Paulf041b252014-01-30 16:19:15 -05001110static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301111{
Prathyush Kdb43fd12012-12-06 20:16:05 +05301112 switch (mode) {
1113 case DRM_MODE_DPMS_ON:
Sean Paulaf65c802014-01-30 16:19:27 -05001114 mixer_poweron(mgr);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301115 break;
1116 case DRM_MODE_DPMS_STANDBY:
1117 case DRM_MODE_DPMS_SUSPEND:
1118 case DRM_MODE_DPMS_OFF:
Sean Paulaf65c802014-01-30 16:19:27 -05001119 mixer_poweroff(mgr);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301120 break;
1121 default:
1122 DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
1123 break;
1124 }
1125}
1126
Sean Paulf041b252014-01-30 16:19:15 -05001127/* Only valid for Mixer version 16.0.33.0 */
1128int mixer_check_mode(struct drm_display_mode *mode)
1129{
1130 u32 w, h;
1131
1132 w = mode->hdisplay;
1133 h = mode->vdisplay;
1134
1135 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1136 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1137 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1138
1139 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1140 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1141 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1142 return 0;
1143
1144 return -EINVAL;
1145}
1146
1147static struct exynos_drm_manager_ops mixer_manager_ops = {
Sean Paulf041b252014-01-30 16:19:15 -05001148 .dpms = mixer_dpms,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001149 .enable_vblank = mixer_enable_vblank,
1150 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301151 .wait_for_vblank = mixer_wait_for_vblank,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001152 .win_mode_set = mixer_win_mode_set,
1153 .win_commit = mixer_win_commit,
1154 .win_disable = mixer_win_disable,
Sean Paulf041b252014-01-30 16:19:15 -05001155};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001156
Sean Paulf041b252014-01-30 16:19:15 -05001157static struct exynos_drm_manager mixer_manager = {
1158 .type = EXYNOS_DISPLAY_TYPE_HDMI,
1159 .ops = &mixer_manager_ops,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001160};
1161
Rahul Sharmadef5e092013-06-19 18:21:08 +05301162static struct mixer_drv_data exynos5420_mxr_drv_data = {
1163 .version = MXR_VER_128_0_0_184,
1164 .is_vp_enabled = 0,
1165};
1166
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301167static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301168 .version = MXR_VER_16_0_33_0,
1169 .is_vp_enabled = 0,
1170};
1171
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301172static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301173 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301174 .is_vp_enabled = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301175};
1176
1177static struct platform_device_id mixer_driver_types[] = {
1178 {
1179 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301180 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301181 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301182 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301183 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301184 }, {
1185 /* end node */
1186 }
1187};
1188
1189static struct of_device_id mixer_match_types[] = {
1190 {
1191 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301192 .data = &exynos5250_mxr_drv_data,
1193 }, {
1194 .compatible = "samsung,exynos5250-mixer",
1195 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301196 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301197 .compatible = "samsung,exynos5420-mixer",
1198 .data = &exynos5420_mxr_drv_data,
1199 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301200 /* end node */
1201 }
1202};
1203
Inki Daef37cd5e2014-05-09 14:25:20 +09001204static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001205{
Inki Daef37cd5e2014-05-09 14:25:20 +09001206 struct platform_device *pdev = to_platform_device(dev);
1207 struct drm_device *drm_dev = data;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001208 struct mixer_context *ctx;
Rahul Sharma1e123442012-10-04 20:48:51 +05301209 struct mixer_drv_data *drv;
Inki Daef37cd5e2014-05-09 14:25:20 +09001210 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001211
1212 dev_info(dev, "probe start\n");
1213
Sean Paulf041b252014-01-30 16:19:15 -05001214 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1215 if (!ctx) {
1216 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001217 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001218 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001219
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001220 mutex_init(&ctx->mixer_mutex);
1221
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301222 if (dev->of_node) {
1223 const struct of_device_id *match;
Sachin Kamate436b092013-06-05 16:00:23 +09001224 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301225 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301226 } else {
1227 drv = (struct mixer_drv_data *)
1228 platform_get_device_id(pdev)->driver_data;
1229 }
1230
Sean Paul45517892014-01-30 16:19:05 -05001231 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001232 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301233 ctx->vp_enabled = drv->is_vp_enabled;
Rahul Sharma1e123442012-10-04 20:48:51 +05301234 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001235 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301236 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001237
Sean Paulf041b252014-01-30 16:19:15 -05001238 mixer_manager.ctx = ctx;
Inki Daef37cd5e2014-05-09 14:25:20 +09001239 ret = mixer_initialize(&mixer_manager, drm_dev);
1240 if (ret)
1241 return ret;
1242
Sean Paulf041b252014-01-30 16:19:15 -05001243 platform_set_drvdata(pdev, &mixer_manager);
Inki Daef37cd5e2014-05-09 14:25:20 +09001244 ret = exynos_drm_crtc_create(&mixer_manager);
1245 if (ret) {
1246 mixer_mgr_remove(&mixer_manager);
1247 return ret;
1248 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001249
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001250 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001251
1252 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001253}
1254
Inki Daef37cd5e2014-05-09 14:25:20 +09001255static void mixer_unbind(struct device *dev, struct device *master, void *data)
1256{
1257 struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
1258 struct drm_crtc *crtc = mgr->crtc;
1259
1260 dev_info(dev, "remove successful\n");
1261
1262 mixer_mgr_remove(mgr);
1263
1264 pm_runtime_disable(dev);
1265
1266 crtc->funcs->destroy(crtc);
1267}
1268
1269static const struct component_ops mixer_component_ops = {
1270 .bind = mixer_bind,
1271 .unbind = mixer_unbind,
1272};
1273
1274static int mixer_probe(struct platform_device *pdev)
1275{
Inki Daedf5225b2014-05-29 18:28:02 +09001276 int ret;
1277
1278 ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
1279 mixer_manager.type);
1280 if (ret)
1281 return ret;
1282
1283 ret = component_add(&pdev->dev, &mixer_component_ops);
1284 if (ret)
1285 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1286
1287 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001288}
1289
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001290static int mixer_remove(struct platform_device *pdev)
1291{
Inki Daedf5225b2014-05-29 18:28:02 +09001292 component_del(&pdev->dev, &mixer_component_ops);
1293 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1294
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001295 return 0;
1296}
1297
1298struct platform_driver mixer_driver = {
1299 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301300 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001301 .owner = THIS_MODULE,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301302 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001303 },
1304 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001305 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301306 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001307};