]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
d8408326 SWK |
2 | /* |
3 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | |
4 | * Authors: | |
5 | * Seung-Woo Kim <sw0312.kim@samsung.com> | |
6 | * Inki Dae <inki.dae@samsung.com> | |
7 | * Joonyoung Shim <jy0922.shim@samsung.com> | |
8 | * | |
9 | * Based on drivers/media/video/s5p-tv/mixer_reg.c | |
d8408326 SWK |
10 | */ |
11 | ||
2bda34d7 SR |
12 | #include <linux/clk.h> |
13 | #include <linux/component.h> | |
14 | #include <linux/delay.h> | |
d8408326 | 15 | #include <linux/i2c.h> |
d8408326 SWK |
16 | #include <linux/interrupt.h> |
17 | #include <linux/irq.h> | |
2bda34d7 SR |
18 | #include <linux/kernel.h> |
19 | #include <linux/ktime.h> | |
3f1c781d | 20 | #include <linux/of.h> |
48f6155a | 21 | #include <linux/of_device.h> |
2bda34d7 SR |
22 | #include <linux/platform_device.h> |
23 | #include <linux/pm_runtime.h> | |
24 | #include <linux/regulator/consumer.h> | |
25 | #include <linux/spinlock.h> | |
26 | #include <linux/wait.h> | |
d8408326 | 27 | |
2bda34d7 SR |
28 | #include <drm/drm_fourcc.h> |
29 | #include <drm/drm_vblank.h> | |
d8408326 SWK |
30 | #include <drm/exynos_drm.h> |
31 | ||
663d8766 | 32 | #include "exynos_drm_crtc.h" |
2bda34d7 | 33 | #include "exynos_drm_drv.h" |
0488f50e | 34 | #include "exynos_drm_fb.h" |
7ee14cdc | 35 | #include "exynos_drm_plane.h" |
2bda34d7 SR |
36 | #include "regs-mixer.h" |
37 | #include "regs-vp.h" | |
22b21ae6 | 38 | |
f041b257 | 39 | #define MIXER_WIN_NR 3 |
fbbb1e1a | 40 | #define VP_DEFAULT_WIN 2 |
d8408326 | 41 | |
2a6e4cd5 TJ |
42 | /* |
43 | * Mixer color space conversion coefficient triplet. | |
44 | * Used for CSC from RGB to YCbCr. | |
45 | * Each coefficient is a 10-bit fixed point number with | |
46 | * sign and no integer part, i.e. | |
47 | * [0:8] = fractional part (representing a value y = x / 2^9) | |
48 | * [9] = sign | |
49 | * Negative values are encoded with two's complement. | |
50 | */ | |
51 | #define MXR_CSC_C(x) ((int)((x) * 512.0) & 0x3ff) | |
52 | #define MXR_CSC_CT(a0, a1, a2) \ | |
53 | ((MXR_CSC_C(a0) << 20) | (MXR_CSC_C(a1) << 10) | (MXR_CSC_C(a2) << 0)) | |
54 | ||
55 | /* YCbCr value, used for mixer background color configuration. */ | |
56 | #define MXR_YCBCR_VAL(y, cb, cr) (((y) << 16) | ((cb) << 8) | ((cr) << 0)) | |
57 | ||
7a57ca7c TJ |
58 | /* The pixelformats that are natively supported by the mixer. */ |
59 | #define MXR_FORMAT_RGB565 4 | |
60 | #define MXR_FORMAT_ARGB1555 5 | |
61 | #define MXR_FORMAT_ARGB4444 6 | |
62 | #define MXR_FORMAT_ARGB8888 7 | |
63 | ||
1e123441 RS |
64 | enum mixer_version_id { |
65 | MXR_VER_0_0_0_16, | |
66 | MXR_VER_16_0_33_0, | |
def5e095 | 67 | MXR_VER_128_0_0_184, |
1e123441 RS |
68 | }; |
69 | ||
a44652e8 AH |
70 | enum mixer_flag_bits { |
71 | MXR_BIT_POWERED, | |
0df5e4ac | 72 | MXR_BIT_VSYNC, |
adeb6f44 TJ |
73 | MXR_BIT_INTERLACE, |
74 | MXR_BIT_VP_ENABLED, | |
75 | MXR_BIT_HAS_SCLK, | |
a44652e8 AH |
76 | }; |
77 | ||
fbbb1e1a MS |
78 | static const uint32_t mixer_formats[] = { |
79 | DRM_FORMAT_XRGB4444, | |
26a7af3e | 80 | DRM_FORMAT_ARGB4444, |
fbbb1e1a | 81 | DRM_FORMAT_XRGB1555, |
26a7af3e | 82 | DRM_FORMAT_ARGB1555, |
fbbb1e1a MS |
83 | DRM_FORMAT_RGB565, |
84 | DRM_FORMAT_XRGB8888, | |
85 | DRM_FORMAT_ARGB8888, | |
86 | }; | |
87 | ||
88 | static const uint32_t vp_formats[] = { | |
89 | DRM_FORMAT_NV12, | |
90 | DRM_FORMAT_NV21, | |
91 | }; | |
92 | ||
22b21ae6 | 93 | struct mixer_context { |
4551789f | 94 | struct platform_device *pdev; |
cf8fc4f1 | 95 | struct device *dev; |
1055b39f | 96 | struct drm_device *drm_dev; |
07dc3678 | 97 | void *dma_priv; |
93bca243 | 98 | struct exynos_drm_crtc *crtc; |
7ee14cdc | 99 | struct exynos_drm_plane planes[MIXER_WIN_NR]; |
a44652e8 | 100 | unsigned long flags; |
22b21ae6 | 101 | |
524c59f1 AH |
102 | int irq; |
103 | void __iomem *mixer_regs; | |
104 | void __iomem *vp_regs; | |
105 | spinlock_t reg_slock; | |
106 | struct clk *mixer; | |
107 | struct clk *vp; | |
108 | struct clk *hdmi; | |
109 | struct clk *sclk_mixer; | |
110 | struct clk *sclk_hdmi; | |
111 | struct clk *mout_mixer; | |
1e123441 | 112 | enum mixer_version_id mxr_ver; |
acc8bf04 | 113 | int scan_value; |
1e123441 RS |
114 | }; |
115 | ||
116 | struct mixer_drv_data { | |
117 | enum mixer_version_id version; | |
1b8e5747 | 118 | bool is_vp_enabled; |
ff830c96 | 119 | bool has_sclk; |
22b21ae6 JS |
120 | }; |
121 | ||
fd2d2fc2 MS |
122 | static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = { |
123 | { | |
124 | .zpos = 0, | |
125 | .type = DRM_PLANE_TYPE_PRIMARY, | |
126 | .pixel_formats = mixer_formats, | |
127 | .num_pixel_formats = ARRAY_SIZE(mixer_formats), | |
a2cb911e | 128 | .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE | |
482582c0 | 129 | EXYNOS_DRM_PLANE_CAP_ZPOS | |
6ac99a32 CM |
130 | EXYNOS_DRM_PLANE_CAP_PIX_BLEND | |
131 | EXYNOS_DRM_PLANE_CAP_WIN_BLEND, | |
fd2d2fc2 MS |
132 | }, { |
133 | .zpos = 1, | |
134 | .type = DRM_PLANE_TYPE_CURSOR, | |
135 | .pixel_formats = mixer_formats, | |
136 | .num_pixel_formats = ARRAY_SIZE(mixer_formats), | |
a2cb911e | 137 | .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE | |
482582c0 | 138 | EXYNOS_DRM_PLANE_CAP_ZPOS | |
6ac99a32 CM |
139 | EXYNOS_DRM_PLANE_CAP_PIX_BLEND | |
140 | EXYNOS_DRM_PLANE_CAP_WIN_BLEND, | |
fd2d2fc2 MS |
141 | }, { |
142 | .zpos = 2, | |
143 | .type = DRM_PLANE_TYPE_OVERLAY, | |
144 | .pixel_formats = vp_formats, | |
145 | .num_pixel_formats = ARRAY_SIZE(vp_formats), | |
a2cb911e | 146 | .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE | |
f40031c2 | 147 | EXYNOS_DRM_PLANE_CAP_ZPOS | |
6ac99a32 CM |
148 | EXYNOS_DRM_PLANE_CAP_TILE | |
149 | EXYNOS_DRM_PLANE_CAP_WIN_BLEND, | |
fd2d2fc2 MS |
150 | }, |
151 | }; | |
152 | ||
d8408326 SWK |
153 | static const u8 filter_y_horiz_tap8[] = { |
154 | 0, -1, -1, -1, -1, -1, -1, -1, | |
155 | -1, -1, -1, -1, -1, 0, 0, 0, | |
156 | 0, 2, 4, 5, 6, 6, 6, 6, | |
157 | 6, 5, 5, 4, 3, 2, 1, 1, | |
158 | 0, -6, -12, -16, -18, -20, -21, -20, | |
159 | -20, -18, -16, -13, -10, -8, -5, -2, | |
160 | 127, 126, 125, 121, 114, 107, 99, 89, | |
161 | 79, 68, 57, 46, 35, 25, 16, 8, | |
162 | }; | |
163 | ||
164 | static const u8 filter_y_vert_tap4[] = { | |
165 | 0, -3, -6, -8, -8, -8, -8, -7, | |
166 | -6, -5, -4, -3, -2, -1, -1, 0, | |
167 | 127, 126, 124, 118, 111, 102, 92, 81, | |
168 | 70, 59, 48, 37, 27, 19, 11, 5, | |
169 | 0, 5, 11, 19, 27, 37, 48, 59, | |
170 | 70, 81, 92, 102, 111, 118, 124, 126, | |
171 | 0, 0, -1, -1, -2, -3, -4, -5, | |
172 | -6, -7, -8, -8, -8, -8, -6, -3, | |
173 | }; | |
174 | ||
175 | static const u8 filter_cr_horiz_tap4[] = { | |
176 | 0, -3, -6, -8, -8, -8, -8, -7, | |
177 | -6, -5, -4, -3, -2, -1, -1, 0, | |
178 | 127, 126, 124, 118, 111, 102, 92, 81, | |
179 | 70, 59, 48, 37, 27, 19, 11, 5, | |
180 | }; | |
181 | ||
524c59f1 | 182 | static inline u32 vp_reg_read(struct mixer_context *ctx, u32 reg_id) |
d8408326 | 183 | { |
524c59f1 | 184 | return readl(ctx->vp_regs + reg_id); |
d8408326 SWK |
185 | } |
186 | ||
524c59f1 | 187 | static inline void vp_reg_write(struct mixer_context *ctx, u32 reg_id, |
d8408326 SWK |
188 | u32 val) |
189 | { | |
524c59f1 | 190 | writel(val, ctx->vp_regs + reg_id); |
d8408326 SWK |
191 | } |
192 | ||
524c59f1 | 193 | static inline void vp_reg_writemask(struct mixer_context *ctx, u32 reg_id, |
d8408326 SWK |
194 | u32 val, u32 mask) |
195 | { | |
524c59f1 | 196 | u32 old = vp_reg_read(ctx, reg_id); |
d8408326 SWK |
197 | |
198 | val = (val & mask) | (old & ~mask); | |
524c59f1 | 199 | writel(val, ctx->vp_regs + reg_id); |
d8408326 SWK |
200 | } |
201 | ||
524c59f1 | 202 | static inline u32 mixer_reg_read(struct mixer_context *ctx, u32 reg_id) |
d8408326 | 203 | { |
524c59f1 | 204 | return readl(ctx->mixer_regs + reg_id); |
d8408326 SWK |
205 | } |
206 | ||
524c59f1 | 207 | static inline void mixer_reg_write(struct mixer_context *ctx, u32 reg_id, |
d8408326 SWK |
208 | u32 val) |
209 | { | |
524c59f1 | 210 | writel(val, ctx->mixer_regs + reg_id); |
d8408326 SWK |
211 | } |
212 | ||
524c59f1 | 213 | static inline void mixer_reg_writemask(struct mixer_context *ctx, |
d8408326 SWK |
214 | u32 reg_id, u32 val, u32 mask) |
215 | { | |
524c59f1 | 216 | u32 old = mixer_reg_read(ctx, reg_id); |
d8408326 SWK |
217 | |
218 | val = (val & mask) | (old & ~mask); | |
524c59f1 | 219 | writel(val, ctx->mixer_regs + reg_id); |
d8408326 SWK |
220 | } |
221 | ||
222 | static void mixer_regs_dump(struct mixer_context *ctx) | |
223 | { | |
224 | #define DUMPREG(reg_id) \ | |
225 | do { \ | |
6be90056 ID |
226 | DRM_DEV_DEBUG_KMS(ctx->dev, #reg_id " = %08x\n", \ |
227 | (u32)readl(ctx->mixer_regs + reg_id)); \ | |
d8408326 SWK |
228 | } while (0) |
229 | ||
230 | DUMPREG(MXR_STATUS); | |
231 | DUMPREG(MXR_CFG); | |
232 | DUMPREG(MXR_INT_EN); | |
233 | DUMPREG(MXR_INT_STATUS); | |
234 | ||
235 | DUMPREG(MXR_LAYER_CFG); | |
236 | DUMPREG(MXR_VIDEO_CFG); | |
237 | ||
238 | DUMPREG(MXR_GRAPHIC0_CFG); | |
239 | DUMPREG(MXR_GRAPHIC0_BASE); | |
240 | DUMPREG(MXR_GRAPHIC0_SPAN); | |
241 | DUMPREG(MXR_GRAPHIC0_WH); | |
242 | DUMPREG(MXR_GRAPHIC0_SXY); | |
243 | DUMPREG(MXR_GRAPHIC0_DXY); | |
244 | ||
245 | DUMPREG(MXR_GRAPHIC1_CFG); | |
246 | DUMPREG(MXR_GRAPHIC1_BASE); | |
247 | DUMPREG(MXR_GRAPHIC1_SPAN); | |
248 | DUMPREG(MXR_GRAPHIC1_WH); | |
249 | DUMPREG(MXR_GRAPHIC1_SXY); | |
250 | DUMPREG(MXR_GRAPHIC1_DXY); | |
251 | #undef DUMPREG | |
252 | } | |
253 | ||
254 | static void vp_regs_dump(struct mixer_context *ctx) | |
255 | { | |
256 | #define DUMPREG(reg_id) \ | |
257 | do { \ | |
6be90056 ID |
258 | DRM_DEV_DEBUG_KMS(ctx->dev, #reg_id " = %08x\n", \ |
259 | (u32) readl(ctx->vp_regs + reg_id)); \ | |
d8408326 SWK |
260 | } while (0) |
261 | ||
262 | DUMPREG(VP_ENABLE); | |
263 | DUMPREG(VP_SRESET); | |
264 | DUMPREG(VP_SHADOW_UPDATE); | |
265 | DUMPREG(VP_FIELD_ID); | |
266 | DUMPREG(VP_MODE); | |
267 | DUMPREG(VP_IMG_SIZE_Y); | |
268 | DUMPREG(VP_IMG_SIZE_C); | |
269 | DUMPREG(VP_PER_RATE_CTRL); | |
270 | DUMPREG(VP_TOP_Y_PTR); | |
271 | DUMPREG(VP_BOT_Y_PTR); | |
272 | DUMPREG(VP_TOP_C_PTR); | |
273 | DUMPREG(VP_BOT_C_PTR); | |
274 | DUMPREG(VP_ENDIAN_MODE); | |
275 | DUMPREG(VP_SRC_H_POSITION); | |
276 | DUMPREG(VP_SRC_V_POSITION); | |
277 | DUMPREG(VP_SRC_WIDTH); | |
278 | DUMPREG(VP_SRC_HEIGHT); | |
279 | DUMPREG(VP_DST_H_POSITION); | |
280 | DUMPREG(VP_DST_V_POSITION); | |
281 | DUMPREG(VP_DST_WIDTH); | |
282 | DUMPREG(VP_DST_HEIGHT); | |
283 | DUMPREG(VP_H_RATIO); | |
284 | DUMPREG(VP_V_RATIO); | |
285 | ||
286 | #undef DUMPREG | |
287 | } | |
288 | ||
524c59f1 | 289 | static inline void vp_filter_set(struct mixer_context *ctx, |
d8408326 SWK |
290 | int reg_id, const u8 *data, unsigned int size) |
291 | { | |
292 | /* assure 4-byte align */ | |
293 | BUG_ON(size & 3); | |
294 | for (; size; size -= 4, reg_id += 4, data += 4) { | |
295 | u32 val = (data[0] << 24) | (data[1] << 16) | | |
296 | (data[2] << 8) | data[3]; | |
524c59f1 | 297 | vp_reg_write(ctx, reg_id, val); |
d8408326 SWK |
298 | } |
299 | } | |
300 | ||
524c59f1 | 301 | static void vp_default_filter(struct mixer_context *ctx) |
d8408326 | 302 | { |
524c59f1 | 303 | vp_filter_set(ctx, VP_POLY8_Y0_LL, |
e25e1b66 | 304 | filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8)); |
524c59f1 | 305 | vp_filter_set(ctx, VP_POLY4_Y0_LL, |
e25e1b66 | 306 | filter_y_vert_tap4, sizeof(filter_y_vert_tap4)); |
524c59f1 | 307 | vp_filter_set(ctx, VP_POLY4_C0_LL, |
e25e1b66 | 308 | filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4)); |
d8408326 SWK |
309 | } |
310 | ||
f657a996 | 311 | static void mixer_cfg_gfx_blend(struct mixer_context *ctx, unsigned int win, |
6ac99a32 | 312 | unsigned int pixel_alpha, unsigned int alpha) |
f657a996 | 313 | { |
6ac99a32 | 314 | u32 win_alpha = alpha >> 8; |
f657a996 MS |
315 | u32 val; |
316 | ||
317 | val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ | |
482582c0 CM |
318 | switch (pixel_alpha) { |
319 | case DRM_MODE_BLEND_PIXEL_NONE: | |
320 | break; | |
321 | case DRM_MODE_BLEND_COVERAGE: | |
322 | val |= MXR_GRP_CFG_PIXEL_BLEND_EN; | |
323 | break; | |
324 | case DRM_MODE_BLEND_PREMULTI: | |
325 | default: | |
f657a996 MS |
326 | val |= MXR_GRP_CFG_BLEND_PRE_MUL; |
327 | val |= MXR_GRP_CFG_PIXEL_BLEND_EN; | |
482582c0 | 328 | break; |
f657a996 | 329 | } |
6ac99a32 CM |
330 | |
331 | if (alpha != DRM_BLEND_ALPHA_OPAQUE) { | |
332 | val |= MXR_GRP_CFG_WIN_BLEND_EN; | |
333 | val |= win_alpha; | |
334 | } | |
524c59f1 | 335 | mixer_reg_writemask(ctx, MXR_GRAPHIC_CFG(win), |
f657a996 MS |
336 | val, MXR_GRP_CFG_MISC_MASK); |
337 | } | |
338 | ||
6ac99a32 | 339 | static void mixer_cfg_vp_blend(struct mixer_context *ctx, unsigned int alpha) |
f657a996 | 340 | { |
6ac99a32 CM |
341 | u32 win_alpha = alpha >> 8; |
342 | u32 val = 0; | |
f657a996 | 343 | |
6ac99a32 CM |
344 | if (alpha != DRM_BLEND_ALPHA_OPAQUE) { |
345 | val |= MXR_VID_CFG_BLEND_EN; | |
346 | val |= win_alpha; | |
347 | } | |
524c59f1 | 348 | mixer_reg_write(ctx, MXR_VIDEO_CFG, val); |
f657a996 MS |
349 | } |
350 | ||
6a3b45ad | 351 | static bool mixer_is_synced(struct mixer_context *ctx) |
d8408326 | 352 | { |
6a3b45ad | 353 | u32 base, shadow; |
d8408326 | 354 | |
6a3b45ad AH |
355 | if (ctx->mxr_ver == MXR_VER_16_0_33_0 || |
356 | ctx->mxr_ver == MXR_VER_128_0_0_184) | |
357 | return !(mixer_reg_read(ctx, MXR_CFG) & | |
358 | MXR_CFG_LAYER_UPDATE_COUNT_MASK); | |
359 | ||
360 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags) && | |
361 | vp_reg_read(ctx, VP_SHADOW_UPDATE)) | |
362 | return false; | |
363 | ||
364 | base = mixer_reg_read(ctx, MXR_CFG); | |
365 | shadow = mixer_reg_read(ctx, MXR_CFG_S); | |
366 | if (base != shadow) | |
367 | return false; | |
368 | ||
369 | base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(0)); | |
370 | shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(0)); | |
371 | if (base != shadow) | |
372 | return false; | |
373 | ||
374 | base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(1)); | |
375 | shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(1)); | |
376 | if (base != shadow) | |
377 | return false; | |
378 | ||
379 | return true; | |
380 | } | |
381 | ||
382 | static int mixer_wait_for_sync(struct mixer_context *ctx) | |
383 | { | |
384 | ktime_t timeout = ktime_add_us(ktime_get(), 100000); | |
385 | ||
386 | while (!mixer_is_synced(ctx)) { | |
387 | usleep_range(1000, 2000); | |
388 | if (ktime_compare(ktime_get(), timeout) > 0) | |
389 | return -ETIMEDOUT; | |
390 | } | |
391 | return 0; | |
392 | } | |
393 | ||
394 | static void mixer_disable_sync(struct mixer_context *ctx) | |
395 | { | |
396 | mixer_reg_writemask(ctx, MXR_STATUS, 0, MXR_STATUS_SYNC_ENABLE); | |
397 | } | |
398 | ||
399 | static void mixer_enable_sync(struct mixer_context *ctx) | |
400 | { | |
401 | if (ctx->mxr_ver == MXR_VER_16_0_33_0 || | |
402 | ctx->mxr_ver == MXR_VER_128_0_0_184) | |
403 | mixer_reg_writemask(ctx, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE); | |
404 | mixer_reg_writemask(ctx, MXR_STATUS, ~0, MXR_STATUS_SYNC_ENABLE); | |
adeb6f44 | 405 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) |
6a3b45ad | 406 | vp_reg_write(ctx, VP_SHADOW_UPDATE, VP_SHADOW_UPDATE_ENABLE); |
d8408326 SWK |
407 | } |
408 | ||
3fc40ca9 | 409 | static void mixer_cfg_scan(struct mixer_context *ctx, int width, int height) |
d8408326 | 410 | { |
d8408326 SWK |
411 | u32 val; |
412 | ||
413 | /* choosing between interlace and progressive mode */ | |
adeb6f44 TJ |
414 | val = test_bit(MXR_BIT_INTERLACE, &ctx->flags) ? |
415 | MXR_CFG_SCAN_INTERLACE : MXR_CFG_SCAN_PROGRESSIVE; | |
d8408326 | 416 | |
acc8bf04 | 417 | if (ctx->mxr_ver == MXR_VER_128_0_0_184) |
524c59f1 | 418 | mixer_reg_write(ctx, MXR_RESOLUTION, |
3fc40ca9 | 419 | MXR_MXR_RES_HEIGHT(height) | MXR_MXR_RES_WIDTH(width)); |
acc8bf04 AH |
420 | else |
421 | val |= ctx->scan_value; | |
d8408326 | 422 | |
524c59f1 | 423 | mixer_reg_writemask(ctx, MXR_CFG, val, MXR_CFG_SCAN_MASK); |
d8408326 SWK |
424 | } |
425 | ||
13e810f1 | 426 | static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, struct drm_display_mode *mode) |
d8408326 | 427 | { |
13e810f1 | 428 | enum hdmi_quantization_range range = drm_default_rgb_quant_range(mode); |
d8408326 SWK |
429 | u32 val; |
430 | ||
13e810f1 CM |
431 | if (mode->vdisplay < 720) { |
432 | val = MXR_CFG_RGB601; | |
e9e5ba93 | 433 | } else { |
13e810f1 CM |
434 | val = MXR_CFG_RGB709; |
435 | ||
2a6e4cd5 | 436 | /* Configure the BT.709 CSC matrix for full range RGB. */ |
524c59f1 | 437 | mixer_reg_write(ctx, MXR_CM_COEFF_Y, |
2a6e4cd5 TJ |
438 | MXR_CSC_CT( 0.184, 0.614, 0.063) | |
439 | MXR_CM_COEFF_RGB_FULL); | |
524c59f1 | 440 | mixer_reg_write(ctx, MXR_CM_COEFF_CB, |
2a6e4cd5 | 441 | MXR_CSC_CT(-0.102, -0.338, 0.440)); |
524c59f1 | 442 | mixer_reg_write(ctx, MXR_CM_COEFF_CR, |
2a6e4cd5 | 443 | MXR_CSC_CT( 0.440, -0.399, -0.040)); |
d8408326 SWK |
444 | } |
445 | ||
13e810f1 CM |
446 | if (range == HDMI_QUANTIZATION_RANGE_FULL) |
447 | val |= MXR_CFG_QUANT_RANGE_FULL; | |
448 | else | |
449 | val |= MXR_CFG_QUANT_RANGE_LIMITED; | |
450 | ||
524c59f1 | 451 | mixer_reg_writemask(ctx, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK); |
d8408326 SWK |
452 | } |
453 | ||
5b1d5bc6 | 454 | static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win, |
a2cb911e | 455 | unsigned int priority, bool enable) |
d8408326 | 456 | { |
d8408326 SWK |
457 | u32 val = enable ? ~0 : 0; |
458 | ||
459 | switch (win) { | |
460 | case 0: | |
524c59f1 AH |
461 | mixer_reg_writemask(ctx, MXR_CFG, val, MXR_CFG_GRP0_ENABLE); |
462 | mixer_reg_writemask(ctx, MXR_LAYER_CFG, | |
a2cb911e MS |
463 | MXR_LAYER_CFG_GRP0_VAL(priority), |
464 | MXR_LAYER_CFG_GRP0_MASK); | |
d8408326 SWK |
465 | break; |
466 | case 1: | |
524c59f1 AH |
467 | mixer_reg_writemask(ctx, MXR_CFG, val, MXR_CFG_GRP1_ENABLE); |
468 | mixer_reg_writemask(ctx, MXR_LAYER_CFG, | |
a2cb911e MS |
469 | MXR_LAYER_CFG_GRP1_VAL(priority), |
470 | MXR_LAYER_CFG_GRP1_MASK); | |
adeb6f44 | 471 | |
d8408326 | 472 | break; |
5e68fef2 | 473 | case VP_DEFAULT_WIN: |
adeb6f44 | 474 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) { |
524c59f1 AH |
475 | vp_reg_writemask(ctx, VP_ENABLE, val, VP_ENABLE_ON); |
476 | mixer_reg_writemask(ctx, MXR_CFG, val, | |
1b8e5747 | 477 | MXR_CFG_VP_ENABLE); |
524c59f1 | 478 | mixer_reg_writemask(ctx, MXR_LAYER_CFG, |
a2cb911e MS |
479 | MXR_LAYER_CFG_VP_VAL(priority), |
480 | MXR_LAYER_CFG_VP_MASK); | |
1b8e5747 | 481 | } |
d8408326 SWK |
482 | break; |
483 | } | |
484 | } | |
485 | ||
486 | static void mixer_run(struct mixer_context *ctx) | |
487 | { | |
524c59f1 | 488 | mixer_reg_writemask(ctx, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); |
d8408326 SWK |
489 | } |
490 | ||
381be025 RS |
491 | static void mixer_stop(struct mixer_context *ctx) |
492 | { | |
381be025 RS |
493 | int timeout = 20; |
494 | ||
524c59f1 | 495 | mixer_reg_writemask(ctx, MXR_STATUS, 0, MXR_STATUS_REG_RUN); |
381be025 | 496 | |
524c59f1 | 497 | while (!(mixer_reg_read(ctx, MXR_STATUS) & MXR_STATUS_REG_IDLE) && |
381be025 RS |
498 | --timeout) |
499 | usleep_range(10000, 12000); | |
381be025 RS |
500 | } |
501 | ||
521d98a3 AH |
502 | static void mixer_commit(struct mixer_context *ctx) |
503 | { | |
504 | struct drm_display_mode *mode = &ctx->crtc->base.state->adjusted_mode; | |
505 | ||
3fc40ca9 | 506 | mixer_cfg_scan(ctx, mode->hdisplay, mode->vdisplay); |
13e810f1 | 507 | mixer_cfg_rgb_fmt(ctx, mode); |
521d98a3 AH |
508 | mixer_run(ctx); |
509 | } | |
510 | ||
2eeb2e5e GP |
511 | static void vp_video_buffer(struct mixer_context *ctx, |
512 | struct exynos_drm_plane *plane) | |
d8408326 | 513 | { |
0114f404 MS |
514 | struct exynos_drm_plane_state *state = |
515 | to_exynos_plane_state(plane->base.state); | |
0114f404 | 516 | struct drm_framebuffer *fb = state->base.fb; |
e47726a1 | 517 | unsigned int priority = state->base.normalized_zpos + 1; |
d8408326 | 518 | unsigned long flags; |
d8408326 | 519 | dma_addr_t luma_addr[2], chroma_addr[2]; |
0f752694 | 520 | bool is_tiled, is_nv21; |
d8408326 SWK |
521 | u32 val; |
522 | ||
0f752694 TJ |
523 | is_nv21 = (fb->format->format == DRM_FORMAT_NV21); |
524 | is_tiled = (fb->modifier == DRM_FORMAT_MOD_SAMSUNG_64_32_TILE); | |
f40031c2 | 525 | |
0488f50e MS |
526 | luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0); |
527 | chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1); | |
d8408326 | 528 | |
71469944 | 529 | if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) { |
0f752694 | 530 | if (is_tiled) { |
d8408326 SWK |
531 | luma_addr[1] = luma_addr[0] + 0x40; |
532 | chroma_addr[1] = chroma_addr[0] + 0x40; | |
533 | } else { | |
2eeb2e5e | 534 | luma_addr[1] = luma_addr[0] + fb->pitches[0]; |
0ccc1c8f | 535 | chroma_addr[1] = chroma_addr[0] + fb->pitches[1]; |
d8408326 SWK |
536 | } |
537 | } else { | |
d8408326 SWK |
538 | luma_addr[1] = 0; |
539 | chroma_addr[1] = 0; | |
540 | } | |
541 | ||
524c59f1 | 542 | spin_lock_irqsave(&ctx->reg_slock, flags); |
d8408326 SWK |
543 | |
544 | /* interlace or progressive scan mode */ | |
adeb6f44 | 545 | val = (test_bit(MXR_BIT_INTERLACE, &ctx->flags) ? ~0 : 0); |
524c59f1 | 546 | vp_reg_writemask(ctx, VP_MODE, val, VP_MODE_LINE_SKIP); |
d8408326 SWK |
547 | |
548 | /* setup format */ | |
0f752694 TJ |
549 | val = (is_nv21 ? VP_MODE_NV21 : VP_MODE_NV12); |
550 | val |= (is_tiled ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR); | |
524c59f1 | 551 | vp_reg_writemask(ctx, VP_MODE, val, VP_MODE_FMT_MASK); |
d8408326 SWK |
552 | |
553 | /* setting size of input image */ | |
524c59f1 | 554 | vp_reg_write(ctx, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) | |
2eeb2e5e | 555 | VP_IMG_VSIZE(fb->height)); |
dc500cfb | 556 | /* chroma plane for NV12/NV21 is half the height of the luma plane */ |
0ccc1c8f | 557 | vp_reg_write(ctx, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[1]) | |
2eeb2e5e | 558 | VP_IMG_VSIZE(fb->height / 2)); |
d8408326 | 559 | |
524c59f1 | 560 | vp_reg_write(ctx, VP_SRC_WIDTH, state->src.w); |
524c59f1 | 561 | vp_reg_write(ctx, VP_SRC_H_POSITION, |
0114f404 | 562 | VP_SRC_H_POSITION_VAL(state->src.x)); |
524c59f1 AH |
563 | vp_reg_write(ctx, VP_DST_WIDTH, state->crtc.w); |
564 | vp_reg_write(ctx, VP_DST_H_POSITION, state->crtc.x); | |
0ccc1c8f | 565 | |
adeb6f44 | 566 | if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) { |
0ccc1c8f TJ |
567 | vp_reg_write(ctx, VP_SRC_HEIGHT, state->src.h / 2); |
568 | vp_reg_write(ctx, VP_SRC_V_POSITION, state->src.y / 2); | |
524c59f1 AH |
569 | vp_reg_write(ctx, VP_DST_HEIGHT, state->crtc.h / 2); |
570 | vp_reg_write(ctx, VP_DST_V_POSITION, state->crtc.y / 2); | |
d8408326 | 571 | } else { |
0ccc1c8f TJ |
572 | vp_reg_write(ctx, VP_SRC_HEIGHT, state->src.h); |
573 | vp_reg_write(ctx, VP_SRC_V_POSITION, state->src.y); | |
524c59f1 AH |
574 | vp_reg_write(ctx, VP_DST_HEIGHT, state->crtc.h); |
575 | vp_reg_write(ctx, VP_DST_V_POSITION, state->crtc.y); | |
d8408326 SWK |
576 | } |
577 | ||
524c59f1 AH |
578 | vp_reg_write(ctx, VP_H_RATIO, state->h_ratio); |
579 | vp_reg_write(ctx, VP_V_RATIO, state->v_ratio); | |
d8408326 | 580 | |
524c59f1 | 581 | vp_reg_write(ctx, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); |
d8408326 SWK |
582 | |
583 | /* set buffer address to vp */ | |
524c59f1 AH |
584 | vp_reg_write(ctx, VP_TOP_Y_PTR, luma_addr[0]); |
585 | vp_reg_write(ctx, VP_BOT_Y_PTR, luma_addr[1]); | |
586 | vp_reg_write(ctx, VP_TOP_C_PTR, chroma_addr[0]); | |
587 | vp_reg_write(ctx, VP_BOT_C_PTR, chroma_addr[1]); | |
d8408326 | 588 | |
e47726a1 | 589 | mixer_cfg_layer(ctx, plane->index, priority, true); |
6ac99a32 | 590 | mixer_cfg_vp_blend(ctx, state->base.alpha); |
d8408326 | 591 | |
524c59f1 | 592 | spin_unlock_irqrestore(&ctx->reg_slock, flags); |
d8408326 | 593 | |
c0734fba | 594 | mixer_regs_dump(ctx); |
d8408326 SWK |
595 | vp_regs_dump(ctx); |
596 | } | |
597 | ||
2eeb2e5e GP |
598 | static void mixer_graph_buffer(struct mixer_context *ctx, |
599 | struct exynos_drm_plane *plane) | |
d8408326 | 600 | { |
0114f404 MS |
601 | struct exynos_drm_plane_state *state = |
602 | to_exynos_plane_state(plane->base.state); | |
0114f404 | 603 | struct drm_framebuffer *fb = state->base.fb; |
e47726a1 | 604 | unsigned int priority = state->base.normalized_zpos + 1; |
d8408326 | 605 | unsigned long flags; |
40bdfb0a | 606 | unsigned int win = plane->index; |
2611015c | 607 | unsigned int x_ratio = 0, y_ratio = 0; |
5dff6905 | 608 | unsigned int dst_x_offset, dst_y_offset; |
482582c0 | 609 | unsigned int pixel_alpha; |
d8408326 SWK |
610 | dma_addr_t dma_addr; |
611 | unsigned int fmt; | |
612 | u32 val; | |
613 | ||
482582c0 CM |
614 | if (fb->format->has_alpha) |
615 | pixel_alpha = state->base.pixel_blend_mode; | |
616 | else | |
617 | pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE; | |
618 | ||
438b74a5 | 619 | switch (fb->format->format) { |
7a57ca7c | 620 | case DRM_FORMAT_XRGB4444: |
26a7af3e | 621 | case DRM_FORMAT_ARGB4444: |
7a57ca7c TJ |
622 | fmt = MXR_FORMAT_ARGB4444; |
623 | break; | |
624 | ||
625 | case DRM_FORMAT_XRGB1555: | |
26a7af3e | 626 | case DRM_FORMAT_ARGB1555: |
7a57ca7c TJ |
627 | fmt = MXR_FORMAT_ARGB1555; |
628 | break; | |
d8408326 | 629 | |
7a57ca7c TJ |
630 | case DRM_FORMAT_RGB565: |
631 | fmt = MXR_FORMAT_RGB565; | |
d8408326 | 632 | break; |
7a57ca7c TJ |
633 | |
634 | case DRM_FORMAT_XRGB8888: | |
635 | case DRM_FORMAT_ARGB8888: | |
1e60d62f | 636 | default: |
7a57ca7c | 637 | fmt = MXR_FORMAT_ARGB8888; |
d8408326 | 638 | break; |
d8408326 SWK |
639 | } |
640 | ||
e463b069 MS |
641 | /* ratio is already checked by common plane code */ |
642 | x_ratio = state->h_ratio == (1 << 15); | |
643 | y_ratio = state->v_ratio == (1 << 15); | |
d8408326 | 644 | |
0114f404 MS |
645 | dst_x_offset = state->crtc.x; |
646 | dst_y_offset = state->crtc.y; | |
d8408326 | 647 | |
5dff6905 | 648 | /* translate dma address base s.t. the source image offset is zero */ |
0488f50e | 649 | dma_addr = exynos_drm_fb_dma_addr(fb, 0) |
272725c7 | 650 | + (state->src.x * fb->format->cpp[0]) |
0114f404 | 651 | + (state->src.y * fb->pitches[0]); |
d8408326 | 652 | |
524c59f1 | 653 | spin_lock_irqsave(&ctx->reg_slock, flags); |
d8408326 SWK |
654 | |
655 | /* setup format */ | |
524c59f1 | 656 | mixer_reg_writemask(ctx, MXR_GRAPHIC_CFG(win), |
d8408326 SWK |
657 | MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK); |
658 | ||
659 | /* setup geometry */ | |
524c59f1 | 660 | mixer_reg_write(ctx, MXR_GRAPHIC_SPAN(win), |
272725c7 | 661 | fb->pitches[0] / fb->format->cpp[0]); |
d8408326 | 662 | |
0114f404 MS |
663 | val = MXR_GRP_WH_WIDTH(state->src.w); |
664 | val |= MXR_GRP_WH_HEIGHT(state->src.h); | |
d8408326 SWK |
665 | val |= MXR_GRP_WH_H_SCALE(x_ratio); |
666 | val |= MXR_GRP_WH_V_SCALE(y_ratio); | |
524c59f1 | 667 | mixer_reg_write(ctx, MXR_GRAPHIC_WH(win), val); |
d8408326 | 668 | |
d8408326 SWK |
669 | /* setup offsets in display image */ |
670 | val = MXR_GRP_DXY_DX(dst_x_offset); | |
671 | val |= MXR_GRP_DXY_DY(dst_y_offset); | |
524c59f1 | 672 | mixer_reg_write(ctx, MXR_GRAPHIC_DXY(win), val); |
d8408326 SWK |
673 | |
674 | /* set buffer address to mixer */ | |
524c59f1 | 675 | mixer_reg_write(ctx, MXR_GRAPHIC_BASE(win), dma_addr); |
d8408326 | 676 | |
e47726a1 | 677 | mixer_cfg_layer(ctx, win, priority, true); |
6ac99a32 | 678 | mixer_cfg_gfx_blend(ctx, win, pixel_alpha, state->base.alpha); |
aaf8b49e | 679 | |
524c59f1 | 680 | spin_unlock_irqrestore(&ctx->reg_slock, flags); |
c0734fba TJ |
681 | |
682 | mixer_regs_dump(ctx); | |
d8408326 SWK |
683 | } |
684 | ||
685 | static void vp_win_reset(struct mixer_context *ctx) | |
686 | { | |
a696394c | 687 | unsigned int tries = 100; |
d8408326 | 688 | |
524c59f1 | 689 | vp_reg_write(ctx, VP_SRESET, VP_SRESET_PROCESSING); |
8646dcb8 | 690 | while (--tries) { |
d8408326 | 691 | /* waiting until VP_SRESET_PROCESSING is 0 */ |
524c59f1 | 692 | if (~vp_reg_read(ctx, VP_SRESET) & VP_SRESET_PROCESSING) |
d8408326 | 693 | break; |
02b3de43 | 694 | mdelay(10); |
d8408326 SWK |
695 | } |
696 | WARN(tries == 0, "failed to reset Video Processor\n"); | |
697 | } | |
698 | ||
cf8fc4f1 JS |
699 | static void mixer_win_reset(struct mixer_context *ctx) |
700 | { | |
cf8fc4f1 | 701 | unsigned long flags; |
cf8fc4f1 | 702 | |
524c59f1 | 703 | spin_lock_irqsave(&ctx->reg_slock, flags); |
cf8fc4f1 | 704 | |
524c59f1 | 705 | mixer_reg_writemask(ctx, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK); |
cf8fc4f1 JS |
706 | |
707 | /* set output in RGB888 mode */ | |
524c59f1 | 708 | mixer_reg_writemask(ctx, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK); |
cf8fc4f1 JS |
709 | |
710 | /* 16 beat burst in DMA */ | |
524c59f1 | 711 | mixer_reg_writemask(ctx, MXR_STATUS, MXR_STATUS_16_BURST, |
cf8fc4f1 JS |
712 | MXR_STATUS_BURST_MASK); |
713 | ||
a2cb911e | 714 | /* reset default layer priority */ |
524c59f1 | 715 | mixer_reg_write(ctx, MXR_LAYER_CFG, 0); |
cf8fc4f1 | 716 | |
2a6e4cd5 | 717 | /* set all background colors to RGB (0,0,0) */ |
524c59f1 AH |
718 | mixer_reg_write(ctx, MXR_BG_COLOR0, MXR_YCBCR_VAL(0, 128, 128)); |
719 | mixer_reg_write(ctx, MXR_BG_COLOR1, MXR_YCBCR_VAL(0, 128, 128)); | |
720 | mixer_reg_write(ctx, MXR_BG_COLOR2, MXR_YCBCR_VAL(0, 128, 128)); | |
cf8fc4f1 | 721 | |
adeb6f44 | 722 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) { |
1b8e5747 RS |
723 | /* configuration of Video Processor Registers */ |
724 | vp_win_reset(ctx); | |
524c59f1 | 725 | vp_default_filter(ctx); |
1b8e5747 | 726 | } |
cf8fc4f1 JS |
727 | |
728 | /* disable all layers */ | |
524c59f1 AH |
729 | mixer_reg_writemask(ctx, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE); |
730 | mixer_reg_writemask(ctx, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE); | |
adeb6f44 | 731 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) |
524c59f1 | 732 | mixer_reg_writemask(ctx, MXR_CFG, 0, MXR_CFG_VP_ENABLE); |
cf8fc4f1 | 733 | |
5dff6905 | 734 | /* set all source image offsets to zero */ |
524c59f1 AH |
735 | mixer_reg_write(ctx, MXR_GRAPHIC_SXY(0), 0); |
736 | mixer_reg_write(ctx, MXR_GRAPHIC_SXY(1), 0); | |
5dff6905 | 737 | |
524c59f1 | 738 | spin_unlock_irqrestore(&ctx->reg_slock, flags); |
cf8fc4f1 JS |
739 | } |
740 | ||
4551789f SP |
741 | static irqreturn_t mixer_irq_handler(int irq, void *arg) |
742 | { | |
743 | struct mixer_context *ctx = arg; | |
6a3b45ad | 744 | u32 val; |
4551789f | 745 | |
524c59f1 | 746 | spin_lock(&ctx->reg_slock); |
4551789f SP |
747 | |
748 | /* read interrupt status for handling and clearing flags for VSYNC */ | |
524c59f1 | 749 | val = mixer_reg_read(ctx, MXR_INT_STATUS); |
4551789f SP |
750 | |
751 | /* handling VSYNC */ | |
752 | if (val & MXR_INT_STATUS_VSYNC) { | |
81a464df AH |
753 | /* vsync interrupt use different bit for read and clear */ |
754 | val |= MXR_INT_CLEAR_VSYNC; | |
755 | val &= ~MXR_INT_STATUS_VSYNC; | |
756 | ||
4551789f | 757 | /* interlace scan need to check shadow register */ |
6a3b45ad AH |
758 | if (test_bit(MXR_BIT_INTERLACE, &ctx->flags) |
759 | && !mixer_is_synced(ctx)) | |
760 | goto out; | |
4551789f | 761 | |
eafd540a | 762 | drm_crtc_handle_vblank(&ctx->crtc->base); |
4551789f SP |
763 | } |
764 | ||
765 | out: | |
766 | /* clear interrupts */ | |
524c59f1 | 767 | mixer_reg_write(ctx, MXR_INT_STATUS, val); |
4551789f | 768 | |
524c59f1 | 769 | spin_unlock(&ctx->reg_slock); |
4551789f SP |
770 | |
771 | return IRQ_HANDLED; | |
772 | } | |
773 | ||
774 | static int mixer_resources_init(struct mixer_context *mixer_ctx) | |
775 | { | |
776 | struct device *dev = &mixer_ctx->pdev->dev; | |
4551789f SP |
777 | struct resource *res; |
778 | int ret; | |
779 | ||
524c59f1 | 780 | spin_lock_init(&mixer_ctx->reg_slock); |
4551789f | 781 | |
524c59f1 AH |
782 | mixer_ctx->mixer = devm_clk_get(dev, "mixer"); |
783 | if (IS_ERR(mixer_ctx->mixer)) { | |
4551789f SP |
784 | dev_err(dev, "failed to get clock 'mixer'\n"); |
785 | return -ENODEV; | |
786 | } | |
787 | ||
524c59f1 AH |
788 | mixer_ctx->hdmi = devm_clk_get(dev, "hdmi"); |
789 | if (IS_ERR(mixer_ctx->hdmi)) { | |
04427ec5 | 790 | dev_err(dev, "failed to get clock 'hdmi'\n"); |
524c59f1 | 791 | return PTR_ERR(mixer_ctx->hdmi); |
04427ec5 MS |
792 | } |
793 | ||
524c59f1 AH |
794 | mixer_ctx->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi"); |
795 | if (IS_ERR(mixer_ctx->sclk_hdmi)) { | |
4551789f SP |
796 | dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); |
797 | return -ENODEV; | |
798 | } | |
799 | res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0); | |
800 | if (res == NULL) { | |
801 | dev_err(dev, "get memory resource failed.\n"); | |
802 | return -ENXIO; | |
803 | } | |
804 | ||
524c59f1 | 805 | mixer_ctx->mixer_regs = devm_ioremap(dev, res->start, |
4551789f | 806 | resource_size(res)); |
524c59f1 | 807 | if (mixer_ctx->mixer_regs == NULL) { |
4551789f SP |
808 | dev_err(dev, "register mapping failed.\n"); |
809 | return -ENXIO; | |
810 | } | |
811 | ||
812 | res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0); | |
813 | if (res == NULL) { | |
814 | dev_err(dev, "get interrupt resource failed.\n"); | |
815 | return -ENXIO; | |
816 | } | |
817 | ||
818 | ret = devm_request_irq(dev, res->start, mixer_irq_handler, | |
819 | 0, "drm_mixer", mixer_ctx); | |
820 | if (ret) { | |
821 | dev_err(dev, "request interrupt failed.\n"); | |
822 | return ret; | |
823 | } | |
524c59f1 | 824 | mixer_ctx->irq = res->start; |
4551789f SP |
825 | |
826 | return 0; | |
827 | } | |
828 | ||
829 | static int vp_resources_init(struct mixer_context *mixer_ctx) | |
830 | { | |
831 | struct device *dev = &mixer_ctx->pdev->dev; | |
4551789f SP |
832 | struct resource *res; |
833 | ||
524c59f1 AH |
834 | mixer_ctx->vp = devm_clk_get(dev, "vp"); |
835 | if (IS_ERR(mixer_ctx->vp)) { | |
4551789f SP |
836 | dev_err(dev, "failed to get clock 'vp'\n"); |
837 | return -ENODEV; | |
838 | } | |
4551789f | 839 | |
adeb6f44 | 840 | if (test_bit(MXR_BIT_HAS_SCLK, &mixer_ctx->flags)) { |
524c59f1 AH |
841 | mixer_ctx->sclk_mixer = devm_clk_get(dev, "sclk_mixer"); |
842 | if (IS_ERR(mixer_ctx->sclk_mixer)) { | |
ff830c96 MS |
843 | dev_err(dev, "failed to get clock 'sclk_mixer'\n"); |
844 | return -ENODEV; | |
845 | } | |
524c59f1 AH |
846 | mixer_ctx->mout_mixer = devm_clk_get(dev, "mout_mixer"); |
847 | if (IS_ERR(mixer_ctx->mout_mixer)) { | |
ff830c96 MS |
848 | dev_err(dev, "failed to get clock 'mout_mixer'\n"); |
849 | return -ENODEV; | |
850 | } | |
851 | ||
524c59f1 AH |
852 | if (mixer_ctx->sclk_hdmi && mixer_ctx->mout_mixer) |
853 | clk_set_parent(mixer_ctx->mout_mixer, | |
854 | mixer_ctx->sclk_hdmi); | |
ff830c96 | 855 | } |
4551789f SP |
856 | |
857 | res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1); | |
858 | if (res == NULL) { | |
859 | dev_err(dev, "get memory resource failed.\n"); | |
860 | return -ENXIO; | |
861 | } | |
862 | ||
524c59f1 | 863 | mixer_ctx->vp_regs = devm_ioremap(dev, res->start, |
4551789f | 864 | resource_size(res)); |
524c59f1 | 865 | if (mixer_ctx->vp_regs == NULL) { |
4551789f SP |
866 | dev_err(dev, "register mapping failed.\n"); |
867 | return -ENXIO; | |
868 | } | |
869 | ||
870 | return 0; | |
871 | } | |
872 | ||
93bca243 | 873 | static int mixer_initialize(struct mixer_context *mixer_ctx, |
f37cd5e8 | 874 | struct drm_device *drm_dev) |
4551789f SP |
875 | { |
876 | int ret; | |
4551789f | 877 | |
eb88e422 | 878 | mixer_ctx->drm_dev = drm_dev; |
4551789f SP |
879 | |
880 | /* acquire resources: regs, irqs, clocks */ | |
881 | ret = mixer_resources_init(mixer_ctx); | |
882 | if (ret) { | |
6f83d208 ID |
883 | DRM_DEV_ERROR(mixer_ctx->dev, |
884 | "mixer_resources_init failed ret=%d\n", ret); | |
4551789f SP |
885 | return ret; |
886 | } | |
887 | ||
adeb6f44 | 888 | if (test_bit(MXR_BIT_VP_ENABLED, &mixer_ctx->flags)) { |
4551789f SP |
889 | /* acquire vp resources: regs, irqs, clocks */ |
890 | ret = vp_resources_init(mixer_ctx); | |
891 | if (ret) { | |
6f83d208 ID |
892 | DRM_DEV_ERROR(mixer_ctx->dev, |
893 | "vp_resources_init failed ret=%d\n", ret); | |
4551789f SP |
894 | return ret; |
895 | } | |
896 | } | |
897 | ||
07dc3678 MS |
898 | return exynos_drm_register_dma(drm_dev, mixer_ctx->dev, |
899 | &mixer_ctx->dma_priv); | |
4551789f SP |
900 | } |
901 | ||
93bca243 | 902 | static void mixer_ctx_remove(struct mixer_context *mixer_ctx) |
1055b39f | 903 | { |
07dc3678 MS |
904 | exynos_drm_unregister_dma(mixer_ctx->drm_dev, mixer_ctx->dev, |
905 | &mixer_ctx->dma_priv); | |
1055b39f ID |
906 | } |
907 | ||
93bca243 | 908 | static int mixer_enable_vblank(struct exynos_drm_crtc *crtc) |
d8408326 | 909 | { |
93bca243 | 910 | struct mixer_context *mixer_ctx = crtc->ctx; |
d8408326 | 911 | |
0df5e4ac AH |
912 | __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags); |
913 | if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) | |
f041b257 | 914 | return 0; |
d8408326 SWK |
915 | |
916 | /* enable vsync interrupt */ | |
524c59f1 AH |
917 | mixer_reg_writemask(mixer_ctx, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC); |
918 | mixer_reg_writemask(mixer_ctx, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC); | |
d8408326 SWK |
919 | |
920 | return 0; | |
921 | } | |
922 | ||
93bca243 | 923 | static void mixer_disable_vblank(struct exynos_drm_crtc *crtc) |
d8408326 | 924 | { |
93bca243 | 925 | struct mixer_context *mixer_ctx = crtc->ctx; |
d8408326 | 926 | |
0df5e4ac AH |
927 | __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags); |
928 | ||
929 | if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) | |
947710c6 | 930 | return; |
947710c6 | 931 | |
d8408326 | 932 | /* disable vsync interrupt */ |
524c59f1 AH |
933 | mixer_reg_writemask(mixer_ctx, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC); |
934 | mixer_reg_writemask(mixer_ctx, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); | |
d8408326 SWK |
935 | } |
936 | ||
3dbaab16 MS |
937 | static void mixer_atomic_begin(struct exynos_drm_crtc *crtc) |
938 | { | |
6a3b45ad | 939 | struct mixer_context *ctx = crtc->ctx; |
3dbaab16 | 940 | |
6a3b45ad | 941 | if (!test_bit(MXR_BIT_POWERED, &ctx->flags)) |
3dbaab16 MS |
942 | return; |
943 | ||
6a3b45ad AH |
944 | if (mixer_wait_for_sync(ctx)) |
945 | dev_err(ctx->dev, "timeout waiting for VSYNC\n"); | |
946 | mixer_disable_sync(ctx); | |
3dbaab16 MS |
947 | } |
948 | ||
1e1d1393 GP |
949 | static void mixer_update_plane(struct exynos_drm_crtc *crtc, |
950 | struct exynos_drm_plane *plane) | |
d8408326 | 951 | { |
93bca243 | 952 | struct mixer_context *mixer_ctx = crtc->ctx; |
d8408326 | 953 | |
6be90056 | 954 | DRM_DEV_DEBUG_KMS(mixer_ctx->dev, "win: %d\n", plane->index); |
d8408326 | 955 | |
a44652e8 | 956 | if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) |
dda9012b | 957 | return; |
dda9012b | 958 | |
5e68fef2 | 959 | if (plane->index == VP_DEFAULT_WIN) |
2eeb2e5e | 960 | vp_video_buffer(mixer_ctx, plane); |
d8408326 | 961 | else |
2eeb2e5e | 962 | mixer_graph_buffer(mixer_ctx, plane); |
d8408326 SWK |
963 | } |
964 | ||
1e1d1393 GP |
965 | static void mixer_disable_plane(struct exynos_drm_crtc *crtc, |
966 | struct exynos_drm_plane *plane) | |
d8408326 | 967 | { |
93bca243 | 968 | struct mixer_context *mixer_ctx = crtc->ctx; |
d8408326 | 969 | unsigned long flags; |
d8408326 | 970 | |
6be90056 | 971 | DRM_DEV_DEBUG_KMS(mixer_ctx->dev, "win: %d\n", plane->index); |
d8408326 | 972 | |
a44652e8 | 973 | if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) |
db43fd16 | 974 | return; |
db43fd16 | 975 | |
524c59f1 | 976 | spin_lock_irqsave(&mixer_ctx->reg_slock, flags); |
a2cb911e | 977 | mixer_cfg_layer(mixer_ctx, plane->index, 0, false); |
524c59f1 | 978 | spin_unlock_irqrestore(&mixer_ctx->reg_slock, flags); |
3dbaab16 MS |
979 | } |
980 | ||
981 | static void mixer_atomic_flush(struct exynos_drm_crtc *crtc) | |
982 | { | |
983 | struct mixer_context *mixer_ctx = crtc->ctx; | |
984 | ||
985 | if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) | |
986 | return; | |
d8408326 | 987 | |
6a3b45ad | 988 | mixer_enable_sync(mixer_ctx); |
a392276d | 989 | exynos_crtc_handle_event(crtc); |
d8408326 SWK |
990 | } |
991 | ||
11f95489 | 992 | static void mixer_atomic_enable(struct exynos_drm_crtc *crtc) |
db43fd16 | 993 | { |
3cecda03 | 994 | struct mixer_context *ctx = crtc->ctx; |
db43fd16 | 995 | |
a44652e8 | 996 | if (test_bit(MXR_BIT_POWERED, &ctx->flags)) |
db43fd16 | 997 | return; |
db43fd16 | 998 | |
af65c804 SP |
999 | pm_runtime_get_sync(ctx->dev); |
1000 | ||
a121d179 AH |
1001 | exynos_drm_pipe_clk_enable(crtc, true); |
1002 | ||
6a3b45ad | 1003 | mixer_disable_sync(ctx); |
3dbaab16 | 1004 | |
524c59f1 | 1005 | mixer_reg_writemask(ctx, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET); |
d74ed937 | 1006 | |
0df5e4ac | 1007 | if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) { |
524c59f1 AH |
1008 | mixer_reg_writemask(ctx, MXR_INT_STATUS, ~0, |
1009 | MXR_INT_CLEAR_VSYNC); | |
1010 | mixer_reg_writemask(ctx, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC); | |
0df5e4ac | 1011 | } |
db43fd16 | 1012 | mixer_win_reset(ctx); |
ccf034a9 | 1013 | |
71469944 AH |
1014 | mixer_commit(ctx); |
1015 | ||
6a3b45ad | 1016 | mixer_enable_sync(ctx); |
3dbaab16 | 1017 | |
ccf034a9 | 1018 | set_bit(MXR_BIT_POWERED, &ctx->flags); |
db43fd16 P |
1019 | } |
1020 | ||
11f95489 | 1021 | static void mixer_atomic_disable(struct exynos_drm_crtc *crtc) |
db43fd16 | 1022 | { |
3cecda03 | 1023 | struct mixer_context *ctx = crtc->ctx; |
c329f667 | 1024 | int i; |
db43fd16 | 1025 | |
a44652e8 | 1026 | if (!test_bit(MXR_BIT_POWERED, &ctx->flags)) |
b4bfa3c7 | 1027 | return; |
db43fd16 | 1028 | |
381be025 | 1029 | mixer_stop(ctx); |
c0734fba | 1030 | mixer_regs_dump(ctx); |
c329f667 JS |
1031 | |
1032 | for (i = 0; i < MIXER_WIN_NR; i++) | |
1e1d1393 | 1033 | mixer_disable_plane(crtc, &ctx->planes[i]); |
db43fd16 | 1034 | |
a121d179 AH |
1035 | exynos_drm_pipe_clk_enable(crtc, false); |
1036 | ||
ccf034a9 | 1037 | pm_runtime_put(ctx->dev); |
b4bfa3c7 | 1038 | |
ccf034a9 | 1039 | clear_bit(MXR_BIT_POWERED, &ctx->flags); |
db43fd16 P |
1040 | } |
1041 | ||
6ace38a5 AH |
1042 | static int mixer_mode_valid(struct exynos_drm_crtc *crtc, |
1043 | const struct drm_display_mode *mode) | |
f041b257 | 1044 | { |
6ace38a5 AH |
1045 | struct mixer_context *ctx = crtc->ctx; |
1046 | u32 w = mode->hdisplay, h = mode->vdisplay; | |
f041b257 | 1047 | |
6be90056 ID |
1048 | DRM_DEV_DEBUG_KMS(ctx->dev, "xres=%d, yres=%d, refresh=%d, intl=%d\n", |
1049 | w, h, mode->vrefresh, | |
1050 | !!(mode->flags & DRM_MODE_FLAG_INTERLACE)); | |
f041b257 | 1051 | |
6ace38a5 AH |
1052 | if (ctx->mxr_ver == MXR_VER_128_0_0_184) |
1053 | return MODE_OK; | |
f041b257 SP |
1054 | |
1055 | if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) || | |
6ace38a5 AH |
1056 | (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) || |
1057 | (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080)) | |
1058 | return MODE_OK; | |
f041b257 | 1059 | |
ae58c03e DD |
1060 | if ((w == 1024 && h == 768) || |
1061 | (w == 1366 && h == 768) || | |
1062 | (w == 1280 && h == 1024)) | |
0900673e AH |
1063 | return MODE_OK; |
1064 | ||
6ace38a5 | 1065 | return MODE_BAD; |
f041b257 SP |
1066 | } |
1067 | ||
acc8bf04 AH |
1068 | static bool mixer_mode_fixup(struct exynos_drm_crtc *crtc, |
1069 | const struct drm_display_mode *mode, | |
1070 | struct drm_display_mode *adjusted_mode) | |
1071 | { | |
1072 | struct mixer_context *ctx = crtc->ctx; | |
1073 | int width = mode->hdisplay, height = mode->vdisplay, i; | |
1074 | ||
5a884be5 | 1075 | static const struct { |
acc8bf04 | 1076 | int hdisplay, vdisplay, htotal, vtotal, scan_val; |
5a884be5 | 1077 | } modes[] = { |
acc8bf04 AH |
1078 | { 720, 480, 858, 525, MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD }, |
1079 | { 720, 576, 864, 625, MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD }, | |
1080 | { 1280, 720, 1650, 750, MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD }, | |
1081 | { 1920, 1080, 2200, 1125, MXR_CFG_SCAN_HD_1080 | | |
1082 | MXR_CFG_SCAN_HD } | |
1083 | }; | |
1084 | ||
1085 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | |
1086 | __set_bit(MXR_BIT_INTERLACE, &ctx->flags); | |
1087 | else | |
1088 | __clear_bit(MXR_BIT_INTERLACE, &ctx->flags); | |
1089 | ||
1090 | if (ctx->mxr_ver == MXR_VER_128_0_0_184) | |
1091 | return true; | |
1092 | ||
1093 | for (i = 0; i < ARRAY_SIZE(modes); ++i) | |
1094 | if (width <= modes[i].hdisplay && height <= modes[i].vdisplay) { | |
1095 | ctx->scan_value = modes[i].scan_val; | |
1096 | if (width < modes[i].hdisplay || | |
1097 | height < modes[i].vdisplay) { | |
1098 | adjusted_mode->hdisplay = modes[i].hdisplay; | |
1099 | adjusted_mode->hsync_start = modes[i].hdisplay; | |
1100 | adjusted_mode->hsync_end = modes[i].htotal; | |
1101 | adjusted_mode->htotal = modes[i].htotal; | |
1102 | adjusted_mode->vdisplay = modes[i].vdisplay; | |
1103 | adjusted_mode->vsync_start = modes[i].vdisplay; | |
1104 | adjusted_mode->vsync_end = modes[i].vtotal; | |
1105 | adjusted_mode->vtotal = modes[i].vtotal; | |
1106 | } | |
1107 | ||
1108 | return true; | |
1109 | } | |
1110 | ||
1111 | return false; | |
1112 | } | |
1113 | ||
f3aaf762 | 1114 | static const struct exynos_drm_crtc_ops mixer_crtc_ops = { |
11f95489 ID |
1115 | .atomic_enable = mixer_atomic_enable, |
1116 | .atomic_disable = mixer_atomic_disable, | |
d8408326 SWK |
1117 | .enable_vblank = mixer_enable_vblank, |
1118 | .disable_vblank = mixer_disable_vblank, | |
3dbaab16 | 1119 | .atomic_begin = mixer_atomic_begin, |
9cc7610a GP |
1120 | .update_plane = mixer_update_plane, |
1121 | .disable_plane = mixer_disable_plane, | |
3dbaab16 | 1122 | .atomic_flush = mixer_atomic_flush, |
6ace38a5 | 1123 | .mode_valid = mixer_mode_valid, |
acc8bf04 | 1124 | .mode_fixup = mixer_mode_fixup, |
f041b257 | 1125 | }; |
0ea6822f | 1126 | |
5e6cc1c5 | 1127 | static const struct mixer_drv_data exynos5420_mxr_drv_data = { |
def5e095 RS |
1128 | .version = MXR_VER_128_0_0_184, |
1129 | .is_vp_enabled = 0, | |
1130 | }; | |
1131 | ||
5e6cc1c5 | 1132 | static const struct mixer_drv_data exynos5250_mxr_drv_data = { |
aaf8b49e RS |
1133 | .version = MXR_VER_16_0_33_0, |
1134 | .is_vp_enabled = 0, | |
1135 | }; | |
1136 | ||
5e6cc1c5 | 1137 | static const struct mixer_drv_data exynos4212_mxr_drv_data = { |
ff830c96 MS |
1138 | .version = MXR_VER_0_0_0_16, |
1139 | .is_vp_enabled = 1, | |
1140 | }; | |
1141 | ||
5e6cc1c5 | 1142 | static const struct mixer_drv_data exynos4210_mxr_drv_data = { |
1e123441 | 1143 | .version = MXR_VER_0_0_0_16, |
1b8e5747 | 1144 | .is_vp_enabled = 1, |
ff830c96 | 1145 | .has_sclk = 1, |
1e123441 RS |
1146 | }; |
1147 | ||
5e6cc1c5 | 1148 | static const struct of_device_id mixer_match_types[] = { |
aaf8b49e | 1149 | { |
ff830c96 MS |
1150 | .compatible = "samsung,exynos4210-mixer", |
1151 | .data = &exynos4210_mxr_drv_data, | |
1152 | }, { | |
1153 | .compatible = "samsung,exynos4212-mixer", | |
1154 | .data = &exynos4212_mxr_drv_data, | |
1155 | }, { | |
aaf8b49e | 1156 | .compatible = "samsung,exynos5-mixer", |
cc57caf0 RS |
1157 | .data = &exynos5250_mxr_drv_data, |
1158 | }, { | |
1159 | .compatible = "samsung,exynos5250-mixer", | |
1160 | .data = &exynos5250_mxr_drv_data, | |
def5e095 RS |
1161 | }, { |
1162 | .compatible = "samsung,exynos5420-mixer", | |
1163 | .data = &exynos5420_mxr_drv_data, | |
1e123441 RS |
1164 | }, { |
1165 | /* end node */ | |
1166 | } | |
1167 | }; | |
39b58a39 | 1168 | MODULE_DEVICE_TABLE(of, mixer_match_types); |
1e123441 | 1169 | |
f37cd5e8 | 1170 | static int mixer_bind(struct device *dev, struct device *manager, void *data) |
d8408326 | 1171 | { |
8103ef1b | 1172 | struct mixer_context *ctx = dev_get_drvdata(dev); |
f37cd5e8 | 1173 | struct drm_device *drm_dev = data; |
7ee14cdc | 1174 | struct exynos_drm_plane *exynos_plane; |
fd2d2fc2 | 1175 | unsigned int i; |
6e2a3b66 | 1176 | int ret; |
d8408326 | 1177 | |
e2dc3f72 AB |
1178 | ret = mixer_initialize(ctx, drm_dev); |
1179 | if (ret) | |
1180 | return ret; | |
1181 | ||
fd2d2fc2 | 1182 | for (i = 0; i < MIXER_WIN_NR; i++) { |
adeb6f44 TJ |
1183 | if (i == VP_DEFAULT_WIN && !test_bit(MXR_BIT_VP_ENABLED, |
1184 | &ctx->flags)) | |
ab144201 MS |
1185 | continue; |
1186 | ||
40bdfb0a | 1187 | ret = exynos_plane_init(drm_dev, &ctx->planes[i], i, |
2c82607b | 1188 | &plane_configs[i]); |
7ee14cdc GP |
1189 | if (ret) |
1190 | return ret; | |
1191 | } | |
1192 | ||
5d3d0995 | 1193 | exynos_plane = &ctx->planes[DEFAULT_WIN]; |
7ee14cdc | 1194 | ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base, |
d644951c | 1195 | EXYNOS_DISPLAY_TYPE_HDMI, &mixer_crtc_ops, ctx); |
93bca243 | 1196 | if (IS_ERR(ctx->crtc)) { |
e2dc3f72 | 1197 | mixer_ctx_remove(ctx); |
93bca243 GP |
1198 | ret = PTR_ERR(ctx->crtc); |
1199 | goto free_ctx; | |
f37cd5e8 | 1200 | } |
d8408326 | 1201 | |
d8408326 | 1202 | return 0; |
93bca243 GP |
1203 | |
1204 | free_ctx: | |
1205 | devm_kfree(dev, ctx); | |
1206 | return ret; | |
d8408326 SWK |
1207 | } |
1208 | ||
f37cd5e8 | 1209 | static void mixer_unbind(struct device *dev, struct device *master, void *data) |
d8408326 | 1210 | { |
8103ef1b | 1211 | struct mixer_context *ctx = dev_get_drvdata(dev); |
f37cd5e8 | 1212 | |
93bca243 | 1213 | mixer_ctx_remove(ctx); |
f37cd5e8 ID |
1214 | } |
1215 | ||
1216 | static const struct component_ops mixer_component_ops = { | |
1217 | .bind = mixer_bind, | |
1218 | .unbind = mixer_unbind, | |
1219 | }; | |
1220 | ||
1221 | static int mixer_probe(struct platform_device *pdev) | |
1222 | { | |
8103ef1b | 1223 | struct device *dev = &pdev->dev; |
48f6155a | 1224 | const struct mixer_drv_data *drv; |
8103ef1b | 1225 | struct mixer_context *ctx; |
df5225bc ID |
1226 | int ret; |
1227 | ||
8103ef1b AH |
1228 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
1229 | if (!ctx) { | |
6f83d208 | 1230 | DRM_DEV_ERROR(dev, "failed to alloc mixer context.\n"); |
8103ef1b AH |
1231 | return -ENOMEM; |
1232 | } | |
1233 | ||
48f6155a | 1234 | drv = of_device_get_match_data(dev); |
8103ef1b AH |
1235 | |
1236 | ctx->pdev = pdev; | |
1237 | ctx->dev = dev; | |
8103ef1b | 1238 | ctx->mxr_ver = drv->version; |
8103ef1b | 1239 | |
adeb6f44 TJ |
1240 | if (drv->is_vp_enabled) |
1241 | __set_bit(MXR_BIT_VP_ENABLED, &ctx->flags); | |
1242 | if (drv->has_sclk) | |
1243 | __set_bit(MXR_BIT_HAS_SCLK, &ctx->flags); | |
1244 | ||
8103ef1b AH |
1245 | platform_set_drvdata(pdev, ctx); |
1246 | ||
df5225bc | 1247 | ret = component_add(&pdev->dev, &mixer_component_ops); |
86650408 AH |
1248 | if (!ret) |
1249 | pm_runtime_enable(dev); | |
df5225bc ID |
1250 | |
1251 | return ret; | |
f37cd5e8 ID |
1252 | } |
1253 | ||
1254 | static int mixer_remove(struct platform_device *pdev) | |
1255 | { | |
8103ef1b AH |
1256 | pm_runtime_disable(&pdev->dev); |
1257 | ||
df5225bc | 1258 | component_del(&pdev->dev, &mixer_component_ops); |
df5225bc | 1259 | |
d8408326 SWK |
1260 | return 0; |
1261 | } | |
1262 | ||
e0fea7e7 | 1263 | static int __maybe_unused exynos_mixer_suspend(struct device *dev) |
ccf034a9 GP |
1264 | { |
1265 | struct mixer_context *ctx = dev_get_drvdata(dev); | |
ccf034a9 | 1266 | |
524c59f1 AH |
1267 | clk_disable_unprepare(ctx->hdmi); |
1268 | clk_disable_unprepare(ctx->mixer); | |
adeb6f44 | 1269 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) { |
524c59f1 | 1270 | clk_disable_unprepare(ctx->vp); |
adeb6f44 | 1271 | if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags)) |
524c59f1 | 1272 | clk_disable_unprepare(ctx->sclk_mixer); |
ccf034a9 GP |
1273 | } |
1274 | ||
1275 | return 0; | |
1276 | } | |
1277 | ||
e0fea7e7 | 1278 | static int __maybe_unused exynos_mixer_resume(struct device *dev) |
ccf034a9 GP |
1279 | { |
1280 | struct mixer_context *ctx = dev_get_drvdata(dev); | |
ccf034a9 GP |
1281 | int ret; |
1282 | ||
524c59f1 | 1283 | ret = clk_prepare_enable(ctx->mixer); |
ccf034a9 | 1284 | if (ret < 0) { |
6f83d208 ID |
1285 | DRM_DEV_ERROR(ctx->dev, |
1286 | "Failed to prepare_enable the mixer clk [%d]\n", | |
1287 | ret); | |
ccf034a9 GP |
1288 | return ret; |
1289 | } | |
524c59f1 | 1290 | ret = clk_prepare_enable(ctx->hdmi); |
ccf034a9 | 1291 | if (ret < 0) { |
6f83d208 ID |
1292 | DRM_DEV_ERROR(dev, |
1293 | "Failed to prepare_enable the hdmi clk [%d]\n", | |
1294 | ret); | |
ccf034a9 GP |
1295 | return ret; |
1296 | } | |
adeb6f44 | 1297 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) { |
524c59f1 | 1298 | ret = clk_prepare_enable(ctx->vp); |
ccf034a9 | 1299 | if (ret < 0) { |
6f83d208 ID |
1300 | DRM_DEV_ERROR(dev, |
1301 | "Failed to prepare_enable the vp clk [%d]\n", | |
1302 | ret); | |
ccf034a9 GP |
1303 | return ret; |
1304 | } | |
adeb6f44 | 1305 | if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags)) { |
524c59f1 | 1306 | ret = clk_prepare_enable(ctx->sclk_mixer); |
ccf034a9 | 1307 | if (ret < 0) { |
6f83d208 ID |
1308 | DRM_DEV_ERROR(dev, |
1309 | "Failed to prepare_enable the " \ | |
ccf034a9 | 1310 | "sclk_mixer clk [%d]\n", |
6f83d208 | 1311 | ret); |
ccf034a9 GP |
1312 | return ret; |
1313 | } | |
1314 | } | |
1315 | } | |
1316 | ||
1317 | return 0; | |
1318 | } | |
ccf034a9 GP |
1319 | |
1320 | static const struct dev_pm_ops exynos_mixer_pm_ops = { | |
1321 | SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL) | |
7e915746 MS |
1322 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
1323 | pm_runtime_force_resume) | |
ccf034a9 GP |
1324 | }; |
1325 | ||
d8408326 SWK |
1326 | struct platform_driver mixer_driver = { |
1327 | .driver = { | |
aaf8b49e | 1328 | .name = "exynos-mixer", |
d8408326 | 1329 | .owner = THIS_MODULE, |
ccf034a9 | 1330 | .pm = &exynos_mixer_pm_ops, |
aaf8b49e | 1331 | .of_match_table = mixer_match_types, |
d8408326 SWK |
1332 | }, |
1333 | .probe = mixer_probe, | |
56550d94 | 1334 | .remove = mixer_remove, |
d8408326 | 1335 | }; |