blob: d1a9944a175971939323892654a80d73bb7224b1 [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>
Marek Szyprowski48f61552016-04-01 15:17:46 +020034#include <linux/of_device.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090035#include <linux/component.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090036
37#include <drm/exynos_drm.h>
38
39#include "exynos_drm_drv.h"
Rahul Sharma663d8762013-01-03 05:44:04 -050040#include "exynos_drm_crtc.h"
Marek Szyprowski0488f502015-11-30 14:53:21 +010041#include "exynos_drm_fb.h"
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +090042#include "exynos_drm_plane.h"
Inki Dae1055b392012-10-19 17:37:35 +090043#include "exynos_drm_iommu.h"
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090044
Sean Paulf041b252014-01-30 16:19:15 -050045#define MIXER_WIN_NR 3
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +090046#define VP_DEFAULT_WIN 2
Seung-Woo Kimd8408322011-12-21 17:39:39 +090047
Tobias Jakobi7a57ca72015-04-27 23:11:59 +020048/* The pixelformats that are natively supported by the mixer. */
49#define MXR_FORMAT_RGB565 4
50#define MXR_FORMAT_ARGB1555 5
51#define MXR_FORMAT_ARGB4444 6
52#define MXR_FORMAT_ARGB8888 7
53
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090054struct mixer_resources {
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090055 int irq;
56 void __iomem *mixer_regs;
57 void __iomem *vp_regs;
58 spinlock_t reg_slock;
59 struct clk *mixer;
60 struct clk *vp;
Marek Szyprowski04427ec2015-02-02 14:20:28 +010061 struct clk *hdmi;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090062 struct clk *sclk_mixer;
63 struct clk *sclk_hdmi;
Marek Szyprowskiff830c92014-07-01 10:10:07 +020064 struct clk *mout_mixer;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090065};
66
Rahul Sharma1e123442012-10-04 20:48:51 +053067enum mixer_version_id {
68 MXR_VER_0_0_0_16,
69 MXR_VER_16_0_33_0,
Rahul Sharmadef5e092013-06-19 18:21:08 +053070 MXR_VER_128_0_0_184,
Rahul Sharma1e123442012-10-04 20:48:51 +053071};
72
Andrzej Hajdaa44652e2015-07-09 08:25:42 +020073enum mixer_flag_bits {
74 MXR_BIT_POWERED,
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +020075 MXR_BIT_VSYNC,
Andrzej Hajdaa44652e2015-07-09 08:25:42 +020076};
77
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +090078static const uint32_t mixer_formats[] = {
79 DRM_FORMAT_XRGB4444,
Tobias Jakobi26a7af32015-12-16 13:21:47 +010080 DRM_FORMAT_ARGB4444,
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +090081 DRM_FORMAT_XRGB1555,
Tobias Jakobi26a7af32015-12-16 13:21:47 +010082 DRM_FORMAT_ARGB1555,
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +090083 DRM_FORMAT_RGB565,
84 DRM_FORMAT_XRGB8888,
85 DRM_FORMAT_ARGB8888,
86};
87
88static const uint32_t vp_formats[] = {
89 DRM_FORMAT_NV12,
90 DRM_FORMAT_NV21,
91};
92
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090093struct mixer_context {
Sean Paul45517892014-01-30 16:19:05 -050094 struct platform_device *pdev;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090095 struct device *dev;
Inki Dae1055b392012-10-19 17:37:35 +090096 struct drm_device *drm_dev;
Gustavo Padovan93bca242015-01-18 18:16:23 +090097 struct exynos_drm_crtc *crtc;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +090098 struct exynos_drm_plane planes[MIXER_WIN_NR];
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090099 int pipe;
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200100 unsigned long flags;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900101 bool interlace;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530102 bool vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200103 bool has_sclk;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900104
105 struct mixer_resources mixer_res;
Rahul Sharma1e123442012-10-04 20:48:51 +0530106 enum mixer_version_id mxr_ver;
Prathyush K6e95d5e2012-12-06 20:16:03 +0530107 wait_queue_head_t wait_vsync_queue;
108 atomic_t wait_vsync_event;
Rahul Sharma1e123442012-10-04 20:48:51 +0530109};
110
111struct mixer_drv_data {
112 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530113 bool is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200114 bool has_sclk;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900115};
116
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100117static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
118 {
119 .zpos = 0,
120 .type = DRM_PLANE_TYPE_PRIMARY,
121 .pixel_formats = mixer_formats,
122 .num_pixel_formats = ARRAY_SIZE(mixer_formats),
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100123 .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
124 EXYNOS_DRM_PLANE_CAP_ZPOS,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100125 }, {
126 .zpos = 1,
127 .type = DRM_PLANE_TYPE_CURSOR,
128 .pixel_formats = mixer_formats,
129 .num_pixel_formats = ARRAY_SIZE(mixer_formats),
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100130 .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE |
131 EXYNOS_DRM_PLANE_CAP_ZPOS,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100132 }, {
133 .zpos = 2,
134 .type = DRM_PLANE_TYPE_OVERLAY,
135 .pixel_formats = vp_formats,
136 .num_pixel_formats = ARRAY_SIZE(vp_formats),
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100137 .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE |
138 EXYNOS_DRM_PLANE_CAP_ZPOS,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +0100139 },
140};
141
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900142static const u8 filter_y_horiz_tap8[] = {
143 0, -1, -1, -1, -1, -1, -1, -1,
144 -1, -1, -1, -1, -1, 0, 0, 0,
145 0, 2, 4, 5, 6, 6, 6, 6,
146 6, 5, 5, 4, 3, 2, 1, 1,
147 0, -6, -12, -16, -18, -20, -21, -20,
148 -20, -18, -16, -13, -10, -8, -5, -2,
149 127, 126, 125, 121, 114, 107, 99, 89,
150 79, 68, 57, 46, 35, 25, 16, 8,
151};
152
153static const u8 filter_y_vert_tap4[] = {
154 0, -3, -6, -8, -8, -8, -8, -7,
155 -6, -5, -4, -3, -2, -1, -1, 0,
156 127, 126, 124, 118, 111, 102, 92, 81,
157 70, 59, 48, 37, 27, 19, 11, 5,
158 0, 5, 11, 19, 27, 37, 48, 59,
159 70, 81, 92, 102, 111, 118, 124, 126,
160 0, 0, -1, -1, -2, -3, -4, -5,
161 -6, -7, -8, -8, -8, -8, -6, -3,
162};
163
164static const u8 filter_cr_horiz_tap4[] = {
165 0, -3, -6, -8, -8, -8, -8, -7,
166 -6, -5, -4, -3, -2, -1, -1, 0,
167 127, 126, 124, 118, 111, 102, 92, 81,
168 70, 59, 48, 37, 27, 19, 11, 5,
169};
170
Marek Szyprowskif657a992015-12-16 13:21:46 +0100171static inline bool is_alpha_format(unsigned int pixel_format)
172{
173 switch (pixel_format) {
174 case DRM_FORMAT_ARGB8888:
Tobias Jakobi26a7af32015-12-16 13:21:47 +0100175 case DRM_FORMAT_ARGB1555:
176 case DRM_FORMAT_ARGB4444:
Marek Szyprowskif657a992015-12-16 13:21:46 +0100177 return true;
178 default:
179 return false;
180 }
181}
182
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900183static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
184{
185 return readl(res->vp_regs + reg_id);
186}
187
188static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
189 u32 val)
190{
191 writel(val, res->vp_regs + reg_id);
192}
193
194static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
195 u32 val, u32 mask)
196{
197 u32 old = vp_reg_read(res, reg_id);
198
199 val = (val & mask) | (old & ~mask);
200 writel(val, res->vp_regs + reg_id);
201}
202
203static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
204{
205 return readl(res->mixer_regs + reg_id);
206}
207
208static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
209 u32 val)
210{
211 writel(val, res->mixer_regs + reg_id);
212}
213
214static inline void mixer_reg_writemask(struct mixer_resources *res,
215 u32 reg_id, u32 val, u32 mask)
216{
217 u32 old = mixer_reg_read(res, reg_id);
218
219 val = (val & mask) | (old & ~mask);
220 writel(val, res->mixer_regs + reg_id);
221}
222
223static void mixer_regs_dump(struct mixer_context *ctx)
224{
225#define DUMPREG(reg_id) \
226do { \
227 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
228 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
229} while (0)
230
231 DUMPREG(MXR_STATUS);
232 DUMPREG(MXR_CFG);
233 DUMPREG(MXR_INT_EN);
234 DUMPREG(MXR_INT_STATUS);
235
236 DUMPREG(MXR_LAYER_CFG);
237 DUMPREG(MXR_VIDEO_CFG);
238
239 DUMPREG(MXR_GRAPHIC0_CFG);
240 DUMPREG(MXR_GRAPHIC0_BASE);
241 DUMPREG(MXR_GRAPHIC0_SPAN);
242 DUMPREG(MXR_GRAPHIC0_WH);
243 DUMPREG(MXR_GRAPHIC0_SXY);
244 DUMPREG(MXR_GRAPHIC0_DXY);
245
246 DUMPREG(MXR_GRAPHIC1_CFG);
247 DUMPREG(MXR_GRAPHIC1_BASE);
248 DUMPREG(MXR_GRAPHIC1_SPAN);
249 DUMPREG(MXR_GRAPHIC1_WH);
250 DUMPREG(MXR_GRAPHIC1_SXY);
251 DUMPREG(MXR_GRAPHIC1_DXY);
252#undef DUMPREG
253}
254
255static void vp_regs_dump(struct mixer_context *ctx)
256{
257#define DUMPREG(reg_id) \
258do { \
259 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
260 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
261} while (0)
262
263 DUMPREG(VP_ENABLE);
264 DUMPREG(VP_SRESET);
265 DUMPREG(VP_SHADOW_UPDATE);
266 DUMPREG(VP_FIELD_ID);
267 DUMPREG(VP_MODE);
268 DUMPREG(VP_IMG_SIZE_Y);
269 DUMPREG(VP_IMG_SIZE_C);
270 DUMPREG(VP_PER_RATE_CTRL);
271 DUMPREG(VP_TOP_Y_PTR);
272 DUMPREG(VP_BOT_Y_PTR);
273 DUMPREG(VP_TOP_C_PTR);
274 DUMPREG(VP_BOT_C_PTR);
275 DUMPREG(VP_ENDIAN_MODE);
276 DUMPREG(VP_SRC_H_POSITION);
277 DUMPREG(VP_SRC_V_POSITION);
278 DUMPREG(VP_SRC_WIDTH);
279 DUMPREG(VP_SRC_HEIGHT);
280 DUMPREG(VP_DST_H_POSITION);
281 DUMPREG(VP_DST_V_POSITION);
282 DUMPREG(VP_DST_WIDTH);
283 DUMPREG(VP_DST_HEIGHT);
284 DUMPREG(VP_H_RATIO);
285 DUMPREG(VP_V_RATIO);
286
287#undef DUMPREG
288}
289
290static inline void vp_filter_set(struct mixer_resources *res,
291 int reg_id, const u8 *data, unsigned int size)
292{
293 /* assure 4-byte align */
294 BUG_ON(size & 3);
295 for (; size; size -= 4, reg_id += 4, data += 4) {
296 u32 val = (data[0] << 24) | (data[1] << 16) |
297 (data[2] << 8) | data[3];
298 vp_reg_write(res, reg_id, val);
299 }
300}
301
302static void vp_default_filter(struct mixer_resources *res)
303{
304 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530305 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900306 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530307 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900308 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530309 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900310}
311
Marek Szyprowskif657a992015-12-16 13:21:46 +0100312static void mixer_cfg_gfx_blend(struct mixer_context *ctx, unsigned int win,
313 bool alpha)
314{
315 struct mixer_resources *res = &ctx->mixer_res;
316 u32 val;
317
318 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
319 if (alpha) {
320 /* blending based on pixel alpha */
321 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
322 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
323 }
324 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
325 val, MXR_GRP_CFG_MISC_MASK);
326}
327
328static void mixer_cfg_vp_blend(struct mixer_context *ctx)
329{
330 struct mixer_resources *res = &ctx->mixer_res;
331 u32 val;
332
333 /*
334 * No blending at the moment since the NV12/NV21 pixelformats don't
335 * have an alpha channel. However the mixer supports a global alpha
336 * value for a layer. Once this functionality is exposed, we can
337 * support blending of the video layer through this.
338 */
339 val = 0;
340 mixer_reg_write(res, MXR_VIDEO_CFG, val);
341}
342
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900343static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
344{
345 struct mixer_resources *res = &ctx->mixer_res;
346
347 /* block update on vsync */
348 mixer_reg_writemask(res, MXR_STATUS, enable ?
349 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
350
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530351 if (ctx->vp_enabled)
352 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900353 VP_SHADOW_UPDATE_ENABLE : 0);
354}
355
356static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
357{
358 struct mixer_resources *res = &ctx->mixer_res;
359 u32 val;
360
361 /* choosing between interlace and progressive mode */
362 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
Tobias Jakobi1e6d4592015-04-07 01:14:50 +0200363 MXR_CFG_SCAN_PROGRESSIVE);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900364
Rahul Sharmadef5e092013-06-19 18:21:08 +0530365 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
366 /* choosing between proper HD and SD mode */
367 if (height <= 480)
368 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
369 else if (height <= 576)
370 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
371 else if (height <= 720)
372 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
373 else if (height <= 1080)
374 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
375 else
376 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
377 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900378
379 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
380}
381
382static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
383{
384 struct mixer_resources *res = &ctx->mixer_res;
385 u32 val;
386
387 if (height == 480) {
388 val = MXR_CFG_RGB601_0_255;
389 } else if (height == 576) {
390 val = MXR_CFG_RGB601_0_255;
391 } else if (height == 720) {
392 val = MXR_CFG_RGB709_16_235;
393 mixer_reg_write(res, MXR_CM_COEFF_Y,
394 (1 << 30) | (94 << 20) | (314 << 10) |
395 (32 << 0));
396 mixer_reg_write(res, MXR_CM_COEFF_CB,
397 (972 << 20) | (851 << 10) | (225 << 0));
398 mixer_reg_write(res, MXR_CM_COEFF_CR,
399 (225 << 20) | (820 << 10) | (1004 << 0));
400 } else if (height == 1080) {
401 val = MXR_CFG_RGB709_16_235;
402 mixer_reg_write(res, MXR_CM_COEFF_Y,
403 (1 << 30) | (94 << 20) | (314 << 10) |
404 (32 << 0));
405 mixer_reg_write(res, MXR_CM_COEFF_CB,
406 (972 << 20) | (851 << 10) | (225 << 0));
407 mixer_reg_write(res, MXR_CM_COEFF_CR,
408 (225 << 20) | (820 << 10) | (1004 << 0));
409 } else {
410 val = MXR_CFG_RGB709_16_235;
411 mixer_reg_write(res, MXR_CM_COEFF_Y,
412 (1 << 30) | (94 << 20) | (314 << 10) |
413 (32 << 0));
414 mixer_reg_write(res, MXR_CM_COEFF_CB,
415 (972 << 20) | (851 << 10) | (225 << 0));
416 mixer_reg_write(res, MXR_CM_COEFF_CR,
417 (225 << 20) | (820 << 10) | (1004 << 0));
418 }
419
420 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
421}
422
Tobias Jakobi5b1d5bc2015-05-06 14:10:22 +0200423static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100424 unsigned int priority, bool enable)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900425{
426 struct mixer_resources *res = &ctx->mixer_res;
427 u32 val = enable ? ~0 : 0;
428
429 switch (win) {
430 case 0:
431 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100432 mixer_reg_writemask(res, MXR_LAYER_CFG,
433 MXR_LAYER_CFG_GRP0_VAL(priority),
434 MXR_LAYER_CFG_GRP0_MASK);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900435 break;
436 case 1:
437 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100438 mixer_reg_writemask(res, MXR_LAYER_CFG,
439 MXR_LAYER_CFG_GRP1_VAL(priority),
440 MXR_LAYER_CFG_GRP1_MASK);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900441 break;
Marek Szyprowski5e68fef2015-12-16 13:21:48 +0100442 case VP_DEFAULT_WIN:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530443 if (ctx->vp_enabled) {
444 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
445 mixer_reg_writemask(res, MXR_CFG, val,
446 MXR_CFG_VP_ENABLE);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100447 mixer_reg_writemask(res, MXR_LAYER_CFG,
448 MXR_LAYER_CFG_VP_VAL(priority),
449 MXR_LAYER_CFG_VP_MASK);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530450 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900451 break;
452 }
453}
454
455static void mixer_run(struct mixer_context *ctx)
456{
457 struct mixer_resources *res = &ctx->mixer_res;
458
459 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900460}
461
Rahul Sharma381be022014-06-23 11:02:22 +0530462static void mixer_stop(struct mixer_context *ctx)
463{
464 struct mixer_resources *res = &ctx->mixer_res;
465 int timeout = 20;
466
467 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
468
469 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
470 --timeout)
471 usleep_range(10000, 12000);
Rahul Sharma381be022014-06-23 11:02:22 +0530472}
473
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900474static void vp_video_buffer(struct mixer_context *ctx,
475 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900476{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100477 struct exynos_drm_plane_state *state =
478 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100479 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900480 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100481 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900482 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900483 dma_addr_t luma_addr[2], chroma_addr[2];
484 bool tiled_mode = false;
485 bool crcb_mode = false;
486 u32 val;
487
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900488 switch (fb->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900489 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900490 crcb_mode = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900491 break;
Tobias Jakobi8f2590f2015-04-27 23:10:16 +0200492 case DRM_FORMAT_NV21:
493 crcb_mode = true;
494 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900495 default:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900496 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900497 fb->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900498 return;
499 }
500
Marek Szyprowski0488f502015-11-30 14:53:21 +0100501 luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
502 chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900503
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900504 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900505 ctx->interlace = true;
506 if (tiled_mode) {
507 luma_addr[1] = luma_addr[0] + 0x40;
508 chroma_addr[1] = chroma_addr[0] + 0x40;
509 } else {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900510 luma_addr[1] = luma_addr[0] + fb->pitches[0];
511 chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900512 }
513 } else {
514 ctx->interlace = false;
515 luma_addr[1] = 0;
516 chroma_addr[1] = 0;
517 }
518
519 spin_lock_irqsave(&res->reg_slock, flags);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900520
521 /* interlace or progressive scan mode */
522 val = (ctx->interlace ? ~0 : 0);
523 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
524
525 /* setup format */
526 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
527 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
528 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
529
530 /* setting size of input image */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900531 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
532 VP_IMG_VSIZE(fb->height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900533 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900534 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
535 VP_IMG_VSIZE(fb->height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900536
Marek Szyprowski0114f402015-11-30 14:53:22 +0100537 vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
538 vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900539 vp_reg_write(res, VP_SRC_H_POSITION,
Marek Szyprowski0114f402015-11-30 14:53:22 +0100540 VP_SRC_H_POSITION_VAL(state->src.x));
541 vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900542
Marek Szyprowski0114f402015-11-30 14:53:22 +0100543 vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
544 vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900545 if (ctx->interlace) {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100546 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
547 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900548 } else {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100549 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
550 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900551 }
552
Marek Szyprowski0114f402015-11-30 14:53:22 +0100553 vp_reg_write(res, VP_H_RATIO, state->h_ratio);
554 vp_reg_write(res, VP_V_RATIO, state->v_ratio);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900555
556 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
557
558 /* set buffer address to vp */
559 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
560 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
561 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
562 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
563
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900564 mixer_cfg_scan(ctx, mode->vdisplay);
565 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100566 mixer_cfg_layer(ctx, plane->index, state->zpos + 1, true);
Marek Szyprowskif657a992015-12-16 13:21:46 +0100567 mixer_cfg_vp_blend(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900568 mixer_run(ctx);
569
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900570 spin_unlock_irqrestore(&res->reg_slock, flags);
571
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200572 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900573 vp_regs_dump(ctx);
574}
575
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530576static void mixer_layer_update(struct mixer_context *ctx)
577{
578 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530579
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530580 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530581}
582
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900583static void mixer_graph_buffer(struct mixer_context *ctx,
584 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900585{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100586 struct exynos_drm_plane_state *state =
587 to_exynos_plane_state(plane->base.state);
Marek Szyprowski2ee35d82015-11-30 14:53:23 +0100588 struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900589 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100590 struct drm_framebuffer *fb = state->base.fb;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900591 unsigned long flags;
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100592 unsigned int win = plane->index;
Tobias Jakobi26110152015-04-07 01:14:52 +0200593 unsigned int x_ratio = 0, y_ratio = 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900594 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900595 dma_addr_t dma_addr;
596 unsigned int fmt;
597 u32 val;
598
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900599 switch (fb->pixel_format) {
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200600 case DRM_FORMAT_XRGB4444:
Tobias Jakobi26a7af32015-12-16 13:21:47 +0100601 case DRM_FORMAT_ARGB4444:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200602 fmt = MXR_FORMAT_ARGB4444;
603 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900604
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200605 case DRM_FORMAT_XRGB1555:
Tobias Jakobi26a7af32015-12-16 13:21:47 +0100606 case DRM_FORMAT_ARGB1555:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200607 fmt = MXR_FORMAT_ARGB1555;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900608 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200609
610 case DRM_FORMAT_RGB565:
611 fmt = MXR_FORMAT_RGB565;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900612 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200613
614 case DRM_FORMAT_XRGB8888:
615 case DRM_FORMAT_ARGB8888:
616 fmt = MXR_FORMAT_ARGB8888;
617 break;
618
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900619 default:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200620 DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
621 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900622 }
623
Marek Szyprowskie463b062015-11-30 14:53:27 +0100624 /* ratio is already checked by common plane code */
625 x_ratio = state->h_ratio == (1 << 15);
626 y_ratio = state->v_ratio == (1 << 15);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900627
Marek Szyprowski0114f402015-11-30 14:53:22 +0100628 dst_x_offset = state->crtc.x;
629 dst_y_offset = state->crtc.y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900630
631 /* converting dma address base and source offset */
Marek Szyprowski0488f502015-11-30 14:53:21 +0100632 dma_addr = exynos_drm_fb_dma_addr(fb, 0)
Marek Szyprowski0114f402015-11-30 14:53:22 +0100633 + (state->src.x * fb->bits_per_pixel >> 3)
634 + (state->src.y * fb->pitches[0]);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900635 src_x_offset = 0;
636 src_y_offset = 0;
637
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900638 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900639 ctx->interlace = true;
640 else
641 ctx->interlace = false;
642
643 spin_lock_irqsave(&res->reg_slock, flags);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900644
645 /* setup format */
646 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
647 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
648
649 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000650 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900651 fb->pitches[0] / (fb->bits_per_pixel >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900652
Rahul Sharmadef5e092013-06-19 18:21:08 +0530653 /* setup display size */
654 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
Gustavo Padovan5d3d0992015-10-12 22:07:48 +0900655 win == DEFAULT_WIN) {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900656 val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
657 val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530658 mixer_reg_write(res, MXR_RESOLUTION, val);
659 }
660
Marek Szyprowski0114f402015-11-30 14:53:22 +0100661 val = MXR_GRP_WH_WIDTH(state->src.w);
662 val |= MXR_GRP_WH_HEIGHT(state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900663 val |= MXR_GRP_WH_H_SCALE(x_ratio);
664 val |= MXR_GRP_WH_V_SCALE(y_ratio);
665 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
666
667 /* setup offsets in source image */
668 val = MXR_GRP_SXY_SX(src_x_offset);
669 val |= MXR_GRP_SXY_SY(src_y_offset);
670 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
671
672 /* setup offsets in display image */
673 val = MXR_GRP_DXY_DX(dst_x_offset);
674 val |= MXR_GRP_DXY_DY(dst_y_offset);
675 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
676
677 /* set buffer address to mixer */
678 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
679
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900680 mixer_cfg_scan(ctx, mode->vdisplay);
681 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100682 mixer_cfg_layer(ctx, win, state->zpos + 1, true);
Marek Szyprowskif657a992015-12-16 13:21:46 +0100683 mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->pixel_format));
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530684
685 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530686 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
687 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530688 mixer_layer_update(ctx);
689
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900690 mixer_run(ctx);
691
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900692 spin_unlock_irqrestore(&res->reg_slock, flags);
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200693
694 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900695}
696
697static void vp_win_reset(struct mixer_context *ctx)
698{
699 struct mixer_resources *res = &ctx->mixer_res;
700 int tries = 100;
701
702 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
703 for (tries = 100; tries; --tries) {
704 /* waiting until VP_SRESET_PROCESSING is 0 */
705 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
706 break;
Tomasz Stanislawski02b3de42015-09-25 14:48:29 +0200707 mdelay(10);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900708 }
709 WARN(tries == 0, "failed to reset Video Processor\n");
710}
711
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900712static void mixer_win_reset(struct mixer_context *ctx)
713{
714 struct mixer_resources *res = &ctx->mixer_res;
715 unsigned long flags;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900716
717 spin_lock_irqsave(&res->reg_slock, flags);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900718
719 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
720
721 /* set output in RGB888 mode */
722 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
723
724 /* 16 beat burst in DMA */
725 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
726 MXR_STATUS_BURST_MASK);
727
Marek Szyprowskia2cb9112015-12-16 13:21:44 +0100728 /* reset default layer priority */
729 mixer_reg_write(res, MXR_LAYER_CFG, 0);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900730
731 /* setting background color */
732 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
733 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
734 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
735
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530736 if (ctx->vp_enabled) {
737 /* configuration of Video Processor Registers */
738 vp_win_reset(ctx);
739 vp_default_filter(res);
740 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900741
742 /* disable all layers */
743 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
744 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530745 if (ctx->vp_enabled)
746 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900747
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900748 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
Marek Szyprowski3dbaab12016-01-05 13:52:52 +0100978static void mixer_atomic_begin(struct exynos_drm_crtc *crtc)
979{
980 struct mixer_context *mixer_ctx = crtc->ctx;
981
982 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
983 return;
984
985 mixer_vsync_set_update(mixer_ctx, false);
986}
987
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900988static void mixer_update_plane(struct exynos_drm_crtc *crtc,
989 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900990{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900991 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900992
Marek Szyprowski40bdfb02015-12-16 13:21:42 +0100993 DRM_DEBUG_KMS("win: %d\n", plane->index);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900994
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200995 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Shirish Sdda90122013-01-23 22:03:18 -0500996 return;
Shirish Sdda90122013-01-23 22:03:18 -0500997
Marek Szyprowski5e68fef2015-12-16 13:21:48 +0100998 if (plane->index == VP_DEFAULT_WIN)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900999 vp_video_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001000 else
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +09001001 mixer_graph_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001002}
1003
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001004static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
1005 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001006{
Gustavo Padovan93bca242015-01-18 18:16:23 +09001007 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001008 struct mixer_resources *res = &mixer_ctx->mixer_res;
1009 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001010
Marek Szyprowski40bdfb02015-12-16 13:21:42 +01001011 DRM_DEBUG_KMS("win: %d\n", plane->index);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001012
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001013 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301014 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301015
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001016 spin_lock_irqsave(&res->reg_slock, flags);
Marek Szyprowskia2cb9112015-12-16 13:21:44 +01001017 mixer_cfg_layer(mixer_ctx, plane->index, 0, false);
Marek Szyprowski3dbaab12016-01-05 13:52:52 +01001018 spin_unlock_irqrestore(&res->reg_slock, flags);
1019}
1020
1021static void mixer_atomic_flush(struct exynos_drm_crtc *crtc)
1022{
1023 struct mixer_context *mixer_ctx = crtc->ctx;
1024
1025 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
1026 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001027
1028 mixer_vsync_set_update(mixer_ctx, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001029}
1030
Gustavo Padovan93bca242015-01-18 18:16:23 +09001031static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -05001032{
Gustavo Padovan93bca242015-01-18 18:16:23 +09001033 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001034 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +05301035
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001036 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush K6e95d5e2012-12-06 20:16:03 +05301037 return;
Prathyush K6e95d5e2012-12-06 20:16:03 +05301038
Gustavo Padovan93bca242015-01-18 18:16:23 +09001039 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001040 if (err < 0) {
1041 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
1042 return;
1043 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301044
Prathyush K6e95d5e2012-12-06 20:16:03 +05301045 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1046
1047 /*
1048 * wait for MIXER to signal VSYNC interrupt or return after
1049 * timeout which is set to 50ms (refresh rate of 20).
1050 */
1051 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1052 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001053 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301054 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301055
Gustavo Padovan93bca242015-01-18 18:16:23 +09001056 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +05301057}
1058
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001059static void mixer_enable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301060{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001061 struct mixer_context *ctx = crtc->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301062 struct mixer_resources *res = &ctx->mixer_res;
1063
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001064 if (test_bit(MXR_BIT_POWERED, &ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301065 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301066
Sean Paulaf65c802014-01-30 16:19:27 -05001067 pm_runtime_get_sync(ctx->dev);
1068
Andrzej Hajdaa121d172016-03-23 14:26:01 +01001069 exynos_drm_pipe_clk_enable(crtc, true);
1070
Marek Szyprowski3dbaab12016-01-05 13:52:52 +01001071 mixer_vsync_set_update(ctx, false);
1072
Rahul Sharmad74ed932014-06-23 11:02:24 +05301073 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1074
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001075 if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
Andrzej Hajdafc0732482015-07-09 08:25:40 +02001076 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001077 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
1078 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301079 mixer_win_reset(ctx);
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001080
Marek Szyprowski3dbaab12016-01-05 13:52:52 +01001081 mixer_vsync_set_update(ctx, true);
1082
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001083 set_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301084}
1085
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001086static void mixer_disable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301087{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001088 struct mixer_context *ctx = crtc->ctx;
Joonyoung Shimc329f662015-06-12 20:34:28 +09001089 int i;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301090
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001091 if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301092 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301093
Rahul Sharma381be022014-06-23 11:02:22 +05301094 mixer_stop(ctx);
Tobias Jakobic0734fb2015-05-06 14:10:21 +02001095 mixer_regs_dump(ctx);
Joonyoung Shimc329f662015-06-12 20:34:28 +09001096
1097 for (i = 0; i < MIXER_WIN_NR; i++)
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001098 mixer_disable_plane(crtc, &ctx->planes[i]);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301099
Andrzej Hajdaa121d172016-03-23 14:26:01 +01001100 exynos_drm_pipe_clk_enable(crtc, false);
1101
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001102 pm_runtime_put(ctx->dev);
1103
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001104 clear_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301105}
1106
Sean Paulf041b252014-01-30 16:19:15 -05001107/* Only valid for Mixer version 16.0.33.0 */
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001108static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
1109 struct drm_crtc_state *state)
Sean Paulf041b252014-01-30 16:19:15 -05001110{
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001111 struct drm_display_mode *mode = &state->adjusted_mode;
Sean Paulf041b252014-01-30 16:19:15 -05001112 u32 w, h;
1113
1114 w = mode->hdisplay;
1115 h = mode->vdisplay;
1116
1117 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1118 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1119 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1120
1121 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1122 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1123 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1124 return 0;
1125
1126 return -EINVAL;
1127}
1128
Krzysztof Kozlowskif3aaf762015-05-07 09:04:45 +09001129static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001130 .enable = mixer_enable,
1131 .disable = mixer_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001132 .enable_vblank = mixer_enable_vblank,
1133 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301134 .wait_for_vblank = mixer_wait_for_vblank,
Marek Szyprowski3dbaab12016-01-05 13:52:52 +01001135 .atomic_begin = mixer_atomic_begin,
Gustavo Padovan9cc76102015-08-03 14:38:05 +09001136 .update_plane = mixer_update_plane,
1137 .disable_plane = mixer_disable_plane,
Marek Szyprowski3dbaab12016-01-05 13:52:52 +01001138 .atomic_flush = mixer_atomic_flush,
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001139 .atomic_check = mixer_atomic_check,
Sean Paulf041b252014-01-30 16:19:15 -05001140};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001141
Rahul Sharmadef5e092013-06-19 18:21:08 +05301142static struct mixer_drv_data exynos5420_mxr_drv_data = {
1143 .version = MXR_VER_128_0_0_184,
1144 .is_vp_enabled = 0,
1145};
1146
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301147static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301148 .version = MXR_VER_16_0_33_0,
1149 .is_vp_enabled = 0,
1150};
1151
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001152static struct mixer_drv_data exynos4212_mxr_drv_data = {
1153 .version = MXR_VER_0_0_0_16,
1154 .is_vp_enabled = 1,
1155};
1156
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301157static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301158 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301159 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001160 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301161};
1162
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301163static struct of_device_id mixer_match_types[] = {
1164 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001165 .compatible = "samsung,exynos4210-mixer",
1166 .data = &exynos4210_mxr_drv_data,
1167 }, {
1168 .compatible = "samsung,exynos4212-mixer",
1169 .data = &exynos4212_mxr_drv_data,
1170 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301171 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301172 .data = &exynos5250_mxr_drv_data,
1173 }, {
1174 .compatible = "samsung,exynos5250-mixer",
1175 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301176 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301177 .compatible = "samsung,exynos5420-mixer",
1178 .data = &exynos5420_mxr_drv_data,
1179 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301180 /* end node */
1181 }
1182};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001183MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301184
Inki Daef37cd5e2014-05-09 14:25:20 +09001185static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001186{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001187 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001188 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001189 struct exynos_drm_plane *exynos_plane;
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001190 unsigned int i;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001191 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001192
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001193 ret = mixer_initialize(ctx, drm_dev);
1194 if (ret)
1195 return ret;
1196
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001197 for (i = 0; i < MIXER_WIN_NR; i++) {
1198 if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
Marek Szyprowskiab144202015-11-30 14:53:24 +01001199 continue;
1200
Marek Szyprowski40bdfb02015-12-16 13:21:42 +01001201 ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
Marek Szyprowskifd2d2fc2015-11-30 14:53:25 +01001202 1 << ctx->pipe, &plane_configs[i]);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001203 if (ret)
1204 return ret;
1205 }
1206
Gustavo Padovan5d3d0992015-10-12 22:07:48 +09001207 exynos_plane = &ctx->planes[DEFAULT_WIN];
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001208 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1209 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1210 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001211 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001212 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001213 ret = PTR_ERR(ctx->crtc);
1214 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001215 }
1216
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001217 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001218
1219free_ctx:
1220 devm_kfree(dev, ctx);
1221 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001222}
1223
1224static void mixer_unbind(struct device *dev, struct device *master, void *data)
1225{
1226 struct mixer_context *ctx = dev_get_drvdata(dev);
1227
Gustavo Padovan93bca242015-01-18 18:16:23 +09001228 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001229}
1230
1231static const struct component_ops mixer_component_ops = {
1232 .bind = mixer_bind,
1233 .unbind = mixer_unbind,
1234};
1235
1236static int mixer_probe(struct platform_device *pdev)
1237{
1238 struct device *dev = &pdev->dev;
Marek Szyprowski48f61552016-04-01 15:17:46 +02001239 const struct mixer_drv_data *drv;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001240 struct mixer_context *ctx;
1241 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001242
Sean Paulf041b252014-01-30 16:19:15 -05001243 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1244 if (!ctx) {
1245 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001246 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001247 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001248
Marek Szyprowski48f61552016-04-01 15:17:46 +02001249 drv = of_device_get_match_data(dev);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301250
Sean Paul45517892014-01-30 16:19:05 -05001251 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001252 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301253 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001254 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301255 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001256 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301257 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001258
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001259 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001260
Inki Daedf5225b2014-05-29 18:28:02 +09001261 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda86650402015-06-11 23:23:37 +09001262 if (!ret)
1263 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001264
1265 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001266}
1267
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001268static int mixer_remove(struct platform_device *pdev)
1269{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001270 pm_runtime_disable(&pdev->dev);
1271
Inki Daedf5225b2014-05-29 18:28:02 +09001272 component_del(&pdev->dev, &mixer_component_ops);
Inki Daedf5225b2014-05-29 18:28:02 +09001273
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001274 return 0;
1275}
1276
Arnd Bergmanne0fea7e2015-11-17 16:08:36 +01001277static int __maybe_unused exynos_mixer_suspend(struct device *dev)
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001278{
1279 struct mixer_context *ctx = dev_get_drvdata(dev);
1280 struct mixer_resources *res = &ctx->mixer_res;
1281
1282 clk_disable_unprepare(res->hdmi);
1283 clk_disable_unprepare(res->mixer);
1284 if (ctx->vp_enabled) {
1285 clk_disable_unprepare(res->vp);
1286 if (ctx->has_sclk)
1287 clk_disable_unprepare(res->sclk_mixer);
1288 }
1289
1290 return 0;
1291}
1292
Arnd Bergmanne0fea7e2015-11-17 16:08:36 +01001293static int __maybe_unused exynos_mixer_resume(struct device *dev)
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001294{
1295 struct mixer_context *ctx = dev_get_drvdata(dev);
1296 struct mixer_resources *res = &ctx->mixer_res;
1297 int ret;
1298
1299 ret = clk_prepare_enable(res->mixer);
1300 if (ret < 0) {
1301 DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
1302 return ret;
1303 }
1304 ret = clk_prepare_enable(res->hdmi);
1305 if (ret < 0) {
1306 DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
1307 return ret;
1308 }
1309 if (ctx->vp_enabled) {
1310 ret = clk_prepare_enable(res->vp);
1311 if (ret < 0) {
1312 DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
1313 ret);
1314 return ret;
1315 }
1316 if (ctx->has_sclk) {
1317 ret = clk_prepare_enable(res->sclk_mixer);
1318 if (ret < 0) {
1319 DRM_ERROR("Failed to prepare_enable the " \
1320 "sclk_mixer clk [%d]\n",
1321 ret);
1322 return ret;
1323 }
1324 }
1325 }
1326
1327 return 0;
1328}
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001329
1330static const struct dev_pm_ops exynos_mixer_pm_ops = {
1331 SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
1332};
1333
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001334struct platform_driver mixer_driver = {
1335 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301336 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001337 .owner = THIS_MODULE,
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001338 .pm = &exynos_mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301339 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001340 },
1341 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001342 .remove = mixer_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001343};