blob: 25a440a375fbfad3d9b7c05e5704a39410a76f44 [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>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090034
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,
Rahul Sharmadef5e092013-06-19 18:21:08 +053081 MXR_VER_128_0_0_184,
Rahul Sharma1e123442012-10-04 20:48:51 +053082};
83
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090084struct mixer_context {
Sean Paul45517892014-01-30 16:19:05 -050085 struct platform_device *pdev;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090086 struct device *dev;
Inki Dae1055b392012-10-19 17:37:35 +090087 struct drm_device *drm_dev;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090088 int pipe;
89 bool interlace;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090090 bool powered;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053091 bool vp_enabled;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090092 u32 int_en;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090093
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090094 struct mutex mixer_mutex;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090095 struct mixer_resources mixer_res;
Joonyoung Shima634dd52012-04-05 20:49:24 +090096 struct hdmi_win_data win_data[MIXER_WIN_NR];
Rahul Sharma1e123442012-10-04 20:48:51 +053097 enum mixer_version_id mxr_ver;
Inki Dae1055b392012-10-19 17:37:35 +090098 void *parent_ctx;
Prathyush K6e95d5e2012-12-06 20:16:03 +053099 wait_queue_head_t wait_vsync_queue;
100 atomic_t wait_vsync_event;
Rahul Sharma1e123442012-10-04 20:48:51 +0530101};
102
103struct mixer_drv_data {
104 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530105 bool is_vp_enabled;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900106};
107
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900108static const u8 filter_y_horiz_tap8[] = {
109 0, -1, -1, -1, -1, -1, -1, -1,
110 -1, -1, -1, -1, -1, 0, 0, 0,
111 0, 2, 4, 5, 6, 6, 6, 6,
112 6, 5, 5, 4, 3, 2, 1, 1,
113 0, -6, -12, -16, -18, -20, -21, -20,
114 -20, -18, -16, -13, -10, -8, -5, -2,
115 127, 126, 125, 121, 114, 107, 99, 89,
116 79, 68, 57, 46, 35, 25, 16, 8,
117};
118
119static const u8 filter_y_vert_tap4[] = {
120 0, -3, -6, -8, -8, -8, -8, -7,
121 -6, -5, -4, -3, -2, -1, -1, 0,
122 127, 126, 124, 118, 111, 102, 92, 81,
123 70, 59, 48, 37, 27, 19, 11, 5,
124 0, 5, 11, 19, 27, 37, 48, 59,
125 70, 81, 92, 102, 111, 118, 124, 126,
126 0, 0, -1, -1, -2, -3, -4, -5,
127 -6, -7, -8, -8, -8, -8, -6, -3,
128};
129
130static const u8 filter_cr_horiz_tap4[] = {
131 0, -3, -6, -8, -8, -8, -8, -7,
132 -6, -5, -4, -3, -2, -1, -1, 0,
133 127, 126, 124, 118, 111, 102, 92, 81,
134 70, 59, 48, 37, 27, 19, 11, 5,
135};
136
137static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
138{
139 return readl(res->vp_regs + reg_id);
140}
141
142static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
143 u32 val)
144{
145 writel(val, res->vp_regs + reg_id);
146}
147
148static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
149 u32 val, u32 mask)
150{
151 u32 old = vp_reg_read(res, reg_id);
152
153 val = (val & mask) | (old & ~mask);
154 writel(val, res->vp_regs + reg_id);
155}
156
157static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
158{
159 return readl(res->mixer_regs + reg_id);
160}
161
162static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
163 u32 val)
164{
165 writel(val, res->mixer_regs + reg_id);
166}
167
168static inline void mixer_reg_writemask(struct mixer_resources *res,
169 u32 reg_id, u32 val, u32 mask)
170{
171 u32 old = mixer_reg_read(res, reg_id);
172
173 val = (val & mask) | (old & ~mask);
174 writel(val, res->mixer_regs + reg_id);
175}
176
177static void mixer_regs_dump(struct mixer_context *ctx)
178{
179#define DUMPREG(reg_id) \
180do { \
181 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
182 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
183} while (0)
184
185 DUMPREG(MXR_STATUS);
186 DUMPREG(MXR_CFG);
187 DUMPREG(MXR_INT_EN);
188 DUMPREG(MXR_INT_STATUS);
189
190 DUMPREG(MXR_LAYER_CFG);
191 DUMPREG(MXR_VIDEO_CFG);
192
193 DUMPREG(MXR_GRAPHIC0_CFG);
194 DUMPREG(MXR_GRAPHIC0_BASE);
195 DUMPREG(MXR_GRAPHIC0_SPAN);
196 DUMPREG(MXR_GRAPHIC0_WH);
197 DUMPREG(MXR_GRAPHIC0_SXY);
198 DUMPREG(MXR_GRAPHIC0_DXY);
199
200 DUMPREG(MXR_GRAPHIC1_CFG);
201 DUMPREG(MXR_GRAPHIC1_BASE);
202 DUMPREG(MXR_GRAPHIC1_SPAN);
203 DUMPREG(MXR_GRAPHIC1_WH);
204 DUMPREG(MXR_GRAPHIC1_SXY);
205 DUMPREG(MXR_GRAPHIC1_DXY);
206#undef DUMPREG
207}
208
209static void vp_regs_dump(struct mixer_context *ctx)
210{
211#define DUMPREG(reg_id) \
212do { \
213 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
214 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
215} while (0)
216
217 DUMPREG(VP_ENABLE);
218 DUMPREG(VP_SRESET);
219 DUMPREG(VP_SHADOW_UPDATE);
220 DUMPREG(VP_FIELD_ID);
221 DUMPREG(VP_MODE);
222 DUMPREG(VP_IMG_SIZE_Y);
223 DUMPREG(VP_IMG_SIZE_C);
224 DUMPREG(VP_PER_RATE_CTRL);
225 DUMPREG(VP_TOP_Y_PTR);
226 DUMPREG(VP_BOT_Y_PTR);
227 DUMPREG(VP_TOP_C_PTR);
228 DUMPREG(VP_BOT_C_PTR);
229 DUMPREG(VP_ENDIAN_MODE);
230 DUMPREG(VP_SRC_H_POSITION);
231 DUMPREG(VP_SRC_V_POSITION);
232 DUMPREG(VP_SRC_WIDTH);
233 DUMPREG(VP_SRC_HEIGHT);
234 DUMPREG(VP_DST_H_POSITION);
235 DUMPREG(VP_DST_V_POSITION);
236 DUMPREG(VP_DST_WIDTH);
237 DUMPREG(VP_DST_HEIGHT);
238 DUMPREG(VP_H_RATIO);
239 DUMPREG(VP_V_RATIO);
240
241#undef DUMPREG
242}
243
244static inline void vp_filter_set(struct mixer_resources *res,
245 int reg_id, const u8 *data, unsigned int size)
246{
247 /* assure 4-byte align */
248 BUG_ON(size & 3);
249 for (; size; size -= 4, reg_id += 4, data += 4) {
250 u32 val = (data[0] << 24) | (data[1] << 16) |
251 (data[2] << 8) | data[3];
252 vp_reg_write(res, reg_id, val);
253 }
254}
255
256static void vp_default_filter(struct mixer_resources *res)
257{
258 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530259 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900260 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530261 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900262 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530263 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900264}
265
266static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
267{
268 struct mixer_resources *res = &ctx->mixer_res;
269
270 /* block update on vsync */
271 mixer_reg_writemask(res, MXR_STATUS, enable ?
272 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
273
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530274 if (ctx->vp_enabled)
275 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900276 VP_SHADOW_UPDATE_ENABLE : 0);
277}
278
279static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
280{
281 struct mixer_resources *res = &ctx->mixer_res;
282 u32 val;
283
284 /* choosing between interlace and progressive mode */
285 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
286 MXR_CFG_SCAN_PROGRASSIVE);
287
Rahul Sharmadef5e092013-06-19 18:21:08 +0530288 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
289 /* choosing between proper HD and SD mode */
290 if (height <= 480)
291 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
292 else if (height <= 576)
293 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
294 else if (height <= 720)
295 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
296 else if (height <= 1080)
297 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
298 else
299 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
300 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900301
302 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
303}
304
305static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
306{
307 struct mixer_resources *res = &ctx->mixer_res;
308 u32 val;
309
310 if (height == 480) {
311 val = MXR_CFG_RGB601_0_255;
312 } else if (height == 576) {
313 val = MXR_CFG_RGB601_0_255;
314 } else if (height == 720) {
315 val = MXR_CFG_RGB709_16_235;
316 mixer_reg_write(res, MXR_CM_COEFF_Y,
317 (1 << 30) | (94 << 20) | (314 << 10) |
318 (32 << 0));
319 mixer_reg_write(res, MXR_CM_COEFF_CB,
320 (972 << 20) | (851 << 10) | (225 << 0));
321 mixer_reg_write(res, MXR_CM_COEFF_CR,
322 (225 << 20) | (820 << 10) | (1004 << 0));
323 } else if (height == 1080) {
324 val = MXR_CFG_RGB709_16_235;
325 mixer_reg_write(res, MXR_CM_COEFF_Y,
326 (1 << 30) | (94 << 20) | (314 << 10) |
327 (32 << 0));
328 mixer_reg_write(res, MXR_CM_COEFF_CB,
329 (972 << 20) | (851 << 10) | (225 << 0));
330 mixer_reg_write(res, MXR_CM_COEFF_CR,
331 (225 << 20) | (820 << 10) | (1004 << 0));
332 } else {
333 val = MXR_CFG_RGB709_16_235;
334 mixer_reg_write(res, MXR_CM_COEFF_Y,
335 (1 << 30) | (94 << 20) | (314 << 10) |
336 (32 << 0));
337 mixer_reg_write(res, MXR_CM_COEFF_CB,
338 (972 << 20) | (851 << 10) | (225 << 0));
339 mixer_reg_write(res, MXR_CM_COEFF_CR,
340 (225 << 20) | (820 << 10) | (1004 << 0));
341 }
342
343 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
344}
345
346static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
347{
348 struct mixer_resources *res = &ctx->mixer_res;
349 u32 val = enable ? ~0 : 0;
350
351 switch (win) {
352 case 0:
353 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
354 break;
355 case 1:
356 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
357 break;
358 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530359 if (ctx->vp_enabled) {
360 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
361 mixer_reg_writemask(res, MXR_CFG, val,
362 MXR_CFG_VP_ENABLE);
363 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900364 break;
365 }
366}
367
368static void mixer_run(struct mixer_context *ctx)
369{
370 struct mixer_resources *res = &ctx->mixer_res;
371
372 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
373
374 mixer_regs_dump(ctx);
375}
376
377static void vp_video_buffer(struct mixer_context *ctx, int win)
378{
379 struct mixer_resources *res = &ctx->mixer_res;
380 unsigned long flags;
381 struct hdmi_win_data *win_data;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900382 unsigned int x_ratio, y_ratio;
YoungJun Cho782953e2013-07-01 13:04:12 +0900383 unsigned int buf_num = 1;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900384 dma_addr_t luma_addr[2], chroma_addr[2];
385 bool tiled_mode = false;
386 bool crcb_mode = false;
387 u32 val;
388
389 win_data = &ctx->win_data[win];
390
391 switch (win_data->pixel_format) {
392 case DRM_FORMAT_NV12MT:
393 tiled_mode = true;
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900394 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900395 crcb_mode = false;
396 buf_num = 2;
397 break;
398 /* TODO: single buffer format NV12, NV21 */
399 default:
400 /* ignore pixel format at disable time */
401 if (!win_data->dma_addr)
402 break;
403
404 DRM_ERROR("pixel format for vp is wrong [%d].\n",
405 win_data->pixel_format);
406 return;
407 }
408
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900409 /* scaling feature: (src << 16) / dst */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900410 x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
411 y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900412
413 if (buf_num == 2) {
414 luma_addr[0] = win_data->dma_addr;
415 chroma_addr[0] = win_data->chroma_dma_addr;
416 } else {
417 luma_addr[0] = win_data->dma_addr;
418 chroma_addr[0] = win_data->dma_addr
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900419 + (win_data->fb_width * win_data->fb_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900420 }
421
422 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
423 ctx->interlace = true;
424 if (tiled_mode) {
425 luma_addr[1] = luma_addr[0] + 0x40;
426 chroma_addr[1] = chroma_addr[0] + 0x40;
427 } else {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900428 luma_addr[1] = luma_addr[0] + win_data->fb_width;
429 chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900430 }
431 } else {
432 ctx->interlace = false;
433 luma_addr[1] = 0;
434 chroma_addr[1] = 0;
435 }
436
437 spin_lock_irqsave(&res->reg_slock, flags);
438 mixer_vsync_set_update(ctx, false);
439
440 /* interlace or progressive scan mode */
441 val = (ctx->interlace ? ~0 : 0);
442 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
443
444 /* setup format */
445 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
446 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
447 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
448
449 /* setting size of input image */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900450 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
451 VP_IMG_VSIZE(win_data->fb_height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900452 /* chroma height has to reduced by 2 to avoid chroma distorions */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900453 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
454 VP_IMG_VSIZE(win_data->fb_height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900455
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900456 vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
457 vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900458 vp_reg_write(res, VP_SRC_H_POSITION,
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900459 VP_SRC_H_POSITION_VAL(win_data->fb_x));
460 vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900461
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900462 vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
463 vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900464 if (ctx->interlace) {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900465 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2);
466 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900467 } else {
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900468 vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
469 vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900470 }
471
472 vp_reg_write(res, VP_H_RATIO, x_ratio);
473 vp_reg_write(res, VP_V_RATIO, y_ratio);
474
475 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
476
477 /* set buffer address to vp */
478 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
479 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
480 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
481 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
482
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900483 mixer_cfg_scan(ctx, win_data->mode_height);
484 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900485 mixer_cfg_layer(ctx, win, true);
486 mixer_run(ctx);
487
488 mixer_vsync_set_update(ctx, true);
489 spin_unlock_irqrestore(&res->reg_slock, flags);
490
491 vp_regs_dump(ctx);
492}
493
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530494static void mixer_layer_update(struct mixer_context *ctx)
495{
496 struct mixer_resources *res = &ctx->mixer_res;
497 u32 val;
498
499 val = mixer_reg_read(res, MXR_CFG);
500
501 /* allow one update per vsync only */
502 if (!(val & MXR_CFG_LAYER_UPDATE_COUNT_MASK))
503 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
504}
505
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900506static void mixer_graph_buffer(struct mixer_context *ctx, int win)
507{
508 struct mixer_resources *res = &ctx->mixer_res;
509 unsigned long flags;
510 struct hdmi_win_data *win_data;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900511 unsigned int x_ratio, y_ratio;
512 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900513 dma_addr_t dma_addr;
514 unsigned int fmt;
515 u32 val;
516
517 win_data = &ctx->win_data[win];
518
519 #define RGB565 4
520 #define ARGB1555 5
521 #define ARGB4444 6
522 #define ARGB8888 7
523
524 switch (win_data->bpp) {
525 case 16:
526 fmt = ARGB4444;
527 break;
528 case 32:
529 fmt = ARGB8888;
530 break;
531 default:
532 fmt = ARGB8888;
533 }
534
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900535 /* 2x scaling feature */
536 x_ratio = 0;
537 y_ratio = 0;
538
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900539 dst_x_offset = win_data->crtc_x;
540 dst_y_offset = win_data->crtc_y;
541
542 /* converting dma address base and source offset */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900543 dma_addr = win_data->dma_addr
544 + (win_data->fb_x * win_data->bpp >> 3)
545 + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900546 src_x_offset = 0;
547 src_y_offset = 0;
548
549 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
550 ctx->interlace = true;
551 else
552 ctx->interlace = false;
553
554 spin_lock_irqsave(&res->reg_slock, flags);
555 mixer_vsync_set_update(ctx, false);
556
557 /* setup format */
558 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
559 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
560
561 /* setup geometry */
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900562 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900563
Rahul Sharmadef5e092013-06-19 18:21:08 +0530564 /* setup display size */
565 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
566 win == MIXER_DEFAULT_WIN) {
567 val = MXR_MXR_RES_HEIGHT(win_data->fb_height);
568 val |= MXR_MXR_RES_WIDTH(win_data->fb_width);
569 mixer_reg_write(res, MXR_RESOLUTION, val);
570 }
571
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900572 val = MXR_GRP_WH_WIDTH(win_data->crtc_width);
573 val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900574 val |= MXR_GRP_WH_H_SCALE(x_ratio);
575 val |= MXR_GRP_WH_V_SCALE(y_ratio);
576 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
577
578 /* setup offsets in source image */
579 val = MXR_GRP_SXY_SX(src_x_offset);
580 val |= MXR_GRP_SXY_SY(src_y_offset);
581 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
582
583 /* setup offsets in display image */
584 val = MXR_GRP_DXY_DX(dst_x_offset);
585 val |= MXR_GRP_DXY_DY(dst_y_offset);
586 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
587
588 /* set buffer address to mixer */
589 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
590
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900591 mixer_cfg_scan(ctx, win_data->mode_height);
592 mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900593 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530594
595 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530596 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
597 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530598 mixer_layer_update(ctx);
599
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900600 mixer_run(ctx);
601
602 mixer_vsync_set_update(ctx, true);
603 spin_unlock_irqrestore(&res->reg_slock, flags);
604}
605
606static void vp_win_reset(struct mixer_context *ctx)
607{
608 struct mixer_resources *res = &ctx->mixer_res;
609 int tries = 100;
610
611 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
612 for (tries = 100; tries; --tries) {
613 /* waiting until VP_SRESET_PROCESSING is 0 */
614 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
615 break;
Sean Paul09760ea2013-01-14 17:03:20 -0500616 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900617 }
618 WARN(tries == 0, "failed to reset Video Processor\n");
619}
620
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900621static void mixer_win_reset(struct mixer_context *ctx)
622{
623 struct mixer_resources *res = &ctx->mixer_res;
624 unsigned long flags;
625 u32 val; /* value stored to register */
626
627 spin_lock_irqsave(&res->reg_slock, flags);
628 mixer_vsync_set_update(ctx, false);
629
630 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
631
632 /* set output in RGB888 mode */
633 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
634
635 /* 16 beat burst in DMA */
636 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
637 MXR_STATUS_BURST_MASK);
638
639 /* setting default layer priority: layer1 > layer0 > video
640 * because typical usage scenario would be
641 * layer1 - OSD
642 * layer0 - framebuffer
643 * video - video overlay
644 */
645 val = MXR_LAYER_CFG_GRP1_VAL(3);
646 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530647 if (ctx->vp_enabled)
648 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900649 mixer_reg_write(res, MXR_LAYER_CFG, val);
650
651 /* setting background color */
652 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
653 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
654 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
655
656 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900657 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
658 val |= MXR_GRP_CFG_WIN_BLEND_EN;
659 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
660
Sean Paul0377f4e2013-04-25 15:13:26 -0400661 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900662 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400663
664 /* Blend layer 1 into layer 0 */
665 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
666 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900667 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
668
Seung-Woo Kim57366032012-05-15 17:22:08 +0900669 /* setting video layers */
670 val = MXR_GRP_CFG_ALPHA_VAL(0);
671 mixer_reg_write(res, MXR_VIDEO_CFG, val);
672
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530673 if (ctx->vp_enabled) {
674 /* configuration of Video Processor Registers */
675 vp_win_reset(ctx);
676 vp_default_filter(res);
677 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900678
679 /* disable all layers */
680 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
681 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530682 if (ctx->vp_enabled)
683 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900684
685 mixer_vsync_set_update(ctx, true);
686 spin_unlock_irqrestore(&res->reg_slock, flags);
687}
688
Sean Paul45517892014-01-30 16:19:05 -0500689static irqreturn_t mixer_irq_handler(int irq, void *arg)
690{
691 struct mixer_context *ctx = arg;
692 struct mixer_resources *res = &ctx->mixer_res;
693 u32 val, base, shadow;
694
695 spin_lock(&res->reg_slock);
696
697 /* read interrupt status for handling and clearing flags for VSYNC */
698 val = mixer_reg_read(res, MXR_INT_STATUS);
699
700 /* handling VSYNC */
701 if (val & MXR_INT_STATUS_VSYNC) {
702 /* interlace scan need to check shadow register */
703 if (ctx->interlace) {
704 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
705 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
706 if (base != shadow)
707 goto out;
708
709 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
710 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
711 if (base != shadow)
712 goto out;
713 }
714
715 drm_handle_vblank(ctx->drm_dev, ctx->pipe);
716 exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
717
718 /* set wait vsync event to zero and wake up queue. */
719 if (atomic_read(&ctx->wait_vsync_event)) {
720 atomic_set(&ctx->wait_vsync_event, 0);
721 wake_up(&ctx->wait_vsync_queue);
722 }
723 }
724
725out:
726 /* clear interrupts */
727 if (~val & MXR_INT_EN_VSYNC) {
728 /* vsync interrupt use different bit for read and clear */
729 val &= ~MXR_INT_EN_VSYNC;
730 val |= MXR_INT_CLEAR_VSYNC;
731 }
732 mixer_reg_write(res, MXR_INT_STATUS, val);
733
734 spin_unlock(&res->reg_slock);
735
736 return IRQ_HANDLED;
737}
738
739static int mixer_resources_init(struct mixer_context *mixer_ctx)
740{
741 struct device *dev = &mixer_ctx->pdev->dev;
742 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
743 struct resource *res;
744 int ret;
745
746 spin_lock_init(&mixer_res->reg_slock);
747
748 mixer_res->mixer = devm_clk_get(dev, "mixer");
749 if (IS_ERR(mixer_res->mixer)) {
750 dev_err(dev, "failed to get clock 'mixer'\n");
751 return -ENODEV;
752 }
753
754 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
755 if (IS_ERR(mixer_res->sclk_hdmi)) {
756 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
757 return -ENODEV;
758 }
759 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
760 if (res == NULL) {
761 dev_err(dev, "get memory resource failed.\n");
762 return -ENXIO;
763 }
764
765 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
766 resource_size(res));
767 if (mixer_res->mixer_regs == NULL) {
768 dev_err(dev, "register mapping failed.\n");
769 return -ENXIO;
770 }
771
772 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
773 if (res == NULL) {
774 dev_err(dev, "get interrupt resource failed.\n");
775 return -ENXIO;
776 }
777
778 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
779 0, "drm_mixer", mixer_ctx);
780 if (ret) {
781 dev_err(dev, "request interrupt failed.\n");
782 return ret;
783 }
784 mixer_res->irq = res->start;
785
786 return 0;
787}
788
789static int vp_resources_init(struct mixer_context *mixer_ctx)
790{
791 struct device *dev = &mixer_ctx->pdev->dev;
792 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
793 struct resource *res;
794
795 mixer_res->vp = devm_clk_get(dev, "vp");
796 if (IS_ERR(mixer_res->vp)) {
797 dev_err(dev, "failed to get clock 'vp'\n");
798 return -ENODEV;
799 }
800 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
801 if (IS_ERR(mixer_res->sclk_mixer)) {
802 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
803 return -ENODEV;
804 }
805 mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
806 if (IS_ERR(mixer_res->sclk_dac)) {
807 dev_err(dev, "failed to get clock 'sclk_dac'\n");
808 return -ENODEV;
809 }
810
811 if (mixer_res->sclk_hdmi)
812 clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
813
814 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
815 if (res == NULL) {
816 dev_err(dev, "get memory resource failed.\n");
817 return -ENXIO;
818 }
819
820 mixer_res->vp_regs = devm_ioremap(dev, res->start,
821 resource_size(res));
822 if (mixer_res->vp_regs == NULL) {
823 dev_err(dev, "register mapping failed.\n");
824 return -ENXIO;
825 }
826
827 return 0;
828}
829
830static int mixer_initialize(void *ctx, struct drm_device *drm_dev)
831{
832 int ret;
833 struct mixer_context *mixer_ctx = ctx;
834
835 mixer_ctx->drm_dev = drm_dev;
836
837 /* acquire resources: regs, irqs, clocks */
838 ret = mixer_resources_init(mixer_ctx);
839 if (ret) {
840 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
841 return ret;
842 }
843
844 if (mixer_ctx->vp_enabled) {
845 /* acquire vp resources: regs, irqs, clocks */
846 ret = vp_resources_init(mixer_ctx);
847 if (ret) {
848 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
849 return ret;
850 }
851 }
852
853 return ret;
854}
855
Inki Dae1055b392012-10-19 17:37:35 +0900856static int mixer_iommu_on(void *ctx, bool enable)
857{
Inki Dae1055b392012-10-19 17:37:35 +0900858 struct mixer_context *mdata = ctx;
Inki Dae1055b392012-10-19 17:37:35 +0900859
Sean Paul45517892014-01-30 16:19:05 -0500860 if (is_drm_iommu_supported(mdata->drm_dev)) {
Inki Dae1055b392012-10-19 17:37:35 +0900861 if (enable)
Sean Paul45517892014-01-30 16:19:05 -0500862 return drm_iommu_attach_device(mdata->drm_dev,
863 mdata->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900864
Sean Paul45517892014-01-30 16:19:05 -0500865 drm_iommu_detach_device(mdata->drm_dev, mdata->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900866 }
867 return 0;
868}
869
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900870static int mixer_enable_vblank(void *ctx, int pipe)
871{
872 struct mixer_context *mixer_ctx = ctx;
873 struct mixer_resources *res = &mixer_ctx->mixer_res;
874
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900875 mixer_ctx->pipe = pipe;
876
877 /* enable vsync interrupt */
878 mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
879 MXR_INT_EN_VSYNC);
880
881 return 0;
882}
883
884static void mixer_disable_vblank(void *ctx)
885{
886 struct mixer_context *mixer_ctx = ctx;
887 struct mixer_resources *res = &mixer_ctx->mixer_res;
888
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900889 /* disable vsync interrupt */
890 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
891}
892
893static void mixer_win_mode_set(void *ctx,
894 struct exynos_drm_overlay *overlay)
895{
896 struct mixer_context *mixer_ctx = ctx;
897 struct hdmi_win_data *win_data;
898 int win;
899
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900900 if (!overlay) {
901 DRM_ERROR("overlay is NULL\n");
902 return;
903 }
904
905 DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
906 overlay->fb_width, overlay->fb_height,
907 overlay->fb_x, overlay->fb_y,
908 overlay->crtc_width, overlay->crtc_height,
909 overlay->crtc_x, overlay->crtc_y);
910
911 win = overlay->zpos;
912 if (win == DEFAULT_ZPOS)
Joonyoung Shima2ee1512012-04-05 20:49:25 +0900913 win = MIXER_DEFAULT_WIN;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900914
Krzysztof Kozlowski1586d802013-05-27 15:00:43 +0900915 if (win < 0 || win >= MIXER_WIN_NR) {
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900916 DRM_ERROR("mixer window[%d] is wrong\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900917 return;
918 }
919
920 win_data = &mixer_ctx->win_data[win];
921
922 win_data->dma_addr = overlay->dma_addr[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900923 win_data->chroma_dma_addr = overlay->dma_addr[1];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900924 win_data->pixel_format = overlay->pixel_format;
925 win_data->bpp = overlay->bpp;
926
927 win_data->crtc_x = overlay->crtc_x;
928 win_data->crtc_y = overlay->crtc_y;
929 win_data->crtc_width = overlay->crtc_width;
930 win_data->crtc_height = overlay->crtc_height;
931
932 win_data->fb_x = overlay->fb_x;
933 win_data->fb_y = overlay->fb_y;
934 win_data->fb_width = overlay->fb_width;
935 win_data->fb_height = overlay->fb_height;
Seung-Woo Kim8dcb96b2012-04-24 18:52:22 +0900936 win_data->src_width = overlay->src_width;
937 win_data->src_height = overlay->src_height;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900938
939 win_data->mode_width = overlay->mode_width;
940 win_data->mode_height = overlay->mode_height;
941
942 win_data->scan_flags = overlay->scan_flag;
943}
944
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900945static void mixer_win_commit(void *ctx, int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900946{
947 struct mixer_context *mixer_ctx = ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900948
YoungJun Chocbc4c332013-06-12 10:44:40 +0900949 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900950
Shirish Sdda90122013-01-23 22:03:18 -0500951 mutex_lock(&mixer_ctx->mixer_mutex);
952 if (!mixer_ctx->powered) {
953 mutex_unlock(&mixer_ctx->mixer_mutex);
954 return;
955 }
956 mutex_unlock(&mixer_ctx->mixer_mutex);
957
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530958 if (win > 1 && mixer_ctx->vp_enabled)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900959 vp_video_buffer(mixer_ctx, win);
960 else
961 mixer_graph_buffer(mixer_ctx, win);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530962
963 mixer_ctx->win_data[win].enabled = true;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900964}
965
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900966static void mixer_win_disable(void *ctx, int win)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900967{
968 struct mixer_context *mixer_ctx = ctx;
969 struct mixer_resources *res = &mixer_ctx->mixer_res;
970 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900971
YoungJun Chocbc4c332013-06-12 10:44:40 +0900972 DRM_DEBUG_KMS("win: %d\n", win);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900973
Prathyush Kdb43fd12012-12-06 20:16:05 +0530974 mutex_lock(&mixer_ctx->mixer_mutex);
975 if (!mixer_ctx->powered) {
976 mutex_unlock(&mixer_ctx->mixer_mutex);
977 mixer_ctx->win_data[win].resume = false;
978 return;
979 }
980 mutex_unlock(&mixer_ctx->mixer_mutex);
981
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900982 spin_lock_irqsave(&res->reg_slock, flags);
983 mixer_vsync_set_update(mixer_ctx, false);
984
985 mixer_cfg_layer(mixer_ctx, win, false);
986
987 mixer_vsync_set_update(mixer_ctx, true);
988 spin_unlock_irqrestore(&res->reg_slock, flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +0530989
990 mixer_ctx->win_data[win].enabled = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900991}
992
Rahul Sharma16844fb2013-06-10 14:50:00 +0530993static int mixer_check_mode(void *ctx, struct drm_display_mode *mode)
Rahul Sharma0ea68222013-01-15 08:11:06 -0500994{
Rahul Sharmadef5e092013-06-19 18:21:08 +0530995 struct mixer_context *mixer_ctx = ctx;
Rahul Sharma0ea68222013-01-15 08:11:06 -0500996 u32 w, h;
997
Rahul Sharma16844fb2013-06-10 14:50:00 +0530998 w = mode->hdisplay;
999 h = mode->vdisplay;
Rahul Sharma0ea68222013-01-15 08:11:06 -05001000
Rahul Sharma16844fb2013-06-10 14:50:00 +05301001 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1002 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1003 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
Rahul Sharma0ea68222013-01-15 08:11:06 -05001004
Rahul Sharmadef5e092013-06-19 18:21:08 +05301005 if (mixer_ctx->mxr_ver == MXR_VER_0_0_0_16 ||
1006 mixer_ctx->mxr_ver == MXR_VER_128_0_0_184)
1007 return 0;
1008
Rahul Sharma0ea68222013-01-15 08:11:06 -05001009 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1010 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1011 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1012 return 0;
1013
1014 return -EINVAL;
1015}
Prathyush K8137a2e2012-12-06 20:16:01 +05301016static void mixer_wait_for_vblank(void *ctx)
1017{
1018 struct mixer_context *mixer_ctx = ctx;
Prathyush K8137a2e2012-12-06 20:16:01 +05301019
Prathyush K6e95d5e2012-12-06 20:16:03 +05301020 mutex_lock(&mixer_ctx->mixer_mutex);
1021 if (!mixer_ctx->powered) {
1022 mutex_unlock(&mixer_ctx->mixer_mutex);
1023 return;
1024 }
1025 mutex_unlock(&mixer_ctx->mixer_mutex);
1026
1027 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1028
1029 /*
1030 * wait for MIXER to signal VSYNC interrupt or return after
1031 * timeout which is set to 50ms (refresh rate of 20).
1032 */
1033 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1034 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001035 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301036 DRM_DEBUG_KMS("vblank wait timed out.\n");
1037}
1038
Prathyush Kdb43fd12012-12-06 20:16:05 +05301039static void mixer_window_suspend(struct mixer_context *ctx)
1040{
1041 struct hdmi_win_data *win_data;
1042 int i;
1043
1044 for (i = 0; i < MIXER_WIN_NR; i++) {
1045 win_data = &ctx->win_data[i];
1046 win_data->resume = win_data->enabled;
1047 mixer_win_disable(ctx, i);
1048 }
1049 mixer_wait_for_vblank(ctx);
1050}
1051
1052static void mixer_window_resume(struct mixer_context *ctx)
1053{
1054 struct hdmi_win_data *win_data;
1055 int i;
1056
1057 for (i = 0; i < MIXER_WIN_NR; i++) {
1058 win_data = &ctx->win_data[i];
1059 win_data->enabled = win_data->resume;
1060 win_data->resume = false;
Sean Paul87244fa2014-01-30 16:19:07 -05001061 if (win_data->enabled)
1062 mixer_win_commit(ctx, i);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301063 }
1064}
1065
1066static void mixer_poweron(struct mixer_context *ctx)
1067{
1068 struct mixer_resources *res = &ctx->mixer_res;
1069
Prathyush Kdb43fd12012-12-06 20:16:05 +05301070 mutex_lock(&ctx->mixer_mutex);
1071 if (ctx->powered) {
1072 mutex_unlock(&ctx->mixer_mutex);
1073 return;
1074 }
1075 ctx->powered = true;
1076 mutex_unlock(&ctx->mixer_mutex);
1077
Sean Paul0bfb1f82013-06-11 12:24:02 +05301078 clk_prepare_enable(res->mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301079 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +05301080 clk_prepare_enable(res->vp);
1081 clk_prepare_enable(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301082 }
1083
1084 mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
1085 mixer_win_reset(ctx);
1086
1087 mixer_window_resume(ctx);
1088}
1089
1090static void mixer_poweroff(struct mixer_context *ctx)
1091{
1092 struct mixer_resources *res = &ctx->mixer_res;
1093
Prathyush Kdb43fd12012-12-06 20:16:05 +05301094 mutex_lock(&ctx->mixer_mutex);
1095 if (!ctx->powered)
1096 goto out;
1097 mutex_unlock(&ctx->mixer_mutex);
1098
1099 mixer_window_suspend(ctx);
1100
1101 ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
1102
Sean Paul0bfb1f82013-06-11 12:24:02 +05301103 clk_disable_unprepare(res->mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301104 if (ctx->vp_enabled) {
Sean Paul0bfb1f82013-06-11 12:24:02 +05301105 clk_disable_unprepare(res->vp);
1106 clk_disable_unprepare(res->sclk_mixer);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301107 }
1108
Prathyush Kdb43fd12012-12-06 20:16:05 +05301109 mutex_lock(&ctx->mixer_mutex);
1110 ctx->powered = false;
1111
1112out:
1113 mutex_unlock(&ctx->mixer_mutex);
1114}
1115
1116static void mixer_dpms(void *ctx, int mode)
1117{
1118 struct mixer_context *mixer_ctx = ctx;
1119
Prathyush Kdb43fd12012-12-06 20:16:05 +05301120 switch (mode) {
1121 case DRM_MODE_DPMS_ON:
Rahul Sharma000f1302012-11-28 11:30:24 +05301122 if (pm_runtime_suspended(mixer_ctx->dev))
1123 pm_runtime_get_sync(mixer_ctx->dev);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301124 break;
1125 case DRM_MODE_DPMS_STANDBY:
1126 case DRM_MODE_DPMS_SUSPEND:
1127 case DRM_MODE_DPMS_OFF:
Rahul Sharma000f1302012-11-28 11:30:24 +05301128 if (!pm_runtime_suspended(mixer_ctx->dev))
1129 pm_runtime_put_sync(mixer_ctx->dev);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301130 break;
1131 default:
1132 DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
1133 break;
1134 }
1135}
1136
Joonyoung Shim578b6062012-04-05 20:49:26 +09001137static struct exynos_mixer_ops mixer_ops = {
1138 /* manager */
Sean Paul45517892014-01-30 16:19:05 -05001139 .initialize = mixer_initialize,
Inki Dae1055b392012-10-19 17:37:35 +09001140 .iommu_on = mixer_iommu_on,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001141 .enable_vblank = mixer_enable_vblank,
1142 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301143 .wait_for_vblank = mixer_wait_for_vblank,
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001144 .dpms = mixer_dpms,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001145 .win_mode_set = mixer_win_mode_set,
1146 .win_commit = mixer_win_commit,
1147 .win_disable = mixer_win_disable,
Rahul Sharma0ea68222013-01-15 08:11:06 -05001148
1149 /* display */
Rahul Sharma16844fb2013-06-10 14:50:00 +05301150 .check_mode = mixer_check_mode,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001151};
1152
Rahul Sharmadef5e092013-06-19 18:21:08 +05301153static struct mixer_drv_data exynos5420_mxr_drv_data = {
1154 .version = MXR_VER_128_0_0_184,
1155 .is_vp_enabled = 0,
1156};
1157
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301158static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301159 .version = MXR_VER_16_0_33_0,
1160 .is_vp_enabled = 0,
1161};
1162
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301163static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301164 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301165 .is_vp_enabled = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301166};
1167
1168static struct platform_device_id mixer_driver_types[] = {
1169 {
1170 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301171 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301172 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301173 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301174 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301175 }, {
1176 /* end node */
1177 }
1178};
1179
1180static struct of_device_id mixer_match_types[] = {
1181 {
1182 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301183 .data = &exynos5250_mxr_drv_data,
1184 }, {
1185 .compatible = "samsung,exynos5250-mixer",
1186 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301187 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301188 .compatible = "samsung,exynos5420-mixer",
1189 .data = &exynos5420_mxr_drv_data,
1190 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301191 /* end node */
1192 }
1193};
1194
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001195static int mixer_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001196{
1197 struct device *dev = &pdev->dev;
1198 struct exynos_drm_hdmi_context *drm_hdmi_ctx;
1199 struct mixer_context *ctx;
Rahul Sharma1e123442012-10-04 20:48:51 +05301200 struct mixer_drv_data *drv;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001201
1202 dev_info(dev, "probe start\n");
1203
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001204 drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301205 GFP_KERNEL);
Sachin Kamat38bb5252013-08-19 19:04:55 +09001206 if (!drm_hdmi_ctx)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001207 return -ENOMEM;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001208
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001209 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
Sachin Kamat38bb5252013-08-19 19:04:55 +09001210 if (!ctx)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001211 return -ENOMEM;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001212
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001213 mutex_init(&ctx->mixer_mutex);
1214
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301215 if (dev->of_node) {
1216 const struct of_device_id *match;
Sachin Kamate436b092013-06-05 16:00:23 +09001217 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301218 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301219 } else {
1220 drv = (struct mixer_drv_data *)
1221 platform_get_device_id(pdev)->driver_data;
1222 }
1223
Sean Paul45517892014-01-30 16:19:05 -05001224 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001225 ctx->dev = dev;
Inki Dae1055b392012-10-19 17:37:35 +09001226 ctx->parent_ctx = (void *)drm_hdmi_ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001227 drm_hdmi_ctx->ctx = (void *)ctx;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301228 ctx->vp_enabled = drv->is_vp_enabled;
Rahul Sharma1e123442012-10-04 20:48:51 +05301229 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001230 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301231 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001232
1233 platform_set_drvdata(pdev, drm_hdmi_ctx);
1234
Rahul Sharma768c3052012-10-04 20:48:56 +05301235 /* attach mixer driver to common hdmi. */
1236 exynos_mixer_drv_attach(drm_hdmi_ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001237
1238 /* register specific callback point to common hdmi. */
Joonyoung Shim578b6062012-04-05 20:49:26 +09001239 exynos_mixer_ops_register(&mixer_ops);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001240
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001241 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001242
1243 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001244}
1245
1246static int mixer_remove(struct platform_device *pdev)
1247{
Sachin Kamat9416dfa2012-06-19 11:47:41 +05301248 dev_info(&pdev->dev, "remove successful\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001249
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001250 pm_runtime_disable(&pdev->dev);
1251
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001252 return 0;
1253}
1254
Joonyoung Shimab27af82012-04-23 19:35:51 +09001255#ifdef CONFIG_PM_SLEEP
1256static int mixer_suspend(struct device *dev)
1257{
1258 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1259 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1260
Rahul Sharma000f1302012-11-28 11:30:24 +05301261 if (pm_runtime_suspended(dev)) {
YoungJun Chocbc4c332013-06-12 10:44:40 +09001262 DRM_DEBUG_KMS("Already suspended\n");
Rahul Sharma000f1302012-11-28 11:30:24 +05301263 return 0;
1264 }
1265
Joonyoung Shimab27af82012-04-23 19:35:51 +09001266 mixer_poweroff(ctx);
1267
1268 return 0;
1269}
Rahul Sharma000f1302012-11-28 11:30:24 +05301270
1271static int mixer_resume(struct device *dev)
1272{
1273 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1274 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1275
Rahul Sharma000f1302012-11-28 11:30:24 +05301276 if (!pm_runtime_suspended(dev)) {
YoungJun Chocbc4c332013-06-12 10:44:40 +09001277 DRM_DEBUG_KMS("Already resumed\n");
Rahul Sharma000f1302012-11-28 11:30:24 +05301278 return 0;
1279 }
1280
1281 mixer_poweron(ctx);
1282
1283 return 0;
1284}
Joonyoung Shimab27af82012-04-23 19:35:51 +09001285#endif
1286
Rahul Sharma000f1302012-11-28 11:30:24 +05301287#ifdef CONFIG_PM_RUNTIME
1288static int mixer_runtime_suspend(struct device *dev)
1289{
1290 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1291 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1292
Rahul Sharma000f1302012-11-28 11:30:24 +05301293 mixer_poweroff(ctx);
1294
1295 return 0;
1296}
1297
1298static int mixer_runtime_resume(struct device *dev)
1299{
1300 struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
1301 struct mixer_context *ctx = drm_hdmi_ctx->ctx;
1302
Rahul Sharma000f1302012-11-28 11:30:24 +05301303 mixer_poweron(ctx);
1304
1305 return 0;
1306}
1307#endif
1308
1309static const struct dev_pm_ops mixer_pm_ops = {
1310 SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume)
1311 SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL)
1312};
Joonyoung Shimab27af82012-04-23 19:35:51 +09001313
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001314struct platform_driver mixer_driver = {
1315 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301316 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001317 .owner = THIS_MODULE,
Joonyoung Shimab27af82012-04-23 19:35:51 +09001318 .pm = &mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301319 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001320 },
1321 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001322 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301323 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001324};