blob: f40de82848dcb55438da7c5755886d58c736719d [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{
403 struct mixer_resources *res = &ctx->mixer_res;
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900404 struct drm_plane_state *state = plane->base.state;
405 struct drm_framebuffer *fb = state->fb;
406 struct drm_display_mode *mode = &state->crtc->mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900407 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900408 dma_addr_t luma_addr[2], chroma_addr[2];
409 bool tiled_mode = false;
410 bool crcb_mode = false;
411 u32 val;
412
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900413 switch (fb->pixel_format) {
Ville Syrjälä363b06a2012-05-14 11:08:51 +0900414 case DRM_FORMAT_NV12:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900415 crcb_mode = false;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900416 break;
Tobias Jakobi8f2590f2015-04-27 23:10:16 +0200417 case DRM_FORMAT_NV21:
418 crcb_mode = true;
419 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900420 default:
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900421 DRM_ERROR("pixel format for vp is wrong [%d].\n",
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900422 fb->pixel_format);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900423 return;
424 }
425
Marek Szyprowski0488f502015-11-30 14:53:21 +0100426 luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
427 chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900428
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900429 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900430 ctx->interlace = true;
431 if (tiled_mode) {
432 luma_addr[1] = luma_addr[0] + 0x40;
433 chroma_addr[1] = chroma_addr[0] + 0x40;
434 } else {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900435 luma_addr[1] = luma_addr[0] + fb->pitches[0];
436 chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900437 }
438 } else {
439 ctx->interlace = false;
440 luma_addr[1] = 0;
441 chroma_addr[1] = 0;
442 }
443
444 spin_lock_irqsave(&res->reg_slock, flags);
445 mixer_vsync_set_update(ctx, false);
446
447 /* interlace or progressive scan mode */
448 val = (ctx->interlace ? ~0 : 0);
449 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
450
451 /* setup format */
452 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
453 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
454 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
455
456 /* setting size of input image */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900457 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
458 VP_IMG_VSIZE(fb->height));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900459 /* chroma height has to reduced by 2 to avoid chroma distorions */
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900460 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
461 VP_IMG_VSIZE(fb->height / 2));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900462
Gustavo Padovand88d2462015-07-16 12:23:38 -0300463 vp_reg_write(res, VP_SRC_WIDTH, plane->src_w);
464 vp_reg_write(res, VP_SRC_HEIGHT, plane->src_h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900465 vp_reg_write(res, VP_SRC_H_POSITION,
Joonyoung Shimcb8a3db2015-04-07 15:59:38 +0900466 VP_SRC_H_POSITION_VAL(plane->src_x));
467 vp_reg_write(res, VP_SRC_V_POSITION, plane->src_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900468
Gustavo Padovand88d2462015-07-16 12:23:38 -0300469 vp_reg_write(res, VP_DST_WIDTH, plane->crtc_w);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900470 vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900471 if (ctx->interlace) {
Gustavo Padovand88d2462015-07-16 12:23:38 -0300472 vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h / 2);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900473 vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900474 } else {
Gustavo Padovand88d2462015-07-16 12:23:38 -0300475 vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900476 vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900477 }
478
Joonyoung Shim3cabaf72015-04-07 15:59:39 +0900479 vp_reg_write(res, VP_H_RATIO, plane->h_ratio);
480 vp_reg_write(res, VP_V_RATIO, plane->v_ratio);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900481
482 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
483
484 /* set buffer address to vp */
485 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
486 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
487 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
488 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
489
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900490 mixer_cfg_scan(ctx, mode->vdisplay);
491 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
492 mixer_cfg_layer(ctx, plane->zpos, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900493 mixer_run(ctx);
494
495 mixer_vsync_set_update(ctx, true);
496 spin_unlock_irqrestore(&res->reg_slock, flags);
497
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200498 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900499 vp_regs_dump(ctx);
500}
501
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530502static void mixer_layer_update(struct mixer_context *ctx)
503{
504 struct mixer_resources *res = &ctx->mixer_res;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530505
Rahul Sharma5c0f4822014-06-23 11:02:23 +0530506 mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530507}
508
Tobias Jakobi26110152015-04-07 01:14:52 +0200509static int mixer_setup_scale(const struct exynos_drm_plane *plane,
510 unsigned int *x_ratio, unsigned int *y_ratio)
511{
Gustavo Padovand88d2462015-07-16 12:23:38 -0300512 if (plane->crtc_w != plane->src_w) {
513 if (plane->crtc_w == 2 * plane->src_w)
Tobias Jakobi26110152015-04-07 01:14:52 +0200514 *x_ratio = 1;
515 else
516 goto fail;
517 }
518
Gustavo Padovand88d2462015-07-16 12:23:38 -0300519 if (plane->crtc_h != plane->src_h) {
520 if (plane->crtc_h == 2 * plane->src_h)
Tobias Jakobi26110152015-04-07 01:14:52 +0200521 *y_ratio = 1;
522 else
523 goto fail;
524 }
525
526 return 0;
527
528fail:
529 DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n");
530 return -ENOTSUPP;
531}
532
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900533static void mixer_graph_buffer(struct mixer_context *ctx,
534 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900535{
536 struct mixer_resources *res = &ctx->mixer_res;
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900537 struct drm_plane_state *state = plane->base.state;
538 struct drm_framebuffer *fb = state->fb;
539 struct drm_display_mode *mode = &state->crtc->mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900540 unsigned long flags;
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900541 unsigned int win = plane->zpos;
Tobias Jakobi26110152015-04-07 01:14:52 +0200542 unsigned int x_ratio = 0, y_ratio = 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900543 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900544 dma_addr_t dma_addr;
545 unsigned int fmt;
546 u32 val;
547
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900548 switch (fb->pixel_format) {
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200549 case DRM_FORMAT_XRGB4444:
550 fmt = MXR_FORMAT_ARGB4444;
551 break;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900552
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200553 case DRM_FORMAT_XRGB1555:
554 fmt = MXR_FORMAT_ARGB1555;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900555 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200556
557 case DRM_FORMAT_RGB565:
558 fmt = MXR_FORMAT_RGB565;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900559 break;
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200560
561 case DRM_FORMAT_XRGB8888:
562 case DRM_FORMAT_ARGB8888:
563 fmt = MXR_FORMAT_ARGB8888;
564 break;
565
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900566 default:
Tobias Jakobi7a57ca72015-04-27 23:11:59 +0200567 DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
568 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900569 }
570
Tobias Jakobi26110152015-04-07 01:14:52 +0200571 /* check if mixer supports requested scaling setup */
572 if (mixer_setup_scale(plane, &x_ratio, &y_ratio))
573 return;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900574
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +0900575 dst_x_offset = plane->crtc_x;
576 dst_y_offset = plane->crtc_y;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900577
578 /* converting dma address base and source offset */
Marek Szyprowski0488f502015-11-30 14:53:21 +0100579 dma_addr = exynos_drm_fb_dma_addr(fb, 0)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900580 + (plane->src_x * fb->bits_per_pixel >> 3)
581 + (plane->src_y * fb->pitches[0]);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900582 src_x_offset = 0;
583 src_y_offset = 0;
584
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900585 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900586 ctx->interlace = true;
587 else
588 ctx->interlace = false;
589
590 spin_lock_irqsave(&res->reg_slock, flags);
591 mixer_vsync_set_update(ctx, false);
592
593 /* setup format */
594 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
595 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
596
597 /* setup geometry */
Daniel Stoneadacb222015-03-17 13:24:58 +0000598 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900599 fb->pitches[0] / (fb->bits_per_pixel >> 3));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900600
Rahul Sharmadef5e092013-06-19 18:21:08 +0530601 /* setup display size */
602 if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
Gustavo Padovan5d3d0992015-10-12 22:07:48 +0900603 win == DEFAULT_WIN) {
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900604 val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
605 val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
Rahul Sharmadef5e092013-06-19 18:21:08 +0530606 mixer_reg_write(res, MXR_RESOLUTION, val);
607 }
608
Gustavo Padovand88d2462015-07-16 12:23:38 -0300609 val = MXR_GRP_WH_WIDTH(plane->src_w);
610 val |= MXR_GRP_WH_HEIGHT(plane->src_h);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900611 val |= MXR_GRP_WH_H_SCALE(x_ratio);
612 val |= MXR_GRP_WH_V_SCALE(y_ratio);
613 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
614
615 /* setup offsets in source image */
616 val = MXR_GRP_SXY_SX(src_x_offset);
617 val |= MXR_GRP_SXY_SY(src_y_offset);
618 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
619
620 /* setup offsets in display image */
621 val = MXR_GRP_DXY_DX(dst_x_offset);
622 val |= MXR_GRP_DXY_DY(dst_y_offset);
623 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
624
625 /* set buffer address to mixer */
626 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
627
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900628 mixer_cfg_scan(ctx, mode->vdisplay);
629 mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900630 mixer_cfg_layer(ctx, win, true);
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530631
632 /* layer update mandatory for mixer 16.0.33.0 */
Rahul Sharmadef5e092013-06-19 18:21:08 +0530633 if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
634 ctx->mxr_ver == MXR_VER_128_0_0_184)
Rahul Sharmaaaf8b492012-10-04 20:48:53 +0530635 mixer_layer_update(ctx);
636
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900637 mixer_run(ctx);
638
639 mixer_vsync_set_update(ctx, true);
640 spin_unlock_irqrestore(&res->reg_slock, flags);
Tobias Jakobic0734fb2015-05-06 14:10:21 +0200641
642 mixer_regs_dump(ctx);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900643}
644
645static void vp_win_reset(struct mixer_context *ctx)
646{
647 struct mixer_resources *res = &ctx->mixer_res;
648 int tries = 100;
649
650 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
651 for (tries = 100; tries; --tries) {
652 /* waiting until VP_SRESET_PROCESSING is 0 */
653 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
654 break;
Tomasz Stanislawski02b3de42015-09-25 14:48:29 +0200655 mdelay(10);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900656 }
657 WARN(tries == 0, "failed to reset Video Processor\n");
658}
659
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900660static void mixer_win_reset(struct mixer_context *ctx)
661{
662 struct mixer_resources *res = &ctx->mixer_res;
663 unsigned long flags;
664 u32 val; /* value stored to register */
665
666 spin_lock_irqsave(&res->reg_slock, flags);
667 mixer_vsync_set_update(ctx, false);
668
669 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
670
671 /* set output in RGB888 mode */
672 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
673
674 /* 16 beat burst in DMA */
675 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
676 MXR_STATUS_BURST_MASK);
677
678 /* setting default layer priority: layer1 > layer0 > video
679 * because typical usage scenario would be
680 * layer1 - OSD
681 * layer0 - framebuffer
682 * video - video overlay
683 */
684 val = MXR_LAYER_CFG_GRP1_VAL(3);
685 val |= MXR_LAYER_CFG_GRP0_VAL(2);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530686 if (ctx->vp_enabled)
687 val |= MXR_LAYER_CFG_VP_VAL(1);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900688 mixer_reg_write(res, MXR_LAYER_CFG, val);
689
690 /* setting background color */
691 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
692 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
693 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
694
695 /* setting graphical layers */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900696 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
697 val |= MXR_GRP_CFG_WIN_BLEND_EN;
698 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
699
Sean Paul0377f4e2013-04-25 15:13:26 -0400700 /* Don't blend layer 0 onto the mixer background */
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900701 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
Sean Paul0377f4e2013-04-25 15:13:26 -0400702
703 /* Blend layer 1 into layer 0 */
704 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
705 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900706 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
707
Seung-Woo Kim57366032012-05-15 17:22:08 +0900708 /* setting video layers */
709 val = MXR_GRP_CFG_ALPHA_VAL(0);
710 mixer_reg_write(res, MXR_VIDEO_CFG, val);
711
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530712 if (ctx->vp_enabled) {
713 /* configuration of Video Processor Registers */
714 vp_win_reset(ctx);
715 vp_default_filter(res);
716 }
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900717
718 /* disable all layers */
719 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
720 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
Rahul Sharma1b8e5742012-10-04 20:48:52 +0530721 if (ctx->vp_enabled)
722 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900723
724 mixer_vsync_set_update(ctx, true);
725 spin_unlock_irqrestore(&res->reg_slock, flags);
726}
727
Sean Paul45517892014-01-30 16:19:05 -0500728static irqreturn_t mixer_irq_handler(int irq, void *arg)
729{
730 struct mixer_context *ctx = arg;
731 struct mixer_resources *res = &ctx->mixer_res;
732 u32 val, base, shadow;
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300733 int win;
Sean Paul45517892014-01-30 16:19:05 -0500734
735 spin_lock(&res->reg_slock);
736
737 /* read interrupt status for handling and clearing flags for VSYNC */
738 val = mixer_reg_read(res, MXR_INT_STATUS);
739
740 /* handling VSYNC */
741 if (val & MXR_INT_STATUS_VSYNC) {
Andrzej Hajda81a464d2015-07-09 10:07:53 +0200742 /* vsync interrupt use different bit for read and clear */
743 val |= MXR_INT_CLEAR_VSYNC;
744 val &= ~MXR_INT_STATUS_VSYNC;
745
Sean Paul45517892014-01-30 16:19:05 -0500746 /* interlace scan need to check shadow register */
747 if (ctx->interlace) {
748 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
749 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
750 if (base != shadow)
751 goto out;
752
753 base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
754 shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
755 if (base != shadow)
756 goto out;
757 }
758
Gustavo Padovaneafd5402015-07-16 12:23:32 -0300759 drm_crtc_handle_vblank(&ctx->crtc->base);
Gustavo Padovan822f6df2015-08-15 13:26:14 -0300760 for (win = 0 ; win < MIXER_WIN_NR ; win++) {
761 struct exynos_drm_plane *plane = &ctx->planes[win];
762
763 if (!plane->pending_fb)
764 continue;
765
766 exynos_drm_crtc_finish_update(ctx->crtc, plane);
767 }
Sean Paul45517892014-01-30 16:19:05 -0500768
769 /* set wait vsync event to zero and wake up queue. */
770 if (atomic_read(&ctx->wait_vsync_event)) {
771 atomic_set(&ctx->wait_vsync_event, 0);
772 wake_up(&ctx->wait_vsync_queue);
773 }
774 }
775
776out:
777 /* clear interrupts */
Sean Paul45517892014-01-30 16:19:05 -0500778 mixer_reg_write(res, MXR_INT_STATUS, val);
779
780 spin_unlock(&res->reg_slock);
781
782 return IRQ_HANDLED;
783}
784
785static int mixer_resources_init(struct mixer_context *mixer_ctx)
786{
787 struct device *dev = &mixer_ctx->pdev->dev;
788 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
789 struct resource *res;
790 int ret;
791
792 spin_lock_init(&mixer_res->reg_slock);
793
794 mixer_res->mixer = devm_clk_get(dev, "mixer");
795 if (IS_ERR(mixer_res->mixer)) {
796 dev_err(dev, "failed to get clock 'mixer'\n");
797 return -ENODEV;
798 }
799
Marek Szyprowski04427ec2015-02-02 14:20:28 +0100800 mixer_res->hdmi = devm_clk_get(dev, "hdmi");
801 if (IS_ERR(mixer_res->hdmi)) {
802 dev_err(dev, "failed to get clock 'hdmi'\n");
803 return PTR_ERR(mixer_res->hdmi);
804 }
805
Sean Paul45517892014-01-30 16:19:05 -0500806 mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
807 if (IS_ERR(mixer_res->sclk_hdmi)) {
808 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
809 return -ENODEV;
810 }
811 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
812 if (res == NULL) {
813 dev_err(dev, "get memory resource failed.\n");
814 return -ENXIO;
815 }
816
817 mixer_res->mixer_regs = devm_ioremap(dev, res->start,
818 resource_size(res));
819 if (mixer_res->mixer_regs == NULL) {
820 dev_err(dev, "register mapping failed.\n");
821 return -ENXIO;
822 }
823
824 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
825 if (res == NULL) {
826 dev_err(dev, "get interrupt resource failed.\n");
827 return -ENXIO;
828 }
829
830 ret = devm_request_irq(dev, res->start, mixer_irq_handler,
831 0, "drm_mixer", mixer_ctx);
832 if (ret) {
833 dev_err(dev, "request interrupt failed.\n");
834 return ret;
835 }
836 mixer_res->irq = res->start;
837
838 return 0;
839}
840
841static int vp_resources_init(struct mixer_context *mixer_ctx)
842{
843 struct device *dev = &mixer_ctx->pdev->dev;
844 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
845 struct resource *res;
846
847 mixer_res->vp = devm_clk_get(dev, "vp");
848 if (IS_ERR(mixer_res->vp)) {
849 dev_err(dev, "failed to get clock 'vp'\n");
850 return -ENODEV;
851 }
Sean Paul45517892014-01-30 16:19:05 -0500852
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200853 if (mixer_ctx->has_sclk) {
854 mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
855 if (IS_ERR(mixer_res->sclk_mixer)) {
856 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
857 return -ENODEV;
858 }
859 mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
860 if (IS_ERR(mixer_res->mout_mixer)) {
861 dev_err(dev, "failed to get clock 'mout_mixer'\n");
862 return -ENODEV;
863 }
864
865 if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
866 clk_set_parent(mixer_res->mout_mixer,
867 mixer_res->sclk_hdmi);
868 }
Sean Paul45517892014-01-30 16:19:05 -0500869
870 res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
871 if (res == NULL) {
872 dev_err(dev, "get memory resource failed.\n");
873 return -ENXIO;
874 }
875
876 mixer_res->vp_regs = devm_ioremap(dev, res->start,
877 resource_size(res));
878 if (mixer_res->vp_regs == NULL) {
879 dev_err(dev, "register mapping failed.\n");
880 return -ENXIO;
881 }
882
883 return 0;
884}
885
Gustavo Padovan93bca242015-01-18 18:16:23 +0900886static int mixer_initialize(struct mixer_context *mixer_ctx,
Inki Daef37cd5e2014-05-09 14:25:20 +0900887 struct drm_device *drm_dev)
Sean Paul45517892014-01-30 16:19:05 -0500888{
889 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +0900890 struct exynos_drm_private *priv;
891 priv = drm_dev->dev_private;
Sean Paul45517892014-01-30 16:19:05 -0500892
Gustavo Padovaneb88e422014-11-26 16:43:27 -0200893 mixer_ctx->drm_dev = drm_dev;
Gustavo Padovan8a326ed2014-11-04 18:44:47 -0200894 mixer_ctx->pipe = priv->pipe++;
Sean Paul45517892014-01-30 16:19:05 -0500895
896 /* acquire resources: regs, irqs, clocks */
897 ret = mixer_resources_init(mixer_ctx);
898 if (ret) {
899 DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
900 return ret;
901 }
902
903 if (mixer_ctx->vp_enabled) {
904 /* acquire vp resources: regs, irqs, clocks */
905 ret = vp_resources_init(mixer_ctx);
906 if (ret) {
907 DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
908 return ret;
909 }
910 }
911
Joonyoung Shimeb7a3fc2015-07-02 21:49:39 +0900912 ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900913 if (ret)
914 priv->pipe--;
Sean Paulf041b252014-01-30 16:19:15 -0500915
Hyungwon Hwangfc2e0132015-06-22 19:05:04 +0900916 return ret;
Sean Paul45517892014-01-30 16:19:05 -0500917}
918
Gustavo Padovan93bca242015-01-18 18:16:23 +0900919static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
Inki Dae1055b392012-10-19 17:37:35 +0900920{
Joonyoung Shimbf566082015-07-02 21:49:38 +0900921 drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
Inki Dae1055b392012-10-19 17:37:35 +0900922}
923
Gustavo Padovan93bca242015-01-18 18:16:23 +0900924static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900925{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900926 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900927 struct mixer_resources *res = &mixer_ctx->mixer_res;
928
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200929 __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
930 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Sean Paulf041b252014-01-30 16:19:15 -0500931 return 0;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900932
933 /* enable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200934 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
935 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900936
937 return 0;
938}
939
Gustavo Padovan93bca242015-01-18 18:16:23 +0900940static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900941{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900942 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900943 struct mixer_resources *res = &mixer_ctx->mixer_res;
944
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +0200945 __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
946
947 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Andrzej Hajda947710c2015-07-09 08:25:41 +0200948 return;
Andrzej Hajda947710c2015-07-09 08:25:41 +0200949
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900950 /* disable vsync interrupt */
Andrzej Hajdafc0732482015-07-09 08:25:40 +0200951 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900952 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
953}
954
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900955static void mixer_update_plane(struct exynos_drm_crtc *crtc,
956 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900957{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900958 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900959
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900960 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900961
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200962 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Shirish Sdda90122013-01-23 22:03:18 -0500963 return;
Shirish Sdda90122013-01-23 22:03:18 -0500964
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900965 if (plane->zpos > 1 && mixer_ctx->vp_enabled)
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900966 vp_video_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900967 else
Gustavo Padovan2eeb2e52015-08-03 14:40:44 +0900968 mixer_graph_buffer(mixer_ctx, plane);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900969}
970
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900971static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
972 struct exynos_drm_plane *plane)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900973{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900974 struct mixer_context *mixer_ctx = crtc->ctx;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900975 struct mixer_resources *res = &mixer_ctx->mixer_res;
976 unsigned long flags;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900977
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900978 DRM_DEBUG_KMS("win: %d\n", plane->zpos);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900979
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200980 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +0530981 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +0530982
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900983 spin_lock_irqsave(&res->reg_slock, flags);
984 mixer_vsync_set_update(mixer_ctx, false);
985
Gustavo Padovan1e1d1392015-08-03 14:39:36 +0900986 mixer_cfg_layer(mixer_ctx, plane->zpos, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900987
988 mixer_vsync_set_update(mixer_ctx, true);
989 spin_unlock_irqrestore(&res->reg_slock, flags);
990}
991
Gustavo Padovan93bca242015-01-18 18:16:23 +0900992static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
Rahul Sharma0ea68222013-01-15 08:11:06 -0500993{
Gustavo Padovan93bca242015-01-18 18:16:23 +0900994 struct mixer_context *mixer_ctx = crtc->ctx;
Joonyoung Shim7c4c5582015-01-18 17:48:29 +0900995 int err;
Prathyush K8137a2e2012-12-06 20:16:01 +0530996
Andrzej Hajdaa44652e2015-07-09 08:25:42 +0200997 if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
Prathyush K6e95d5e2012-12-06 20:16:03 +0530998 return;
Prathyush K6e95d5e2012-12-06 20:16:03 +0530999
Gustavo Padovan93bca242015-01-18 18:16:23 +09001000 err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
Joonyoung Shim7c4c5582015-01-18 17:48:29 +09001001 if (err < 0) {
1002 DRM_DEBUG_KMS("failed to acquire vblank counter\n");
1003 return;
1004 }
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301005
Prathyush K6e95d5e2012-12-06 20:16:03 +05301006 atomic_set(&mixer_ctx->wait_vsync_event, 1);
1007
1008 /*
1009 * wait for MIXER to signal VSYNC interrupt or return after
1010 * timeout which is set to 50ms (refresh rate of 20).
1011 */
1012 if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
1013 !atomic_read(&mixer_ctx->wait_vsync_event),
Daniel Vetterbfd83032013-12-11 11:34:41 +01001014 HZ/20))
Prathyush K8137a2e2012-12-06 20:16:01 +05301015 DRM_DEBUG_KMS("vblank wait timed out.\n");
Rahul Sharma5d39b9e2014-06-23 11:02:25 +05301016
Gustavo Padovan93bca242015-01-18 18:16:23 +09001017 drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
Prathyush K8137a2e2012-12-06 20:16:01 +05301018}
1019
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001020static void mixer_enable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301021{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001022 struct mixer_context *ctx = crtc->ctx;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301023 struct mixer_resources *res = &ctx->mixer_res;
1024
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001025 if (test_bit(MXR_BIT_POWERED, &ctx->flags))
Prathyush Kdb43fd12012-12-06 20:16:05 +05301026 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301027
Sean Paulaf65c802014-01-30 16:19:27 -05001028 pm_runtime_get_sync(ctx->dev);
1029
Rahul Sharmad74ed932014-06-23 11:02:24 +05301030 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
1031
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001032 if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
Andrzej Hajdafc0732482015-07-09 08:25:40 +02001033 mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
Andrzej Hajda0df5e4a2015-07-09 08:25:43 +02001034 mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
1035 }
Prathyush Kdb43fd12012-12-06 20:16:05 +05301036 mixer_win_reset(ctx);
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001037
1038 set_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301039}
1040
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001041static void mixer_disable(struct exynos_drm_crtc *crtc)
Prathyush Kdb43fd12012-12-06 20:16:05 +05301042{
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001043 struct mixer_context *ctx = crtc->ctx;
Joonyoung Shimc329f662015-06-12 20:34:28 +09001044 int i;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301045
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001046 if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
Rahul Sharmab4bfa3c2014-06-23 11:02:21 +05301047 return;
Prathyush Kdb43fd12012-12-06 20:16:05 +05301048
Rahul Sharma381be022014-06-23 11:02:22 +05301049 mixer_stop(ctx);
Tobias Jakobic0734fb2015-05-06 14:10:21 +02001050 mixer_regs_dump(ctx);
Joonyoung Shimc329f662015-06-12 20:34:28 +09001051
1052 for (i = 0; i < MIXER_WIN_NR; i++)
Gustavo Padovan1e1d1392015-08-03 14:39:36 +09001053 mixer_disable_plane(crtc, &ctx->planes[i]);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301054
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001055 pm_runtime_put(ctx->dev);
1056
Andrzej Hajdaa44652e2015-07-09 08:25:42 +02001057 clear_bit(MXR_BIT_POWERED, &ctx->flags);
Prathyush Kdb43fd12012-12-06 20:16:05 +05301058}
1059
Sean Paulf041b252014-01-30 16:19:15 -05001060/* Only valid for Mixer version 16.0.33.0 */
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001061static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
1062 struct drm_crtc_state *state)
Sean Paulf041b252014-01-30 16:19:15 -05001063{
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001064 struct drm_display_mode *mode = &state->adjusted_mode;
Sean Paulf041b252014-01-30 16:19:15 -05001065 u32 w, h;
1066
1067 w = mode->hdisplay;
1068 h = mode->vdisplay;
1069
1070 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1071 mode->hdisplay, mode->vdisplay, mode->vrefresh,
1072 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1073
1074 if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1075 (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1076 (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1077 return 0;
1078
1079 return -EINVAL;
1080}
1081
Krzysztof Kozlowskif3aaf762015-05-07 09:04:45 +09001082static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
Gustavo Padovan3cecda02015-06-01 12:04:55 -03001083 .enable = mixer_enable,
1084 .disable = mixer_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001085 .enable_vblank = mixer_enable_vblank,
1086 .disable_vblank = mixer_disable_vblank,
Prathyush K8137a2e2012-12-06 20:16:01 +05301087 .wait_for_vblank = mixer_wait_for_vblank,
Gustavo Padovan9cc76102015-08-03 14:38:05 +09001088 .update_plane = mixer_update_plane,
1089 .disable_plane = mixer_disable_plane,
Andrzej Hajda3ae24362015-10-26 13:03:40 +01001090 .atomic_check = mixer_atomic_check,
Sean Paulf041b252014-01-30 16:19:15 -05001091};
Rahul Sharma0ea68222013-01-15 08:11:06 -05001092
Rahul Sharmadef5e092013-06-19 18:21:08 +05301093static struct mixer_drv_data exynos5420_mxr_drv_data = {
1094 .version = MXR_VER_128_0_0_184,
1095 .is_vp_enabled = 0,
1096};
1097
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301098static struct mixer_drv_data exynos5250_mxr_drv_data = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301099 .version = MXR_VER_16_0_33_0,
1100 .is_vp_enabled = 0,
1101};
1102
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001103static struct mixer_drv_data exynos4212_mxr_drv_data = {
1104 .version = MXR_VER_0_0_0_16,
1105 .is_vp_enabled = 1,
1106};
1107
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301108static struct mixer_drv_data exynos4210_mxr_drv_data = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301109 .version = MXR_VER_0_0_0_16,
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301110 .is_vp_enabled = 1,
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001111 .has_sclk = 1,
Rahul Sharma1e123442012-10-04 20:48:51 +05301112};
1113
Krzysztof Kozlowskid6b16302015-05-02 00:56:36 +09001114static const struct platform_device_id mixer_driver_types[] = {
Rahul Sharma1e123442012-10-04 20:48:51 +05301115 {
1116 .name = "s5p-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301117 .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
Rahul Sharma1e123442012-10-04 20:48:51 +05301118 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301119 .name = "exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301120 .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301121 }, {
1122 /* end node */
1123 }
1124};
1125
1126static struct of_device_id mixer_match_types[] = {
1127 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001128 .compatible = "samsung,exynos4210-mixer",
1129 .data = &exynos4210_mxr_drv_data,
1130 }, {
1131 .compatible = "samsung,exynos4212-mixer",
1132 .data = &exynos4212_mxr_drv_data,
1133 }, {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301134 .compatible = "samsung,exynos5-mixer",
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301135 .data = &exynos5250_mxr_drv_data,
1136 }, {
1137 .compatible = "samsung,exynos5250-mixer",
1138 .data = &exynos5250_mxr_drv_data,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301139 }, {
Rahul Sharmadef5e092013-06-19 18:21:08 +05301140 .compatible = "samsung,exynos5420-mixer",
1141 .data = &exynos5420_mxr_drv_data,
1142 }, {
Rahul Sharma1e123442012-10-04 20:48:51 +05301143 /* end node */
1144 }
1145};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001146MODULE_DEVICE_TABLE(of, mixer_match_types);
Rahul Sharma1e123442012-10-04 20:48:51 +05301147
Inki Daef37cd5e2014-05-09 14:25:20 +09001148static int mixer_bind(struct device *dev, struct device *manager, void *data)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001149{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001150 struct mixer_context *ctx = dev_get_drvdata(dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001151 struct drm_device *drm_dev = data;
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001152 struct exynos_drm_plane *exynos_plane;
Gustavo Padovan6e2a3b62015-04-03 21:05:52 +09001153 unsigned int zpos;
1154 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001155
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001156 ret = mixer_initialize(ctx, drm_dev);
1157 if (ret)
1158 return ret;
1159
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001160 for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) {
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +09001161 enum drm_plane_type type;
1162 const uint32_t *formats;
1163 unsigned int fcount;
1164
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +09001165 if (zpos < VP_DEFAULT_WIN) {
1166 formats = mixer_formats;
1167 fcount = ARRAY_SIZE(mixer_formats);
1168 } else {
1169 formats = vp_formats;
1170 fcount = ARRAY_SIZE(vp_formats);
1171 }
1172
Gustavo Padovan323db0e2015-09-04 19:05:57 -03001173 type = exynos_plane_get_type(zpos, CURSOR_WIN);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001174 ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
Marek Szyprowskifbbb1e12015-08-31 00:53:57 +09001175 1 << ctx->pipe, type, formats, fcount,
1176 zpos);
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001177 if (ret)
1178 return ret;
1179 }
1180
Gustavo Padovan5d3d0992015-10-12 22:07:48 +09001181 exynos_plane = &ctx->planes[DEFAULT_WIN];
Gustavo Padovan7ee14cd2015-04-03 21:03:40 +09001182 ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
1183 ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
1184 &mixer_crtc_ops, ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001185 if (IS_ERR(ctx->crtc)) {
Alban Browaeyse2dc3f72015-01-29 22:18:40 +01001186 mixer_ctx_remove(ctx);
Gustavo Padovan93bca242015-01-18 18:16:23 +09001187 ret = PTR_ERR(ctx->crtc);
1188 goto free_ctx;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001189 }
1190
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001191 return 0;
Gustavo Padovan93bca242015-01-18 18:16:23 +09001192
1193free_ctx:
1194 devm_kfree(dev, ctx);
1195 return ret;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001196}
1197
1198static void mixer_unbind(struct device *dev, struct device *master, void *data)
1199{
1200 struct mixer_context *ctx = dev_get_drvdata(dev);
1201
Gustavo Padovan93bca242015-01-18 18:16:23 +09001202 mixer_ctx_remove(ctx);
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001203}
1204
1205static const struct component_ops mixer_component_ops = {
1206 .bind = mixer_bind,
1207 .unbind = mixer_unbind,
1208};
1209
1210static int mixer_probe(struct platform_device *pdev)
1211{
1212 struct device *dev = &pdev->dev;
1213 struct mixer_drv_data *drv;
1214 struct mixer_context *ctx;
1215 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001216
Sean Paulf041b252014-01-30 16:19:15 -05001217 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1218 if (!ctx) {
1219 DRM_ERROR("failed to alloc mixer context.\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001220 return -ENOMEM;
Sean Paulf041b252014-01-30 16:19:15 -05001221 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001222
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301223 if (dev->of_node) {
1224 const struct of_device_id *match;
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001225
Sachin Kamate436b092013-06-05 16:00:23 +09001226 match = of_match_node(mixer_match_types, dev->of_node);
Rahul Sharma2cdc53b2012-10-31 09:36:26 +05301227 drv = (struct mixer_drv_data *)match->data;
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301228 } else {
1229 drv = (struct mixer_drv_data *)
1230 platform_get_device_id(pdev)->driver_data;
1231 }
1232
Sean Paul45517892014-01-30 16:19:05 -05001233 ctx->pdev = pdev;
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001234 ctx->dev = dev;
Rahul Sharma1b8e5742012-10-04 20:48:52 +05301235 ctx->vp_enabled = drv->is_vp_enabled;
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001236 ctx->has_sclk = drv->has_sclk;
Rahul Sharma1e123442012-10-04 20:48:51 +05301237 ctx->mxr_ver = drv->version;
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001238 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K6e95d5e2012-12-06 20:16:03 +05301239 atomic_set(&ctx->wait_vsync_event, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001240
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001241 platform_set_drvdata(pdev, ctx);
Inki Daedf5225b2014-05-29 18:28:02 +09001242
Inki Daedf5225b2014-05-29 18:28:02 +09001243 ret = component_add(&pdev->dev, &mixer_component_ops);
Andrzej Hajda86650402015-06-11 23:23:37 +09001244 if (!ret)
1245 pm_runtime_enable(dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001246
1247 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001248}
1249
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001250static int mixer_remove(struct platform_device *pdev)
1251{
Andrzej Hajda8103ef12014-11-24 14:12:46 +09001252 pm_runtime_disable(&pdev->dev);
1253
Inki Daedf5225b2014-05-29 18:28:02 +09001254 component_del(&pdev->dev, &mixer_component_ops);
Inki Daedf5225b2014-05-29 18:28:02 +09001255
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001256 return 0;
1257}
1258
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001259#ifdef CONFIG_PM_SLEEP
1260static int exynos_mixer_suspend(struct device *dev)
1261{
1262 struct mixer_context *ctx = dev_get_drvdata(dev);
1263 struct mixer_resources *res = &ctx->mixer_res;
1264
1265 clk_disable_unprepare(res->hdmi);
1266 clk_disable_unprepare(res->mixer);
1267 if (ctx->vp_enabled) {
1268 clk_disable_unprepare(res->vp);
1269 if (ctx->has_sclk)
1270 clk_disable_unprepare(res->sclk_mixer);
1271 }
1272
1273 return 0;
1274}
1275
1276static int exynos_mixer_resume(struct device *dev)
1277{
1278 struct mixer_context *ctx = dev_get_drvdata(dev);
1279 struct mixer_resources *res = &ctx->mixer_res;
1280 int ret;
1281
1282 ret = clk_prepare_enable(res->mixer);
1283 if (ret < 0) {
1284 DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
1285 return ret;
1286 }
1287 ret = clk_prepare_enable(res->hdmi);
1288 if (ret < 0) {
1289 DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
1290 return ret;
1291 }
1292 if (ctx->vp_enabled) {
1293 ret = clk_prepare_enable(res->vp);
1294 if (ret < 0) {
1295 DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
1296 ret);
1297 return ret;
1298 }
1299 if (ctx->has_sclk) {
1300 ret = clk_prepare_enable(res->sclk_mixer);
1301 if (ret < 0) {
1302 DRM_ERROR("Failed to prepare_enable the " \
1303 "sclk_mixer clk [%d]\n",
1304 ret);
1305 return ret;
1306 }
1307 }
1308 }
1309
1310 return 0;
1311}
1312#endif
1313
1314static const struct dev_pm_ops exynos_mixer_pm_ops = {
1315 SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
1316};
1317
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001318struct platform_driver mixer_driver = {
1319 .driver = {
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301320 .name = "exynos-mixer",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001321 .owner = THIS_MODULE,
Gustavo Padovanccf034a2015-09-04 17:15:46 -03001322 .pm = &exynos_mixer_pm_ops,
Rahul Sharmaaaf8b492012-10-04 20:48:53 +05301323 .of_match_table = mixer_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001324 },
1325 .probe = mixer_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001326 .remove = mixer_remove,
Rahul Sharma1e123442012-10-04 20:48:51 +05301327 .id_table = mixer_driver_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001328};