blob: ae7b122274acad6f4358f84c8ee21c69f3181cc3 [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"
Marek Szyprowski0488f502015-11-30 14:53:21 +010040#include "exynos_drm_fb.h"
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +090041#include "exynos_drm_plane.h"
Inki Dae1055b392012-10-19 17:37:35 +090042#include "exynos_drm_iommu.h"
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090043
Sean Paulf041b252014-01-30 16:19:15 -050044#define MIXER_WIN_NR 3
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +090045#define VP_DEFAULT_WIN 2
Seung-Woo Kimd8408322011-12-21 17:39:39 +090046
Tobias Jakobi7a57ca72015-04-27 23:11:59 +020047/* The pixelformats that are natively supported by the mixer. */
48#define MXR_FORMAT_RGB565 4
49#define MXR_FORMAT_ARGB1555 5
50#define MXR_FORMAT_ARGB4444 6
51#define MXR_FORMAT_ARGB8888 7
52
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090053struct mixer_resources {
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090054 int irq;
55 void __iomem *mixer_regs;
56 void __iomem *vp_regs;
57 spinlock_t reg_slock;
58 struct clk *mixer;
59 struct clk *vp;
Marek Szyprowski04427ec2015-02-02 14:20:28 +010060 struct clk *hdmi;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090061 struct clk *sclk_mixer;
62 struct clk *sclk_hdmi;
Marek Szyprowskiff830c92014-07-01 10:10:07 +020063 struct clk *mout_mixer;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090064};
65
Rahul Sharma1e123442012-10-04 20:48:51 +053066enum mixer_version_id {
67 MXR_VER_0_0_0_16,
68 MXR_VER_16_0_33_0,
Rahul Sharmadef5e092013-06-19 18:21:08 +053069 MXR_VER_128_0_0_184,
Rahul Sharma1e123442012-10-04 20:48:51 +053070};
71
Andrzej Hajdaa44652e2015-07-09 08:25:42 +020072enum mixer_flag_bits {
73 MXR_BIT_POWERED,
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +020074 MXR_BIT_VSYNC,
Andrzej Hajdaa44652e2015-07-09 08:25:42 +020075};
76
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +090077static const uint32_t mixer_formats[] = {
78 DRM_FORMAT_XRGB4444,
79 DRM_FORMAT_XRGB1555,
80 DRM_FORMAT_RGB565,
81 DRM_FORMAT_XRGB8888,
82 DRM_FORMAT_ARGB8888,
83};
84
85static const uint32_t vp_formats[] = {
86 DRM_FORMAT_NV12,
87 DRM_FORMAT_NV21,
88};
89
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090090struct mixer_context {
Sean Paul45517892014-01-30 16:19:05 -050091 struct platform_device *pdev;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090092 struct device *dev;
Inki Dae1055b392012-10-19 17:37:35 +090093 struct drm_device *drm_dev;
Gustavo Padovan93bca242015-01-18 18:16:23 +090094 struct exynos_drm_crtc *crtc;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +090095 struct exynos_drm_plane planes[MIXER_WIN_NR];
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090096 int pipe;
Andrzej Hajdaa44652e2015-07-09 08:25:42 +020097 unsigned long flags;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090098 bool interlace;
Rahul Sharma1b8e5742012-10-04 20:48:52 +053099 bool vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200100 bool has_sclk;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900101
102 struct mixer_resources mixer_res;
Rahul Sharma1e123442012-10-04 20:48:51 +0530103 enum mixer_version_id mxr_ver;
Prathyush K6e95d5e2012-12-06 20:16:03 +0530104 wait_queue_head_t wait_vsync_queue;
105 atomic_t wait_vsync_event;
Rahul Sharma1e123442012-10-04 20:48:51 +0530106};
107
108struct mixer_drv_data {
109 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530110 bool is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200111 bool has_sclk;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900112};
113
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100114static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
115 {
116 .zpos = 0,
117 .type = DRM_PLANE_TYPE_PRIMARY,
118 .pixel_formats = mixer_formats,
119 .num_pixel_formats = ARRAY_SIZE(mixer_formats),
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100120 .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
121 EXYNOS_DRM_PLANE_CAP_ZPOS,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100122 }, {
123 .zpos = 1,
124 .type = DRM_PLANE_TYPE_CURSOR,
125 .pixel_formats = mixer_formats,
126 .num_pixel_formats = ARRAY_SIZE(mixer_formats),
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100127 .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
128 EXYNOS_DRM_PLANE_CAP_ZPOS,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100129 }, {
130 .zpos = 2,
131 .type = DRM_PLANE_TYPE_OVERLAY,
132 .pixel_formats = vp_formats,
133 .num_pixel_formats = ARRAY_SIZE(vp_formats),
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100134 .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE |
135 EXYNOS_DRM_PLANE_CAP_ZPOS,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100136 },
137};
138
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900139static const u8 filter_y_horiz_tap8[] = {
140 0, -1, -1, -1, -1, -1, -1, -1,
141 -1, -1, -1, -1, -1, 0, 0, 0,
142 0, 2, 4, 5, 6, 6, 6, 6,
143 6, 5, 5, 4, 3, 2, 1, 1,
144 0, -6, -12, -16, -18, -20, -21, -20,
145 -20, -18, -16, -13, -10, -8, -5, -2,
146 127, 126, 125, 121, 114, 107, 99, 89,
147 79, 68, 57, 46, 35, 25, 16, 8,
148};
149
150static const u8 filter_y_vert_tap4[] = {
151 0, -3, -6, -8, -8, -8, -8, -7,
152 -6, -5, -4, -3, -2, -1, -1, 0,
153 127, 126, 124, 118, 111, 102, 92, 81,
154 70, 59, 48, 37, 27, 19, 11, 5,
155 0, 5, 11, 19, 27, 37, 48, 59,
156 70, 81, 92, 102, 111, 118, 124, 126,
157 0, 0, -1, -1, -2, -3, -4, -5,
158 -6, -7, -8, -8, -8, -8, -6, -3,
159};
160
161static const u8 filter_cr_horiz_tap4[] = {
162 0, -3, -6, -8, -8, -8, -8, -7,
163 -6, -5, -4, -3, -2, -1, -1, 0,
164 127, 126, 124, 118, 111, 102, 92, 81,
165 70, 59, 48, 37, 27, 19, 11, 5,
166};
167
Marek Szyprowskif657a992015-12-16 13:21:46 +0100168static inline bool is_alpha_format(unsigned int pixel_format)
169{
170 switch (pixel_format) {
171 case DRM_FORMAT_ARGB8888:
172 return true;
173 default:
174 return false;
175 }
176}
177
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900178static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
179{
180 return readl(res->vp_regs + reg_id);
181}
182
183static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
184 u32 val)
185{
186 writel(val, res->vp_regs + reg_id);
187}
188
189static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
190 u32 val, u32 mask)
191{
192 u32 old = vp_reg_read(res, reg_id);
193
194 val = (val & mask) | (old & ~mask);
195 writel(val, res->vp_regs + reg_id);
196}
197
198static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
199{
200 return readl(res->mixer_regs + reg_id);
201}
202
203static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
204 u32 val)
205{
206 writel(val, res->mixer_regs + reg_id);
207}
208
209static inline void mixer_reg_writemask(struct mixer_resources *res,
210 u32 reg_id, u32 val, u32 mask)
211{
212 u32 old = mixer_reg_read(res, reg_id);
213
214 val = (val & mask) | (old & ~mask);
215 writel(val, res->mixer_regs + reg_id);
216}
217
218static void mixer_regs_dump(struct mixer_context *ctx)
219{
220#define DUMPREG(reg_id) \
221do { \
222 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
223 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
224} while (0)
225
226 DUMPREG(MXR_STATUS);
227 DUMPREG(MXR_CFG);
228 DUMPREG(MXR_INT_EN);
229 DUMPREG(MXR_INT_STATUS);
230
231 DUMPREG(MXR_LAYER_CFG);
232 DUMPREG(MXR_VIDEO_CFG);
233
234 DUMPREG(MXR_GRAPHIC0_CFG);
235 DUMPREG(MXR_GRAPHIC0_BASE);
236 DUMPREG(MXR_GRAPHIC0_SPAN);
237 DUMPREG(MXR_GRAPHIC0_WH);
238 DUMPREG(MXR_GRAPHIC0_SXY);
239 DUMPREG(MXR_GRAPHIC0_DXY);
240
241 DUMPREG(MXR_GRAPHIC1_CFG);
242 DUMPREG(MXR_GRAPHIC1_BASE);
243 DUMPREG(MXR_GRAPHIC1_SPAN);
244 DUMPREG(MXR_GRAPHIC1_WH);
245 DUMPREG(MXR_GRAPHIC1_SXY);
246 DUMPREG(MXR_GRAPHIC1_DXY);
247#undef DUMPREG
248}
249
250static void vp_regs_dump(struct mixer_context *ctx)
251{
252#define DUMPREG(reg_id) \
253do { \
254 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
255 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
256} while (0)
257
258 DUMPREG(VP_ENABLE);
259 DUMPREG(VP_SRESET);
260 DUMPREG(VP_SHADOW_UPDATE);
261 DUMPREG(VP_FIELD_ID);
262 DUMPREG(VP_MODE);
263 DUMPREG(VP_IMG_SIZE_Y);
264 DUMPREG(VP_IMG_SIZE_C);
265 DUMPREG(VP_PER_RATE_CTRL);
266 DUMPREG(VP_TOP_Y_PTR);
267 DUMPREG(VP_BOT_Y_PTR);
268 DUMPREG(VP_TOP_C_PTR);
269 DUMPREG(VP_BOT_C_PTR);
270 DUMPREG(VP_ENDIAN_MODE);
271 DUMPREG(VP_SRC_H_POSITION);
272 DUMPREG(VP_SRC_V_POSITION);
273 DUMPREG(VP_SRC_WIDTH);
274 DUMPREG(VP_SRC_HEIGHT);
275 DUMPREG(VP_DST_H_POSITION);
276 DUMPREG(VP_DST_V_POSITION);
277 DUMPREG(VP_DST_WIDTH);
278 DUMPREG(VP_DST_HEIGHT);
279 DUMPREG(VP_H_RATIO);
280 DUMPREG(VP_V_RATIO);
281
282#undef DUMPREG
283}
284
285static inline void vp_filter_set(struct mixer_resources *res,
286 int reg_id, const u8 *data, unsigned int size)
287{
288 /* assure 4-byte align */
289 BUG_ON(size & 3);
290 for (; size; size -= 4, reg_id += 4, data += 4) {
291 u32 val = (data[0] << 24) | (data[1] << 16) |
292 (data[2] << 8) | data[3];
293 vp_reg_write(res, reg_id, val);
294 }
295}
296
297static void vp_default_filter(struct mixer_resources *res)
298{
299 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530300 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900301 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530302 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900303 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530304 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900305}
306
Marek Szyprowskif657a992015-12-16 13:21:46 +0100307static void mixer_cfg_gfx_blend(struct mixer_context *ctx, unsigned int win,
308 bool alpha)
309{
310 struct mixer_resources *res = &ctx->mixer_res;
311 u32 val;
312
313 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
314 if (alpha) {
315 /* blending based on pixel alpha */
316 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
317 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
318 }
319 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
320 val, MXR_GRP_CFG_MISC_MASK);
321}
322
323static void mixer_cfg_vp_blend(struct mixer_context *ctx)
324{
325 struct mixer_resources *res = &ctx->mixer_res;
326 u32 val;
327
328 /*
329 * No blending at the moment since the NV12/NV21 pixelformats don't
330 * have an alpha channel. However the mixer supports a global alpha
331 * value for a layer. Once this functionality is exposed, we can
332 * support blending of the video layer through this.
333 */
334 val = 0;
335 mixer_reg_write(res, MXR_VIDEO_CFG, val);
336}
337
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900338static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
339{
340 struct mixer_resources *res = &ctx->mixer_res;
341
342 /* block update on vsync */
343 mixer_reg_writemask(res, MXR_STATUS, enable ?
344 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
345
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530346 if (ctx->vp_enabled)
347 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900348 VP_SHADOW_UPDATE_ENABLE : 0);
349}
350
351static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
352{
353 struct mixer_resources *res = &ctx->mixer_res;
354 u32 val;
355
356 /* choosing between interlace and progressive mode */
357 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
Tobias Jakobi1e6d4592015-04-07 01:14:50 +0200358 MXR_CFG_SCAN_PROGRESSIVE);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900359
Rahul Sharmadef5e092013-06-19 18:21:08 +0530360 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
361 /* choosing between proper HD and SD mode */
362 if (height <= 480)
363 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
364 else if (height <= 576)
365 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
366 else if (height <= 720)
367 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
368 else if (height <= 1080)
369 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
370 else
371 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
372 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900373
374 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
375}
376
377static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
378{
379 struct mixer_resources *res = &ctx->mixer_res;
380 u32 val;
381
382 if (height == 480) {
383 val = MXR_CFG_RGB601_0_255;
384 } else if (height == 576) {
385 val = MXR_CFG_RGB601_0_255;
386 } else if (height == 720) {
387 val = MXR_CFG_RGB709_16_235;
388 mixer_reg_write(res, MXR_CM_COEFF_Y,
389 (1 << 30) | (94 << 20) | (314 << 10) |
390 (32 << 0));
391 mixer_reg_write(res, MXR_CM_COEFF_CB,
392 (972 << 20) | (851 << 10) | (225 << 0));
393 mixer_reg_write(res, MXR_CM_COEFF_CR,
394 (225 << 20) | (820 << 10) | (1004 << 0));
395 } else if (height == 1080) {
396 val = MXR_CFG_RGB709_16_235;
397 mixer_reg_write(res, MXR_CM_COEFF_Y,
398 (1 << 30) | (94 << 20) | (314 << 10) |
399 (32 << 0));
400 mixer_reg_write(res, MXR_CM_COEFF_CB,
401 (972 << 20) | (851 << 10) | (225 << 0));
402 mixer_reg_write(res, MXR_CM_COEFF_CR,
403 (225 << 20) | (820 << 10) | (1004 << 0));
404 } else {
405 val = MXR_CFG_RGB709_16_235;
406 mixer_reg_write(res, MXR_CM_COEFF_Y,
407 (1 << 30) | (94 << 20) | (314 << 10) |
408 (32 << 0));
409 mixer_reg_write(res, MXR_CM_COEFF_CB,
410 (972 << 20) | (851 << 10) | (225 << 0));
411 mixer_reg_write(res, MXR_CM_COEFF_CR,
412 (225 << 20) | (820 << 10) | (1004 << 0));
413 }
414
415 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
416}
417
Tobias Jakobi5b1d5bc2015-05-06 14:10:22 +0200418static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100419 unsigned int priority, bool enable)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900420{
421 struct mixer_resources *res = &ctx->mixer_res;
422 u32 val = enable ? ~0 : 0;
423
424 switch (win) {
425 case 0:
426 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100427 mixer_reg_writemask(res, MXR_LAYER_CFG,
428 MXR_LAYER_CFG_GRP0_VAL(priority),
429 MXR_LAYER_CFG_GRP0_MASK);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900430 break;
431 case 1:
432 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100433 mixer_reg_writemask(res, MXR_LAYER_CFG,
434 MXR_LAYER_CFG_GRP1_VAL(priority),
435 MXR_LAYER_CFG_GRP1_MASK);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900436 break;
437 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530438 if (ctx->vp_enabled) {
439 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
440 mixer_reg_writemask(res, MXR_CFG, val,
441 MXR_CFG_VP_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100442 mixer_reg_writemask(res, MXR_LAYER_CFG,
443 MXR_LAYER_CFG_VP_VAL(priority),
444 MXR_LAYER_CFG_VP_MASK);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530445 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900446 break;
447 }
448}
449
450static void mixer_run(struct mixer_context *ctx)
451{
452 struct mixer_resources *res = &ctx->mixer_res;
453
454 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900455}
456
Rahul Sharma381be022014-06-23 11:02:22 +0530457static void mixer_stop(struct mixer_context *ctx)
458{
459 struct mixer_resources *res = &ctx->mixer_res;
460 int timeout = 20;
461
462 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
463
464 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
465 --timeout)
466 usleep_range(10000, 12000);
Rahul Sharma381be022014-06-23 11:02:22 +0530467}
468
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900469static void vp_video_buffer(struct mixer_context *ctx,
470 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900471{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100472 struct exynos_drm_plane_state *state =
473 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100474 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900475 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100476 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900477 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900478 dma_addr_t luma_addr[2], chroma_addr[2];
479 bool tiled_mode = false;
480 bool crcb_mode = false;
481 u32 val;
482
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900483 switch (fb->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900484 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900485 crcb_mode = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900486 break;
Tobias Jakobi8f2590f2015-04-27 23:10:16 +0200487 case DRM_FORMAT_NV21:
488 crcb_mode = true;
489 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900490 default:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900491 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900492 fb->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900493 return;
494 }
495
Marek Szyprowski0488f502015-11-30 14:53:21 +0100496 luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
497 chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900498
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900499 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900500 ctx->interlace = true;
501 if (tiled_mode) {
502 luma_addr[1] = luma_addr[0] + 0x40;
503 chroma_addr[1] = chroma_addr[0] + 0x40;
504 } else {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900505 luma_addr[1] = luma_addr[0] + fb->pitches[0];
506 chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900507 }
508 } else {
509 ctx->interlace = false;
510 luma_addr[1] = 0;
511 chroma_addr[1] = 0;
512 }
513
514 spin_lock_irqsave(&res->reg_slock, flags);
515 mixer_vsync_set_update(ctx, false);
516
517 /* interlace or progressive scan mode */
518 val = (ctx->interlace ? ~0 : 0);
519 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
520
521 /* setup format */
522 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
523 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
524 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
525
526 /* setting size of input image */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900527 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
528 VP_IMG_VSIZE(fb->height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900529 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900530 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
531 VP_IMG_VSIZE(fb->height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900532
Marek Szyprowski0114f402015-11-30 14:53:22 +0100533 vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
534 vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900535 vp_reg_write(res, VP_SRC_H_POSITION,
Marek Szyprowski0114f402015-11-30 14:53:22 +0100536 VP_SRC_H_POSITION_VAL(state->src.x));
537 vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900538
Marek Szyprowski0114f402015-11-30 14:53:22 +0100539 vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
540 vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900541 if (ctx->interlace) {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100542 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
543 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900544 } else {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100545 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
546 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900547 }
548
Marek Szyprowski0114f402015-11-30 14:53:22 +0100549 vp_reg_write(res, VP_H_RATIO, state->h_ratio);
550 vp_reg_write(res, VP_V_RATIO, state->v_ratio);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900551
552 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
553
554 /* set buffer address to vp */
555 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
556 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
557 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
558 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
559
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900560 mixer_cfg_scan(ctx, mode->vdisplay);
561 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100562 mixer_cfg_layer(ctx, plane->index, state->zpos + 1, true);
Marek Szyprowskif657a992015-12-16 13:21:46 +0100563 mixer_cfg_vp_blend(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900564 mixer_run(ctx);
565
566 mixer_vsync_set_update(ctx, true);
567 spin_unlock_irqrestore(&res->reg_slock, flags);
568
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200569 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900570 vp_regs_dump(ctx);
571}
572
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530573static void mixer_layer_update(struct mixer_context *ctx)
574{
575 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530576
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530577 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530578}
579
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900580static void mixer_graph_buffer(struct mixer_context *ctx,
581 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900582{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100583 struct exynos_drm_plane_state *state =
584 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100585 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900586 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100587 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900588 unsigned long flags;
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100589 unsigned int win = plane->index;
Tobias Jakobi26110152015-04-07 01:14:52 +0200590 unsigned int x_ratio = 0, y_ratio = 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900591 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900592 dma_addr_t dma_addr;
593 unsigned int fmt;
594 u32 val;
595
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900596 switch (fb->pixel_format) {
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200597 case DRM_FORMAT_XRGB4444:
598 fmt = MXR_FORMAT_ARGB4444;
599 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900600
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200601 case DRM_FORMAT_XRGB1555:
602 fmt = MXR_FORMAT_ARGB1555;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900603 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200604
605 case DRM_FORMAT_RGB565:
606 fmt = MXR_FORMAT_RGB565;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900607 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200608
609 case DRM_FORMAT_XRGB8888:
610 case DRM_FORMAT_ARGB8888:
611 fmt = MXR_FORMAT_ARGB8888;
612 break;
613
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900614 default:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200615 DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
616 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900617 }
618
Marek Szyprowskie463b062015-11-30 14:53:27 +0100619 /* ratio is already checked by common plane code */
620 x_ratio = state->h_ratio == (1 << 15);
621 y_ratio = state->v_ratio == (1 << 15);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900622
Marek Szyprowski0114f402015-11-30 14:53:22 +0100623 dst_x_offset = state->crtc.x;
624 dst_y_offset = state->crtc.y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900625
626 /* converting dma address base and source offset */
Marek Szyprowski0488f502015-11-30 14:53:21 +0100627 dma_addr = exynos_drm_fb_dma_addr(fb, 0)
Marek Szyprowski0114f402015-11-30 14:53:22 +0100628 + (state->src.x * fb->bits_per_pixel >> 3)
629 + (state->src.y * fb->pitches[0]);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900630 src_x_offset = 0;
631 src_y_offset = 0;
632
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900633 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900634 ctx->interlace = true;
635 else
636 ctx->interlace = false;
637
638 spin_lock_irqsave(&res->reg_slock, flags);
639 mixer_vsync_set_update(ctx, false);
640
641 /* setup format */
642 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
643 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
644
645 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000646 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900647 fb->pitches[0] / (fb->bits_per_pixel >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900648
Rahul Sharmadef5e092013-06-19 18:21:08 +0530649 /* setup display size */
650 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
Gustavo Padovan5d3d0992015-10-12 22:07:48 +0900651 win == DEFAULT_WIN) {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900652 val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
653 val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530654 mixer_reg_write(res, MXR_RESOLUTION, val);
655 }
656
Marek Szyprowski0114f402015-11-30 14:53:22 +0100657 val = MXR_GRP_WH_WIDTH(state->src.w);
658 val |= MXR_GRP_WH_HEIGHT(state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900659 val |= MXR_GRP_WH_H_SCALE(x_ratio);
660 val |= MXR_GRP_WH_V_SCALE(y_ratio);
661 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
662
663 /* setup offsets in source image */
664 val = MXR_GRP_SXY_SX(src_x_offset);
665 val |= MXR_GRP_SXY_SY(src_y_offset);
666 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
667
668 /* setup offsets in display image */
669 val = MXR_GRP_DXY_DX(dst_x_offset);
670 val |= MXR_GRP_DXY_DY(dst_y_offset);
671 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
672
673 /* set buffer address to mixer */
674 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
675
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900676 mixer_cfg_scan(ctx, mode->vdisplay);
677 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100678 mixer_cfg_layer(ctx, win, state->zpos + 1, true);
Marek Szyprowskif657a992015-12-16 13:21:46 +0100679 mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->pixel_format));
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530680
681 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530682 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
683 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530684 mixer_layer_update(ctx);
685
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900686 mixer_run(ctx);
687
688 mixer_vsync_set_update(ctx, true);
689 spin_unlock_irqrestore(&res->reg_slock, flags);
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200690
691 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900692}
693
694static void vp_win_reset(struct mixer_context *ctx)
695{
696 struct mixer_resources *res = &ctx->mixer_res;
697 int tries = 100;
698
699 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
700 for (tries = 100; tries; --tries) {
701 /* waiting until VP_SRESET_PROCESSING is 0 */
702 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
703 break;
Tomasz Stanislawski02b3de42015-09-25 14:48:29 +0200704 mdelay(10);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900705 }
706 WARN(tries == 0, "failed to reset Video Processor\n");
707}
708
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900709static void mixer_win_reset(struct mixer_context *ctx)
710{
711 struct mixer_resources *res = &ctx->mixer_res;
712 unsigned long flags;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900713
714 spin_lock_irqsave(&res->reg_slock, flags);
715 mixer_vsync_set_update(ctx, false);
716
717 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
718
719 /* set output in RGB888 mode */
720 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
721
722 /* 16 beat burst in DMA */
723 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
724 MXR_STATUS_BURST_MASK);
725
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100726 /* reset default layer priority */
727 mixer_reg_write(res, MXR_LAYER_CFG, 0);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900728
729 /* setting background color */
730 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
731 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
732 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
733
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530734 if (ctx->vp_enabled) {
735 /* configuration of Video Processor Registers */
736 vp_win_reset(ctx);
737 vp_default_filter(res);
738 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900739
740 /* disable all layers */
741 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
742 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530743 if (ctx->vp_enabled)
744 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900745
746 mixer_vsync_set_update(ctx, true);
747 spin_unlock_irqrestore(&res->reg_slock, flags);
748}
749
Sean Paul45517892014-01-30 16:19:05 -0500750static irqreturn_t mixer_irq_handler(int irq, void *arg)
751{
752 struct mixer_context *ctx = arg;
753 struct mixer_resources *res = &ctx->mixer_res;
754 u32 val, base, shadow;
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300755 int win;
Sean Paul45517892014-01-30 16:19:05 -0500756
757 spin_lock(&res->reg_slock);
758
759 /* read interrupt status for handling and clearing flags for VSYNC */
760 val = mixer_reg_read(res, MXR_INT_STATUS);
761
762 /* handling VSYNC */
763 if (val & MXR_INT_STATUS_VSYNC) {
Andrzej Hajda81a464d2015-07-09 10:07:53 +0200764 /* vsync interrupt use different bit for read and clear */
765 val |= MXR_INT_CLEAR_VSYNC;
766 val &= ~MXR_INT_STATUS_VSYNC;
767
Sean Paul45517892014-01-30 16:19:05 -0500768 /* interlace scan need to check shadow register */
769 if (ctx->interlace) {
770 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
771 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
772 if (base != shadow)
773 goto out;
774
775 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
776 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
777 if (base != shadow)
778 goto out;
779 }
780
Gustavo Padovaneafd5402015-07-16 12:23:32 -0300781 drm_crtc_handle_vblank(&ctx->crtc->base);
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300782 for (win = 0 ; win < MIXER_WIN_NR ; win++) {
783 struct exynos_drm_plane *plane = &ctx->planes[win];
784
785 if (!plane->pending_fb)
786 continue;
787
788 exynos_drm_crtc_finish_update(ctx->crtc, plane);
789 }
Sean Paul45517892014-01-30 16:19:05 -0500790
791 /* set wait vsync event to zero and wake up queue. */
792 if (atomic_read(&ctx->wait_vsync_event)) {
793 atomic_set(&ctx->wait_vsync_event, 0);
794 wake_up(&ctx->wait_vsync_queue);
795 }
796 }
797
798out:
799 /* clear interrupts */
Sean Paul45517892014-01-30 16:19:05 -0500800 mixer_reg_write(res, MXR_INT_STATUS, val);
801
802 spin_unlock(&res->reg_slock);
803
804 return IRQ_HANDLED;
805}
806
807static int mixer_resources_init(struct mixer_context *mixer_ctx)
808{
809 struct device *dev = &mixer_ctx->pdev->dev;
810 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
811 struct resource *res;
812 int ret;
813
814 spin_lock_init(&mixer_res->reg_slock);
815
816 mixer_res->mixer = devm_clk_get(dev, "mixer");
817 if (IS_ERR(mixer_res->mixer)) {
818 dev_err(dev, "failed to get clock 'mixer'\n");
819 return -ENODEV;
820 }
821
Marek Szyprowski04427ec2015-02-02 14:20:28 +0100822 mixer_res->hdmi = devm_clk_get(dev, "hdmi");
823 if (IS_ERR(mixer_res->hdmi)) {
824 dev_err(dev, "failed to get clock 'hdmi'\n");
825 return PTR_ERR(mixer_res->hdmi);
826 }
827
Sean Paul45517892014-01-30 16:19:05 -0500828 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
829 if (IS_ERR(mixer_res->sclk_hdmi)) {
830 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
831 return -ENODEV;
832 }
833 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
834 if (res == NULL) {
835 dev_err(dev, "get memory resource failed.\n");
836 return -ENXIO;
837 }
838
839 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
840 resource_size(res));
841 if (mixer_res->mixer_regs == NULL) {
842 dev_err(dev, "register mapping failed.\n");
843 return -ENXIO;
844 }
845
846 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
847 if (res == NULL) {
848 dev_err(dev, "get interrupt resource failed.\n");
849 return -ENXIO;
850 }
851
852 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
853 0, "drm_mixer", mixer_ctx);
854 if (ret) {
855 dev_err(dev, "request interrupt failed.\n");
856 return ret;
857 }
858 mixer_res->irq = res->start;
859
860 return 0;
861}
862
863static int vp_resources_init(struct mixer_context *mixer_ctx)
864{
865 struct device *dev = &mixer_ctx->pdev->dev;
866 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
867 struct resource *res;
868
869 mixer_res->vp = devm_clk_get(dev, "vp");
870 if (IS_ERR(mixer_res->vp)) {
871 dev_err(dev, "failed to get clock 'vp'\n");
872 return -ENODEV;
873 }
Sean Paul45517892014-01-30 16:19:05 -0500874
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200875 if (mixer_ctx->has_sclk) {
876 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
877 if (IS_ERR(mixer_res->sclk_mixer)) {
878 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
879 return -ENODEV;
880 }
881 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
882 if (IS_ERR(mixer_res->mout_mixer)) {
883 dev_err(dev, "failed to get clock 'mout_mixer'\n");
884 return -ENODEV;
885 }
886
887 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
888 clk_set_parent(mixer_res->mout_mixer,
889 mixer_res->sclk_hdmi);
890 }
Sean Paul45517892014-01-30 16:19:05 -0500891
892 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
893 if (res == NULL) {
894 dev_err(dev, "get memory resource failed.\n");
895 return -ENXIO;
896 }
897
898 mixer_res->vp_regs = devm_ioremap(dev, res->start,
899 resource_size(res));
900 if (mixer_res->vp_regs == NULL) {
901 dev_err(dev, "register mapping failed.\n");
902 return -ENXIO;
903 }
904
905 return 0;
906}
907
Gustavo Padovan93bca242015-01-18 18:16:23 +0900908static int mixer_initialize(struct mixer_context *mixer_ctx,
Inki Daef37cd5e2014-05-09 14:25:20 +0900909 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500910{
911 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +0900912 struct exynos_drm_private *priv;
913 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500914
Gustavo Padovaneb88e422014-11-26 16:43:27 -0200915 mixer_ctx->drm_dev = drm_dev;
Gustavo Padovan8a326ed2014-11-04 18:44:47 -0200916 mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500917
918 /* acquire resources: regs, irqs, clocks */
919 ret = mixer_resources_init(mixer_ctx);
920 if (ret) {
921 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
922 return ret;
923 }
924
925 if (mixer_ctx->vp_enabled) {
926 /* acquire vp resources: regs, irqs, clocks */
927 ret = vp_resources_init(mixer_ctx);
928 if (ret) {
929 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
930 return ret;
931 }
932 }
933
Joonyoung Shimeb7a3fc2015-07-02 21:49:39 +0900934 ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900935 if (ret)
936 priv->pipe--;
Sean Paulf041b252014-01-30 16:19:15 -0500937
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900938 return ret;
Sean Paul45517892014-01-30 16:19:05 -0500939}
940
Gustavo Padovan93bca242015-01-18 18:16:23 +0900941static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
Inki Dae1055b392012-10-19 17:37:35 +0900942{
Joonyoung Shimbf566082015-07-02 21:49:38 +0900943 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900944}
945
Gustavo Padovan93bca242015-01-18 18:16:23 +0900946static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900947{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900948 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900949 struct mixer_resources *res = &mixer_ctx->mixer_res;
950
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200951 __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
952 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Sean Paulf041b252014-01-30 16:19:15 -0500953 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900954
955 /* enable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200956 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
957 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900958
959 return 0;
960}
961
Gustavo Padovan93bca242015-01-18 18:16:23 +0900962static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900963{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900964 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900965 struct mixer_resources *res = &mixer_ctx->mixer_res;
966
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200967 __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
968
969 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Andrzej Hajda947710c2015-07-09 08:25:41 +0200970 return;
Andrzej Hajda947710c2015-07-09 08:25:41 +0200971
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900972 /* disable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200973 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900974 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
975}
976
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900977static void mixer_update_plane(struct exynos_drm_crtc *crtc,
978 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900979{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900980 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900981
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100982 DRM_DEBUG_KMS("win: %d\n", plane->index);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900983
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200984 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Shirish Sdda90122013-01-23 22:03:18 -0500985 return;
Shirish Sdda90122013-01-23 22:03:18 -0500986
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100987 if (plane->index > 1 && mixer_ctx->vp_enabled)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900988 vp_video_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900989 else
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900990 mixer_graph_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900991}
992
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900993static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
994 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900995{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900996 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900997 struct mixer_resources *res = &mixer_ctx->mixer_res;
998 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900999
Marek Szyprowski40bdfb02015-12-16 13:21:42 +01001000 DRM_DEBUG_KMS("win: %d\n", plane->index);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001001
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001002 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301003 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301004
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001005 spin_lock_irqsave(&res->reg_slock, flags);
1006 mixer_vsync_set_update(mixer_ctx, false);
1007
Marek Szyprowskia2cb9112015-12-16 13:21:44 +01001008 mixer_cfg_layer(mixer_ctx, plane->index, 0, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001009
1010 mixer_vsync_set_update(mixer_ctx, true);
1011 spin_unlock_irqrestore(&res->reg_slock, flags);
1012}
1013
Gustavo Padovan93bca242015-01-18 18:16:23 +09001014static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -05001015{
Gustavo Padovan93bca242015-01-18 18:16:23 +09001016 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001017 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +05301018
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001019 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush K6e95d5e2012-12-06 20:16:03 +05301020 return;
Prathyush K6e95d5e2012-12-06 20:16:03 +05301021
Gustavo Padovan93bca242015-01-18 18:16:23 +09001022 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001023 if (err < 0) {
1024 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
1025 return;
1026 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301027
Prathyush K6e95d5e2012-12-06 20:16:03 +05301028 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1029
1030 /*
1031 * wait for MIXER to signal VSYNC interrupt or return after
1032 * timeout which is set to 50ms (refresh rate of 20).
1033 */
1034 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1035 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001036 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301037 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301038
Gustavo Padovan93bca242015-01-18 18:16:23 +09001039 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +05301040}
1041
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001042static void mixer_enable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301043{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001044 struct mixer_context *ctx = crtc->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301045 struct mixer_resources *res = &ctx->mixer_res;
1046
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001047 if (test_bit(MXR_BIT_POWERED, &ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301048 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301049
Sean Paulaf65c802014-01-30 16:19:27 -05001050 pm_runtime_get_sync(ctx->dev);
1051
Rahul Sharmad74ed932014-06-23 11:02:24 +05301052 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1053
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001054 if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
Andrzej Hajdafc0732482015-07-09 08:25:40 +02001055 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001056 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
1057 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301058 mixer_win_reset(ctx);
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001059
1060 set_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301061}
1062
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001063static void mixer_disable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301064{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001065 struct mixer_context *ctx = crtc->ctx;
Joonyoung Shimc329f662015-06-12 20:34:28 +09001066 int i;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301067
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001068 if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301069 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301070
Rahul Sharma381be022014-06-23 11:02:22 +05301071 mixer_stop(ctx);
Tobias Jakobic0734fb2015-05-06 14:10:21 +02001072 mixer_regs_dump(ctx);
Joonyoung Shimc329f662015-06-12 20:34:28 +09001073
1074 for (i = 0; i < MIXER_WIN_NR; i++)
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001075 mixer_disable_plane(crtc, &ctx->planes[i]);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301076
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001077 pm_runtime_put(ctx->dev);
1078
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001079 clear_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301080}
1081
Sean Paulf041b252014-01-30 16:19:15 -05001082/* Only valid for Mixer version 16.0.33.0 */
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001083static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
1084 struct drm_crtc_state *state)
Sean Paulf041b252014-01-30 16:19:15 -05001085{
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001086 struct drm_display_mode *mode = &state->adjusted_mode;
Sean Paulf041b252014-01-30 16:19:15 -05001087 u32 w, h;
1088
1089 w = mode->hdisplay;
1090 h = mode->vdisplay;
1091
1092 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1093 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1094 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1095
1096 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1097 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1098 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1099 return 0;
1100
1101 return -EINVAL;
1102}
1103
Krzysztof Kozlowskif3aaf762015-05-07 09:04:45 +09001104static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001105 .enable = mixer_enable,
1106 .disable = mixer_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001107 .enable_vblank = mixer_enable_vblank,
1108 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301109 .wait_for_vblank = mixer_wait_for_vblank,
Gustavo Padovan9cc76102015-08-03 14:38:05 +09001110 .update_plane = mixer_update_plane,
1111 .disable_plane = mixer_disable_plane,
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001112 .atomic_check = mixer_atomic_check,
Sean Paulf041b252014-01-30 16:19:15 -05001113};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001114
Rahul Sharmadef5e092013-06-19 18:21:08 +05301115static struct mixer_drv_data exynos5420_mxr_drv_data = {
1116 .version = MXR_VER_128_0_0_184,
1117 .is_vp_enabled = 0,
1118};
1119
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301120static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301121 .version = MXR_VER_16_0_33_0,
1122 .is_vp_enabled = 0,
1123};
1124
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001125static struct mixer_drv_data exynos4212_mxr_drv_data = {
1126 .version = MXR_VER_0_0_0_16,
1127 .is_vp_enabled = 1,
1128};
1129
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301130static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301131 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301132 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001133 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301134};
1135
Krzysztof Kozlowskid6b16302015-05-02 00:56:36 +09001136static const struct platform_device_id mixer_driver_types[] = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301137 {
1138 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301139 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301140 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301141 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301142 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301143 }, {
1144 /* end node */
1145 }
1146};
1147
1148static struct of_device_id mixer_match_types[] = {
1149 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001150 .compatible = "samsung,exynos4210-mixer",
1151 .data = &exynos4210_mxr_drv_data,
1152 }, {
1153 .compatible = "samsung,exynos4212-mixer",
1154 .data = &exynos4212_mxr_drv_data,
1155 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301156 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301157 .data = &exynos5250_mxr_drv_data,
1158 }, {
1159 .compatible = "samsung,exynos5250-mixer",
1160 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301161 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301162 .compatible = "samsung,exynos5420-mixer",
1163 .data = &exynos5420_mxr_drv_data,
1164 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301165 /* end node */
1166 }
1167};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001168MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301169
Inki Daef37cd5e2014-05-09 14:25:20 +09001170static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001171{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001172 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001173 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001174 struct exynos_drm_plane *exynos_plane;
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001175 unsigned int i;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001176 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001177
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001178 ret = mixer_initialize(ctx, drm_dev);
1179 if (ret)
1180 return ret;
1181
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001182 for (i = 0; i < MIXER_WIN_NR; i++) {
1183 if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
Marek Szyprowskiab144202015-11-30 14:53:24 +01001184 continue;
1185
Marek Szyprowski40bdfb02015-12-16 13:21:42 +01001186 ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001187 1 << ctx->pipe, &plane_configs[i]);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001188 if (ret)
1189 return ret;
1190 }
1191
Gustavo Padovan5d3d0992015-10-12 22:07:48 +09001192 exynos_plane = &ctx->planes[DEFAULT_WIN];
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001193 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1194 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1195 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001196 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001197 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001198 ret = PTR_ERR(ctx->crtc);
1199 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001200 }
1201
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001202 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001203
1204free_ctx:
1205 devm_kfree(dev, ctx);
1206 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001207}
1208
1209static void mixer_unbind(struct device *dev, struct device *master, void *data)
1210{
1211 struct mixer_context *ctx = dev_get_drvdata(dev);
1212
Gustavo Padovan93bca242015-01-18 18:16:23 +09001213 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001214}
1215
1216static const struct component_ops mixer_component_ops = {
1217 .bind = mixer_bind,
1218 .unbind = mixer_unbind,
1219};
1220
1221static int mixer_probe(struct platform_device *pdev)
1222{
1223 struct device *dev = &pdev->dev;
1224 struct mixer_drv_data *drv;
1225 struct mixer_context *ctx;
1226 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001227
Sean Paulf041b252014-01-30 16:19:15 -05001228 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1229 if (!ctx) {
1230 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001231 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001232 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001233
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301234 if (dev->of_node) {
1235 const struct of_device_id *match;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001236
Sachin Kamate436b092013-06-05 16:00:23 +09001237 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301238 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301239 } else {
1240 drv = (struct mixer_drv_data *)
1241 platform_get_device_id(pdev)->driver_data;
1242 }
1243
Sean Paul45517892014-01-30 16:19:05 -05001244 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001245 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301246 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001247 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301248 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001249 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301250 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001251
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001252 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001253
Inki Daedf5225b2014-05-29 18:28:02 +09001254 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda86650402015-06-11 23:23:37 +09001255 if (!ret)
1256 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001257
1258 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001259}
1260
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001261static int mixer_remove(struct platform_device *pdev)
1262{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001263 pm_runtime_disable(&pdev->dev);
1264
Inki Daedf5225b2014-05-29 18:28:02 +09001265 component_del(&pdev->dev, &mixer_component_ops);
Inki Daedf5225b2014-05-29 18:28:02 +09001266
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001267 return 0;
1268}
1269
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001270#ifdef CONFIG_PM_SLEEP
1271static int exynos_mixer_suspend(struct device *dev)
1272{
1273 struct mixer_context *ctx = dev_get_drvdata(dev);
1274 struct mixer_resources *res = &ctx->mixer_res;
1275
1276 clk_disable_unprepare(res->hdmi);
1277 clk_disable_unprepare(res->mixer);
1278 if (ctx->vp_enabled) {
1279 clk_disable_unprepare(res->vp);
1280 if (ctx->has_sclk)
1281 clk_disable_unprepare(res->sclk_mixer);
1282 }
1283
1284 return 0;
1285}
1286
1287static int exynos_mixer_resume(struct device *dev)
1288{
1289 struct mixer_context *ctx = dev_get_drvdata(dev);
1290 struct mixer_resources *res = &ctx->mixer_res;
1291 int ret;
1292
1293 ret = clk_prepare_enable(res->mixer);
1294 if (ret < 0) {
1295 DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
1296 return ret;
1297 }
1298 ret = clk_prepare_enable(res->hdmi);
1299 if (ret < 0) {
1300 DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
1301 return ret;
1302 }
1303 if (ctx->vp_enabled) {
1304 ret = clk_prepare_enable(res->vp);
1305 if (ret < 0) {
1306 DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
1307 ret);
1308 return ret;
1309 }
1310 if (ctx->has_sclk) {
1311 ret = clk_prepare_enable(res->sclk_mixer);
1312 if (ret < 0) {
1313 DRM_ERROR("Failed to prepare_enable the " \
1314 "sclk_mixer clk [%d]\n",
1315 ret);
1316 return ret;
1317 }
1318 }
1319 }
1320
1321 return 0;
1322}
1323#endif
1324
1325static const struct dev_pm_ops exynos_mixer_pm_ops = {
1326 SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
1327};
1328
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001329struct platform_driver mixer_driver = {
1330 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301331 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001332 .owner = THIS_MODULE,
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001333 .pm = &exynos_mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301334 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001335 },
1336 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001337 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301338 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001339};