blob: 4190285798efc48ec56a9de7db24791277f57480 [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 Szyprowski6178d3d2015-11-30 14:53:26 +0100120 .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100121 }, {
122 .zpos = 1,
123 .type = DRM_PLANE_TYPE_CURSOR,
124 .pixel_formats = mixer_formats,
125 .num_pixel_formats = ARRAY_SIZE(mixer_formats),
Marek Szyprowski6178d3d2015-11-30 14:53:26 +0100126 .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100127 }, {
128 .zpos = 2,
129 .type = DRM_PLANE_TYPE_OVERLAY,
130 .pixel_formats = vp_formats,
131 .num_pixel_formats = ARRAY_SIZE(vp_formats),
Marek Szyprowski6178d3d2015-11-30 14:53:26 +0100132 .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100133 },
134};
135
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900136static const u8 filter_y_horiz_tap8[] = {
137 0, -1, -1, -1, -1, -1, -1, -1,
138 -1, -1, -1, -1, -1, 0, 0, 0,
139 0, 2, 4, 5, 6, 6, 6, 6,
140 6, 5, 5, 4, 3, 2, 1, 1,
141 0, -6, -12, -16, -18, -20, -21, -20,
142 -20, -18, -16, -13, -10, -8, -5, -2,
143 127, 126, 125, 121, 114, 107, 99, 89,
144 79, 68, 57, 46, 35, 25, 16, 8,
145};
146
147static const u8 filter_y_vert_tap4[] = {
148 0, -3, -6, -8, -8, -8, -8, -7,
149 -6, -5, -4, -3, -2, -1, -1, 0,
150 127, 126, 124, 118, 111, 102, 92, 81,
151 70, 59, 48, 37, 27, 19, 11, 5,
152 0, 5, 11, 19, 27, 37, 48, 59,
153 70, 81, 92, 102, 111, 118, 124, 126,
154 0, 0, -1, -1, -2, -3, -4, -5,
155 -6, -7, -8, -8, -8, -8, -6, -3,
156};
157
158static const u8 filter_cr_horiz_tap4[] = {
159 0, -3, -6, -8, -8, -8, -8, -7,
160 -6, -5, -4, -3, -2, -1, -1, 0,
161 127, 126, 124, 118, 111, 102, 92, 81,
162 70, 59, 48, 37, 27, 19, 11, 5,
163};
164
165static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
166{
167 return readl(res->vp_regs + reg_id);
168}
169
170static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
171 u32 val)
172{
173 writel(val, res->vp_regs + reg_id);
174}
175
176static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
177 u32 val, u32 mask)
178{
179 u32 old = vp_reg_read(res, reg_id);
180
181 val = (val & mask) | (old & ~mask);
182 writel(val, res->vp_regs + reg_id);
183}
184
185static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
186{
187 return readl(res->mixer_regs + reg_id);
188}
189
190static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
191 u32 val)
192{
193 writel(val, res->mixer_regs + reg_id);
194}
195
196static inline void mixer_reg_writemask(struct mixer_resources *res,
197 u32 reg_id, u32 val, u32 mask)
198{
199 u32 old = mixer_reg_read(res, reg_id);
200
201 val = (val & mask) | (old & ~mask);
202 writel(val, res->mixer_regs + reg_id);
203}
204
205static void mixer_regs_dump(struct mixer_context *ctx)
206{
207#define DUMPREG(reg_id) \
208do { \
209 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
210 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
211} while (0)
212
213 DUMPREG(MXR_STATUS);
214 DUMPREG(MXR_CFG);
215 DUMPREG(MXR_INT_EN);
216 DUMPREG(MXR_INT_STATUS);
217
218 DUMPREG(MXR_LAYER_CFG);
219 DUMPREG(MXR_VIDEO_CFG);
220
221 DUMPREG(MXR_GRAPHIC0_CFG);
222 DUMPREG(MXR_GRAPHIC0_BASE);
223 DUMPREG(MXR_GRAPHIC0_SPAN);
224 DUMPREG(MXR_GRAPHIC0_WH);
225 DUMPREG(MXR_GRAPHIC0_SXY);
226 DUMPREG(MXR_GRAPHIC0_DXY);
227
228 DUMPREG(MXR_GRAPHIC1_CFG);
229 DUMPREG(MXR_GRAPHIC1_BASE);
230 DUMPREG(MXR_GRAPHIC1_SPAN);
231 DUMPREG(MXR_GRAPHIC1_WH);
232 DUMPREG(MXR_GRAPHIC1_SXY);
233 DUMPREG(MXR_GRAPHIC1_DXY);
234#undef DUMPREG
235}
236
237static void vp_regs_dump(struct mixer_context *ctx)
238{
239#define DUMPREG(reg_id) \
240do { \
241 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
242 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
243} while (0)
244
245 DUMPREG(VP_ENABLE);
246 DUMPREG(VP_SRESET);
247 DUMPREG(VP_SHADOW_UPDATE);
248 DUMPREG(VP_FIELD_ID);
249 DUMPREG(VP_MODE);
250 DUMPREG(VP_IMG_SIZE_Y);
251 DUMPREG(VP_IMG_SIZE_C);
252 DUMPREG(VP_PER_RATE_CTRL);
253 DUMPREG(VP_TOP_Y_PTR);
254 DUMPREG(VP_BOT_Y_PTR);
255 DUMPREG(VP_TOP_C_PTR);
256 DUMPREG(VP_BOT_C_PTR);
257 DUMPREG(VP_ENDIAN_MODE);
258 DUMPREG(VP_SRC_H_POSITION);
259 DUMPREG(VP_SRC_V_POSITION);
260 DUMPREG(VP_SRC_WIDTH);
261 DUMPREG(VP_SRC_HEIGHT);
262 DUMPREG(VP_DST_H_POSITION);
263 DUMPREG(VP_DST_V_POSITION);
264 DUMPREG(VP_DST_WIDTH);
265 DUMPREG(VP_DST_HEIGHT);
266 DUMPREG(VP_H_RATIO);
267 DUMPREG(VP_V_RATIO);
268
269#undef DUMPREG
270}
271
272static inline void vp_filter_set(struct mixer_resources *res,
273 int reg_id, const u8 *data, unsigned int size)
274{
275 /* assure 4-byte align */
276 BUG_ON(size & 3);
277 for (; size; size -= 4, reg_id += 4, data += 4) {
278 u32 val = (data[0] << 24) | (data[1] << 16) |
279 (data[2] << 8) | data[3];
280 vp_reg_write(res, reg_id, val);
281 }
282}
283
284static void vp_default_filter(struct mixer_resources *res)
285{
286 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530287 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900288 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530289 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900290 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530291 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900292}
293
294static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
295{
296 struct mixer_resources *res = &ctx->mixer_res;
297
298 /* block update on vsync */
299 mixer_reg_writemask(res, MXR_STATUS, enable ?
300 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
301
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530302 if (ctx->vp_enabled)
303 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900304 VP_SHADOW_UPDATE_ENABLE : 0);
305}
306
307static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
308{
309 struct mixer_resources *res = &ctx->mixer_res;
310 u32 val;
311
312 /* choosing between interlace and progressive mode */
313 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
Tobias Jakobi1e6d4592015-04-07 01:14:50 +0200314 MXR_CFG_SCAN_PROGRESSIVE);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900315
Rahul Sharmadef5e092013-06-19 18:21:08 +0530316 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
317 /* choosing between proper HD and SD mode */
318 if (height <= 480)
319 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
320 else if (height <= 576)
321 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
322 else if (height <= 720)
323 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
324 else if (height <= 1080)
325 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
326 else
327 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
328 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900329
330 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
331}
332
333static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
334{
335 struct mixer_resources *res = &ctx->mixer_res;
336 u32 val;
337
338 if (height == 480) {
339 val = MXR_CFG_RGB601_0_255;
340 } else if (height == 576) {
341 val = MXR_CFG_RGB601_0_255;
342 } else if (height == 720) {
343 val = MXR_CFG_RGB709_16_235;
344 mixer_reg_write(res, MXR_CM_COEFF_Y,
345 (1 << 30) | (94 << 20) | (314 << 10) |
346 (32 << 0));
347 mixer_reg_write(res, MXR_CM_COEFF_CB,
348 (972 << 20) | (851 << 10) | (225 << 0));
349 mixer_reg_write(res, MXR_CM_COEFF_CR,
350 (225 << 20) | (820 << 10) | (1004 << 0));
351 } else if (height == 1080) {
352 val = MXR_CFG_RGB709_16_235;
353 mixer_reg_write(res, MXR_CM_COEFF_Y,
354 (1 << 30) | (94 << 20) | (314 << 10) |
355 (32 << 0));
356 mixer_reg_write(res, MXR_CM_COEFF_CB,
357 (972 << 20) | (851 << 10) | (225 << 0));
358 mixer_reg_write(res, MXR_CM_COEFF_CR,
359 (225 << 20) | (820 << 10) | (1004 << 0));
360 } else {
361 val = MXR_CFG_RGB709_16_235;
362 mixer_reg_write(res, MXR_CM_COEFF_Y,
363 (1 << 30) | (94 << 20) | (314 << 10) |
364 (32 << 0));
365 mixer_reg_write(res, MXR_CM_COEFF_CB,
366 (972 << 20) | (851 << 10) | (225 << 0));
367 mixer_reg_write(res, MXR_CM_COEFF_CR,
368 (225 << 20) | (820 << 10) | (1004 << 0));
369 }
370
371 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
372}
373
Tobias Jakobi5b1d5bc2015-05-06 14:10:22 +0200374static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
375 bool enable)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900376{
377 struct mixer_resources *res = &ctx->mixer_res;
378 u32 val = enable ? ~0 : 0;
379
380 switch (win) {
381 case 0:
382 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
383 break;
384 case 1:
385 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
386 break;
387 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530388 if (ctx->vp_enabled) {
389 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
390 mixer_reg_writemask(res, MXR_CFG, val,
391 MXR_CFG_VP_ENABLE);
Joonyoung Shimf1e716d2014-07-25 19:59:10 +0900392
393 /* control blending of graphic layer 0 */
394 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
395 MXR_GRP_CFG_BLEND_PRE_MUL |
396 MXR_GRP_CFG_PIXEL_BLEND_EN);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530397 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900398 break;
399 }
400}
401
402static void mixer_run(struct mixer_context *ctx)
403{
404 struct mixer_resources *res = &ctx->mixer_res;
405
406 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900407}
408
Rahul Sharma381be022014-06-23 11:02:22 +0530409static void mixer_stop(struct mixer_context *ctx)
410{
411 struct mixer_resources *res = &ctx->mixer_res;
412 int timeout = 20;
413
414 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
415
416 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
417 --timeout)
418 usleep_range(10000, 12000);
Rahul Sharma381be022014-06-23 11:02:22 +0530419}
420
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900421static void vp_video_buffer(struct mixer_context *ctx,
422 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900423{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100424 struct exynos_drm_plane_state *state =
425 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100426 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900427 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100428 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900429 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900430 dma_addr_t luma_addr[2], chroma_addr[2];
431 bool tiled_mode = false;
432 bool crcb_mode = false;
433 u32 val;
434
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900435 switch (fb->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900436 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900437 crcb_mode = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900438 break;
Tobias Jakobi8f2590f2015-04-27 23:10:16 +0200439 case DRM_FORMAT_NV21:
440 crcb_mode = true;
441 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900442 default:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900443 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900444 fb->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900445 return;
446 }
447
Marek Szyprowski0488f502015-11-30 14:53:21 +0100448 luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
449 chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900450
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900451 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900452 ctx->interlace = true;
453 if (tiled_mode) {
454 luma_addr[1] = luma_addr[0] + 0x40;
455 chroma_addr[1] = chroma_addr[0] + 0x40;
456 } else {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900457 luma_addr[1] = luma_addr[0] + fb->pitches[0];
458 chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900459 }
460 } else {
461 ctx->interlace = false;
462 luma_addr[1] = 0;
463 chroma_addr[1] = 0;
464 }
465
466 spin_lock_irqsave(&res->reg_slock, flags);
467 mixer_vsync_set_update(ctx, false);
468
469 /* interlace or progressive scan mode */
470 val = (ctx->interlace ? ~0 : 0);
471 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
472
473 /* setup format */
474 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
475 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
476 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
477
478 /* setting size of input image */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900479 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
480 VP_IMG_VSIZE(fb->height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900481 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900482 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
483 VP_IMG_VSIZE(fb->height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900484
Marek Szyprowski0114f402015-11-30 14:53:22 +0100485 vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
486 vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900487 vp_reg_write(res, VP_SRC_H_POSITION,
Marek Szyprowski0114f402015-11-30 14:53:22 +0100488 VP_SRC_H_POSITION_VAL(state->src.x));
489 vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900490
Marek Szyprowski0114f402015-11-30 14:53:22 +0100491 vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
492 vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900493 if (ctx->interlace) {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100494 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
495 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900496 } else {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100497 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
498 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900499 }
500
Marek Szyprowski0114f402015-11-30 14:53:22 +0100501 vp_reg_write(res, VP_H_RATIO, state->h_ratio);
502 vp_reg_write(res, VP_V_RATIO, state->v_ratio);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900503
504 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
505
506 /* set buffer address to vp */
507 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
508 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
509 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
510 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
511
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900512 mixer_cfg_scan(ctx, mode->vdisplay);
513 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
514 mixer_cfg_layer(ctx, plane->zpos, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900515 mixer_run(ctx);
516
517 mixer_vsync_set_update(ctx, true);
518 spin_unlock_irqrestore(&res->reg_slock, flags);
519
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200520 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900521 vp_regs_dump(ctx);
522}
523
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530524static void mixer_layer_update(struct mixer_context *ctx)
525{
526 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530527
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530528 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530529}
530
Tobias Jakobi26110152015-04-07 01:14:52 +0200531static int mixer_setup_scale(const struct exynos_drm_plane *plane,
532 unsigned int *x_ratio, unsigned int *y_ratio)
533{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100534 struct exynos_drm_plane_state *state =
535 to_exynos_plane_state(plane->base.state);
536
537 if (state->crtc.w != state->src.w) {
538 if (state->crtc.w == 2 * state->src.w)
Tobias Jakobi26110152015-04-07 01:14:52 +0200539 *x_ratio = 1;
540 else
541 goto fail;
542 }
543
Marek Szyprowski0114f402015-11-30 14:53:22 +0100544 if (state->crtc.h != state->src.h) {
545 if (state->crtc.h == 2 * state->src.h)
Tobias Jakobi26110152015-04-07 01:14:52 +0200546 *y_ratio = 1;
547 else
548 goto fail;
549 }
550
551 return 0;
552
553fail:
554 DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n");
555 return -ENOTSUPP;
556}
557
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900558static void mixer_graph_buffer(struct mixer_context *ctx,
559 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900560{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100561 struct exynos_drm_plane_state *state =
562 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100563 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900564 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100565 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900566 unsigned long flags;
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900567 unsigned int win = plane->zpos;
Tobias Jakobi26110152015-04-07 01:14:52 +0200568 unsigned int x_ratio = 0, y_ratio = 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900569 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900570 dma_addr_t dma_addr;
571 unsigned int fmt;
572 u32 val;
573
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900574 switch (fb->pixel_format) {
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200575 case DRM_FORMAT_XRGB4444:
576 fmt = MXR_FORMAT_ARGB4444;
577 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900578
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200579 case DRM_FORMAT_XRGB1555:
580 fmt = MXR_FORMAT_ARGB1555;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900581 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200582
583 case DRM_FORMAT_RGB565:
584 fmt = MXR_FORMAT_RGB565;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900585 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200586
587 case DRM_FORMAT_XRGB8888:
588 case DRM_FORMAT_ARGB8888:
589 fmt = MXR_FORMAT_ARGB8888;
590 break;
591
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900592 default:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200593 DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
594 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900595 }
596
Tobias Jakobi26110152015-04-07 01:14:52 +0200597 /* check if mixer supports requested scaling setup */
598 if (mixer_setup_scale(plane, &x_ratio, &y_ratio))
599 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900600
Marek Szyprowski0114f402015-11-30 14:53:22 +0100601 dst_x_offset = state->crtc.x;
602 dst_y_offset = state->crtc.y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900603
604 /* converting dma address base and source offset */
Marek Szyprowski0488f502015-11-30 14:53:21 +0100605 dma_addr = exynos_drm_fb_dma_addr(fb, 0)
Marek Szyprowski0114f402015-11-30 14:53:22 +0100606 + (state->src.x * fb->bits_per_pixel >> 3)
607 + (state->src.y * fb->pitches[0]);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900608 src_x_offset = 0;
609 src_y_offset = 0;
610
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900611 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900612 ctx->interlace = true;
613 else
614 ctx->interlace = false;
615
616 spin_lock_irqsave(&res->reg_slock, flags);
617 mixer_vsync_set_update(ctx, false);
618
619 /* setup format */
620 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
621 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
622
623 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000624 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900625 fb->pitches[0] / (fb->bits_per_pixel >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900626
Rahul Sharmadef5e092013-06-19 18:21:08 +0530627 /* setup display size */
628 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
Gustavo Padovan5d3d0992015-10-12 22:07:48 +0900629 win == DEFAULT_WIN) {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900630 val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
631 val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530632 mixer_reg_write(res, MXR_RESOLUTION, val);
633 }
634
Marek Szyprowski0114f402015-11-30 14:53:22 +0100635 val = MXR_GRP_WH_WIDTH(state->src.w);
636 val |= MXR_GRP_WH_HEIGHT(state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900637 val |= MXR_GRP_WH_H_SCALE(x_ratio);
638 val |= MXR_GRP_WH_V_SCALE(y_ratio);
639 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
640
641 /* setup offsets in source image */
642 val = MXR_GRP_SXY_SX(src_x_offset);
643 val |= MXR_GRP_SXY_SY(src_y_offset);
644 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
645
646 /* setup offsets in display image */
647 val = MXR_GRP_DXY_DX(dst_x_offset);
648 val |= MXR_GRP_DXY_DY(dst_y_offset);
649 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
650
651 /* set buffer address to mixer */
652 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
653
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900654 mixer_cfg_scan(ctx, mode->vdisplay);
655 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900656 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530657
658 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530659 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
660 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530661 mixer_layer_update(ctx);
662
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900663 mixer_run(ctx);
664
665 mixer_vsync_set_update(ctx, true);
666 spin_unlock_irqrestore(&res->reg_slock, flags);
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200667
668 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900669}
670
671static void vp_win_reset(struct mixer_context *ctx)
672{
673 struct mixer_resources *res = &ctx->mixer_res;
674 int tries = 100;
675
676 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
677 for (tries = 100; tries; --tries) {
678 /* waiting until VP_SRESET_PROCESSING is 0 */
679 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
680 break;
Tomasz Stanislawski02b3de42015-09-25 14:48:29 +0200681 mdelay(10);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900682 }
683 WARN(tries == 0, "failed to reset Video Processor\n");
684}
685
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900686static void mixer_win_reset(struct mixer_context *ctx)
687{
688 struct mixer_resources *res = &ctx->mixer_res;
689 unsigned long flags;
690 u32 val; /* value stored to register */
691
692 spin_lock_irqsave(&res->reg_slock, flags);
693 mixer_vsync_set_update(ctx, false);
694
695 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
696
697 /* set output in RGB888 mode */
698 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
699
700 /* 16 beat burst in DMA */
701 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
702 MXR_STATUS_BURST_MASK);
703
704 /* setting default layer priority: layer1 > layer0 > video
705 * because typical usage scenario would be
706 * layer1 - OSD
707 * layer0 - framebuffer
708 * video - video overlay
709 */
710 val = MXR_LAYER_CFG_GRP1_VAL(3);
711 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530712 if (ctx->vp_enabled)
713 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900714 mixer_reg_write(res, MXR_LAYER_CFG, val);
715
716 /* setting background color */
717 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
718 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
719 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
720
721 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900722 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
723 val |= MXR_GRP_CFG_WIN_BLEND_EN;
724 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
725
Sean Paul0377f4e2013-04-25 15:13:26 -0400726 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900727 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400728
729 /* Blend layer 1 into layer 0 */
730 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
731 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900732 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
733
Seung-Woo Kim57366032012-05-15 17:22:08 +0900734 /* setting video layers */
735 val = MXR_GRP_CFG_ALPHA_VAL(0);
736 mixer_reg_write(res, MXR_VIDEO_CFG, val);
737
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530738 if (ctx->vp_enabled) {
739 /* configuration of Video Processor Registers */
740 vp_win_reset(ctx);
741 vp_default_filter(res);
742 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900743
744 /* disable all layers */
745 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
746 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530747 if (ctx->vp_enabled)
748 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900749
750 mixer_vsync_set_update(ctx, true);
751 spin_unlock_irqrestore(&res->reg_slock, flags);
752}
753
Sean Paul45517892014-01-30 16:19:05 -0500754static irqreturn_t mixer_irq_handler(int irq, void *arg)
755{
756 struct mixer_context *ctx = arg;
757 struct mixer_resources *res = &ctx->mixer_res;
758 u32 val, base, shadow;
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300759 int win;
Sean Paul45517892014-01-30 16:19:05 -0500760
761 spin_lock(&res->reg_slock);
762
763 /* read interrupt status for handling and clearing flags for VSYNC */
764 val = mixer_reg_read(res, MXR_INT_STATUS);
765
766 /* handling VSYNC */
767 if (val & MXR_INT_STATUS_VSYNC) {
Andrzej Hajda81a464d2015-07-09 10:07:53 +0200768 /* vsync interrupt use different bit for read and clear */
769 val |= MXR_INT_CLEAR_VSYNC;
770 val &= ~MXR_INT_STATUS_VSYNC;
771
Sean Paul45517892014-01-30 16:19:05 -0500772 /* interlace scan need to check shadow register */
773 if (ctx->interlace) {
774 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
775 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
776 if (base != shadow)
777 goto out;
778
779 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
780 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
781 if (base != shadow)
782 goto out;
783 }
784
Gustavo Padovaneafd5402015-07-16 12:23:32 -0300785 drm_crtc_handle_vblank(&ctx->crtc->base);
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300786 for (win = 0 ; win < MIXER_WIN_NR ; win++) {
787 struct exynos_drm_plane *plane = &ctx->planes[win];
788
789 if (!plane->pending_fb)
790 continue;
791
792 exynos_drm_crtc_finish_update(ctx->crtc, plane);
793 }
Sean Paul45517892014-01-30 16:19:05 -0500794
795 /* set wait vsync event to zero and wake up queue. */
796 if (atomic_read(&ctx->wait_vsync_event)) {
797 atomic_set(&ctx->wait_vsync_event, 0);
798 wake_up(&ctx->wait_vsync_queue);
799 }
800 }
801
802out:
803 /* clear interrupts */
Sean Paul45517892014-01-30 16:19:05 -0500804 mixer_reg_write(res, MXR_INT_STATUS, val);
805
806 spin_unlock(&res->reg_slock);
807
808 return IRQ_HANDLED;
809}
810
811static int mixer_resources_init(struct mixer_context *mixer_ctx)
812{
813 struct device *dev = &mixer_ctx->pdev->dev;
814 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
815 struct resource *res;
816 int ret;
817
818 spin_lock_init(&mixer_res->reg_slock);
819
820 mixer_res->mixer = devm_clk_get(dev, "mixer");
821 if (IS_ERR(mixer_res->mixer)) {
822 dev_err(dev, "failed to get clock 'mixer'\n");
823 return -ENODEV;
824 }
825
Marek Szyprowski04427ec2015-02-02 14:20:28 +0100826 mixer_res->hdmi = devm_clk_get(dev, "hdmi");
827 if (IS_ERR(mixer_res->hdmi)) {
828 dev_err(dev, "failed to get clock 'hdmi'\n");
829 return PTR_ERR(mixer_res->hdmi);
830 }
831
Sean Paul45517892014-01-30 16:19:05 -0500832 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
833 if (IS_ERR(mixer_res->sclk_hdmi)) {
834 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
835 return -ENODEV;
836 }
837 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
838 if (res == NULL) {
839 dev_err(dev, "get memory resource failed.\n");
840 return -ENXIO;
841 }
842
843 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
844 resource_size(res));
845 if (mixer_res->mixer_regs == NULL) {
846 dev_err(dev, "register mapping failed.\n");
847 return -ENXIO;
848 }
849
850 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
851 if (res == NULL) {
852 dev_err(dev, "get interrupt resource failed.\n");
853 return -ENXIO;
854 }
855
856 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
857 0, "drm_mixer", mixer_ctx);
858 if (ret) {
859 dev_err(dev, "request interrupt failed.\n");
860 return ret;
861 }
862 mixer_res->irq = res->start;
863
864 return 0;
865}
866
867static int vp_resources_init(struct mixer_context *mixer_ctx)
868{
869 struct device *dev = &mixer_ctx->pdev->dev;
870 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
871 struct resource *res;
872
873 mixer_res->vp = devm_clk_get(dev, "vp");
874 if (IS_ERR(mixer_res->vp)) {
875 dev_err(dev, "failed to get clock 'vp'\n");
876 return -ENODEV;
877 }
Sean Paul45517892014-01-30 16:19:05 -0500878
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200879 if (mixer_ctx->has_sclk) {
880 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
881 if (IS_ERR(mixer_res->sclk_mixer)) {
882 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
883 return -ENODEV;
884 }
885 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
886 if (IS_ERR(mixer_res->mout_mixer)) {
887 dev_err(dev, "failed to get clock 'mout_mixer'\n");
888 return -ENODEV;
889 }
890
891 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
892 clk_set_parent(mixer_res->mout_mixer,
893 mixer_res->sclk_hdmi);
894 }
Sean Paul45517892014-01-30 16:19:05 -0500895
896 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
897 if (res == NULL) {
898 dev_err(dev, "get memory resource failed.\n");
899 return -ENXIO;
900 }
901
902 mixer_res->vp_regs = devm_ioremap(dev, res->start,
903 resource_size(res));
904 if (mixer_res->vp_regs == NULL) {
905 dev_err(dev, "register mapping failed.\n");
906 return -ENXIO;
907 }
908
909 return 0;
910}
911
Gustavo Padovan93bca242015-01-18 18:16:23 +0900912static int mixer_initialize(struct mixer_context *mixer_ctx,
Inki Daef37cd5e2014-05-09 14:25:20 +0900913 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500914{
915 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +0900916 struct exynos_drm_private *priv;
917 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500918
Gustavo Padovaneb88e422014-11-26 16:43:27 -0200919 mixer_ctx->drm_dev = drm_dev;
Gustavo Padovan8a326ed2014-11-04 18:44:47 -0200920 mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500921
922 /* acquire resources: regs, irqs, clocks */
923 ret = mixer_resources_init(mixer_ctx);
924 if (ret) {
925 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
926 return ret;
927 }
928
929 if (mixer_ctx->vp_enabled) {
930 /* acquire vp resources: regs, irqs, clocks */
931 ret = vp_resources_init(mixer_ctx);
932 if (ret) {
933 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
934 return ret;
935 }
936 }
937
Joonyoung Shimeb7a3fc2015-07-02 21:49:39 +0900938 ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900939 if (ret)
940 priv->pipe--;
Sean Paulf041b252014-01-30 16:19:15 -0500941
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900942 return ret;
Sean Paul45517892014-01-30 16:19:05 -0500943}
944
Gustavo Padovan93bca242015-01-18 18:16:23 +0900945static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
Inki Dae1055b392012-10-19 17:37:35 +0900946{
Joonyoung Shimbf566082015-07-02 21:49:38 +0900947 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900948}
949
Gustavo Padovan93bca242015-01-18 18:16:23 +0900950static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900951{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900952 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900953 struct mixer_resources *res = &mixer_ctx->mixer_res;
954
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200955 __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
956 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Sean Paulf041b252014-01-30 16:19:15 -0500957 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900958
959 /* enable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200960 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
961 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900962
963 return 0;
964}
965
Gustavo Padovan93bca242015-01-18 18:16:23 +0900966static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900967{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900968 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900969 struct mixer_resources *res = &mixer_ctx->mixer_res;
970
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200971 __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
972
973 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Andrzej Hajda947710c2015-07-09 08:25:41 +0200974 return;
Andrzej Hajda947710c2015-07-09 08:25:41 +0200975
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900976 /* disable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200977 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900978 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
979}
980
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900981static void mixer_update_plane(struct exynos_drm_crtc *crtc,
982 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900983{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900984 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900985
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900986 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900987
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200988 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Shirish Sdda90122013-01-23 22:03:18 -0500989 return;
Shirish Sdda90122013-01-23 22:03:18 -0500990
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900991 if (plane->zpos > 1 && mixer_ctx->vp_enabled)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900992 vp_video_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900993 else
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900994 mixer_graph_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900995}
996
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900997static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
998 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900999{
Gustavo Padovan93bca242015-01-18 18:16:23 +09001000 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001001 struct mixer_resources *res = &mixer_ctx->mixer_res;
1002 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001003
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001004 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001005
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001006 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301007 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301008
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001009 spin_lock_irqsave(&res->reg_slock, flags);
1010 mixer_vsync_set_update(mixer_ctx, false);
1011
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001012 mixer_cfg_layer(mixer_ctx, plane->zpos, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001013
1014 mixer_vsync_set_update(mixer_ctx, true);
1015 spin_unlock_irqrestore(&res->reg_slock, flags);
1016}
1017
Gustavo Padovan93bca242015-01-18 18:16:23 +09001018static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -05001019{
Gustavo Padovan93bca242015-01-18 18:16:23 +09001020 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001021 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +05301022
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001023 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush K6e95d5e2012-12-06 20:16:03 +05301024 return;
Prathyush K6e95d5e2012-12-06 20:16:03 +05301025
Gustavo Padovan93bca242015-01-18 18:16:23 +09001026 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001027 if (err < 0) {
1028 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
1029 return;
1030 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301031
Prathyush K6e95d5e2012-12-06 20:16:03 +05301032 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1033
1034 /*
1035 * wait for MIXER to signal VSYNC interrupt or return after
1036 * timeout which is set to 50ms (refresh rate of 20).
1037 */
1038 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1039 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001040 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301041 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301042
Gustavo Padovan93bca242015-01-18 18:16:23 +09001043 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +05301044}
1045
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001046static void mixer_enable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301047{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001048 struct mixer_context *ctx = crtc->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301049 struct mixer_resources *res = &ctx->mixer_res;
1050
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001051 if (test_bit(MXR_BIT_POWERED, &ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301052 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301053
Sean Paulaf65c802014-01-30 16:19:27 -05001054 pm_runtime_get_sync(ctx->dev);
1055
Rahul Sharmad74ed932014-06-23 11:02:24 +05301056 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1057
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001058 if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
Andrzej Hajdafc0732482015-07-09 08:25:40 +02001059 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001060 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
1061 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301062 mixer_win_reset(ctx);
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001063
1064 set_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301065}
1066
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001067static void mixer_disable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301068{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001069 struct mixer_context *ctx = crtc->ctx;
Joonyoung Shimc329f662015-06-12 20:34:28 +09001070 int i;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301071
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001072 if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301073 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301074
Rahul Sharma381be022014-06-23 11:02:22 +05301075 mixer_stop(ctx);
Tobias Jakobic0734fb2015-05-06 14:10:21 +02001076 mixer_regs_dump(ctx);
Joonyoung Shimc329f662015-06-12 20:34:28 +09001077
1078 for (i = 0; i < MIXER_WIN_NR; i++)
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001079 mixer_disable_plane(crtc, &ctx->planes[i]);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301080
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001081 pm_runtime_put(ctx->dev);
1082
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001083 clear_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301084}
1085
Sean Paulf041b252014-01-30 16:19:15 -05001086/* Only valid for Mixer version 16.0.33.0 */
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001087static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
1088 struct drm_crtc_state *state)
Sean Paulf041b252014-01-30 16:19:15 -05001089{
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001090 struct drm_display_mode *mode = &state->adjusted_mode;
Sean Paulf041b252014-01-30 16:19:15 -05001091 u32 w, h;
1092
1093 w = mode->hdisplay;
1094 h = mode->vdisplay;
1095
1096 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1097 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1098 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1099
1100 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1101 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1102 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1103 return 0;
1104
1105 return -EINVAL;
1106}
1107
Krzysztof Kozlowskif3aaf762015-05-07 09:04:45 +09001108static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001109 .enable = mixer_enable,
1110 .disable = mixer_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001111 .enable_vblank = mixer_enable_vblank,
1112 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301113 .wait_for_vblank = mixer_wait_for_vblank,
Gustavo Padovan9cc76102015-08-03 14:38:05 +09001114 .update_plane = mixer_update_plane,
1115 .disable_plane = mixer_disable_plane,
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001116 .atomic_check = mixer_atomic_check,
Sean Paulf041b252014-01-30 16:19:15 -05001117};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001118
Rahul Sharmadef5e092013-06-19 18:21:08 +05301119static struct mixer_drv_data exynos5420_mxr_drv_data = {
1120 .version = MXR_VER_128_0_0_184,
1121 .is_vp_enabled = 0,
1122};
1123
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301124static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301125 .version = MXR_VER_16_0_33_0,
1126 .is_vp_enabled = 0,
1127};
1128
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001129static struct mixer_drv_data exynos4212_mxr_drv_data = {
1130 .version = MXR_VER_0_0_0_16,
1131 .is_vp_enabled = 1,
1132};
1133
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301134static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301135 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301136 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001137 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301138};
1139
Krzysztof Kozlowskid6b16302015-05-02 00:56:36 +09001140static const struct platform_device_id mixer_driver_types[] = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301141 {
1142 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301143 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301144 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301145 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301146 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301147 }, {
1148 /* end node */
1149 }
1150};
1151
1152static struct of_device_id mixer_match_types[] = {
1153 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001154 .compatible = "samsung,exynos4210-mixer",
1155 .data = &exynos4210_mxr_drv_data,
1156 }, {
1157 .compatible = "samsung,exynos4212-mixer",
1158 .data = &exynos4212_mxr_drv_data,
1159 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301160 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301161 .data = &exynos5250_mxr_drv_data,
1162 }, {
1163 .compatible = "samsung,exynos5250-mixer",
1164 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301165 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301166 .compatible = "samsung,exynos5420-mixer",
1167 .data = &exynos5420_mxr_drv_data,
1168 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301169 /* end node */
1170 }
1171};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001172MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301173
Inki Daef37cd5e2014-05-09 14:25:20 +09001174static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001175{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001176 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001177 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001178 struct exynos_drm_plane *exynos_plane;
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001179 unsigned int i;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001180 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001181
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001182 ret = mixer_initialize(ctx, drm_dev);
1183 if (ret)
1184 return ret;
1185
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001186 for (i = 0; i < MIXER_WIN_NR; i++) {
1187 if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
Marek Szyprowskiab144202015-11-30 14:53:24 +01001188 continue;
1189
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001190 ret = exynos_plane_init(drm_dev, &ctx->planes[i],
1191 1 << ctx->pipe, &plane_configs[i]);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001192 if (ret)
1193 return ret;
1194 }
1195
Gustavo Padovan5d3d0992015-10-12 22:07:48 +09001196 exynos_plane = &ctx->planes[DEFAULT_WIN];
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001197 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1198 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1199 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001200 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001201 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001202 ret = PTR_ERR(ctx->crtc);
1203 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001204 }
1205
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001206 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001207
1208free_ctx:
1209 devm_kfree(dev, ctx);
1210 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001211}
1212
1213static void mixer_unbind(struct device *dev, struct device *master, void *data)
1214{
1215 struct mixer_context *ctx = dev_get_drvdata(dev);
1216
Gustavo Padovan93bca242015-01-18 18:16:23 +09001217 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001218}
1219
1220static const struct component_ops mixer_component_ops = {
1221 .bind = mixer_bind,
1222 .unbind = mixer_unbind,
1223};
1224
1225static int mixer_probe(struct platform_device *pdev)
1226{
1227 struct device *dev = &pdev->dev;
1228 struct mixer_drv_data *drv;
1229 struct mixer_context *ctx;
1230 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001231
Sean Paulf041b252014-01-30 16:19:15 -05001232 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1233 if (!ctx) {
1234 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001235 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001236 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001237
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301238 if (dev->of_node) {
1239 const struct of_device_id *match;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001240
Sachin Kamate436b092013-06-05 16:00:23 +09001241 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301242 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301243 } else {
1244 drv = (struct mixer_drv_data *)
1245 platform_get_device_id(pdev)->driver_data;
1246 }
1247
Sean Paul45517892014-01-30 16:19:05 -05001248 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001249 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301250 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001251 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301252 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001253 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301254 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001255
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001256 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001257
Inki Daedf5225b2014-05-29 18:28:02 +09001258 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda86650402015-06-11 23:23:37 +09001259 if (!ret)
1260 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001261
1262 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001263}
1264
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001265static int mixer_remove(struct platform_device *pdev)
1266{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001267 pm_runtime_disable(&pdev->dev);
1268
Inki Daedf5225b2014-05-29 18:28:02 +09001269 component_del(&pdev->dev, &mixer_component_ops);
Inki Daedf5225b2014-05-29 18:28:02 +09001270
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001271 return 0;
1272}
1273
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001274#ifdef CONFIG_PM_SLEEP
1275static int exynos_mixer_suspend(struct device *dev)
1276{
1277 struct mixer_context *ctx = dev_get_drvdata(dev);
1278 struct mixer_resources *res = &ctx->mixer_res;
1279
1280 clk_disable_unprepare(res->hdmi);
1281 clk_disable_unprepare(res->mixer);
1282 if (ctx->vp_enabled) {
1283 clk_disable_unprepare(res->vp);
1284 if (ctx->has_sclk)
1285 clk_disable_unprepare(res->sclk_mixer);
1286 }
1287
1288 return 0;
1289}
1290
1291static int exynos_mixer_resume(struct device *dev)
1292{
1293 struct mixer_context *ctx = dev_get_drvdata(dev);
1294 struct mixer_resources *res = &ctx->mixer_res;
1295 int ret;
1296
1297 ret = clk_prepare_enable(res->mixer);
1298 if (ret < 0) {
1299 DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
1300 return ret;
1301 }
1302 ret = clk_prepare_enable(res->hdmi);
1303 if (ret < 0) {
1304 DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
1305 return ret;
1306 }
1307 if (ctx->vp_enabled) {
1308 ret = clk_prepare_enable(res->vp);
1309 if (ret < 0) {
1310 DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
1311 ret);
1312 return ret;
1313 }
1314 if (ctx->has_sclk) {
1315 ret = clk_prepare_enable(res->sclk_mixer);
1316 if (ret < 0) {
1317 DRM_ERROR("Failed to prepare_enable the " \
1318 "sclk_mixer clk [%d]\n",
1319 ret);
1320 return ret;
1321 }
1322 }
1323 }
1324
1325 return 0;
1326}
1327#endif
1328
1329static const struct dev_pm_ops exynos_mixer_pm_ops = {
1330 SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
1331};
1332
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001333struct platform_driver mixer_driver = {
1334 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301335 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001336 .owner = THIS_MODULE,
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001337 .pm = &exynos_mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301338 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001339 },
1340 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001341 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301342 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001343};