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