blob: 154537a23c900c2762702745ec9993afc3703b2b [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
Gustavo Padovan323db0e2015-09-04 19:05:57 -030046#define CURSOR_WIN 1
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,
80 DRM_FORMAT_XRGB1555,
81 DRM_FORMAT_RGB565,
82 DRM_FORMAT_XRGB8888,
83 DRM_FORMAT_ARGB8888,
84};
85
86static const uint32_t vp_formats[] = {
87 DRM_FORMAT_NV12,
88 DRM_FORMAT_NV21,
89};
90
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090091struct mixer_context {
Sean Paul45517892014-01-30 16:19:05 -050092 struct platform_device *pdev;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +090093 struct device *dev;
Inki Dae1055b392012-10-19 17:37:35 +090094 struct drm_device *drm_dev;
Gustavo Padovan93bca242015-01-18 18:16:23 +090095 struct exynos_drm_crtc *crtc;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +090096 struct exynos_drm_plane planes[MIXER_WIN_NR];
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090097 int pipe;
Andrzej Hajdaa44652e2015-07-09 08:25:42 +020098 unsigned long flags;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +090099 bool interlace;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530100 bool vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200101 bool has_sclk;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900102
103 struct mixer_resources mixer_res;
Rahul Sharma1e123442012-10-04 20:48:51 +0530104 enum mixer_version_id mxr_ver;
Prathyush K6e95d5e2012-12-06 20:16:03 +0530105 wait_queue_head_t wait_vsync_queue;
106 atomic_t wait_vsync_event;
Rahul Sharma1e123442012-10-04 20:48:51 +0530107};
108
109struct mixer_drv_data {
110 enum mixer_version_id version;
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530111 bool is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200112 bool has_sclk;
Joonyoung Shim22b21ae2012-03-15 17:19:04 +0900113};
114
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900115static const u8 filter_y_horiz_tap8[] = {
116 0, -1, -1, -1, -1, -1, -1, -1,
117 -1, -1, -1, -1, -1, 0, 0, 0,
118 0, 2, 4, 5, 6, 6, 6, 6,
119 6, 5, 5, 4, 3, 2, 1, 1,
120 0, -6, -12, -16, -18, -20, -21, -20,
121 -20, -18, -16, -13, -10, -8, -5, -2,
122 127, 126, 125, 121, 114, 107, 99, 89,
123 79, 68, 57, 46, 35, 25, 16, 8,
124};
125
126static const u8 filter_y_vert_tap4[] = {
127 0, -3, -6, -8, -8, -8, -8, -7,
128 -6, -5, -4, -3, -2, -1, -1, 0,
129 127, 126, 124, 118, 111, 102, 92, 81,
130 70, 59, 48, 37, 27, 19, 11, 5,
131 0, 5, 11, 19, 27, 37, 48, 59,
132 70, 81, 92, 102, 111, 118, 124, 126,
133 0, 0, -1, -1, -2, -3, -4, -5,
134 -6, -7, -8, -8, -8, -8, -6, -3,
135};
136
137static const u8 filter_cr_horiz_tap4[] = {
138 0, -3, -6, -8, -8, -8, -8, -7,
139 -6, -5, -4, -3, -2, -1, -1, 0,
140 127, 126, 124, 118, 111, 102, 92, 81,
141 70, 59, 48, 37, 27, 19, 11, 5,
142};
143
144static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
145{
146 return readl(res->vp_regs + reg_id);
147}
148
149static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
150 u32 val)
151{
152 writel(val, res->vp_regs + reg_id);
153}
154
155static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
156 u32 val, u32 mask)
157{
158 u32 old = vp_reg_read(res, reg_id);
159
160 val = (val & mask) | (old & ~mask);
161 writel(val, res->vp_regs + reg_id);
162}
163
164static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
165{
166 return readl(res->mixer_regs + reg_id);
167}
168
169static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
170 u32 val)
171{
172 writel(val, res->mixer_regs + reg_id);
173}
174
175static inline void mixer_reg_writemask(struct mixer_resources *res,
176 u32 reg_id, u32 val, u32 mask)
177{
178 u32 old = mixer_reg_read(res, reg_id);
179
180 val = (val & mask) | (old & ~mask);
181 writel(val, res->mixer_regs + reg_id);
182}
183
184static void mixer_regs_dump(struct mixer_context *ctx)
185{
186#define DUMPREG(reg_id) \
187do { \
188 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
189 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
190} while (0)
191
192 DUMPREG(MXR_STATUS);
193 DUMPREG(MXR_CFG);
194 DUMPREG(MXR_INT_EN);
195 DUMPREG(MXR_INT_STATUS);
196
197 DUMPREG(MXR_LAYER_CFG);
198 DUMPREG(MXR_VIDEO_CFG);
199
200 DUMPREG(MXR_GRAPHIC0_CFG);
201 DUMPREG(MXR_GRAPHIC0_BASE);
202 DUMPREG(MXR_GRAPHIC0_SPAN);
203 DUMPREG(MXR_GRAPHIC0_WH);
204 DUMPREG(MXR_GRAPHIC0_SXY);
205 DUMPREG(MXR_GRAPHIC0_DXY);
206
207 DUMPREG(MXR_GRAPHIC1_CFG);
208 DUMPREG(MXR_GRAPHIC1_BASE);
209 DUMPREG(MXR_GRAPHIC1_SPAN);
210 DUMPREG(MXR_GRAPHIC1_WH);
211 DUMPREG(MXR_GRAPHIC1_SXY);
212 DUMPREG(MXR_GRAPHIC1_DXY);
213#undef DUMPREG
214}
215
216static void vp_regs_dump(struct mixer_context *ctx)
217{
218#define DUMPREG(reg_id) \
219do { \
220 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
221 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
222} while (0)
223
224 DUMPREG(VP_ENABLE);
225 DUMPREG(VP_SRESET);
226 DUMPREG(VP_SHADOW_UPDATE);
227 DUMPREG(VP_FIELD_ID);
228 DUMPREG(VP_MODE);
229 DUMPREG(VP_IMG_SIZE_Y);
230 DUMPREG(VP_IMG_SIZE_C);
231 DUMPREG(VP_PER_RATE_CTRL);
232 DUMPREG(VP_TOP_Y_PTR);
233 DUMPREG(VP_BOT_Y_PTR);
234 DUMPREG(VP_TOP_C_PTR);
235 DUMPREG(VP_BOT_C_PTR);
236 DUMPREG(VP_ENDIAN_MODE);
237 DUMPREG(VP_SRC_H_POSITION);
238 DUMPREG(VP_SRC_V_POSITION);
239 DUMPREG(VP_SRC_WIDTH);
240 DUMPREG(VP_SRC_HEIGHT);
241 DUMPREG(VP_DST_H_POSITION);
242 DUMPREG(VP_DST_V_POSITION);
243 DUMPREG(VP_DST_WIDTH);
244 DUMPREG(VP_DST_HEIGHT);
245 DUMPREG(VP_H_RATIO);
246 DUMPREG(VP_V_RATIO);
247
248#undef DUMPREG
249}
250
251static inline void vp_filter_set(struct mixer_resources *res,
252 int reg_id, const u8 *data, unsigned int size)
253{
254 /* assure 4-byte align */
255 BUG_ON(size & 3);
256 for (; size; size -= 4, reg_id += 4, data += 4) {
257 u32 val = (data[0] << 24) | (data[1] << 16) |
258 (data[2] << 8) | data[3];
259 vp_reg_write(res, reg_id, val);
260 }
261}
262
263static void vp_default_filter(struct mixer_resources *res)
264{
265 vp_filter_set(res, VP_POLY8_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530266 filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900267 vp_filter_set(res, VP_POLY4_Y0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530268 filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900269 vp_filter_set(res, VP_POLY4_C0_LL,
Sachin Kamate25e1b62012-08-31 15:50:48 +0530270 filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900271}
272
273static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
274{
275 struct mixer_resources *res = &ctx->mixer_res;
276
277 /* block update on vsync */
278 mixer_reg_writemask(res, MXR_STATUS, enable ?
279 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
280
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530281 if (ctx->vp_enabled)
282 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900283 VP_SHADOW_UPDATE_ENABLE : 0);
284}
285
286static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
287{
288 struct mixer_resources *res = &ctx->mixer_res;
289 u32 val;
290
291 /* choosing between interlace and progressive mode */
292 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
Tobias Jakobi1e6d4592015-04-07 01:14:50 +0200293 MXR_CFG_SCAN_PROGRESSIVE);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900294
Rahul Sharmadef5e092013-06-19 18:21:08 +0530295 if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
296 /* choosing between proper HD and SD mode */
297 if (height <= 480)
298 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
299 else if (height <= 576)
300 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
301 else if (height <= 720)
302 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
303 else if (height <= 1080)
304 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
305 else
306 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
307 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900308
309 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
310}
311
312static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
313{
314 struct mixer_resources *res = &ctx->mixer_res;
315 u32 val;
316
317 if (height == 480) {
318 val = MXR_CFG_RGB601_0_255;
319 } else if (height == 576) {
320 val = MXR_CFG_RGB601_0_255;
321 } else if (height == 720) {
322 val = MXR_CFG_RGB709_16_235;
323 mixer_reg_write(res, MXR_CM_COEFF_Y,
324 (1 << 30) | (94 << 20) | (314 << 10) |
325 (32 << 0));
326 mixer_reg_write(res, MXR_CM_COEFF_CB,
327 (972 << 20) | (851 << 10) | (225 << 0));
328 mixer_reg_write(res, MXR_CM_COEFF_CR,
329 (225 << 20) | (820 << 10) | (1004 << 0));
330 } else if (height == 1080) {
331 val = MXR_CFG_RGB709_16_235;
332 mixer_reg_write(res, MXR_CM_COEFF_Y,
333 (1 << 30) | (94 << 20) | (314 << 10) |
334 (32 << 0));
335 mixer_reg_write(res, MXR_CM_COEFF_CB,
336 (972 << 20) | (851 << 10) | (225 << 0));
337 mixer_reg_write(res, MXR_CM_COEFF_CR,
338 (225 << 20) | (820 << 10) | (1004 << 0));
339 } else {
340 val = MXR_CFG_RGB709_16_235;
341 mixer_reg_write(res, MXR_CM_COEFF_Y,
342 (1 << 30) | (94 << 20) | (314 << 10) |
343 (32 << 0));
344 mixer_reg_write(res, MXR_CM_COEFF_CB,
345 (972 << 20) | (851 << 10) | (225 << 0));
346 mixer_reg_write(res, MXR_CM_COEFF_CR,
347 (225 << 20) | (820 << 10) | (1004 << 0));
348 }
349
350 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
351}
352
Tobias Jakobi5b1d5bc2015-05-06 14:10:22 +0200353static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
354 bool enable)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900355{
356 struct mixer_resources *res = &ctx->mixer_res;
357 u32 val = enable ? ~0 : 0;
358
359 switch (win) {
360 case 0:
361 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
362 break;
363 case 1:
364 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
365 break;
366 case 2:
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530367 if (ctx->vp_enabled) {
368 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
369 mixer_reg_writemask(res, MXR_CFG, val,
370 MXR_CFG_VP_ENABLE);
Joonyoung Shimf1e716d2014-07-25 19:59:10 +0900371
372 /* control blending of graphic layer 0 */
373 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
374 MXR_GRP_CFG_BLEND_PRE_MUL |
375 MXR_GRP_CFG_PIXEL_BLEND_EN);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530376 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900377 break;
378 }
379}
380
381static void mixer_run(struct mixer_context *ctx)
382{
383 struct mixer_resources *res = &ctx->mixer_res;
384
385 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900386}
387
Rahul Sharma381be022014-06-23 11:02:22 +0530388static void mixer_stop(struct mixer_context *ctx)
389{
390 struct mixer_resources *res = &ctx->mixer_res;
391 int timeout = 20;
392
393 mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
394
395 while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
396 --timeout)
397 usleep_range(10000, 12000);
Rahul Sharma381be022014-06-23 11:02:22 +0530398}
399
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900400static void vp_video_buffer(struct mixer_context *ctx,
401 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900402{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100403 struct exynos_drm_plane_state *state =
404 to_exynos_plane_state(plane->base.state);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900405 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100406 struct drm_framebuffer *fb = state->base.fb;
407 struct drm_display_mode *mode = &state->base.crtc->mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900408 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900409 dma_addr_t luma_addr[2], chroma_addr[2];
410 bool tiled_mode = false;
411 bool crcb_mode = false;
412 u32 val;
413
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900414 switch (fb->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900415 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900416 crcb_mode = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900417 break;
Tobias Jakobi8f2590f2015-04-27 23:10:16 +0200418 case DRM_FORMAT_NV21:
419 crcb_mode = true;
420 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900421 default:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900422 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900423 fb->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900424 return;
425 }
426
Marek Szyprowski0488f502015-11-30 14:53:21 +0100427 luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
428 chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900429
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900430 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900431 ctx->interlace = true;
432 if (tiled_mode) {
433 luma_addr[1] = luma_addr[0] + 0x40;
434 chroma_addr[1] = chroma_addr[0] + 0x40;
435 } else {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900436 luma_addr[1] = luma_addr[0] + fb->pitches[0];
437 chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900438 }
439 } else {
440 ctx->interlace = false;
441 luma_addr[1] = 0;
442 chroma_addr[1] = 0;
443 }
444
445 spin_lock_irqsave(&res->reg_slock, flags);
446 mixer_vsync_set_update(ctx, false);
447
448 /* interlace or progressive scan mode */
449 val = (ctx->interlace ? ~0 : 0);
450 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
451
452 /* setup format */
453 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
454 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
455 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
456
457 /* setting size of input image */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900458 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
459 VP_IMG_VSIZE(fb->height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900460 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900461 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
462 VP_IMG_VSIZE(fb->height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900463
Marek Szyprowski0114f402015-11-30 14:53:22 +0100464 vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
465 vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900466 vp_reg_write(res, VP_SRC_H_POSITION,
Marek Szyprowski0114f402015-11-30 14:53:22 +0100467 VP_SRC_H_POSITION_VAL(state->src.x));
468 vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900469
Marek Szyprowski0114f402015-11-30 14:53:22 +0100470 vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
471 vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900472 if (ctx->interlace) {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100473 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
474 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900475 } else {
Marek Szyprowski0114f402015-11-30 14:53:22 +0100476 vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
477 vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900478 }
479
Marek Szyprowski0114f402015-11-30 14:53:22 +0100480 vp_reg_write(res, VP_H_RATIO, state->h_ratio);
481 vp_reg_write(res, VP_V_RATIO, state->v_ratio);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900482
483 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
484
485 /* set buffer address to vp */
486 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
487 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
488 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
489 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
490
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900491 mixer_cfg_scan(ctx, mode->vdisplay);
492 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
493 mixer_cfg_layer(ctx, plane->zpos, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900494 mixer_run(ctx);
495
496 mixer_vsync_set_update(ctx, true);
497 spin_unlock_irqrestore(&res->reg_slock, flags);
498
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200499 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900500 vp_regs_dump(ctx);
501}
502
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530503static void mixer_layer_update(struct mixer_context *ctx)
504{
505 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530506
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530507 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530508}
509
Tobias Jakobi26110152015-04-07 01:14:52 +0200510static int mixer_setup_scale(const struct exynos_drm_plane *plane,
511 unsigned int *x_ratio, unsigned int *y_ratio)
512{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100513 struct exynos_drm_plane_state *state =
514 to_exynos_plane_state(plane->base.state);
515
516 if (state->crtc.w != state->src.w) {
517 if (state->crtc.w == 2 * state->src.w)
Tobias Jakobi26110152015-04-07 01:14:52 +0200518 *x_ratio = 1;
519 else
520 goto fail;
521 }
522
Marek Szyprowski0114f402015-11-30 14:53:22 +0100523 if (state->crtc.h != state->src.h) {
524 if (state->crtc.h == 2 * state->src.h)
Tobias Jakobi26110152015-04-07 01:14:52 +0200525 *y_ratio = 1;
526 else
527 goto fail;
528 }
529
530 return 0;
531
532fail:
533 DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n");
534 return -ENOTSUPP;
535}
536
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900537static void mixer_graph_buffer(struct mixer_context *ctx,
538 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900539{
Marek Szyprowski0114f402015-11-30 14:53:22 +0100540 struct exynos_drm_plane_state *state =
541 to_exynos_plane_state(plane->base.state);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900542 struct mixer_resources *res = &ctx->mixer_res;
Marek Szyprowski0114f402015-11-30 14:53:22 +0100543 struct drm_framebuffer *fb = state->base.fb;
544 struct drm_display_mode *mode = &state->base.crtc->mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900545 unsigned long flags;
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900546 unsigned int win = plane->zpos;
Tobias Jakobi26110152015-04-07 01:14:52 +0200547 unsigned int x_ratio = 0, y_ratio = 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900548 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900549 dma_addr_t dma_addr;
550 unsigned int fmt;
551 u32 val;
552
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900553 switch (fb->pixel_format) {
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200554 case DRM_FORMAT_XRGB4444:
555 fmt = MXR_FORMAT_ARGB4444;
556 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900557
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200558 case DRM_FORMAT_XRGB1555:
559 fmt = MXR_FORMAT_ARGB1555;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900560 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200561
562 case DRM_FORMAT_RGB565:
563 fmt = MXR_FORMAT_RGB565;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900564 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200565
566 case DRM_FORMAT_XRGB8888:
567 case DRM_FORMAT_ARGB8888:
568 fmt = MXR_FORMAT_ARGB8888;
569 break;
570
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900571 default:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200572 DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
573 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900574 }
575
Tobias Jakobi26110152015-04-07 01:14:52 +0200576 /* check if mixer supports requested scaling setup */
577 if (mixer_setup_scale(plane, &x_ratio, &y_ratio))
578 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900579
Marek Szyprowski0114f402015-11-30 14:53:22 +0100580 dst_x_offset = state->crtc.x;
581 dst_y_offset = state->crtc.y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900582
583 /* converting dma address base and source offset */
Marek Szyprowski0488f502015-11-30 14:53:21 +0100584 dma_addr = exynos_drm_fb_dma_addr(fb, 0)
Marek Szyprowski0114f402015-11-30 14:53:22 +0100585 + (state->src.x * fb->bits_per_pixel >> 3)
586 + (state->src.y * fb->pitches[0]);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900587 src_x_offset = 0;
588 src_y_offset = 0;
589
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900590 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900591 ctx->interlace = true;
592 else
593 ctx->interlace = false;
594
595 spin_lock_irqsave(&res->reg_slock, flags);
596 mixer_vsync_set_update(ctx, false);
597
598 /* setup format */
599 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
600 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
601
602 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000603 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900604 fb->pitches[0] / (fb->bits_per_pixel >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900605
Rahul Sharmadef5e092013-06-19 18:21:08 +0530606 /* setup display size */
607 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
Gustavo Padovan5d3d0992015-10-12 22:07:48 +0900608 win == DEFAULT_WIN) {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900609 val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
610 val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530611 mixer_reg_write(res, MXR_RESOLUTION, val);
612 }
613
Marek Szyprowski0114f402015-11-30 14:53:22 +0100614 val = MXR_GRP_WH_WIDTH(state->src.w);
615 val |= MXR_GRP_WH_HEIGHT(state->src.h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900616 val |= MXR_GRP_WH_H_SCALE(x_ratio);
617 val |= MXR_GRP_WH_V_SCALE(y_ratio);
618 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
619
620 /* setup offsets in source image */
621 val = MXR_GRP_SXY_SX(src_x_offset);
622 val |= MXR_GRP_SXY_SY(src_y_offset);
623 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
624
625 /* setup offsets in display image */
626 val = MXR_GRP_DXY_DX(dst_x_offset);
627 val |= MXR_GRP_DXY_DY(dst_y_offset);
628 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
629
630 /* set buffer address to mixer */
631 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
632
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900633 mixer_cfg_scan(ctx, mode->vdisplay);
634 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900635 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530636
637 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530638 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
639 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530640 mixer_layer_update(ctx);
641
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900642 mixer_run(ctx);
643
644 mixer_vsync_set_update(ctx, true);
645 spin_unlock_irqrestore(&res->reg_slock, flags);
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200646
647 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900648}
649
650static void vp_win_reset(struct mixer_context *ctx)
651{
652 struct mixer_resources *res = &ctx->mixer_res;
653 int tries = 100;
654
655 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
656 for (tries = 100; tries; --tries) {
657 /* waiting until VP_SRESET_PROCESSING is 0 */
658 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
659 break;
Tomasz Stanislawski02b3de42015-09-25 14:48:29 +0200660 mdelay(10);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900661 }
662 WARN(tries == 0, "failed to reset Video Processor\n");
663}
664
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900665static void mixer_win_reset(struct mixer_context *ctx)
666{
667 struct mixer_resources *res = &ctx->mixer_res;
668 unsigned long flags;
669 u32 val; /* value stored to register */
670
671 spin_lock_irqsave(&res->reg_slock, flags);
672 mixer_vsync_set_update(ctx, false);
673
674 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
675
676 /* set output in RGB888 mode */
677 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
678
679 /* 16 beat burst in DMA */
680 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
681 MXR_STATUS_BURST_MASK);
682
683 /* setting default layer priority: layer1 > layer0 > video
684 * because typical usage scenario would be
685 * layer1 - OSD
686 * layer0 - framebuffer
687 * video - video overlay
688 */
689 val = MXR_LAYER_CFG_GRP1_VAL(3);
690 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530691 if (ctx->vp_enabled)
692 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900693 mixer_reg_write(res, MXR_LAYER_CFG, val);
694
695 /* setting background color */
696 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
697 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
698 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
699
700 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900701 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
702 val |= MXR_GRP_CFG_WIN_BLEND_EN;
703 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
704
Sean Paul0377f4e2013-04-25 15:13:26 -0400705 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900706 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400707
708 /* Blend layer 1 into layer 0 */
709 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
710 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900711 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
712
Seung-Woo Kim57366032012-05-15 17:22:08 +0900713 /* setting video layers */
714 val = MXR_GRP_CFG_ALPHA_VAL(0);
715 mixer_reg_write(res, MXR_VIDEO_CFG, val);
716
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530717 if (ctx->vp_enabled) {
718 /* configuration of Video Processor Registers */
719 vp_win_reset(ctx);
720 vp_default_filter(res);
721 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900722
723 /* disable all layers */
724 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
725 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530726 if (ctx->vp_enabled)
727 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900728
729 mixer_vsync_set_update(ctx, true);
730 spin_unlock_irqrestore(&res->reg_slock, flags);
731}
732
Sean Paul45517892014-01-30 16:19:05 -0500733static irqreturn_t mixer_irq_handler(int irq, void *arg)
734{
735 struct mixer_context *ctx = arg;
736 struct mixer_resources *res = &ctx->mixer_res;
737 u32 val, base, shadow;
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300738 int win;
Sean Paul45517892014-01-30 16:19:05 -0500739
740 spin_lock(&res->reg_slock);
741
742 /* read interrupt status for handling and clearing flags for VSYNC */
743 val = mixer_reg_read(res, MXR_INT_STATUS);
744
745 /* handling VSYNC */
746 if (val & MXR_INT_STATUS_VSYNC) {
Andrzej Hajda81a464d2015-07-09 10:07:53 +0200747 /* vsync interrupt use different bit for read and clear */
748 val |= MXR_INT_CLEAR_VSYNC;
749 val &= ~MXR_INT_STATUS_VSYNC;
750
Sean Paul45517892014-01-30 16:19:05 -0500751 /* interlace scan need to check shadow register */
752 if (ctx->interlace) {
753 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
754 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
755 if (base != shadow)
756 goto out;
757
758 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
759 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
760 if (base != shadow)
761 goto out;
762 }
763
Gustavo Padovaneafd5402015-07-16 12:23:32 -0300764 drm_crtc_handle_vblank(&ctx->crtc->base);
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300765 for (win = 0 ; win < MIXER_WIN_NR ; win++) {
766 struct exynos_drm_plane *plane = &ctx->planes[win];
767
768 if (!plane->pending_fb)
769 continue;
770
771 exynos_drm_crtc_finish_update(ctx->crtc, plane);
772 }
Sean Paul45517892014-01-30 16:19:05 -0500773
774 /* set wait vsync event to zero and wake up queue. */
775 if (atomic_read(&ctx->wait_vsync_event)) {
776 atomic_set(&ctx->wait_vsync_event, 0);
777 wake_up(&ctx->wait_vsync_queue);
778 }
779 }
780
781out:
782 /* clear interrupts */
Sean Paul45517892014-01-30 16:19:05 -0500783 mixer_reg_write(res, MXR_INT_STATUS, val);
784
785 spin_unlock(&res->reg_slock);
786
787 return IRQ_HANDLED;
788}
789
790static int mixer_resources_init(struct mixer_context *mixer_ctx)
791{
792 struct device *dev = &mixer_ctx->pdev->dev;
793 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
794 struct resource *res;
795 int ret;
796
797 spin_lock_init(&mixer_res->reg_slock);
798
799 mixer_res->mixer = devm_clk_get(dev, "mixer");
800 if (IS_ERR(mixer_res->mixer)) {
801 dev_err(dev, "failed to get clock 'mixer'\n");
802 return -ENODEV;
803 }
804
Marek Szyprowski04427ec2015-02-02 14:20:28 +0100805 mixer_res->hdmi = devm_clk_get(dev, "hdmi");
806 if (IS_ERR(mixer_res->hdmi)) {
807 dev_err(dev, "failed to get clock 'hdmi'\n");
808 return PTR_ERR(mixer_res->hdmi);
809 }
810
Sean Paul45517892014-01-30 16:19:05 -0500811 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
812 if (IS_ERR(mixer_res->sclk_hdmi)) {
813 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
814 return -ENODEV;
815 }
816 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
817 if (res == NULL) {
818 dev_err(dev, "get memory resource failed.\n");
819 return -ENXIO;
820 }
821
822 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
823 resource_size(res));
824 if (mixer_res->mixer_regs == NULL) {
825 dev_err(dev, "register mapping failed.\n");
826 return -ENXIO;
827 }
828
829 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
830 if (res == NULL) {
831 dev_err(dev, "get interrupt resource failed.\n");
832 return -ENXIO;
833 }
834
835 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
836 0, "drm_mixer", mixer_ctx);
837 if (ret) {
838 dev_err(dev, "request interrupt failed.\n");
839 return ret;
840 }
841 mixer_res->irq = res->start;
842
843 return 0;
844}
845
846static int vp_resources_init(struct mixer_context *mixer_ctx)
847{
848 struct device *dev = &mixer_ctx->pdev->dev;
849 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
850 struct resource *res;
851
852 mixer_res->vp = devm_clk_get(dev, "vp");
853 if (IS_ERR(mixer_res->vp)) {
854 dev_err(dev, "failed to get clock 'vp'\n");
855 return -ENODEV;
856 }
Sean Paul45517892014-01-30 16:19:05 -0500857
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200858 if (mixer_ctx->has_sclk) {
859 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
860 if (IS_ERR(mixer_res->sclk_mixer)) {
861 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
862 return -ENODEV;
863 }
864 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
865 if (IS_ERR(mixer_res->mout_mixer)) {
866 dev_err(dev, "failed to get clock 'mout_mixer'\n");
867 return -ENODEV;
868 }
869
870 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
871 clk_set_parent(mixer_res->mout_mixer,
872 mixer_res->sclk_hdmi);
873 }
Sean Paul45517892014-01-30 16:19:05 -0500874
875 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
876 if (res == NULL) {
877 dev_err(dev, "get memory resource failed.\n");
878 return -ENXIO;
879 }
880
881 mixer_res->vp_regs = devm_ioremap(dev, res->start,
882 resource_size(res));
883 if (mixer_res->vp_regs == NULL) {
884 dev_err(dev, "register mapping failed.\n");
885 return -ENXIO;
886 }
887
888 return 0;
889}
890
Gustavo Padovan93bca242015-01-18 18:16:23 +0900891static int mixer_initialize(struct mixer_context *mixer_ctx,
Inki Daef37cd5e2014-05-09 14:25:20 +0900892 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500893{
894 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +0900895 struct exynos_drm_private *priv;
896 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500897
Gustavo Padovaneb88e422014-11-26 16:43:27 -0200898 mixer_ctx->drm_dev = drm_dev;
Gustavo Padovan8a326ed2014-11-04 18:44:47 -0200899 mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500900
901 /* acquire resources: regs, irqs, clocks */
902 ret = mixer_resources_init(mixer_ctx);
903 if (ret) {
904 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
905 return ret;
906 }
907
908 if (mixer_ctx->vp_enabled) {
909 /* acquire vp resources: regs, irqs, clocks */
910 ret = vp_resources_init(mixer_ctx);
911 if (ret) {
912 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
913 return ret;
914 }
915 }
916
Joonyoung Shimeb7a3fc2015-07-02 21:49:39 +0900917 ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900918 if (ret)
919 priv->pipe--;
Sean Paulf041b252014-01-30 16:19:15 -0500920
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900921 return ret;
Sean Paul45517892014-01-30 16:19:05 -0500922}
923
Gustavo Padovan93bca242015-01-18 18:16:23 +0900924static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
Inki Dae1055b392012-10-19 17:37:35 +0900925{
Joonyoung Shimbf566082015-07-02 21:49:38 +0900926 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900927}
928
Gustavo Padovan93bca242015-01-18 18:16:23 +0900929static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900930{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900931 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900932 struct mixer_resources *res = &mixer_ctx->mixer_res;
933
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200934 __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
935 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Sean Paulf041b252014-01-30 16:19:15 -0500936 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900937
938 /* enable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200939 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
940 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900941
942 return 0;
943}
944
Gustavo Padovan93bca242015-01-18 18:16:23 +0900945static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900946{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900947 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900948 struct mixer_resources *res = &mixer_ctx->mixer_res;
949
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200950 __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
951
952 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Andrzej Hajda947710c2015-07-09 08:25:41 +0200953 return;
Andrzej Hajda947710c2015-07-09 08:25:41 +0200954
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900955 /* disable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200956 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900957 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
958}
959
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900960static void mixer_update_plane(struct exynos_drm_crtc *crtc,
961 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900962{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900963 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900964
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900965 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900966
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200967 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Shirish Sdda90122013-01-23 22:03:18 -0500968 return;
Shirish Sdda90122013-01-23 22:03:18 -0500969
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900970 if (plane->zpos > 1 && mixer_ctx->vp_enabled)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900971 vp_video_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900972 else
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900973 mixer_graph_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900974}
975
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900976static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
977 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900978{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900979 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900980 struct mixer_resources *res = &mixer_ctx->mixer_res;
981 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900982
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900983 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900984
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200985 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +0530986 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +0530987
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900988 spin_lock_irqsave(&res->reg_slock, flags);
989 mixer_vsync_set_update(mixer_ctx, false);
990
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900991 mixer_cfg_layer(mixer_ctx, plane->zpos, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900992
993 mixer_vsync_set_update(mixer_ctx, true);
994 spin_unlock_irqrestore(&res->reg_slock, flags);
995}
996
Gustavo Padovan93bca242015-01-18 18:16:23 +0900997static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -0500998{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900999 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001000 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +05301001
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001002 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush K6e95d5e2012-12-06 20:16:03 +05301003 return;
Prathyush K6e95d5e2012-12-06 20:16:03 +05301004
Gustavo Padovan93bca242015-01-18 18:16:23 +09001005 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001006 if (err < 0) {
1007 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
1008 return;
1009 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301010
Prathyush K6e95d5e2012-12-06 20:16:03 +05301011 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1012
1013 /*
1014 * wait for MIXER to signal VSYNC interrupt or return after
1015 * timeout which is set to 50ms (refresh rate of 20).
1016 */
1017 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1018 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001019 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301020 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301021
Gustavo Padovan93bca242015-01-18 18:16:23 +09001022 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +05301023}
1024
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001025static void mixer_enable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301026{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001027 struct mixer_context *ctx = crtc->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301028 struct mixer_resources *res = &ctx->mixer_res;
1029
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001030 if (test_bit(MXR_BIT_POWERED, &ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301031 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301032
Sean Paulaf65c802014-01-30 16:19:27 -05001033 pm_runtime_get_sync(ctx->dev);
1034
Rahul Sharmad74ed932014-06-23 11:02:24 +05301035 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1036
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001037 if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
Andrzej Hajdafc0732482015-07-09 08:25:40 +02001038 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001039 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
1040 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301041 mixer_win_reset(ctx);
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001042
1043 set_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301044}
1045
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001046static void mixer_disable(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;
Joonyoung Shimc329f662015-06-12 20:34:28 +09001049 int i;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301050
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001051 if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301052 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301053
Rahul Sharma381be022014-06-23 11:02:22 +05301054 mixer_stop(ctx);
Tobias Jakobic0734fb2015-05-06 14:10:21 +02001055 mixer_regs_dump(ctx);
Joonyoung Shimc329f662015-06-12 20:34:28 +09001056
1057 for (i = 0; i < MIXER_WIN_NR; i++)
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001058 mixer_disable_plane(crtc, &ctx->planes[i]);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301059
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001060 pm_runtime_put(ctx->dev);
1061
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001062 clear_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301063}
1064
Sean Paulf041b252014-01-30 16:19:15 -05001065/* Only valid for Mixer version 16.0.33.0 */
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001066static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
1067 struct drm_crtc_state *state)
Sean Paulf041b252014-01-30 16:19:15 -05001068{
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001069 struct drm_display_mode *mode = &state->adjusted_mode;
Sean Paulf041b252014-01-30 16:19:15 -05001070 u32 w, h;
1071
1072 w = mode->hdisplay;
1073 h = mode->vdisplay;
1074
1075 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1076 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1077 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1078
1079 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1080 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1081 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1082 return 0;
1083
1084 return -EINVAL;
1085}
1086
Krzysztof Kozlowskif3aaf762015-05-07 09:04:45 +09001087static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001088 .enable = mixer_enable,
1089 .disable = mixer_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001090 .enable_vblank = mixer_enable_vblank,
1091 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301092 .wait_for_vblank = mixer_wait_for_vblank,
Gustavo Padovan9cc76102015-08-03 14:38:05 +09001093 .update_plane = mixer_update_plane,
1094 .disable_plane = mixer_disable_plane,
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001095 .atomic_check = mixer_atomic_check,
Sean Paulf041b252014-01-30 16:19:15 -05001096};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001097
Rahul Sharmadef5e092013-06-19 18:21:08 +05301098static struct mixer_drv_data exynos5420_mxr_drv_data = {
1099 .version = MXR_VER_128_0_0_184,
1100 .is_vp_enabled = 0,
1101};
1102
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301103static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301104 .version = MXR_VER_16_0_33_0,
1105 .is_vp_enabled = 0,
1106};
1107
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001108static struct mixer_drv_data exynos4212_mxr_drv_data = {
1109 .version = MXR_VER_0_0_0_16,
1110 .is_vp_enabled = 1,
1111};
1112
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301113static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301114 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301115 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001116 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301117};
1118
Krzysztof Kozlowskid6b16302015-05-02 00:56:36 +09001119static const struct platform_device_id mixer_driver_types[] = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301120 {
1121 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301122 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301123 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301124 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301125 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301126 }, {
1127 /* end node */
1128 }
1129};
1130
1131static struct of_device_id mixer_match_types[] = {
1132 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001133 .compatible = "samsung,exynos4210-mixer",
1134 .data = &exynos4210_mxr_drv_data,
1135 }, {
1136 .compatible = "samsung,exynos4212-mixer",
1137 .data = &exynos4212_mxr_drv_data,
1138 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301139 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301140 .data = &exynos5250_mxr_drv_data,
1141 }, {
1142 .compatible = "samsung,exynos5250-mixer",
1143 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301144 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301145 .compatible = "samsung,exynos5420-mixer",
1146 .data = &exynos5420_mxr_drv_data,
1147 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301148 /* end node */
1149 }
1150};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001151MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301152
Inki Daef37cd5e2014-05-09 14:25:20 +09001153static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001154{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001155 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001156 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001157 struct exynos_drm_plane *exynos_plane;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001158 unsigned int zpos;
1159 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001160
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001161 ret = mixer_initialize(ctx, drm_dev);
1162 if (ret)
1163 return ret;
1164
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001165 for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) {
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +09001166 enum drm_plane_type type;
1167 const uint32_t *formats;
1168 unsigned int fcount;
1169
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +09001170 if (zpos < VP_DEFAULT_WIN) {
1171 formats = mixer_formats;
1172 fcount = ARRAY_SIZE(mixer_formats);
1173 } else {
1174 formats = vp_formats;
1175 fcount = ARRAY_SIZE(vp_formats);
1176 }
1177
Gustavo Padovan323db0e2015-09-04 19:05:57 -03001178 type = exynos_plane_get_type(zpos, CURSOR_WIN);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001179 ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +09001180 1 << ctx->pipe, type, formats, fcount,
1181 zpos);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001182 if (ret)
1183 return ret;
1184 }
1185
Gustavo Padovan5d3d0992015-10-12 22:07:48 +09001186 exynos_plane = &ctx->planes[DEFAULT_WIN];
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001187 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1188 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1189 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001190 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001191 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001192 ret = PTR_ERR(ctx->crtc);
1193 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001194 }
1195
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001196 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001197
1198free_ctx:
1199 devm_kfree(dev, ctx);
1200 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001201}
1202
1203static void mixer_unbind(struct device *dev, struct device *master, void *data)
1204{
1205 struct mixer_context *ctx = dev_get_drvdata(dev);
1206
Gustavo Padovan93bca242015-01-18 18:16:23 +09001207 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001208}
1209
1210static const struct component_ops mixer_component_ops = {
1211 .bind = mixer_bind,
1212 .unbind = mixer_unbind,
1213};
1214
1215static int mixer_probe(struct platform_device *pdev)
1216{
1217 struct device *dev = &pdev->dev;
1218 struct mixer_drv_data *drv;
1219 struct mixer_context *ctx;
1220 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001221
Sean Paulf041b252014-01-30 16:19:15 -05001222 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1223 if (!ctx) {
1224 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001225 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001226 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001227
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301228 if (dev->of_node) {
1229 const struct of_device_id *match;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001230
Sachin Kamate436b092013-06-05 16:00:23 +09001231 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301232 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301233 } else {
1234 drv = (struct mixer_drv_data *)
1235 platform_get_device_id(pdev)->driver_data;
1236 }
1237
Sean Paul45517892014-01-30 16:19:05 -05001238 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001239 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301240 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001241 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301242 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001243 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301244 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001245
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001246 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001247
Inki Daedf5225b2014-05-29 18:28:02 +09001248 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda86650402015-06-11 23:23:37 +09001249 if (!ret)
1250 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001251
1252 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001253}
1254
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001255static int mixer_remove(struct platform_device *pdev)
1256{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001257 pm_runtime_disable(&pdev->dev);
1258
Inki Daedf5225b2014-05-29 18:28:02 +09001259 component_del(&pdev->dev, &mixer_component_ops);
Inki Daedf5225b2014-05-29 18:28:02 +09001260
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001261 return 0;
1262}
1263
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001264#ifdef CONFIG_PM_SLEEP
1265static int exynos_mixer_suspend(struct device *dev)
1266{
1267 struct mixer_context *ctx = dev_get_drvdata(dev);
1268 struct mixer_resources *res = &ctx->mixer_res;
1269
1270 clk_disable_unprepare(res->hdmi);
1271 clk_disable_unprepare(res->mixer);
1272 if (ctx->vp_enabled) {
1273 clk_disable_unprepare(res->vp);
1274 if (ctx->has_sclk)
1275 clk_disable_unprepare(res->sclk_mixer);
1276 }
1277
1278 return 0;
1279}
1280
1281static int exynos_mixer_resume(struct device *dev)
1282{
1283 struct mixer_context *ctx = dev_get_drvdata(dev);
1284 struct mixer_resources *res = &ctx->mixer_res;
1285 int ret;
1286
1287 ret = clk_prepare_enable(res->mixer);
1288 if (ret < 0) {
1289 DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
1290 return ret;
1291 }
1292 ret = clk_prepare_enable(res->hdmi);
1293 if (ret < 0) {
1294 DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
1295 return ret;
1296 }
1297 if (ctx->vp_enabled) {
1298 ret = clk_prepare_enable(res->vp);
1299 if (ret < 0) {
1300 DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
1301 ret);
1302 return ret;
1303 }
1304 if (ctx->has_sclk) {
1305 ret = clk_prepare_enable(res->sclk_mixer);
1306 if (ret < 0) {
1307 DRM_ERROR("Failed to prepare_enable the " \
1308 "sclk_mixer clk [%d]\n",
1309 ret);
1310 return ret;
1311 }
1312 }
1313 }
1314
1315 return 0;
1316}
1317#endif
1318
1319static const struct dev_pm_ops exynos_mixer_pm_ops = {
1320 SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
1321};
1322
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001323struct platform_driver mixer_driver = {
1324 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301325 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001326 .owner = THIS_MODULE,
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001327 .pm = &exynos_mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301328 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001329 },
1330 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001331 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301332 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001333};