blob: c0d128bc084b5925646e22ddc5ff6324025a73e9 [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
168static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
169{
170 return readl(res->vp_regs + reg_id);
171}
172
173static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
174 u32 val)
175{
176 writel(val, res->vp_regs + reg_id);
177}
178
179static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
180 u32 val, u32 mask)
181{
182 u32 old = vp_reg_read(res, reg_id);
183
184 val = (val & mask) | (old & ~mask);
185 writel(val, res->vp_regs + reg_id);
186}
187
188static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
189{
190 return readl(res->mixer_regs + reg_id);
191}
192
193static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
194 u32 val)
195{
196 writel(val, res->mixer_regs + reg_id);
197}
198
199static inline void mixer_reg_writemask(struct mixer_resources *res,
200 u32 reg_id, u32 val, u32 mask)
201{
202 u32 old = mixer_reg_read(res, reg_id);
203
204 val = (val & mask) | (old & ~mask);
205 writel(val, res->mixer_regs + reg_id);
206}
207
208static void mixer_regs_dump(struct mixer_context *ctx)
209{
210#define DUMPREG(reg_id) \
211do { \
212 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
213 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
214} while (0)
215
216 DUMPREG(MXR_STATUS);
217 DUMPREG(MXR_CFG);
218 DUMPREG(MXR_INT_EN);
219 DUMPREG(MXR_INT_STATUS);
220
221 DUMPREG(MXR_LAYER_CFG);
222 DUMPREG(MXR_VIDEO_CFG);
223
224 DUMPREG(MXR_GRAPHIC0_CFG);
225 DUMPREG(MXR_GRAPHIC0_BASE);
226 DUMPREG(MXR_GRAPHIC0_SPAN);
227 DUMPREG(MXR_GRAPHIC0_WH);
228 DUMPREG(MXR_GRAPHIC0_SXY);
229 DUMPREG(MXR_GRAPHIC0_DXY);
230
231 DUMPREG(MXR_GRAPHIC1_CFG);
232 DUMPREG(MXR_GRAPHIC1_BASE);
233 DUMPREG(MXR_GRAPHIC1_SPAN);
234 DUMPREG(MXR_GRAPHIC1_WH);
235 DUMPREG(MXR_GRAPHIC1_SXY);
236 DUMPREG(MXR_GRAPHIC1_DXY);
237#undef DUMPREG
238}
239
240static void vp_regs_dump(struct mixer_context *ctx)
241{
242#define DUMPREG(reg_id) \
243do { \
244 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
245 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
246} while (0)
247
248 DUMPREG(VP_ENABLE);
249 DUMPREG(VP_SRESET);
250 DUMPREG(VP_SHADOW_UPDATE);
251 DUMPREG(VP_FIELD_ID);
252 DUMPREG(VP_MODE);
253 DUMPREG(VP_IMG_SIZE_Y);
254 DUMPREG(VP_IMG_SIZE_C);
255 DUMPREG(VP_PER_RATE_CTRL);
256 DUMPREG(VP_TOP_Y_PTR);
257 DUMPREG(VP_BOT_Y_PTR);
258 DUMPREG(VP_TOP_C_PTR);
259 DUMPREG(VP_BOT_C_PTR);
260 DUMPREG(VP_ENDIAN_MODE);
261 DUMPREG(VP_SRC_H_POSITION);
262 DUMPREG(VP_SRC_V_POSITION);
263 DUMPREG(VP_SRC_WIDTH);
264 DUMPREG(VP_SRC_HEIGHT);
265 DUMPREG(VP_DST_H_POSITION);
266 DUMPREG(VP_DST_V_POSITION);
267 DUMPREG(VP_DST_WIDTH);
268 DUMPREG(VP_DST_HEIGHT);
269 DUMPREG(VP_H_RATIO);
270 DUMPREG(VP_V_RATIO);
271
272#undef DUMPREG
273}
274
275static inline void vp_filter_set(struct mixer_resources *res,
276 int reg_id, const u8 *data, unsigned int size)
277{
278 /* assure 4-byte align */
279 BUG_ON(size & 3);
280 for (; size; size -= 4, reg_id += 4, data += 4) {
281 u32 val = (data[0] << 24) | (data[1] << 16) |
282 (data[2] << 8) | data[3];
283 vp_reg_write(res, reg_id, val);
284 }
285}
286
287static void vp_default_filter(struct mixer_resources *res)
288{
289 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530290 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900291 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530292 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900293 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530294 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900295}
296
297static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
298{
299 struct mixer_resources *res = &ctx->mixer_res;
300
301 /* block update on vsync */
302 mixer_reg_writemask(res, MXR_STATUS, enable ?
303 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
304
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530305 if (ctx->vp_enabled)
306 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900307 VP_SHADOW_UPDATE_ENABLE : 0);
308}
309
310static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
311{
312 struct mixer_resources *res = &ctx->mixer_res;
313 u32 val;
314
315 /* choosing between interlace and progressive mode */
316 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
Tobias Jakobi1e6d4592015-04-07 01:14:50 +0200317 MXR_CFG_SCAN_PROGRESSIVE);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900318
Rahul Sharmadef5e092013-06-19 18:21:08 +0530319 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
320 /* choosing between proper HD and SD mode */
321 if (height <= 480)
322 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
323 else if (height <= 576)
324 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
325 else if (height <= 720)
326 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
327 else if (height <= 1080)
328 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
329 else
330 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
331 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900332
333 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
334}
335
336static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
337{
338 struct mixer_resources *res = &ctx->mixer_res;
339 u32 val;
340
341 if (height == 480) {
342 val = MXR_CFG_RGB601_0_255;
343 } else if (height == 576) {
344 val = MXR_CFG_RGB601_0_255;
345 } else if (height == 720) {
346 val = MXR_CFG_RGB709_16_235;
347 mixer_reg_write(res, MXR_CM_COEFF_Y,
348 (1 << 30) | (94 << 20) | (314 << 10) |
349 (32 << 0));
350 mixer_reg_write(res, MXR_CM_COEFF_CB,
351 (972 << 20) | (851 << 10) | (225 << 0));
352 mixer_reg_write(res, MXR_CM_COEFF_CR,
353 (225 << 20) | (820 << 10) | (1004 << 0));
354 } else if (height == 1080) {
355 val = MXR_CFG_RGB709_16_235;
356 mixer_reg_write(res, MXR_CM_COEFF_Y,
357 (1 << 30) | (94 << 20) | (314 << 10) |
358 (32 << 0));
359 mixer_reg_write(res, MXR_CM_COEFF_CB,
360 (972 << 20) | (851 << 10) | (225 << 0));
361 mixer_reg_write(res, MXR_CM_COEFF_CR,
362 (225 << 20) | (820 << 10) | (1004 << 0));
363 } else {
364 val = MXR_CFG_RGB709_16_235;
365 mixer_reg_write(res, MXR_CM_COEFF_Y,
366 (1 << 30) | (94 << 20) | (314 << 10) |
367 (32 << 0));
368 mixer_reg_write(res, MXR_CM_COEFF_CB,
369 (972 << 20) | (851 << 10) | (225 << 0));
370 mixer_reg_write(res, MXR_CM_COEFF_CR,
371 (225 << 20) | (820 << 10) | (1004 << 0));
372 }
373
374 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
375}
376
Tobias Jakobi5b1d5bc2015-05-06 14:10:22 +0200377static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100378 unsigned int priority, bool enable)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900379{
380 struct mixer_resources *res = &ctx->mixer_res;
381 u32 val = enable ? ~0 : 0;
382
383 switch (win) {
384 case 0:
385 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100386 mixer_reg_writemask(res, MXR_LAYER_CFG,
387 MXR_LAYER_CFG_GRP0_VAL(priority),
388 MXR_LAYER_CFG_GRP0_MASK);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900389 break;
390 case 1:
391 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100392 mixer_reg_writemask(res, MXR_LAYER_CFG,
393 MXR_LAYER_CFG_GRP1_VAL(priority),
394 MXR_LAYER_CFG_GRP1_MASK);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900395 break;
396 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530397 if (ctx->vp_enabled) {
398 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
399 mixer_reg_writemask(res, MXR_CFG, val,
400 MXR_CFG_VP_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100401 mixer_reg_writemask(res, MXR_LAYER_CFG,
402 MXR_LAYER_CFG_VP_VAL(priority),
403 MXR_LAYER_CFG_VP_MASK);
Joonyoung Shimf1e716d2014-07-25 19:59:10 +0900404
405 /* control blending of graphic layer 0 */
406 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
407 MXR_GRP_CFG_BLEND_PRE_MUL |
408 MXR_GRP_CFG_PIXEL_BLEND_EN);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530409 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900410 break;
411 }
412}
413
414static void mixer_run(struct mixer_context *ctx)
415{
416 struct mixer_resources *res = &ctx->mixer_res;
417
418 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900419}
420
Rahul Sharma381be022014-06-23 11:02:22 +0530421static void mixer_stop(struct mixer_context *ctx)
422{
423 struct mixer_resources *res = &ctx->mixer_res;
424 int timeout = 20;
425
426 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
427
428 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
429 --timeout)
430 usleep_range(10000, 12000);
Rahul Sharma381be022014-06-23 11:02:22 +0530431}
432
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900433static void vp_video_buffer(struct mixer_context *ctx,
434 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900435{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100436 struct exynos_drm_plane_state *state =
437 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100438 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900439 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100440 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900441 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900442 dma_addr_t luma_addr[2], chroma_addr[2];
443 bool tiled_mode = false;
444 bool crcb_mode = false;
445 u32 val;
446
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900447 switch (fb->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900448 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900449 crcb_mode = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900450 break;
Tobias Jakobi8f2590f2015-04-27 23:10:16 +0200451 case DRM_FORMAT_NV21:
452 crcb_mode = true;
453 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900454 default:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900455 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900456 fb->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900457 return;
458 }
459
Marek Szyprowski0488f502015-11-30 14:53:21 +0100460 luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
461 chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900462
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900463 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900464 ctx->interlace = true;
465 if (tiled_mode) {
466 luma_addr[1] = luma_addr[0] + 0x40;
467 chroma_addr[1] = chroma_addr[0] + 0x40;
468 } else {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900469 luma_addr[1] = luma_addr[0] + fb->pitches[0];
470 chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900471 }
472 } else {
473 ctx->interlace = false;
474 luma_addr[1] = 0;
475 chroma_addr[1] = 0;
476 }
477
478 spin_lock_irqsave(&res->reg_slock, flags);
479 mixer_vsync_set_update(ctx, false);
480
481 /* interlace or progressive scan mode */
482 val = (ctx->interlace ? ~0 : 0);
483 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
484
485 /* setup format */
486 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
487 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
488 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
489
490 /* setting size of input image */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900491 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
492 VP_IMG_VSIZE(fb->height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900493 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900494 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
495 VP_IMG_VSIZE(fb->height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900496
Marek Szyprowski0114f402015-11-30 14:53:22 +0100497 vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
498 vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900499 vp_reg_write(res, VP_SRC_H_POSITION,
Marek Szyprowski0114f402015-11-30 14:53:22 +0100500 VP_SRC_H_POSITION_VAL(state->src.x));
501 vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900502
Marek Szyprowski0114f402015-11-30 14:53:22 +0100503 vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
504 vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900505 if (ctx->interlace) {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100506 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
507 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900508 } else {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100509 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
510 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900511 }
512
Marek Szyprowski0114f402015-11-30 14:53:22 +0100513 vp_reg_write(res, VP_H_RATIO, state->h_ratio);
514 vp_reg_write(res, VP_V_RATIO, state->v_ratio);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900515
516 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
517
518 /* set buffer address to vp */
519 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
520 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
521 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
522 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
523
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900524 mixer_cfg_scan(ctx, mode->vdisplay);
525 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100526 mixer_cfg_layer(ctx, plane->index, state->zpos + 1, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900527 mixer_run(ctx);
528
529 mixer_vsync_set_update(ctx, true);
530 spin_unlock_irqrestore(&res->reg_slock, flags);
531
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200532 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900533 vp_regs_dump(ctx);
534}
535
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530536static void mixer_layer_update(struct mixer_context *ctx)
537{
538 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530539
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530540 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530541}
542
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900543static void mixer_graph_buffer(struct mixer_context *ctx,
544 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900545{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100546 struct exynos_drm_plane_state *state =
547 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100548 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900549 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100550 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900551 unsigned long flags;
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100552 unsigned int win = plane->index;
Tobias Jakobi26110152015-04-07 01:14:52 +0200553 unsigned int x_ratio = 0, y_ratio = 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900554 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900555 dma_addr_t dma_addr;
556 unsigned int fmt;
557 u32 val;
558
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900559 switch (fb->pixel_format) {
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200560 case DRM_FORMAT_XRGB4444:
561 fmt = MXR_FORMAT_ARGB4444;
562 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900563
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200564 case DRM_FORMAT_XRGB1555:
565 fmt = MXR_FORMAT_ARGB1555;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900566 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200567
568 case DRM_FORMAT_RGB565:
569 fmt = MXR_FORMAT_RGB565;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900570 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200571
572 case DRM_FORMAT_XRGB8888:
573 case DRM_FORMAT_ARGB8888:
574 fmt = MXR_FORMAT_ARGB8888;
575 break;
576
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900577 default:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200578 DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
579 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900580 }
581
Marek Szyprowskie463b062015-11-30 14:53:27 +0100582 /* ratio is already checked by common plane code */
583 x_ratio = state->h_ratio == (1 << 15);
584 y_ratio = state->v_ratio == (1 << 15);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900585
Marek Szyprowski0114f402015-11-30 14:53:22 +0100586 dst_x_offset = state->crtc.x;
587 dst_y_offset = state->crtc.y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900588
589 /* converting dma address base and source offset */
Marek Szyprowski0488f502015-11-30 14:53:21 +0100590 dma_addr = exynos_drm_fb_dma_addr(fb, 0)
Marek Szyprowski0114f402015-11-30 14:53:22 +0100591 + (state->src.x * fb->bits_per_pixel >> 3)
592 + (state->src.y * fb->pitches[0]);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900593 src_x_offset = 0;
594 src_y_offset = 0;
595
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900596 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900597 ctx->interlace = true;
598 else
599 ctx->interlace = false;
600
601 spin_lock_irqsave(&res->reg_slock, flags);
602 mixer_vsync_set_update(ctx, false);
603
604 /* setup format */
605 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
606 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
607
608 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000609 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900610 fb->pitches[0] / (fb->bits_per_pixel >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900611
Rahul Sharmadef5e092013-06-19 18:21:08 +0530612 /* setup display size */
613 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
Gustavo Padovan5d3d0992015-10-12 22:07:48 +0900614 win == DEFAULT_WIN) {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900615 val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
616 val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530617 mixer_reg_write(res, MXR_RESOLUTION, val);
618 }
619
Marek Szyprowski0114f402015-11-30 14:53:22 +0100620 val = MXR_GRP_WH_WIDTH(state->src.w);
621 val |= MXR_GRP_WH_HEIGHT(state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900622 val |= MXR_GRP_WH_H_SCALE(x_ratio);
623 val |= MXR_GRP_WH_V_SCALE(y_ratio);
624 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
625
626 /* setup offsets in source image */
627 val = MXR_GRP_SXY_SX(src_x_offset);
628 val |= MXR_GRP_SXY_SY(src_y_offset);
629 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
630
631 /* setup offsets in display image */
632 val = MXR_GRP_DXY_DX(dst_x_offset);
633 val |= MXR_GRP_DXY_DY(dst_y_offset);
634 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
635
636 /* set buffer address to mixer */
637 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
638
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900639 mixer_cfg_scan(ctx, mode->vdisplay);
640 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100641 mixer_cfg_layer(ctx, win, state->zpos + 1, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530642
643 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530644 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
645 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530646 mixer_layer_update(ctx);
647
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900648 mixer_run(ctx);
649
650 mixer_vsync_set_update(ctx, true);
651 spin_unlock_irqrestore(&res->reg_slock, flags);
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200652
653 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900654}
655
656static void vp_win_reset(struct mixer_context *ctx)
657{
658 struct mixer_resources *res = &ctx->mixer_res;
659 int tries = 100;
660
661 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
662 for (tries = 100; tries; --tries) {
663 /* waiting until VP_SRESET_PROCESSING is 0 */
664 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
665 break;
Tomasz Stanislawski02b3de42015-09-25 14:48:29 +0200666 mdelay(10);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900667 }
668 WARN(tries == 0, "failed to reset Video Processor\n");
669}
670
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900671static void mixer_win_reset(struct mixer_context *ctx)
672{
673 struct mixer_resources *res = &ctx->mixer_res;
674 unsigned long flags;
675 u32 val; /* value stored to register */
676
677 spin_lock_irqsave(&res->reg_slock, flags);
678 mixer_vsync_set_update(ctx, false);
679
680 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
681
682 /* set output in RGB888 mode */
683 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
684
685 /* 16 beat burst in DMA */
686 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
687 MXR_STATUS_BURST_MASK);
688
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100689 /* reset default layer priority */
690 mixer_reg_write(res, MXR_LAYER_CFG, 0);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900691
692 /* setting background color */
693 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
694 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
695 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
696
697 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900698 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
699 val |= MXR_GRP_CFG_WIN_BLEND_EN;
700 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
701
Sean Paul0377f4e2013-04-25 15:13:26 -0400702 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900703 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400704
705 /* Blend layer 1 into layer 0 */
706 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
707 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900708 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
709
Seung-Woo Kim57366032012-05-15 17:22:08 +0900710 /* setting video layers */
711 val = MXR_GRP_CFG_ALPHA_VAL(0);
712 mixer_reg_write(res, MXR_VIDEO_CFG, val);
713
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530714 if (ctx->vp_enabled) {
715 /* configuration of Video Processor Registers */
716 vp_win_reset(ctx);
717 vp_default_filter(res);
718 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900719
720 /* disable all layers */
721 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
722 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530723 if (ctx->vp_enabled)
724 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900725
726 mixer_vsync_set_update(ctx, true);
727 spin_unlock_irqrestore(&res->reg_slock, flags);
728}
729
Sean Paul45517892014-01-30 16:19:05 -0500730static irqreturn_t mixer_irq_handler(int irq, void *arg)
731{
732 struct mixer_context *ctx = arg;
733 struct mixer_resources *res = &ctx->mixer_res;
734 u32 val, base, shadow;
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300735 int win;
Sean Paul45517892014-01-30 16:19:05 -0500736
737 spin_lock(&res->reg_slock);
738
739 /* read interrupt status for handling and clearing flags for VSYNC */
740 val = mixer_reg_read(res, MXR_INT_STATUS);
741
742 /* handling VSYNC */
743 if (val & MXR_INT_STATUS_VSYNC) {
Andrzej Hajda81a464d2015-07-09 10:07:53 +0200744 /* vsync interrupt use different bit for read and clear */
745 val |= MXR_INT_CLEAR_VSYNC;
746 val &= ~MXR_INT_STATUS_VSYNC;
747
Sean Paul45517892014-01-30 16:19:05 -0500748 /* interlace scan need to check shadow register */
749 if (ctx->interlace) {
750 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
751 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
752 if (base != shadow)
753 goto out;
754
755 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
756 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
757 if (base != shadow)
758 goto out;
759 }
760
Gustavo Padovaneafd5402015-07-16 12:23:32 -0300761 drm_crtc_handle_vblank(&ctx->crtc->base);
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300762 for (win = 0 ; win < MIXER_WIN_NR ; win++) {
763 struct exynos_drm_plane *plane = &ctx->planes[win];
764
765 if (!plane->pending_fb)
766 continue;
767
768 exynos_drm_crtc_finish_update(ctx->crtc, plane);
769 }
Sean Paul45517892014-01-30 16:19:05 -0500770
771 /* set wait vsync event to zero and wake up queue. */
772 if (atomic_read(&ctx->wait_vsync_event)) {
773 atomic_set(&ctx->wait_vsync_event, 0);
774 wake_up(&ctx->wait_vsync_queue);
775 }
776 }
777
778out:
779 /* clear interrupts */
Sean Paul45517892014-01-30 16:19:05 -0500780 mixer_reg_write(res, MXR_INT_STATUS, val);
781
782 spin_unlock(&res->reg_slock);
783
784 return IRQ_HANDLED;
785}
786
787static int mixer_resources_init(struct mixer_context *mixer_ctx)
788{
789 struct device *dev = &mixer_ctx->pdev->dev;
790 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
791 struct resource *res;
792 int ret;
793
794 spin_lock_init(&mixer_res->reg_slock);
795
796 mixer_res->mixer = devm_clk_get(dev, "mixer");
797 if (IS_ERR(mixer_res->mixer)) {
798 dev_err(dev, "failed to get clock 'mixer'\n");
799 return -ENODEV;
800 }
801
Marek Szyprowski04427ec2015-02-02 14:20:28 +0100802 mixer_res->hdmi = devm_clk_get(dev, "hdmi");
803 if (IS_ERR(mixer_res->hdmi)) {
804 dev_err(dev, "failed to get clock 'hdmi'\n");
805 return PTR_ERR(mixer_res->hdmi);
806 }
807
Sean Paul45517892014-01-30 16:19:05 -0500808 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
809 if (IS_ERR(mixer_res->sclk_hdmi)) {
810 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
811 return -ENODEV;
812 }
813 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
814 if (res == NULL) {
815 dev_err(dev, "get memory resource failed.\n");
816 return -ENXIO;
817 }
818
819 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
820 resource_size(res));
821 if (mixer_res->mixer_regs == NULL) {
822 dev_err(dev, "register mapping failed.\n");
823 return -ENXIO;
824 }
825
826 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
827 if (res == NULL) {
828 dev_err(dev, "get interrupt resource failed.\n");
829 return -ENXIO;
830 }
831
832 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
833 0, "drm_mixer", mixer_ctx);
834 if (ret) {
835 dev_err(dev, "request interrupt failed.\n");
836 return ret;
837 }
838 mixer_res->irq = res->start;
839
840 return 0;
841}
842
843static int vp_resources_init(struct mixer_context *mixer_ctx)
844{
845 struct device *dev = &mixer_ctx->pdev->dev;
846 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
847 struct resource *res;
848
849 mixer_res->vp = devm_clk_get(dev, "vp");
850 if (IS_ERR(mixer_res->vp)) {
851 dev_err(dev, "failed to get clock 'vp'\n");
852 return -ENODEV;
853 }
Sean Paul45517892014-01-30 16:19:05 -0500854
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200855 if (mixer_ctx->has_sclk) {
856 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
857 if (IS_ERR(mixer_res->sclk_mixer)) {
858 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
859 return -ENODEV;
860 }
861 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
862 if (IS_ERR(mixer_res->mout_mixer)) {
863 dev_err(dev, "failed to get clock 'mout_mixer'\n");
864 return -ENODEV;
865 }
866
867 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
868 clk_set_parent(mixer_res->mout_mixer,
869 mixer_res->sclk_hdmi);
870 }
Sean Paul45517892014-01-30 16:19:05 -0500871
872 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
873 if (res == NULL) {
874 dev_err(dev, "get memory resource failed.\n");
875 return -ENXIO;
876 }
877
878 mixer_res->vp_regs = devm_ioremap(dev, res->start,
879 resource_size(res));
880 if (mixer_res->vp_regs == NULL) {
881 dev_err(dev, "register mapping failed.\n");
882 return -ENXIO;
883 }
884
885 return 0;
886}
887
Gustavo Padovan93bca242015-01-18 18:16:23 +0900888static int mixer_initialize(struct mixer_context *mixer_ctx,
Inki Daef37cd5e2014-05-09 14:25:20 +0900889 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500890{
891 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +0900892 struct exynos_drm_private *priv;
893 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500894
Gustavo Padovaneb88e422014-11-26 16:43:27 -0200895 mixer_ctx->drm_dev = drm_dev;
Gustavo Padovan8a326ed2014-11-04 18:44:47 -0200896 mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500897
898 /* acquire resources: regs, irqs, clocks */
899 ret = mixer_resources_init(mixer_ctx);
900 if (ret) {
901 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
902 return ret;
903 }
904
905 if (mixer_ctx->vp_enabled) {
906 /* acquire vp resources: regs, irqs, clocks */
907 ret = vp_resources_init(mixer_ctx);
908 if (ret) {
909 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
910 return ret;
911 }
912 }
913
Joonyoung Shimeb7a3fc2015-07-02 21:49:39 +0900914 ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900915 if (ret)
916 priv->pipe--;
Sean Paulf041b252014-01-30 16:19:15 -0500917
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900918 return ret;
Sean Paul45517892014-01-30 16:19:05 -0500919}
920
Gustavo Padovan93bca242015-01-18 18:16:23 +0900921static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
Inki Dae1055b392012-10-19 17:37:35 +0900922{
Joonyoung Shimbf566082015-07-02 21:49:38 +0900923 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900924}
925
Gustavo Padovan93bca242015-01-18 18:16:23 +0900926static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900927{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900928 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900929 struct mixer_resources *res = &mixer_ctx->mixer_res;
930
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200931 __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
932 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Sean Paulf041b252014-01-30 16:19:15 -0500933 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900934
935 /* enable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200936 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
937 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900938
939 return 0;
940}
941
Gustavo Padovan93bca242015-01-18 18:16:23 +0900942static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900943{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900944 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900945 struct mixer_resources *res = &mixer_ctx->mixer_res;
946
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200947 __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
948
949 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Andrzej Hajda947710c2015-07-09 08:25:41 +0200950 return;
Andrzej Hajda947710c2015-07-09 08:25:41 +0200951
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900952 /* disable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200953 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900954 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
955}
956
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900957static void mixer_update_plane(struct exynos_drm_crtc *crtc,
958 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900959{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900960 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900961
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100962 DRM_DEBUG_KMS("win: %d\n", plane->index);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900963
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200964 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Shirish Sdda90122013-01-23 22:03:18 -0500965 return;
Shirish Sdda90122013-01-23 22:03:18 -0500966
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100967 if (plane->index > 1 && mixer_ctx->vp_enabled)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900968 vp_video_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900969 else
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900970 mixer_graph_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900971}
972
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900973static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
974 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900975{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900976 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900977 struct mixer_resources *res = &mixer_ctx->mixer_res;
978 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900979
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100980 DRM_DEBUG_KMS("win: %d\n", plane->index);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900981
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200982 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +0530983 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +0530984
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900985 spin_lock_irqsave(&res->reg_slock, flags);
986 mixer_vsync_set_update(mixer_ctx, false);
987
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100988 mixer_cfg_layer(mixer_ctx, plane->index, 0, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900989
990 mixer_vsync_set_update(mixer_ctx, true);
991 spin_unlock_irqrestore(&res->reg_slock, flags);
992}
993
Gustavo Padovan93bca242015-01-18 18:16:23 +0900994static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -0500995{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900996 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +0900997 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +0530998
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200999 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush K6e95d5e2012-12-06 20:16:03 +05301000 return;
Prathyush K6e95d5e2012-12-06 20:16:03 +05301001
Gustavo Padovan93bca242015-01-18 18:16:23 +09001002 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001003 if (err < 0) {
1004 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
1005 return;
1006 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301007
Prathyush K6e95d5e2012-12-06 20:16:03 +05301008 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1009
1010 /*
1011 * wait for MIXER to signal VSYNC interrupt or return after
1012 * timeout which is set to 50ms (refresh rate of 20).
1013 */
1014 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1015 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001016 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301017 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301018
Gustavo Padovan93bca242015-01-18 18:16:23 +09001019 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +05301020}
1021
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001022static void mixer_enable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301023{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001024 struct mixer_context *ctx = crtc->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301025 struct mixer_resources *res = &ctx->mixer_res;
1026
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001027 if (test_bit(MXR_BIT_POWERED, &ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301028 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301029
Sean Paulaf65c802014-01-30 16:19:27 -05001030 pm_runtime_get_sync(ctx->dev);
1031
Rahul Sharmad74ed932014-06-23 11:02:24 +05301032 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1033
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001034 if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
Andrzej Hajdafc0732482015-07-09 08:25:40 +02001035 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001036 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
1037 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301038 mixer_win_reset(ctx);
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001039
1040 set_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301041}
1042
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001043static void mixer_disable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301044{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001045 struct mixer_context *ctx = crtc->ctx;
Joonyoung Shimc329f662015-06-12 20:34:28 +09001046 int i;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301047
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001048 if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301049 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301050
Rahul Sharma381be022014-06-23 11:02:22 +05301051 mixer_stop(ctx);
Tobias Jakobic0734fb2015-05-06 14:10:21 +02001052 mixer_regs_dump(ctx);
Joonyoung Shimc329f662015-06-12 20:34:28 +09001053
1054 for (i = 0; i < MIXER_WIN_NR; i++)
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001055 mixer_disable_plane(crtc, &ctx->planes[i]);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301056
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001057 pm_runtime_put(ctx->dev);
1058
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001059 clear_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301060}
1061
Sean Paulf041b252014-01-30 16:19:15 -05001062/* Only valid for Mixer version 16.0.33.0 */
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001063static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
1064 struct drm_crtc_state *state)
Sean Paulf041b252014-01-30 16:19:15 -05001065{
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001066 struct drm_display_mode *mode = &state->adjusted_mode;
Sean Paulf041b252014-01-30 16:19:15 -05001067 u32 w, h;
1068
1069 w = mode->hdisplay;
1070 h = mode->vdisplay;
1071
1072 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1073 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1074 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1075
1076 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1077 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1078 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1079 return 0;
1080
1081 return -EINVAL;
1082}
1083
Krzysztof Kozlowskif3aaf762015-05-07 09:04:45 +09001084static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001085 .enable = mixer_enable,
1086 .disable = mixer_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001087 .enable_vblank = mixer_enable_vblank,
1088 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301089 .wait_for_vblank = mixer_wait_for_vblank,
Gustavo Padovan9cc76102015-08-03 14:38:05 +09001090 .update_plane = mixer_update_plane,
1091 .disable_plane = mixer_disable_plane,
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001092 .atomic_check = mixer_atomic_check,
Sean Paulf041b252014-01-30 16:19:15 -05001093};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001094
Rahul Sharmadef5e092013-06-19 18:21:08 +05301095static struct mixer_drv_data exynos5420_mxr_drv_data = {
1096 .version = MXR_VER_128_0_0_184,
1097 .is_vp_enabled = 0,
1098};
1099
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301100static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301101 .version = MXR_VER_16_0_33_0,
1102 .is_vp_enabled = 0,
1103};
1104
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001105static struct mixer_drv_data exynos4212_mxr_drv_data = {
1106 .version = MXR_VER_0_0_0_16,
1107 .is_vp_enabled = 1,
1108};
1109
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301110static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301111 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301112 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001113 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301114};
1115
Krzysztof Kozlowskid6b16302015-05-02 00:56:36 +09001116static const struct platform_device_id mixer_driver_types[] = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301117 {
1118 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301119 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301120 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301121 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301122 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301123 }, {
1124 /* end node */
1125 }
1126};
1127
1128static struct of_device_id mixer_match_types[] = {
1129 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001130 .compatible = "samsung,exynos4210-mixer",
1131 .data = &exynos4210_mxr_drv_data,
1132 }, {
1133 .compatible = "samsung,exynos4212-mixer",
1134 .data = &exynos4212_mxr_drv_data,
1135 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301136 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301137 .data = &exynos5250_mxr_drv_data,
1138 }, {
1139 .compatible = "samsung,exynos5250-mixer",
1140 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301141 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301142 .compatible = "samsung,exynos5420-mixer",
1143 .data = &exynos5420_mxr_drv_data,
1144 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301145 /* end node */
1146 }
1147};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001148MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301149
Inki Daef37cd5e2014-05-09 14:25:20 +09001150static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001151{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001152 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001153 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001154 struct exynos_drm_plane *exynos_plane;
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001155 unsigned int i;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001156 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001157
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001158 ret = mixer_initialize(ctx, drm_dev);
1159 if (ret)
1160 return ret;
1161
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001162 for (i = 0; i < MIXER_WIN_NR; i++) {
1163 if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
Marek Szyprowskiab144202015-11-30 14:53:24 +01001164 continue;
1165
Marek Szyprowski40bdfb02015-12-16 13:21:42 +01001166 ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001167 1 << ctx->pipe, &plane_configs[i]);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001168 if (ret)
1169 return ret;
1170 }
1171
Gustavo Padovan5d3d0992015-10-12 22:07:48 +09001172 exynos_plane = &ctx->planes[DEFAULT_WIN];
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001173 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1174 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1175 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001176 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001177 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001178 ret = PTR_ERR(ctx->crtc);
1179 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001180 }
1181
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001182 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001183
1184free_ctx:
1185 devm_kfree(dev, ctx);
1186 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001187}
1188
1189static void mixer_unbind(struct device *dev, struct device *master, void *data)
1190{
1191 struct mixer_context *ctx = dev_get_drvdata(dev);
1192
Gustavo Padovan93bca242015-01-18 18:16:23 +09001193 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001194}
1195
1196static const struct component_ops mixer_component_ops = {
1197 .bind = mixer_bind,
1198 .unbind = mixer_unbind,
1199};
1200
1201static int mixer_probe(struct platform_device *pdev)
1202{
1203 struct device *dev = &pdev->dev;
1204 struct mixer_drv_data *drv;
1205 struct mixer_context *ctx;
1206 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001207
Sean Paulf041b252014-01-30 16:19:15 -05001208 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1209 if (!ctx) {
1210 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001211 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001212 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001213
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301214 if (dev->of_node) {
1215 const struct of_device_id *match;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001216
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;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301226 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001227 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301228 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001229 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301230 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001231
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001232 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001233
Inki Daedf5225b2014-05-29 18:28:02 +09001234 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda86650402015-06-11 23:23:37 +09001235 if (!ret)
1236 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001237
1238 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001239}
1240
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001241static int mixer_remove(struct platform_device *pdev)
1242{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001243 pm_runtime_disable(&pdev->dev);
1244
Inki Daedf5225b2014-05-29 18:28:02 +09001245 component_del(&pdev->dev, &mixer_component_ops);
Inki Daedf5225b2014-05-29 18:28:02 +09001246
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001247 return 0;
1248}
1249
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001250#ifdef CONFIG_PM_SLEEP
1251static int exynos_mixer_suspend(struct device *dev)
1252{
1253 struct mixer_context *ctx = dev_get_drvdata(dev);
1254 struct mixer_resources *res = &ctx->mixer_res;
1255
1256 clk_disable_unprepare(res->hdmi);
1257 clk_disable_unprepare(res->mixer);
1258 if (ctx->vp_enabled) {
1259 clk_disable_unprepare(res->vp);
1260 if (ctx->has_sclk)
1261 clk_disable_unprepare(res->sclk_mixer);
1262 }
1263
1264 return 0;
1265}
1266
1267static int exynos_mixer_resume(struct device *dev)
1268{
1269 struct mixer_context *ctx = dev_get_drvdata(dev);
1270 struct mixer_resources *res = &ctx->mixer_res;
1271 int ret;
1272
1273 ret = clk_prepare_enable(res->mixer);
1274 if (ret < 0) {
1275 DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
1276 return ret;
1277 }
1278 ret = clk_prepare_enable(res->hdmi);
1279 if (ret < 0) {
1280 DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
1281 return ret;
1282 }
1283 if (ctx->vp_enabled) {
1284 ret = clk_prepare_enable(res->vp);
1285 if (ret < 0) {
1286 DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
1287 ret);
1288 return ret;
1289 }
1290 if (ctx->has_sclk) {
1291 ret = clk_prepare_enable(res->sclk_mixer);
1292 if (ret < 0) {
1293 DRM_ERROR("Failed to prepare_enable the " \
1294 "sclk_mixer clk [%d]\n",
1295 ret);
1296 return ret;
1297 }
1298 }
1299 }
1300
1301 return 0;
1302}
1303#endif
1304
1305static const struct dev_pm_ops exynos_mixer_pm_ops = {
1306 SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
1307};
1308
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001309struct platform_driver mixer_driver = {
1310 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301311 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001312 .owner = THIS_MODULE,
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001313 .pm = &exynos_mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301314 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001315 },
1316 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001317 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301318 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001319};