blob: a229f86d221adc79d947d71dbd70f63a1a278081 [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),
120 }, {
121 .zpos = 1,
122 .type = DRM_PLANE_TYPE_CURSOR,
123 .pixel_formats = mixer_formats,
124 .num_pixel_formats = ARRAY_SIZE(mixer_formats),
125 }, {
126 .zpos = 2,
127 .type = DRM_PLANE_TYPE_OVERLAY,
128 .pixel_formats = vp_formats,
129 .num_pixel_formats = ARRAY_SIZE(vp_formats),
130 },
131};
132
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900133static const u8 filter_y_horiz_tap8[] = {
134 0, -1, -1, -1, -1, -1, -1, -1,
135 -1, -1, -1, -1, -1, 0, 0, 0,
136 0, 2, 4, 5, 6, 6, 6, 6,
137 6, 5, 5, 4, 3, 2, 1, 1,
138 0, -6, -12, -16, -18, -20, -21, -20,
139 -20, -18, -16, -13, -10, -8, -5, -2,
140 127, 126, 125, 121, 114, 107, 99, 89,
141 79, 68, 57, 46, 35, 25, 16, 8,
142};
143
144static const u8 filter_y_vert_tap4[] = {
145 0, -3, -6, -8, -8, -8, -8, -7,
146 -6, -5, -4, -3, -2, -1, -1, 0,
147 127, 126, 124, 118, 111, 102, 92, 81,
148 70, 59, 48, 37, 27, 19, 11, 5,
149 0, 5, 11, 19, 27, 37, 48, 59,
150 70, 81, 92, 102, 111, 118, 124, 126,
151 0, 0, -1, -1, -2, -3, -4, -5,
152 -6, -7, -8, -8, -8, -8, -6, -3,
153};
154
155static const u8 filter_cr_horiz_tap4[] = {
156 0, -3, -6, -8, -8, -8, -8, -7,
157 -6, -5, -4, -3, -2, -1, -1, 0,
158 127, 126, 124, 118, 111, 102, 92, 81,
159 70, 59, 48, 37, 27, 19, 11, 5,
160};
161
162static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
163{
164 return readl(res->vp_regs + reg_id);
165}
166
167static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
168 u32 val)
169{
170 writel(val, res->vp_regs + reg_id);
171}
172
173static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
174 u32 val, u32 mask)
175{
176 u32 old = vp_reg_read(res, reg_id);
177
178 val = (val & mask) | (old & ~mask);
179 writel(val, res->vp_regs + reg_id);
180}
181
182static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
183{
184 return readl(res->mixer_regs + reg_id);
185}
186
187static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
188 u32 val)
189{
190 writel(val, res->mixer_regs + reg_id);
191}
192
193static inline void mixer_reg_writemask(struct mixer_resources *res,
194 u32 reg_id, u32 val, u32 mask)
195{
196 u32 old = mixer_reg_read(res, reg_id);
197
198 val = (val & mask) | (old & ~mask);
199 writel(val, res->mixer_regs + reg_id);
200}
201
202static void mixer_regs_dump(struct mixer_context *ctx)
203{
204#define DUMPREG(reg_id) \
205do { \
206 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
207 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
208} while (0)
209
210 DUMPREG(MXR_STATUS);
211 DUMPREG(MXR_CFG);
212 DUMPREG(MXR_INT_EN);
213 DUMPREG(MXR_INT_STATUS);
214
215 DUMPREG(MXR_LAYER_CFG);
216 DUMPREG(MXR_VIDEO_CFG);
217
218 DUMPREG(MXR_GRAPHIC0_CFG);
219 DUMPREG(MXR_GRAPHIC0_BASE);
220 DUMPREG(MXR_GRAPHIC0_SPAN);
221 DUMPREG(MXR_GRAPHIC0_WH);
222 DUMPREG(MXR_GRAPHIC0_SXY);
223 DUMPREG(MXR_GRAPHIC0_DXY);
224
225 DUMPREG(MXR_GRAPHIC1_CFG);
226 DUMPREG(MXR_GRAPHIC1_BASE);
227 DUMPREG(MXR_GRAPHIC1_SPAN);
228 DUMPREG(MXR_GRAPHIC1_WH);
229 DUMPREG(MXR_GRAPHIC1_SXY);
230 DUMPREG(MXR_GRAPHIC1_DXY);
231#undef DUMPREG
232}
233
234static void vp_regs_dump(struct mixer_context *ctx)
235{
236#define DUMPREG(reg_id) \
237do { \
238 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
239 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
240} while (0)
241
242 DUMPREG(VP_ENABLE);
243 DUMPREG(VP_SRESET);
244 DUMPREG(VP_SHADOW_UPDATE);
245 DUMPREG(VP_FIELD_ID);
246 DUMPREG(VP_MODE);
247 DUMPREG(VP_IMG_SIZE_Y);
248 DUMPREG(VP_IMG_SIZE_C);
249 DUMPREG(VP_PER_RATE_CTRL);
250 DUMPREG(VP_TOP_Y_PTR);
251 DUMPREG(VP_BOT_Y_PTR);
252 DUMPREG(VP_TOP_C_PTR);
253 DUMPREG(VP_BOT_C_PTR);
254 DUMPREG(VP_ENDIAN_MODE);
255 DUMPREG(VP_SRC_H_POSITION);
256 DUMPREG(VP_SRC_V_POSITION);
257 DUMPREG(VP_SRC_WIDTH);
258 DUMPREG(VP_SRC_HEIGHT);
259 DUMPREG(VP_DST_H_POSITION);
260 DUMPREG(VP_DST_V_POSITION);
261 DUMPREG(VP_DST_WIDTH);
262 DUMPREG(VP_DST_HEIGHT);
263 DUMPREG(VP_H_RATIO);
264 DUMPREG(VP_V_RATIO);
265
266#undef DUMPREG
267}
268
269static inline void vp_filter_set(struct mixer_resources *res,
270 int reg_id, const u8 *data, unsigned int size)
271{
272 /* assure 4-byte align */
273 BUG_ON(size & 3);
274 for (; size; size -= 4, reg_id += 4, data += 4) {
275 u32 val = (data[0] << 24) | (data[1] << 16) |
276 (data[2] << 8) | data[3];
277 vp_reg_write(res, reg_id, val);
278 }
279}
280
281static void vp_default_filter(struct mixer_resources *res)
282{
283 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530284 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900285 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530286 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900287 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530288 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900289}
290
291static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
292{
293 struct mixer_resources *res = &ctx->mixer_res;
294
295 /* block update on vsync */
296 mixer_reg_writemask(res, MXR_STATUS, enable ?
297 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
298
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530299 if (ctx->vp_enabled)
300 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900301 VP_SHADOW_UPDATE_ENABLE : 0);
302}
303
304static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
305{
306 struct mixer_resources *res = &ctx->mixer_res;
307 u32 val;
308
309 /* choosing between interlace and progressive mode */
310 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
Tobias Jakobi1e6d4592015-04-07 01:14:50 +0200311 MXR_CFG_SCAN_PROGRESSIVE);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900312
Rahul Sharmadef5e092013-06-19 18:21:08 +0530313 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
314 /* choosing between proper HD and SD mode */
315 if (height <= 480)
316 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
317 else if (height <= 576)
318 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
319 else if (height <= 720)
320 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
321 else if (height <= 1080)
322 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
323 else
324 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
325 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900326
327 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
328}
329
330static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
331{
332 struct mixer_resources *res = &ctx->mixer_res;
333 u32 val;
334
335 if (height == 480) {
336 val = MXR_CFG_RGB601_0_255;
337 } else if (height == 576) {
338 val = MXR_CFG_RGB601_0_255;
339 } else if (height == 720) {
340 val = MXR_CFG_RGB709_16_235;
341 mixer_reg_write(res, MXR_CM_COEFF_Y,
342 (1 << 30) | (94 << 20) | (314 << 10) |
343 (32 << 0));
344 mixer_reg_write(res, MXR_CM_COEFF_CB,
345 (972 << 20) | (851 << 10) | (225 << 0));
346 mixer_reg_write(res, MXR_CM_COEFF_CR,
347 (225 << 20) | (820 << 10) | (1004 << 0));
348 } else if (height == 1080) {
349 val = MXR_CFG_RGB709_16_235;
350 mixer_reg_write(res, MXR_CM_COEFF_Y,
351 (1 << 30) | (94 << 20) | (314 << 10) |
352 (32 << 0));
353 mixer_reg_write(res, MXR_CM_COEFF_CB,
354 (972 << 20) | (851 << 10) | (225 << 0));
355 mixer_reg_write(res, MXR_CM_COEFF_CR,
356 (225 << 20) | (820 << 10) | (1004 << 0));
357 } else {
358 val = MXR_CFG_RGB709_16_235;
359 mixer_reg_write(res, MXR_CM_COEFF_Y,
360 (1 << 30) | (94 << 20) | (314 << 10) |
361 (32 << 0));
362 mixer_reg_write(res, MXR_CM_COEFF_CB,
363 (972 << 20) | (851 << 10) | (225 << 0));
364 mixer_reg_write(res, MXR_CM_COEFF_CR,
365 (225 << 20) | (820 << 10) | (1004 << 0));
366 }
367
368 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
369}
370
Tobias Jakobi5b1d5bc2015-05-06 14:10:22 +0200371static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
372 bool enable)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900373{
374 struct mixer_resources *res = &ctx->mixer_res;
375 u32 val = enable ? ~0 : 0;
376
377 switch (win) {
378 case 0:
379 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
380 break;
381 case 1:
382 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
383 break;
384 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530385 if (ctx->vp_enabled) {
386 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
387 mixer_reg_writemask(res, MXR_CFG, val,
388 MXR_CFG_VP_ENABLE);
Joonyoung Shimf1e716d2014-07-25 19:59:10 +0900389
390 /* control blending of graphic layer 0 */
391 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
392 MXR_GRP_CFG_BLEND_PRE_MUL |
393 MXR_GRP_CFG_PIXEL_BLEND_EN);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530394 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900395 break;
396 }
397}
398
399static void mixer_run(struct mixer_context *ctx)
400{
401 struct mixer_resources *res = &ctx->mixer_res;
402
403 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900404}
405
Rahul Sharma381be022014-06-23 11:02:22 +0530406static void mixer_stop(struct mixer_context *ctx)
407{
408 struct mixer_resources *res = &ctx->mixer_res;
409 int timeout = 20;
410
411 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
412
413 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
414 --timeout)
415 usleep_range(10000, 12000);
Rahul Sharma381be022014-06-23 11:02:22 +0530416}
417
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900418static void vp_video_buffer(struct mixer_context *ctx,
419 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900420{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100421 struct exynos_drm_plane_state *state =
422 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100423 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900424 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100425 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900426 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900427 dma_addr_t luma_addr[2], chroma_addr[2];
428 bool tiled_mode = false;
429 bool crcb_mode = false;
430 u32 val;
431
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900432 switch (fb->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900433 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900434 crcb_mode = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900435 break;
Tobias Jakobi8f2590f2015-04-27 23:10:16 +0200436 case DRM_FORMAT_NV21:
437 crcb_mode = true;
438 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900439 default:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900440 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900441 fb->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900442 return;
443 }
444
Marek Szyprowski0488f502015-11-30 14:53:21 +0100445 luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
446 chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900447
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900448 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900449 ctx->interlace = true;
450 if (tiled_mode) {
451 luma_addr[1] = luma_addr[0] + 0x40;
452 chroma_addr[1] = chroma_addr[0] + 0x40;
453 } else {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900454 luma_addr[1] = luma_addr[0] + fb->pitches[0];
455 chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900456 }
457 } else {
458 ctx->interlace = false;
459 luma_addr[1] = 0;
460 chroma_addr[1] = 0;
461 }
462
463 spin_lock_irqsave(&res->reg_slock, flags);
464 mixer_vsync_set_update(ctx, false);
465
466 /* interlace or progressive scan mode */
467 val = (ctx->interlace ? ~0 : 0);
468 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
469
470 /* setup format */
471 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
472 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
473 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
474
475 /* setting size of input image */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900476 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
477 VP_IMG_VSIZE(fb->height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900478 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900479 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
480 VP_IMG_VSIZE(fb->height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900481
Marek Szyprowski0114f402015-11-30 14:53:22 +0100482 vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
483 vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900484 vp_reg_write(res, VP_SRC_H_POSITION,
Marek Szyprowski0114f402015-11-30 14:53:22 +0100485 VP_SRC_H_POSITION_VAL(state->src.x));
486 vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900487
Marek Szyprowski0114f402015-11-30 14:53:22 +0100488 vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
489 vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900490 if (ctx->interlace) {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100491 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
492 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900493 } else {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100494 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
495 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900496 }
497
Marek Szyprowski0114f402015-11-30 14:53:22 +0100498 vp_reg_write(res, VP_H_RATIO, state->h_ratio);
499 vp_reg_write(res, VP_V_RATIO, state->v_ratio);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900500
501 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
502
503 /* set buffer address to vp */
504 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
505 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
506 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
507 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
508
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900509 mixer_cfg_scan(ctx, mode->vdisplay);
510 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
511 mixer_cfg_layer(ctx, plane->zpos, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900512 mixer_run(ctx);
513
514 mixer_vsync_set_update(ctx, true);
515 spin_unlock_irqrestore(&res->reg_slock, flags);
516
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200517 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900518 vp_regs_dump(ctx);
519}
520
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530521static void mixer_layer_update(struct mixer_context *ctx)
522{
523 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530524
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530525 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530526}
527
Tobias Jakobi26110152015-04-07 01:14:52 +0200528static int mixer_setup_scale(const struct exynos_drm_plane *plane,
529 unsigned int *x_ratio, unsigned int *y_ratio)
530{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100531 struct exynos_drm_plane_state *state =
532 to_exynos_plane_state(plane->base.state);
533
534 if (state->crtc.w != state->src.w) {
535 if (state->crtc.w == 2 * state->src.w)
Tobias Jakobi26110152015-04-07 01:14:52 +0200536 *x_ratio = 1;
537 else
538 goto fail;
539 }
540
Marek Szyprowski0114f402015-11-30 14:53:22 +0100541 if (state->crtc.h != state->src.h) {
542 if (state->crtc.h == 2 * state->src.h)
Tobias Jakobi26110152015-04-07 01:14:52 +0200543 *y_ratio = 1;
544 else
545 goto fail;
546 }
547
548 return 0;
549
550fail:
551 DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n");
552 return -ENOTSUPP;
553}
554
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900555static void mixer_graph_buffer(struct mixer_context *ctx,
556 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900557{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100558 struct exynos_drm_plane_state *state =
559 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100560 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900561 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100562 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900563 unsigned long flags;
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900564 unsigned int win = plane->zpos;
Tobias Jakobi26110152015-04-07 01:14:52 +0200565 unsigned int x_ratio = 0, y_ratio = 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900566 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900567 dma_addr_t dma_addr;
568 unsigned int fmt;
569 u32 val;
570
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900571 switch (fb->pixel_format) {
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200572 case DRM_FORMAT_XRGB4444:
573 fmt = MXR_FORMAT_ARGB4444;
574 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900575
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200576 case DRM_FORMAT_XRGB1555:
577 fmt = MXR_FORMAT_ARGB1555;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900578 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200579
580 case DRM_FORMAT_RGB565:
581 fmt = MXR_FORMAT_RGB565;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900582 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200583
584 case DRM_FORMAT_XRGB8888:
585 case DRM_FORMAT_ARGB8888:
586 fmt = MXR_FORMAT_ARGB8888;
587 break;
588
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900589 default:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200590 DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
591 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900592 }
593
Tobias Jakobi26110152015-04-07 01:14:52 +0200594 /* check if mixer supports requested scaling setup */
595 if (mixer_setup_scale(plane, &x_ratio, &y_ratio))
596 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900597
Marek Szyprowski0114f402015-11-30 14:53:22 +0100598 dst_x_offset = state->crtc.x;
599 dst_y_offset = state->crtc.y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900600
601 /* converting dma address base and source offset */
Marek Szyprowski0488f502015-11-30 14:53:21 +0100602 dma_addr = exynos_drm_fb_dma_addr(fb, 0)
Marek Szyprowski0114f402015-11-30 14:53:22 +0100603 + (state->src.x * fb->bits_per_pixel >> 3)
604 + (state->src.y * fb->pitches[0]);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900605 src_x_offset = 0;
606 src_y_offset = 0;
607
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900608 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900609 ctx->interlace = true;
610 else
611 ctx->interlace = false;
612
613 spin_lock_irqsave(&res->reg_slock, flags);
614 mixer_vsync_set_update(ctx, false);
615
616 /* setup format */
617 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
618 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
619
620 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000621 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900622 fb->pitches[0] / (fb->bits_per_pixel >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900623
Rahul Sharmadef5e092013-06-19 18:21:08 +0530624 /* setup display size */
625 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
Gustavo Padovan5d3d0992015-10-12 22:07:48 +0900626 win == DEFAULT_WIN) {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900627 val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
628 val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530629 mixer_reg_write(res, MXR_RESOLUTION, val);
630 }
631
Marek Szyprowski0114f402015-11-30 14:53:22 +0100632 val = MXR_GRP_WH_WIDTH(state->src.w);
633 val |= MXR_GRP_WH_HEIGHT(state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900634 val |= MXR_GRP_WH_H_SCALE(x_ratio);
635 val |= MXR_GRP_WH_V_SCALE(y_ratio);
636 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
637
638 /* setup offsets in source image */
639 val = MXR_GRP_SXY_SX(src_x_offset);
640 val |= MXR_GRP_SXY_SY(src_y_offset);
641 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
642
643 /* setup offsets in display image */
644 val = MXR_GRP_DXY_DX(dst_x_offset);
645 val |= MXR_GRP_DXY_DY(dst_y_offset);
646 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
647
648 /* set buffer address to mixer */
649 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
650
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900651 mixer_cfg_scan(ctx, mode->vdisplay);
652 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900653 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530654
655 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530656 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
657 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530658 mixer_layer_update(ctx);
659
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900660 mixer_run(ctx);
661
662 mixer_vsync_set_update(ctx, true);
663 spin_unlock_irqrestore(&res->reg_slock, flags);
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200664
665 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900666}
667
668static void vp_win_reset(struct mixer_context *ctx)
669{
670 struct mixer_resources *res = &ctx->mixer_res;
671 int tries = 100;
672
673 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
674 for (tries = 100; tries; --tries) {
675 /* waiting until VP_SRESET_PROCESSING is 0 */
676 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
677 break;
Tomasz Stanislawski02b3de42015-09-25 14:48:29 +0200678 mdelay(10);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900679 }
680 WARN(tries == 0, "failed to reset Video Processor\n");
681}
682
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900683static void mixer_win_reset(struct mixer_context *ctx)
684{
685 struct mixer_resources *res = &ctx->mixer_res;
686 unsigned long flags;
687 u32 val; /* value stored to register */
688
689 spin_lock_irqsave(&res->reg_slock, flags);
690 mixer_vsync_set_update(ctx, false);
691
692 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
693
694 /* set output in RGB888 mode */
695 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
696
697 /* 16 beat burst in DMA */
698 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
699 MXR_STATUS_BURST_MASK);
700
701 /* setting default layer priority: layer1 > layer0 > video
702 * because typical usage scenario would be
703 * layer1 - OSD
704 * layer0 - framebuffer
705 * video - video overlay
706 */
707 val = MXR_LAYER_CFG_GRP1_VAL(3);
708 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530709 if (ctx->vp_enabled)
710 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900711 mixer_reg_write(res, MXR_LAYER_CFG, val);
712
713 /* setting background color */
714 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
715 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
716 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
717
718 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900719 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
720 val |= MXR_GRP_CFG_WIN_BLEND_EN;
721 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
722
Sean Paul0377f4e2013-04-25 15:13:26 -0400723 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900724 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400725
726 /* Blend layer 1 into layer 0 */
727 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
728 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900729 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
730
Seung-Woo Kim57366032012-05-15 17:22:08 +0900731 /* setting video layers */
732 val = MXR_GRP_CFG_ALPHA_VAL(0);
733 mixer_reg_write(res, MXR_VIDEO_CFG, val);
734
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530735 if (ctx->vp_enabled) {
736 /* configuration of Video Processor Registers */
737 vp_win_reset(ctx);
738 vp_default_filter(res);
739 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900740
741 /* disable all layers */
742 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
743 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530744 if (ctx->vp_enabled)
745 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900746
747 mixer_vsync_set_update(ctx, true);
748 spin_unlock_irqrestore(&res->reg_slock, flags);
749}
750
Sean Paul45517892014-01-30 16:19:05 -0500751static irqreturn_t mixer_irq_handler(int irq, void *arg)
752{
753 struct mixer_context *ctx = arg;
754 struct mixer_resources *res = &ctx->mixer_res;
755 u32 val, base, shadow;
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300756 int win;
Sean Paul45517892014-01-30 16:19:05 -0500757
758 spin_lock(&res->reg_slock);
759
760 /* read interrupt status for handling and clearing flags for VSYNC */
761 val = mixer_reg_read(res, MXR_INT_STATUS);
762
763 /* handling VSYNC */
764 if (val & MXR_INT_STATUS_VSYNC) {
Andrzej Hajda81a464d2015-07-09 10:07:53 +0200765 /* vsync interrupt use different bit for read and clear */
766 val |= MXR_INT_CLEAR_VSYNC;
767 val &= ~MXR_INT_STATUS_VSYNC;
768
Sean Paul45517892014-01-30 16:19:05 -0500769 /* interlace scan need to check shadow register */
770 if (ctx->interlace) {
771 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
772 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
773 if (base != shadow)
774 goto out;
775
776 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
777 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
778 if (base != shadow)
779 goto out;
780 }
781
Gustavo Padovaneafd5402015-07-16 12:23:32 -0300782 drm_crtc_handle_vblank(&ctx->crtc->base);
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300783 for (win = 0 ; win < MIXER_WIN_NR ; win++) {
784 struct exynos_drm_plane *plane = &ctx->planes[win];
785
786 if (!plane->pending_fb)
787 continue;
788
789 exynos_drm_crtc_finish_update(ctx->crtc, plane);
790 }
Sean Paul45517892014-01-30 16:19:05 -0500791
792 /* set wait vsync event to zero and wake up queue. */
793 if (atomic_read(&ctx->wait_vsync_event)) {
794 atomic_set(&ctx->wait_vsync_event, 0);
795 wake_up(&ctx->wait_vsync_queue);
796 }
797 }
798
799out:
800 /* clear interrupts */
Sean Paul45517892014-01-30 16:19:05 -0500801 mixer_reg_write(res, MXR_INT_STATUS, val);
802
803 spin_unlock(&res->reg_slock);
804
805 return IRQ_HANDLED;
806}
807
808static int mixer_resources_init(struct mixer_context *mixer_ctx)
809{
810 struct device *dev = &mixer_ctx->pdev->dev;
811 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
812 struct resource *res;
813 int ret;
814
815 spin_lock_init(&mixer_res->reg_slock);
816
817 mixer_res->mixer = devm_clk_get(dev, "mixer");
818 if (IS_ERR(mixer_res->mixer)) {
819 dev_err(dev, "failed to get clock 'mixer'\n");
820 return -ENODEV;
821 }
822
Marek Szyprowski04427ec2015-02-02 14:20:28 +0100823 mixer_res->hdmi = devm_clk_get(dev, "hdmi");
824 if (IS_ERR(mixer_res->hdmi)) {
825 dev_err(dev, "failed to get clock 'hdmi'\n");
826 return PTR_ERR(mixer_res->hdmi);
827 }
828
Sean Paul45517892014-01-30 16:19:05 -0500829 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
830 if (IS_ERR(mixer_res->sclk_hdmi)) {
831 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
832 return -ENODEV;
833 }
834 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
835 if (res == NULL) {
836 dev_err(dev, "get memory resource failed.\n");
837 return -ENXIO;
838 }
839
840 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
841 resource_size(res));
842 if (mixer_res->mixer_regs == NULL) {
843 dev_err(dev, "register mapping failed.\n");
844 return -ENXIO;
845 }
846
847 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
848 if (res == NULL) {
849 dev_err(dev, "get interrupt resource failed.\n");
850 return -ENXIO;
851 }
852
853 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
854 0, "drm_mixer", mixer_ctx);
855 if (ret) {
856 dev_err(dev, "request interrupt failed.\n");
857 return ret;
858 }
859 mixer_res->irq = res->start;
860
861 return 0;
862}
863
864static int vp_resources_init(struct mixer_context *mixer_ctx)
865{
866 struct device *dev = &mixer_ctx->pdev->dev;
867 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
868 struct resource *res;
869
870 mixer_res->vp = devm_clk_get(dev, "vp");
871 if (IS_ERR(mixer_res->vp)) {
872 dev_err(dev, "failed to get clock 'vp'\n");
873 return -ENODEV;
874 }
Sean Paul45517892014-01-30 16:19:05 -0500875
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200876 if (mixer_ctx->has_sclk) {
877 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
878 if (IS_ERR(mixer_res->sclk_mixer)) {
879 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
880 return -ENODEV;
881 }
882 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
883 if (IS_ERR(mixer_res->mout_mixer)) {
884 dev_err(dev, "failed to get clock 'mout_mixer'\n");
885 return -ENODEV;
886 }
887
888 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
889 clk_set_parent(mixer_res->mout_mixer,
890 mixer_res->sclk_hdmi);
891 }
Sean Paul45517892014-01-30 16:19:05 -0500892
893 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
894 if (res == NULL) {
895 dev_err(dev, "get memory resource failed.\n");
896 return -ENXIO;
897 }
898
899 mixer_res->vp_regs = devm_ioremap(dev, res->start,
900 resource_size(res));
901 if (mixer_res->vp_regs == NULL) {
902 dev_err(dev, "register mapping failed.\n");
903 return -ENXIO;
904 }
905
906 return 0;
907}
908
Gustavo Padovan93bca242015-01-18 18:16:23 +0900909static int mixer_initialize(struct mixer_context *mixer_ctx,
Inki Daef37cd5e2014-05-09 14:25:20 +0900910 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500911{
912 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +0900913 struct exynos_drm_private *priv;
914 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500915
Gustavo Padovaneb88e422014-11-26 16:43:27 -0200916 mixer_ctx->drm_dev = drm_dev;
Gustavo Padovan8a326ed2014-11-04 18:44:47 -0200917 mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500918
919 /* acquire resources: regs, irqs, clocks */
920 ret = mixer_resources_init(mixer_ctx);
921 if (ret) {
922 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
923 return ret;
924 }
925
926 if (mixer_ctx->vp_enabled) {
927 /* acquire vp resources: regs, irqs, clocks */
928 ret = vp_resources_init(mixer_ctx);
929 if (ret) {
930 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
931 return ret;
932 }
933 }
934
Joonyoung Shimeb7a3fc2015-07-02 21:49:39 +0900935 ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900936 if (ret)
937 priv->pipe--;
Sean Paulf041b252014-01-30 16:19:15 -0500938
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900939 return ret;
Sean Paul45517892014-01-30 16:19:05 -0500940}
941
Gustavo Padovan93bca242015-01-18 18:16:23 +0900942static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
Inki Dae1055b392012-10-19 17:37:35 +0900943{
Joonyoung Shimbf566082015-07-02 21:49:38 +0900944 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900945}
946
Gustavo Padovan93bca242015-01-18 18:16:23 +0900947static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900948{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900949 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900950 struct mixer_resources *res = &mixer_ctx->mixer_res;
951
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200952 __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
953 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Sean Paulf041b252014-01-30 16:19:15 -0500954 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900955
956 /* enable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200957 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
958 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900959
960 return 0;
961}
962
Gustavo Padovan93bca242015-01-18 18:16:23 +0900963static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900964{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900965 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900966 struct mixer_resources *res = &mixer_ctx->mixer_res;
967
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200968 __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
969
970 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Andrzej Hajda947710c2015-07-09 08:25:41 +0200971 return;
Andrzej Hajda947710c2015-07-09 08:25:41 +0200972
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900973 /* disable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200974 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900975 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
976}
977
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900978static void mixer_update_plane(struct exynos_drm_crtc *crtc,
979 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900980{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900981 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900982
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900983 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900984
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200985 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Shirish Sdda90122013-01-23 22:03:18 -0500986 return;
Shirish Sdda90122013-01-23 22:03:18 -0500987
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900988 if (plane->zpos > 1 && mixer_ctx->vp_enabled)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900989 vp_video_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900990 else
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900991 mixer_graph_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900992}
993
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900994static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
995 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900996{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900997 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900998 struct mixer_resources *res = &mixer_ctx->mixer_res;
999 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001000
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001001 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001002
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001003 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301004 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301005
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001006 spin_lock_irqsave(&res->reg_slock, flags);
1007 mixer_vsync_set_update(mixer_ctx, false);
1008
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001009 mixer_cfg_layer(mixer_ctx, plane->zpos, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001010
1011 mixer_vsync_set_update(mixer_ctx, true);
1012 spin_unlock_irqrestore(&res->reg_slock, flags);
1013}
1014
Gustavo Padovan93bca242015-01-18 18:16:23 +09001015static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -05001016{
Gustavo Padovan93bca242015-01-18 18:16:23 +09001017 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001018 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +05301019
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001020 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush K6e95d5e2012-12-06 20:16:03 +05301021 return;
Prathyush K6e95d5e2012-12-06 20:16:03 +05301022
Gustavo Padovan93bca242015-01-18 18:16:23 +09001023 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001024 if (err < 0) {
1025 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
1026 return;
1027 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301028
Prathyush K6e95d5e2012-12-06 20:16:03 +05301029 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1030
1031 /*
1032 * wait for MIXER to signal VSYNC interrupt or return after
1033 * timeout which is set to 50ms (refresh rate of 20).
1034 */
1035 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1036 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001037 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301038 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301039
Gustavo Padovan93bca242015-01-18 18:16:23 +09001040 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +05301041}
1042
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001043static void mixer_enable(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;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301046 struct mixer_resources *res = &ctx->mixer_res;
1047
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001048 if (test_bit(MXR_BIT_POWERED, &ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301049 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301050
Sean Paulaf65c802014-01-30 16:19:27 -05001051 pm_runtime_get_sync(ctx->dev);
1052
Rahul Sharmad74ed932014-06-23 11:02:24 +05301053 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1054
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001055 if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
Andrzej Hajdafc0732482015-07-09 08:25:40 +02001056 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001057 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
1058 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301059 mixer_win_reset(ctx);
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001060
1061 set_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301062}
1063
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001064static void mixer_disable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301065{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001066 struct mixer_context *ctx = crtc->ctx;
Joonyoung Shimc329f662015-06-12 20:34:28 +09001067 int i;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301068
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001069 if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301070 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301071
Rahul Sharma381be022014-06-23 11:02:22 +05301072 mixer_stop(ctx);
Tobias Jakobic0734fb2015-05-06 14:10:21 +02001073 mixer_regs_dump(ctx);
Joonyoung Shimc329f662015-06-12 20:34:28 +09001074
1075 for (i = 0; i < MIXER_WIN_NR; i++)
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001076 mixer_disable_plane(crtc, &ctx->planes[i]);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301077
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001078 pm_runtime_put(ctx->dev);
1079
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001080 clear_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301081}
1082
Sean Paulf041b252014-01-30 16:19:15 -05001083/* Only valid for Mixer version 16.0.33.0 */
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001084static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
1085 struct drm_crtc_state *state)
Sean Paulf041b252014-01-30 16:19:15 -05001086{
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001087 struct drm_display_mode *mode = &state->adjusted_mode;
Sean Paulf041b252014-01-30 16:19:15 -05001088 u32 w, h;
1089
1090 w = mode->hdisplay;
1091 h = mode->vdisplay;
1092
1093 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1094 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1095 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1096
1097 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1098 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1099 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1100 return 0;
1101
1102 return -EINVAL;
1103}
1104
Krzysztof Kozlowskif3aaf762015-05-07 09:04:45 +09001105static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001106 .enable = mixer_enable,
1107 .disable = mixer_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001108 .enable_vblank = mixer_enable_vblank,
1109 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301110 .wait_for_vblank = mixer_wait_for_vblank,
Gustavo Padovan9cc76102015-08-03 14:38:05 +09001111 .update_plane = mixer_update_plane,
1112 .disable_plane = mixer_disable_plane,
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001113 .atomic_check = mixer_atomic_check,
Sean Paulf041b252014-01-30 16:19:15 -05001114};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001115
Rahul Sharmadef5e092013-06-19 18:21:08 +05301116static struct mixer_drv_data exynos5420_mxr_drv_data = {
1117 .version = MXR_VER_128_0_0_184,
1118 .is_vp_enabled = 0,
1119};
1120
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301121static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301122 .version = MXR_VER_16_0_33_0,
1123 .is_vp_enabled = 0,
1124};
1125
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001126static struct mixer_drv_data exynos4212_mxr_drv_data = {
1127 .version = MXR_VER_0_0_0_16,
1128 .is_vp_enabled = 1,
1129};
1130
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301131static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301132 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301133 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001134 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301135};
1136
Krzysztof Kozlowskid6b16302015-05-02 00:56:36 +09001137static const struct platform_device_id mixer_driver_types[] = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301138 {
1139 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301140 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301141 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301142 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301143 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301144 }, {
1145 /* end node */
1146 }
1147};
1148
1149static struct of_device_id mixer_match_types[] = {
1150 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001151 .compatible = "samsung,exynos4210-mixer",
1152 .data = &exynos4210_mxr_drv_data,
1153 }, {
1154 .compatible = "samsung,exynos4212-mixer",
1155 .data = &exynos4212_mxr_drv_data,
1156 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301157 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301158 .data = &exynos5250_mxr_drv_data,
1159 }, {
1160 .compatible = "samsung,exynos5250-mixer",
1161 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301162 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301163 .compatible = "samsung,exynos5420-mixer",
1164 .data = &exynos5420_mxr_drv_data,
1165 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301166 /* end node */
1167 }
1168};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001169MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301170
Inki Daef37cd5e2014-05-09 14:25:20 +09001171static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001172{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001173 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001174 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001175 struct exynos_drm_plane *exynos_plane;
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001176 unsigned int i;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001177 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001178
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001179 ret = mixer_initialize(ctx, drm_dev);
1180 if (ret)
1181 return ret;
1182
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001183 for (i = 0; i < MIXER_WIN_NR; i++) {
1184 if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
Marek Szyprowskiab144202015-11-30 14:53:24 +01001185 continue;
1186
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001187 ret = exynos_plane_init(drm_dev, &ctx->planes[i],
1188 1 << ctx->pipe, &plane_configs[i]);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001189 if (ret)
1190 return ret;
1191 }
1192
Gustavo Padovan5d3d0992015-10-12 22:07:48 +09001193 exynos_plane = &ctx->planes[DEFAULT_WIN];
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001194 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1195 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1196 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001197 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001198 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001199 ret = PTR_ERR(ctx->crtc);
1200 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001201 }
1202
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001203 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001204
1205free_ctx:
1206 devm_kfree(dev, ctx);
1207 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001208}
1209
1210static void mixer_unbind(struct device *dev, struct device *master, void *data)
1211{
1212 struct mixer_context *ctx = dev_get_drvdata(dev);
1213
Gustavo Padovan93bca242015-01-18 18:16:23 +09001214 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001215}
1216
1217static const struct component_ops mixer_component_ops = {
1218 .bind = mixer_bind,
1219 .unbind = mixer_unbind,
1220};
1221
1222static int mixer_probe(struct platform_device *pdev)
1223{
1224 struct device *dev = &pdev->dev;
1225 struct mixer_drv_data *drv;
1226 struct mixer_context *ctx;
1227 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001228
Sean Paulf041b252014-01-30 16:19:15 -05001229 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1230 if (!ctx) {
1231 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001232 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001233 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001234
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301235 if (dev->of_node) {
1236 const struct of_device_id *match;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001237
Sachin Kamate436b092013-06-05 16:00:23 +09001238 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301239 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301240 } else {
1241 drv = (struct mixer_drv_data *)
1242 platform_get_device_id(pdev)->driver_data;
1243 }
1244
Sean Paul45517892014-01-30 16:19:05 -05001245 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001246 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301247 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001248 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301249 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001250 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301251 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001252
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001253 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001254
Inki Daedf5225b2014-05-29 18:28:02 +09001255 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda86650402015-06-11 23:23:37 +09001256 if (!ret)
1257 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001258
1259 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001260}
1261
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001262static int mixer_remove(struct platform_device *pdev)
1263{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001264 pm_runtime_disable(&pdev->dev);
1265
Inki Daedf5225b2014-05-29 18:28:02 +09001266 component_del(&pdev->dev, &mixer_component_ops);
Inki Daedf5225b2014-05-29 18:28:02 +09001267
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001268 return 0;
1269}
1270
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001271#ifdef CONFIG_PM_SLEEP
1272static int exynos_mixer_suspend(struct device *dev)
1273{
1274 struct mixer_context *ctx = dev_get_drvdata(dev);
1275 struct mixer_resources *res = &ctx->mixer_res;
1276
1277 clk_disable_unprepare(res->hdmi);
1278 clk_disable_unprepare(res->mixer);
1279 if (ctx->vp_enabled) {
1280 clk_disable_unprepare(res->vp);
1281 if (ctx->has_sclk)
1282 clk_disable_unprepare(res->sclk_mixer);
1283 }
1284
1285 return 0;
1286}
1287
1288static int exynos_mixer_resume(struct device *dev)
1289{
1290 struct mixer_context *ctx = dev_get_drvdata(dev);
1291 struct mixer_resources *res = &ctx->mixer_res;
1292 int ret;
1293
1294 ret = clk_prepare_enable(res->mixer);
1295 if (ret < 0) {
1296 DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
1297 return ret;
1298 }
1299 ret = clk_prepare_enable(res->hdmi);
1300 if (ret < 0) {
1301 DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
1302 return ret;
1303 }
1304 if (ctx->vp_enabled) {
1305 ret = clk_prepare_enable(res->vp);
1306 if (ret < 0) {
1307 DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
1308 ret);
1309 return ret;
1310 }
1311 if (ctx->has_sclk) {
1312 ret = clk_prepare_enable(res->sclk_mixer);
1313 if (ret < 0) {
1314 DRM_ERROR("Failed to prepare_enable the " \
1315 "sclk_mixer clk [%d]\n",
1316 ret);
1317 return ret;
1318 }
1319 }
1320 }
1321
1322 return 0;
1323}
1324#endif
1325
1326static const struct dev_pm_ops exynos_mixer_pm_ops = {
1327 SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
1328};
1329
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001330struct platform_driver mixer_driver = {
1331 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301332 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001333 .owner = THIS_MODULE,
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001334 .pm = &exynos_mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301335 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001336 },
1337 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001338 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301339 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001340};