1604be855SAndy Yan // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2604be855SAndy Yan /*
3604be855SAndy Yan * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
4604be855SAndy Yan * Author: Andy Yan <andy.yan@rock-chips.com>
5604be855SAndy Yan */
6604be855SAndy Yan #include <linux/bitfield.h>
7604be855SAndy Yan #include <linux/clk.h>
8604be855SAndy Yan #include <linux/component.h>
9604be855SAndy Yan #include <linux/delay.h>
10604be855SAndy Yan #include <linux/iopoll.h>
11604be855SAndy Yan #include <linux/kernel.h>
1272bd9ea3SVille Syrjälä #include <linux/media-bus-format.h>
13604be855SAndy Yan #include <linux/mfd/syscon.h>
14604be855SAndy Yan #include <linux/module.h>
15604be855SAndy Yan #include <linux/of.h>
16604be855SAndy Yan #include <linux/of_graph.h>
17604be855SAndy Yan #include <linux/platform_device.h>
18604be855SAndy Yan #include <linux/pm_runtime.h>
19604be855SAndy Yan #include <linux/regmap.h>
20604be855SAndy Yan #include <linux/swab.h>
21604be855SAndy Yan
22604be855SAndy Yan #include <drm/drm.h>
23604be855SAndy Yan #include <drm/drm_atomic.h>
24604be855SAndy Yan #include <drm/drm_atomic_uapi.h>
2590bb087fSVille Syrjälä #include <drm/drm_blend.h>
26604be855SAndy Yan #include <drm/drm_crtc.h>
2777996455SAndy Yan #include <linux/debugfs.h>
28604be855SAndy Yan #include <drm/drm_debugfs.h>
29604be855SAndy Yan #include <drm/drm_flip_work.h>
30720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
3177996455SAndy Yan #include <drm/drm_gem_framebuffer_helper.h>
32604be855SAndy Yan #include <drm/drm_probe_helper.h>
33604be855SAndy Yan #include <drm/drm_vblank.h>
34604be855SAndy Yan
35604be855SAndy Yan #include <uapi/linux/videodev2.h>
36604be855SAndy Yan
37604be855SAndy Yan #include "rockchip_drm_gem.h"
38604be855SAndy Yan #include "rockchip_drm_vop2.h"
39c66c6d7cSMichael Riesch #include "rockchip_rgb.h"
40604be855SAndy Yan
41604be855SAndy Yan /*
42604be855SAndy Yan * VOP2 architecture
43604be855SAndy Yan *
44604be855SAndy Yan +----------+ +-------------+ +-----------+
45604be855SAndy Yan | Cluster | | Sel 1 from 6| | 1 from 3 |
46604be855SAndy Yan | window0 | | Layer0 | | RGB |
47604be855SAndy Yan +----------+ +-------------+ +---------------+ +-------------+ +-----------+
48604be855SAndy Yan +----------+ +-------------+ |N from 6 layers| | |
49604be855SAndy Yan | Cluster | | Sel 1 from 6| | Overlay0 +--->| Video Port0 | +-----------+
50604be855SAndy Yan | window1 | | Layer1 | | | | | | 1 from 3 |
51604be855SAndy Yan +----------+ +-------------+ +---------------+ +-------------+ | LVDS |
52604be855SAndy Yan +----------+ +-------------+ +-----------+
53604be855SAndy Yan | Esmart | | Sel 1 from 6|
54604be855SAndy Yan | window0 | | Layer2 | +---------------+ +-------------+ +-----------+
55604be855SAndy Yan +----------+ +-------------+ |N from 6 Layers| | | +--> | 1 from 3 |
56604be855SAndy Yan +----------+ +-------------+ --------> | Overlay1 +--->| Video Port1 | | MIPI |
57604be855SAndy Yan | Esmart | | Sel 1 from 6| --------> | | | | +-----------+
58604be855SAndy Yan | Window1 | | Layer3 | +---------------+ +-------------+
59604be855SAndy Yan +----------+ +-------------+ +-----------+
60604be855SAndy Yan +----------+ +-------------+ | 1 from 3 |
61604be855SAndy Yan | Smart | | Sel 1 from 6| +---------------+ +-------------+ | HDMI |
62604be855SAndy Yan | Window0 | | Layer4 | |N from 6 Layers| | | +-----------+
63604be855SAndy Yan +----------+ +-------------+ | Overlay2 +--->| Video Port2 |
64604be855SAndy Yan +----------+ +-------------+ | | | | +-----------+
65604be855SAndy Yan | Smart | | Sel 1 from 6| +---------------+ +-------------+ | 1 from 3 |
66604be855SAndy Yan | Window1 | | Layer5 | | eDP |
67604be855SAndy Yan +----------+ +-------------+ +-----------+
68604be855SAndy Yan *
69604be855SAndy Yan */
70604be855SAndy Yan
71604be855SAndy Yan enum vop2_data_format {
72604be855SAndy Yan VOP2_FMT_ARGB8888 = 0,
73604be855SAndy Yan VOP2_FMT_RGB888,
74604be855SAndy Yan VOP2_FMT_RGB565,
75604be855SAndy Yan VOP2_FMT_XRGB101010,
76604be855SAndy Yan VOP2_FMT_YUV420SP,
77604be855SAndy Yan VOP2_FMT_YUV422SP,
78604be855SAndy Yan VOP2_FMT_YUV444SP,
79604be855SAndy Yan VOP2_FMT_YUYV422 = 8,
80604be855SAndy Yan VOP2_FMT_YUYV420,
81604be855SAndy Yan VOP2_FMT_VYUY422,
82604be855SAndy Yan VOP2_FMT_VYUY420,
83604be855SAndy Yan VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
84604be855SAndy Yan VOP2_FMT_YUV420SP_TILE_16x2,
85604be855SAndy Yan VOP2_FMT_YUV422SP_TILE_8x4,
86604be855SAndy Yan VOP2_FMT_YUV422SP_TILE_16x2,
87604be855SAndy Yan VOP2_FMT_YUV420SP_10,
88604be855SAndy Yan VOP2_FMT_YUV422SP_10,
89604be855SAndy Yan VOP2_FMT_YUV444SP_10,
90604be855SAndy Yan };
91604be855SAndy Yan
92604be855SAndy Yan enum vop2_afbc_format {
93604be855SAndy Yan VOP2_AFBC_FMT_RGB565,
94604be855SAndy Yan VOP2_AFBC_FMT_ARGB2101010 = 2,
95604be855SAndy Yan VOP2_AFBC_FMT_YUV420_10BIT,
96604be855SAndy Yan VOP2_AFBC_FMT_RGB888,
97604be855SAndy Yan VOP2_AFBC_FMT_ARGB8888,
98604be855SAndy Yan VOP2_AFBC_FMT_YUV420 = 9,
99604be855SAndy Yan VOP2_AFBC_FMT_YUV422 = 0xb,
100604be855SAndy Yan VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
101604be855SAndy Yan VOP2_AFBC_FMT_INVALID = -1,
102604be855SAndy Yan };
103604be855SAndy Yan
1042c1268e7SCristian Ciocaltea #define VOP2_MAX_DCLK_RATE 600000000
1052c1268e7SCristian Ciocaltea
10677996455SAndy Yan /*
10777996455SAndy Yan * bus-format types.
10877996455SAndy Yan */
10977996455SAndy Yan struct drm_bus_format_enum_list {
11077996455SAndy Yan int type;
11177996455SAndy Yan const char *name;
11277996455SAndy Yan };
11377996455SAndy Yan
11477996455SAndy Yan static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = {
11577996455SAndy Yan { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
11677996455SAndy Yan { MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" },
11777996455SAndy Yan { MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" },
11877996455SAndy Yan { MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" },
11977996455SAndy Yan { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" },
12077996455SAndy Yan { MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" },
12177996455SAndy Yan { MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" },
12277996455SAndy Yan { MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" },
12377996455SAndy Yan { MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" },
12477996455SAndy Yan { MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" },
12577996455SAndy Yan { MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" },
12677996455SAndy Yan { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" },
12777996455SAndy Yan { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" },
12877996455SAndy Yan { MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" },
12977996455SAndy Yan { MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" },
13077996455SAndy Yan { MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" },
13177996455SAndy Yan { MEDIA_BUS_FMT_RGB101010_1X30, "RGB101010_1X30" },
13277996455SAndy Yan { MEDIA_BUS_FMT_YUYV10_1X20, "YUYV10_1X20" },
13377996455SAndy Yan };
13477996455SAndy Yan
DRM_ENUM_NAME_FN(drm_get_bus_format_name,drm_bus_format_enum_list)13577996455SAndy Yan static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list)
13677996455SAndy Yan
13781a06f1dSAndy Yan static const struct regmap_config vop2_regmap_config;
13881a06f1dSAndy Yan
139604be855SAndy Yan static void vop2_lock(struct vop2 *vop2)
140604be855SAndy Yan {
141604be855SAndy Yan mutex_lock(&vop2->vop2_lock);
142604be855SAndy Yan }
143604be855SAndy Yan
vop2_unlock(struct vop2 * vop2)144604be855SAndy Yan static void vop2_unlock(struct vop2 *vop2)
145604be855SAndy Yan {
146604be855SAndy Yan mutex_unlock(&vop2->vop2_lock);
147604be855SAndy Yan }
148604be855SAndy Yan
vop2_win_disable(struct vop2_win * win)149604be855SAndy Yan static void vop2_win_disable(struct vop2_win *win)
150604be855SAndy Yan {
151604be855SAndy Yan vop2_win_write(win, VOP2_WIN_ENABLE, 0);
152604be855SAndy Yan
153604be855SAndy Yan if (vop2_cluster_window(win))
154604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
155604be855SAndy Yan }
156604be855SAndy Yan
vop2_get_bpp(const struct drm_format_info * format)15745ad07c7SAndy Yan static u32 vop2_get_bpp(const struct drm_format_info *format)
15845ad07c7SAndy Yan {
15945ad07c7SAndy Yan switch (format->format) {
16045ad07c7SAndy Yan case DRM_FORMAT_YUV420_8BIT:
16145ad07c7SAndy Yan return 12;
16245ad07c7SAndy Yan case DRM_FORMAT_YUV420_10BIT:
16345ad07c7SAndy Yan return 15;
16445ad07c7SAndy Yan case DRM_FORMAT_VUY101010:
16545ad07c7SAndy Yan return 30;
16645ad07c7SAndy Yan default:
16745ad07c7SAndy Yan return drm_format_info_bpp(format, 0);
16845ad07c7SAndy Yan }
16945ad07c7SAndy Yan }
17045ad07c7SAndy Yan
vop2_convert_format(u32 format)171604be855SAndy Yan static enum vop2_data_format vop2_convert_format(u32 format)
172604be855SAndy Yan {
173604be855SAndy Yan switch (format) {
174bfd8a5c2SAndy Yan case DRM_FORMAT_XRGB2101010:
175bfd8a5c2SAndy Yan case DRM_FORMAT_ARGB2101010:
176bfd8a5c2SAndy Yan case DRM_FORMAT_XBGR2101010:
177bfd8a5c2SAndy Yan case DRM_FORMAT_ABGR2101010:
178bfd8a5c2SAndy Yan return VOP2_FMT_XRGB101010;
179604be855SAndy Yan case DRM_FORMAT_XRGB8888:
180604be855SAndy Yan case DRM_FORMAT_ARGB8888:
181604be855SAndy Yan case DRM_FORMAT_XBGR8888:
182604be855SAndy Yan case DRM_FORMAT_ABGR8888:
183604be855SAndy Yan return VOP2_FMT_ARGB8888;
184604be855SAndy Yan case DRM_FORMAT_RGB888:
185604be855SAndy Yan case DRM_FORMAT_BGR888:
186604be855SAndy Yan return VOP2_FMT_RGB888;
187604be855SAndy Yan case DRM_FORMAT_RGB565:
188604be855SAndy Yan case DRM_FORMAT_BGR565:
189604be855SAndy Yan return VOP2_FMT_RGB565;
190604be855SAndy Yan case DRM_FORMAT_NV12:
191bfd8a5c2SAndy Yan case DRM_FORMAT_NV21:
192bfd8a5c2SAndy Yan case DRM_FORMAT_YUV420_8BIT:
193604be855SAndy Yan return VOP2_FMT_YUV420SP;
194bfd8a5c2SAndy Yan case DRM_FORMAT_NV15:
195bfd8a5c2SAndy Yan case DRM_FORMAT_YUV420_10BIT:
196bfd8a5c2SAndy Yan return VOP2_FMT_YUV420SP_10;
197604be855SAndy Yan case DRM_FORMAT_NV16:
198bfd8a5c2SAndy Yan case DRM_FORMAT_NV61:
199604be855SAndy Yan return VOP2_FMT_YUV422SP;
2005fc6aa7dSJonas Karlman case DRM_FORMAT_NV20:
201bfd8a5c2SAndy Yan case DRM_FORMAT_Y210:
202bfd8a5c2SAndy Yan return VOP2_FMT_YUV422SP_10;
203604be855SAndy Yan case DRM_FORMAT_NV24:
204bfd8a5c2SAndy Yan case DRM_FORMAT_NV42:
205604be855SAndy Yan return VOP2_FMT_YUV444SP;
2065fc6aa7dSJonas Karlman case DRM_FORMAT_NV30:
2075fc6aa7dSJonas Karlman return VOP2_FMT_YUV444SP_10;
208604be855SAndy Yan case DRM_FORMAT_YUYV:
209604be855SAndy Yan case DRM_FORMAT_YVYU:
210604be855SAndy Yan return VOP2_FMT_VYUY422;
211604be855SAndy Yan case DRM_FORMAT_VYUY:
212604be855SAndy Yan case DRM_FORMAT_UYVY:
213604be855SAndy Yan return VOP2_FMT_YUYV422;
214604be855SAndy Yan default:
215604be855SAndy Yan DRM_ERROR("unsupported format[%08x]\n", format);
216604be855SAndy Yan return -EINVAL;
217604be855SAndy Yan }
218604be855SAndy Yan }
219604be855SAndy Yan
vop2_convert_afbc_format(u32 format)220604be855SAndy Yan static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
221604be855SAndy Yan {
222604be855SAndy Yan switch (format) {
223bfd8a5c2SAndy Yan case DRM_FORMAT_XRGB2101010:
224bfd8a5c2SAndy Yan case DRM_FORMAT_ARGB2101010:
225bfd8a5c2SAndy Yan case DRM_FORMAT_XBGR2101010:
226bfd8a5c2SAndy Yan case DRM_FORMAT_ABGR2101010:
227bfd8a5c2SAndy Yan return VOP2_AFBC_FMT_ARGB2101010;
228604be855SAndy Yan case DRM_FORMAT_XRGB8888:
229604be855SAndy Yan case DRM_FORMAT_ARGB8888:
230604be855SAndy Yan case DRM_FORMAT_XBGR8888:
231604be855SAndy Yan case DRM_FORMAT_ABGR8888:
232604be855SAndy Yan return VOP2_AFBC_FMT_ARGB8888;
233604be855SAndy Yan case DRM_FORMAT_RGB888:
234604be855SAndy Yan case DRM_FORMAT_BGR888:
235604be855SAndy Yan return VOP2_AFBC_FMT_RGB888;
236604be855SAndy Yan case DRM_FORMAT_RGB565:
237604be855SAndy Yan case DRM_FORMAT_BGR565:
238604be855SAndy Yan return VOP2_AFBC_FMT_RGB565;
239bfd8a5c2SAndy Yan case DRM_FORMAT_YUV420_8BIT:
240bfd8a5c2SAndy Yan return VOP2_AFBC_FMT_YUV420;
241bfd8a5c2SAndy Yan case DRM_FORMAT_YUV420_10BIT:
242bfd8a5c2SAndy Yan return VOP2_AFBC_FMT_YUV420_10BIT;
243bfd8a5c2SAndy Yan case DRM_FORMAT_YVYU:
244bfd8a5c2SAndy Yan case DRM_FORMAT_YUYV:
245bfd8a5c2SAndy Yan case DRM_FORMAT_VYUY:
246bfd8a5c2SAndy Yan case DRM_FORMAT_UYVY:
247bfd8a5c2SAndy Yan return VOP2_AFBC_FMT_YUV422;
248bfd8a5c2SAndy Yan case DRM_FORMAT_Y210:
249bfd8a5c2SAndy Yan return VOP2_AFBC_FMT_YUV422_10BIT;
250604be855SAndy Yan default:
251604be855SAndy Yan return VOP2_AFBC_FMT_INVALID;
252604be855SAndy Yan }
253604be855SAndy Yan
254604be855SAndy Yan return VOP2_AFBC_FMT_INVALID;
255604be855SAndy Yan }
256604be855SAndy Yan
vop2_win_rb_swap(u32 format)257604be855SAndy Yan static bool vop2_win_rb_swap(u32 format)
258604be855SAndy Yan {
259604be855SAndy Yan switch (format) {
260bfd8a5c2SAndy Yan case DRM_FORMAT_XBGR2101010:
261bfd8a5c2SAndy Yan case DRM_FORMAT_ABGR2101010:
262604be855SAndy Yan case DRM_FORMAT_XBGR8888:
263604be855SAndy Yan case DRM_FORMAT_ABGR8888:
264604be855SAndy Yan case DRM_FORMAT_BGR888:
265604be855SAndy Yan case DRM_FORMAT_BGR565:
266604be855SAndy Yan return true;
267604be855SAndy Yan default:
268604be855SAndy Yan return false;
269604be855SAndy Yan }
270604be855SAndy Yan }
271604be855SAndy Yan
vop2_afbc_uv_swap(u32 format)272604be855SAndy Yan static bool vop2_afbc_uv_swap(u32 format)
273604be855SAndy Yan {
274bfd8a5c2SAndy Yan switch (format) {
275bfd8a5c2SAndy Yan case DRM_FORMAT_YUYV:
276bfd8a5c2SAndy Yan case DRM_FORMAT_Y210:
277bfd8a5c2SAndy Yan case DRM_FORMAT_YUV420_8BIT:
278bfd8a5c2SAndy Yan case DRM_FORMAT_YUV420_10BIT:
279bfd8a5c2SAndy Yan return true;
280bfd8a5c2SAndy Yan default:
281604be855SAndy Yan return false;
282604be855SAndy Yan }
283bfd8a5c2SAndy Yan }
284604be855SAndy Yan
vop2_win_uv_swap(u32 format)285604be855SAndy Yan static bool vop2_win_uv_swap(u32 format)
286604be855SAndy Yan {
287604be855SAndy Yan switch (format) {
288604be855SAndy Yan case DRM_FORMAT_NV12:
289604be855SAndy Yan case DRM_FORMAT_NV16:
290604be855SAndy Yan case DRM_FORMAT_NV24:
291bfd8a5c2SAndy Yan case DRM_FORMAT_NV15:
2925fc6aa7dSJonas Karlman case DRM_FORMAT_NV20:
2935fc6aa7dSJonas Karlman case DRM_FORMAT_NV30:
294bfd8a5c2SAndy Yan case DRM_FORMAT_YUYV:
295bfd8a5c2SAndy Yan case DRM_FORMAT_UYVY:
296604be855SAndy Yan return true;
297604be855SAndy Yan default:
298604be855SAndy Yan return false;
299604be855SAndy Yan }
300604be855SAndy Yan }
301604be855SAndy Yan
vop2_win_dither_up(u32 format)302604be855SAndy Yan static bool vop2_win_dither_up(u32 format)
303604be855SAndy Yan {
304604be855SAndy Yan switch (format) {
305604be855SAndy Yan case DRM_FORMAT_BGR565:
306604be855SAndy Yan case DRM_FORMAT_RGB565:
307604be855SAndy Yan return true;
308604be855SAndy Yan default:
309604be855SAndy Yan return false;
310604be855SAndy Yan }
311604be855SAndy Yan }
312604be855SAndy Yan
vop2_output_uv_swap(u32 bus_format,u32 output_mode)313604be855SAndy Yan static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
314604be855SAndy Yan {
315604be855SAndy Yan /*
316604be855SAndy Yan * FIXME:
317604be855SAndy Yan *
318604be855SAndy Yan * There is no media type for YUV444 output,
319604be855SAndy Yan * so when out_mode is AAAA or P888, assume output is YUV444 on
320604be855SAndy Yan * yuv format.
321604be855SAndy Yan *
322604be855SAndy Yan * From H/W testing, YUV444 mode need a rb swap.
323604be855SAndy Yan */
324604be855SAndy Yan if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
325604be855SAndy Yan bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
326604be855SAndy Yan bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
327604be855SAndy Yan bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
328604be855SAndy Yan ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
329604be855SAndy Yan bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
330604be855SAndy Yan (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
331604be855SAndy Yan output_mode == ROCKCHIP_OUT_MODE_P888)))
332604be855SAndy Yan return true;
333604be855SAndy Yan else
334604be855SAndy Yan return false;
335604be855SAndy Yan }
336604be855SAndy Yan
vop2_output_rg_swap(struct vop2 * vop2,u32 bus_format)3375a028e8fSAndy Yan static bool vop2_output_rg_swap(struct vop2 *vop2, u32 bus_format)
3385a028e8fSAndy Yan {
339301618edSAndy Yan if (vop2->version == VOP_VERSION_RK3588) {
3405a028e8fSAndy Yan if (bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
3415a028e8fSAndy Yan bus_format == MEDIA_BUS_FMT_YUV10_1X30)
3425a028e8fSAndy Yan return true;
3435a028e8fSAndy Yan }
3445a028e8fSAndy Yan
3455a028e8fSAndy Yan return false;
3465a028e8fSAndy Yan }
3475a028e8fSAndy Yan
is_yuv_output(u32 bus_format)348604be855SAndy Yan static bool is_yuv_output(u32 bus_format)
349604be855SAndy Yan {
350604be855SAndy Yan switch (bus_format) {
351604be855SAndy Yan case MEDIA_BUS_FMT_YUV8_1X24:
352604be855SAndy Yan case MEDIA_BUS_FMT_YUV10_1X30:
353604be855SAndy Yan case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
354604be855SAndy Yan case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
355604be855SAndy Yan case MEDIA_BUS_FMT_YUYV8_2X8:
356604be855SAndy Yan case MEDIA_BUS_FMT_YVYU8_2X8:
357604be855SAndy Yan case MEDIA_BUS_FMT_UYVY8_2X8:
358604be855SAndy Yan case MEDIA_BUS_FMT_VYUY8_2X8:
359604be855SAndy Yan case MEDIA_BUS_FMT_YUYV8_1X16:
360604be855SAndy Yan case MEDIA_BUS_FMT_YVYU8_1X16:
361604be855SAndy Yan case MEDIA_BUS_FMT_UYVY8_1X16:
362604be855SAndy Yan case MEDIA_BUS_FMT_VYUY8_1X16:
363604be855SAndy Yan return true;
364604be855SAndy Yan default:
365604be855SAndy Yan return false;
366604be855SAndy Yan }
367604be855SAndy Yan }
368604be855SAndy Yan
rockchip_afbc(struct drm_plane * plane,u64 modifier)369604be855SAndy Yan static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
370604be855SAndy Yan {
371604be855SAndy Yan int i;
372604be855SAndy Yan
373604be855SAndy Yan if (modifier == DRM_FORMAT_MOD_LINEAR)
374604be855SAndy Yan return false;
375604be855SAndy Yan
376604be855SAndy Yan for (i = 0 ; i < plane->modifier_count; i++)
377604be855SAndy Yan if (plane->modifiers[i] == modifier)
378604be855SAndy Yan return true;
379604be855SAndy Yan
380604be855SAndy Yan return false;
381604be855SAndy Yan }
382604be855SAndy Yan
rockchip_vop2_mod_supported(struct drm_plane * plane,u32 format,u64 modifier)383604be855SAndy Yan static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
384604be855SAndy Yan u64 modifier)
385604be855SAndy Yan {
386604be855SAndy Yan struct vop2_win *win = to_vop2_win(plane);
387604be855SAndy Yan struct vop2 *vop2 = win->vop2;
388604be855SAndy Yan
389604be855SAndy Yan if (modifier == DRM_FORMAT_MOD_INVALID)
390604be855SAndy Yan return false;
391604be855SAndy Yan
392301618edSAndy Yan if (vop2->version == VOP_VERSION_RK3568) {
393df063c0bSAndy Yan if (vop2_cluster_window(win)) {
394df063c0bSAndy Yan if (modifier == DRM_FORMAT_MOD_LINEAR) {
395df063c0bSAndy Yan drm_dbg_kms(vop2->drm,
396df063c0bSAndy Yan "Cluster window only supports format with afbc\n");
397df063c0bSAndy Yan return false;
398df063c0bSAndy Yan }
399df063c0bSAndy Yan }
400df063c0bSAndy Yan }
401df063c0bSAndy Yan
4027e8a56c7SAndy Yan if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) {
403301618edSAndy Yan if (vop2->version == VOP_VERSION_RK3588) {
4047e8a56c7SAndy Yan if (!rockchip_afbc(plane, modifier)) {
4057e8a56c7SAndy Yan drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n");
4067e8a56c7SAndy Yan return false;
4077e8a56c7SAndy Yan }
4087e8a56c7SAndy Yan }
4097e8a56c7SAndy Yan }
4107e8a56c7SAndy Yan
411604be855SAndy Yan if (modifier == DRM_FORMAT_MOD_LINEAR)
412604be855SAndy Yan return true;
413604be855SAndy Yan
414604be855SAndy Yan if (!rockchip_afbc(plane, modifier)) {
415eb23cffdSMichael Tretter drm_dbg_kms(vop2->drm, "Unsupported format modifier 0x%llx\n",
416604be855SAndy Yan modifier);
417604be855SAndy Yan
418604be855SAndy Yan return false;
419604be855SAndy Yan }
420604be855SAndy Yan
421604be855SAndy Yan return vop2_convert_afbc_format(format) >= 0;
422604be855SAndy Yan }
423604be855SAndy Yan
424bebad6bdSAndy Yan /*
425bebad6bdSAndy Yan * 0: Full mode, 16 lines for one tail
426bebad6bdSAndy Yan * 1: half block mode, 8 lines one tail
427bebad6bdSAndy Yan */
vop2_half_block_enable(struct drm_plane_state * pstate)428bebad6bdSAndy Yan static bool vop2_half_block_enable(struct drm_plane_state *pstate)
429bebad6bdSAndy Yan {
430bebad6bdSAndy Yan if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90))
431bebad6bdSAndy Yan return false;
432bebad6bdSAndy Yan else
433bebad6bdSAndy Yan return true;
434bebad6bdSAndy Yan }
435bebad6bdSAndy Yan
vop2_afbc_transform_offset(struct drm_plane_state * pstate,bool afbc_half_block_en)436604be855SAndy Yan static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
437604be855SAndy Yan bool afbc_half_block_en)
438604be855SAndy Yan {
439604be855SAndy Yan struct drm_rect *src = &pstate->src;
440604be855SAndy Yan struct drm_framebuffer *fb = pstate->fb;
44145ad07c7SAndy Yan u32 bpp = vop2_get_bpp(fb->format);
442604be855SAndy Yan u32 vir_width = (fb->pitches[0] << 3) / bpp;
443604be855SAndy Yan u32 width = drm_rect_width(src) >> 16;
444604be855SAndy Yan u32 height = drm_rect_height(src) >> 16;
445604be855SAndy Yan u32 act_xoffset = src->x1 >> 16;
446604be855SAndy Yan u32 act_yoffset = src->y1 >> 16;
447604be855SAndy Yan u32 align16_crop = 0;
448604be855SAndy Yan u32 align64_crop = 0;
449604be855SAndy Yan u32 height_tmp;
450604be855SAndy Yan u8 tx, ty;
451604be855SAndy Yan u8 bottom_crop_line_num = 0;
452604be855SAndy Yan
453604be855SAndy Yan /* 16 pixel align */
454604be855SAndy Yan if (height & 0xf)
455604be855SAndy Yan align16_crop = 16 - (height & 0xf);
456604be855SAndy Yan
457604be855SAndy Yan height_tmp = height + align16_crop;
458604be855SAndy Yan
459604be855SAndy Yan /* 64 pixel align */
460604be855SAndy Yan if (height_tmp & 0x3f)
461604be855SAndy Yan align64_crop = 64 - (height_tmp & 0x3f);
462604be855SAndy Yan
463604be855SAndy Yan bottom_crop_line_num = align16_crop + align64_crop;
464604be855SAndy Yan
465604be855SAndy Yan switch (pstate->rotation &
466604be855SAndy Yan (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
467604be855SAndy Yan DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
468604be855SAndy Yan case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
469604be855SAndy Yan tx = 16 - ((act_xoffset + width) & 0xf);
470604be855SAndy Yan ty = bottom_crop_line_num - act_yoffset;
471604be855SAndy Yan break;
472604be855SAndy Yan case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
473604be855SAndy Yan tx = bottom_crop_line_num - act_yoffset;
474604be855SAndy Yan ty = vir_width - width - act_xoffset;
475604be855SAndy Yan break;
476604be855SAndy Yan case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
477604be855SAndy Yan tx = act_yoffset;
478604be855SAndy Yan ty = act_xoffset;
479604be855SAndy Yan break;
480604be855SAndy Yan case DRM_MODE_REFLECT_X:
481604be855SAndy Yan tx = 16 - ((act_xoffset + width) & 0xf);
482604be855SAndy Yan ty = act_yoffset;
483604be855SAndy Yan break;
484604be855SAndy Yan case DRM_MODE_REFLECT_Y:
485604be855SAndy Yan tx = act_xoffset;
486604be855SAndy Yan ty = bottom_crop_line_num - act_yoffset;
487604be855SAndy Yan break;
488604be855SAndy Yan case DRM_MODE_ROTATE_90:
489604be855SAndy Yan tx = bottom_crop_line_num - act_yoffset;
490604be855SAndy Yan ty = act_xoffset;
491604be855SAndy Yan break;
492604be855SAndy Yan case DRM_MODE_ROTATE_270:
493604be855SAndy Yan tx = act_yoffset;
494604be855SAndy Yan ty = vir_width - width - act_xoffset;
495604be855SAndy Yan break;
496604be855SAndy Yan case 0:
497604be855SAndy Yan tx = act_xoffset;
498604be855SAndy Yan ty = act_yoffset;
499604be855SAndy Yan break;
500604be855SAndy Yan }
501604be855SAndy Yan
502604be855SAndy Yan if (afbc_half_block_en)
503604be855SAndy Yan ty &= 0x7f;
504604be855SAndy Yan
505604be855SAndy Yan #define TRANSFORM_XOFFSET GENMASK(7, 0)
506604be855SAndy Yan #define TRANSFORM_YOFFSET GENMASK(23, 16)
507604be855SAndy Yan return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
508604be855SAndy Yan FIELD_PREP(TRANSFORM_YOFFSET, ty);
509604be855SAndy Yan }
510604be855SAndy Yan
511604be855SAndy Yan /*
512604be855SAndy Yan * A Cluster window has 2048 x 16 line buffer, which can
513604be855SAndy Yan * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
514604be855SAndy Yan * for Cluster_lb_mode register:
515604be855SAndy Yan * 0: half mode, for plane input width range 2048 ~ 4096
516604be855SAndy Yan * 1: half mode, for cluster work at 2 * 2048 plane mode
517604be855SAndy Yan * 2: half mode, for rotate_90/270 mode
518604be855SAndy Yan *
519604be855SAndy Yan */
vop2_get_cluster_lb_mode(struct vop2_win * win,struct drm_plane_state * pstate)520604be855SAndy Yan static int vop2_get_cluster_lb_mode(struct vop2_win *win,
521604be855SAndy Yan struct drm_plane_state *pstate)
522604be855SAndy Yan {
523604be855SAndy Yan if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
524604be855SAndy Yan (pstate->rotation & DRM_MODE_ROTATE_90))
525604be855SAndy Yan return 2;
526604be855SAndy Yan else
527604be855SAndy Yan return 0;
528604be855SAndy Yan }
529604be855SAndy Yan
vop2_scale_factor(u32 src,u32 dst)530604be855SAndy Yan static u16 vop2_scale_factor(u32 src, u32 dst)
531604be855SAndy Yan {
532604be855SAndy Yan u32 fac;
533604be855SAndy Yan int shift;
534604be855SAndy Yan
535604be855SAndy Yan if (src == dst)
536604be855SAndy Yan return 0;
537604be855SAndy Yan
538604be855SAndy Yan if (dst < 2)
539604be855SAndy Yan return U16_MAX;
540604be855SAndy Yan
541604be855SAndy Yan if (src < 2)
542604be855SAndy Yan return 0;
543604be855SAndy Yan
544604be855SAndy Yan if (src > dst)
545604be855SAndy Yan shift = 12;
546604be855SAndy Yan else
547604be855SAndy Yan shift = 16;
548604be855SAndy Yan
549604be855SAndy Yan src--;
550604be855SAndy Yan dst--;
551604be855SAndy Yan
552604be855SAndy Yan fac = DIV_ROUND_UP(src << shift, dst) - 1;
553604be855SAndy Yan
554604be855SAndy Yan if (fac > U16_MAX)
555604be855SAndy Yan return U16_MAX;
556604be855SAndy Yan
557604be855SAndy Yan return fac;
558604be855SAndy Yan }
559604be855SAndy Yan
vop2_setup_scale(struct vop2 * vop2,const struct vop2_win * win,u32 src_w,u32 src_h,u32 dst_w,u32 dst_h,u32 pixel_format)560604be855SAndy Yan static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
561604be855SAndy Yan u32 src_w, u32 src_h, u32 dst_w,
562604be855SAndy Yan u32 dst_h, u32 pixel_format)
563604be855SAndy Yan {
564604be855SAndy Yan const struct drm_format_info *info;
565604be855SAndy Yan u16 hor_scl_mode, ver_scl_mode;
566604be855SAndy Yan u16 hscl_filter_mode, vscl_filter_mode;
567e80c219fSDetlev Casanova uint16_t cbcr_src_w = src_w;
568e80c219fSDetlev Casanova uint16_t cbcr_src_h = src_h;
569604be855SAndy Yan u8 gt2 = 0;
570604be855SAndy Yan u8 gt4 = 0;
571604be855SAndy Yan u32 val;
572604be855SAndy Yan
573604be855SAndy Yan info = drm_format_info(pixel_format);
574604be855SAndy Yan
575604be855SAndy Yan if (src_h >= (4 * dst_h)) {
576604be855SAndy Yan gt4 = 1;
577604be855SAndy Yan src_h >>= 2;
578604be855SAndy Yan } else if (src_h >= (2 * dst_h)) {
579604be855SAndy Yan gt2 = 1;
580604be855SAndy Yan src_h >>= 1;
581604be855SAndy Yan }
582604be855SAndy Yan
583604be855SAndy Yan hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
584604be855SAndy Yan ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
585604be855SAndy Yan
586604be855SAndy Yan if (hor_scl_mode == SCALE_UP)
587604be855SAndy Yan hscl_filter_mode = VOP2_SCALE_UP_BIC;
588604be855SAndy Yan else
589604be855SAndy Yan hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
590604be855SAndy Yan
591604be855SAndy Yan if (ver_scl_mode == SCALE_UP)
592604be855SAndy Yan vscl_filter_mode = VOP2_SCALE_UP_BIL;
593604be855SAndy Yan else
594604be855SAndy Yan vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
595604be855SAndy Yan
596604be855SAndy Yan /*
597604be855SAndy Yan * RK3568 VOP Esmart/Smart dsp_w should be even pixel
598604be855SAndy Yan * at scale down mode
599604be855SAndy Yan */
600604be855SAndy Yan if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
601604be855SAndy Yan if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
602604be855SAndy Yan drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
603604be855SAndy Yan win->data->name, dst_w);
604604be855SAndy Yan dst_w++;
605604be855SAndy Yan }
606604be855SAndy Yan }
607604be855SAndy Yan
608604be855SAndy Yan val = vop2_scale_factor(src_w, dst_w);
609604be855SAndy Yan vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
610604be855SAndy Yan val = vop2_scale_factor(src_h, dst_h);
611604be855SAndy Yan vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
612604be855SAndy Yan
613604be855SAndy Yan vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
614604be855SAndy Yan vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
615604be855SAndy Yan
616604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
617604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
618604be855SAndy Yan
619604be855SAndy Yan if (vop2_cluster_window(win))
620604be855SAndy Yan return;
621604be855SAndy Yan
622604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
623604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
624604be855SAndy Yan
625604be855SAndy Yan if (info->is_yuv) {
626e80c219fSDetlev Casanova cbcr_src_w /= info->hsub;
627e80c219fSDetlev Casanova cbcr_src_h /= info->vsub;
628604be855SAndy Yan
629604be855SAndy Yan gt4 = 0;
630604be855SAndy Yan gt2 = 0;
631604be855SAndy Yan
632e80c219fSDetlev Casanova if (cbcr_src_h >= (4 * dst_h)) {
633604be855SAndy Yan gt4 = 1;
634e80c219fSDetlev Casanova cbcr_src_h >>= 2;
635e80c219fSDetlev Casanova } else if (cbcr_src_h >= (2 * dst_h)) {
636604be855SAndy Yan gt2 = 1;
637e80c219fSDetlev Casanova cbcr_src_h >>= 1;
638604be855SAndy Yan }
639604be855SAndy Yan
640e80c219fSDetlev Casanova hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
641e80c219fSDetlev Casanova ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
642604be855SAndy Yan
643e80c219fSDetlev Casanova val = vop2_scale_factor(cbcr_src_w, dst_w);
644604be855SAndy Yan vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
645604be855SAndy Yan
646e80c219fSDetlev Casanova val = vop2_scale_factor(cbcr_src_h, dst_h);
647604be855SAndy Yan vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
648604be855SAndy Yan
649604be855SAndy Yan vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
650604be855SAndy Yan vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
651604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
652604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
653604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
654604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
655604be855SAndy Yan }
656604be855SAndy Yan }
657604be855SAndy Yan
vop2_convert_csc_mode(int csc_mode)658604be855SAndy Yan static int vop2_convert_csc_mode(int csc_mode)
659604be855SAndy Yan {
660604be855SAndy Yan switch (csc_mode) {
661604be855SAndy Yan case V4L2_COLORSPACE_SMPTE170M:
662604be855SAndy Yan case V4L2_COLORSPACE_470_SYSTEM_M:
663604be855SAndy Yan case V4L2_COLORSPACE_470_SYSTEM_BG:
664604be855SAndy Yan return CSC_BT601L;
665604be855SAndy Yan case V4L2_COLORSPACE_REC709:
666604be855SAndy Yan case V4L2_COLORSPACE_SMPTE240M:
667604be855SAndy Yan case V4L2_COLORSPACE_DEFAULT:
668604be855SAndy Yan return CSC_BT709L;
669604be855SAndy Yan case V4L2_COLORSPACE_JPEG:
670604be855SAndy Yan return CSC_BT601F;
671604be855SAndy Yan case V4L2_COLORSPACE_BT2020:
672604be855SAndy Yan return CSC_BT2020;
673604be855SAndy Yan default:
674604be855SAndy Yan return CSC_BT709L;
675604be855SAndy Yan }
676604be855SAndy Yan }
677604be855SAndy Yan
678604be855SAndy Yan /*
679604be855SAndy Yan * colorspace path:
680604be855SAndy Yan * Input Win csc Output
681604be855SAndy Yan * 1. YUV(2020) --> Y2R->2020To709->R2Y --> YUV_OUTPUT(601/709)
682604be855SAndy Yan * RGB --> R2Y __/
683604be855SAndy Yan *
684604be855SAndy Yan * 2. YUV(2020) --> bypasss --> YUV_OUTPUT(2020)
685604be855SAndy Yan * RGB --> 709To2020->R2Y __/
686604be855SAndy Yan *
687604be855SAndy Yan * 3. YUV(2020) --> Y2R->2020To709 --> RGB_OUTPUT(709)
688604be855SAndy Yan * RGB --> R2Y __/
689604be855SAndy Yan *
690604be855SAndy Yan * 4. YUV(601/709)-> Y2R->709To2020->R2Y --> YUV_OUTPUT(2020)
691604be855SAndy Yan * RGB --> 709To2020->R2Y __/
692604be855SAndy Yan *
693604be855SAndy Yan * 5. YUV(601/709)-> bypass --> YUV_OUTPUT(709)
694604be855SAndy Yan * RGB --> R2Y __/
695604be855SAndy Yan *
696604be855SAndy Yan * 6. YUV(601/709)-> bypass --> YUV_OUTPUT(601)
697604be855SAndy Yan * RGB --> R2Y(601) __/
698604be855SAndy Yan *
699604be855SAndy Yan * 7. YUV --> Y2R(709) --> RGB_OUTPUT(709)
700604be855SAndy Yan * RGB --> bypass __/
701604be855SAndy Yan *
702604be855SAndy Yan * 8. RGB --> 709To2020->R2Y --> YUV_OUTPUT(2020)
703604be855SAndy Yan *
704604be855SAndy Yan * 9. RGB --> R2Y(709) --> YUV_OUTPUT(709)
705604be855SAndy Yan *
706604be855SAndy Yan * 10. RGB --> R2Y(601) --> YUV_OUTPUT(601)
707604be855SAndy Yan *
708604be855SAndy Yan * 11. RGB --> bypass --> RGB_OUTPUT(709)
709604be855SAndy Yan */
710604be855SAndy Yan
vop2_setup_csc_mode(struct vop2_video_port * vp,struct vop2_win * win,struct drm_plane_state * pstate)711604be855SAndy Yan static void vop2_setup_csc_mode(struct vop2_video_port *vp,
712604be855SAndy Yan struct vop2_win *win,
713604be855SAndy Yan struct drm_plane_state *pstate)
714604be855SAndy Yan {
715604be855SAndy Yan struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
716604be855SAndy Yan int is_input_yuv = pstate->fb->format->is_yuv;
717604be855SAndy Yan int is_output_yuv = is_yuv_output(vcstate->bus_format);
718604be855SAndy Yan int input_csc = V4L2_COLORSPACE_DEFAULT;
719604be855SAndy Yan int output_csc = vcstate->color_space;
720604be855SAndy Yan bool r2y_en, y2r_en;
721604be855SAndy Yan int csc_mode;
722604be855SAndy Yan
723604be855SAndy Yan if (is_input_yuv && !is_output_yuv) {
724604be855SAndy Yan y2r_en = true;
725604be855SAndy Yan r2y_en = false;
726604be855SAndy Yan csc_mode = vop2_convert_csc_mode(input_csc);
727604be855SAndy Yan } else if (!is_input_yuv && is_output_yuv) {
728604be855SAndy Yan y2r_en = false;
729604be855SAndy Yan r2y_en = true;
730604be855SAndy Yan csc_mode = vop2_convert_csc_mode(output_csc);
731604be855SAndy Yan } else {
732604be855SAndy Yan y2r_en = false;
733604be855SAndy Yan r2y_en = false;
734604be855SAndy Yan csc_mode = false;
735604be855SAndy Yan }
736604be855SAndy Yan
737604be855SAndy Yan vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
738604be855SAndy Yan vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
739604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
740604be855SAndy Yan }
741604be855SAndy Yan
vop2_crtc_enable_irq(struct vop2_video_port * vp,u32 irq)742604be855SAndy Yan static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
743604be855SAndy Yan {
744604be855SAndy Yan struct vop2 *vop2 = vp->vop2;
745604be855SAndy Yan
746604be855SAndy Yan vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
747604be855SAndy Yan vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
748604be855SAndy Yan }
749604be855SAndy Yan
vop2_crtc_disable_irq(struct vop2_video_port * vp,u32 irq)750604be855SAndy Yan static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
751604be855SAndy Yan {
752604be855SAndy Yan struct vop2 *vop2 = vp->vop2;
753604be855SAndy Yan
754604be855SAndy Yan vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
755604be855SAndy Yan }
756604be855SAndy Yan
vop2_core_clks_prepare_enable(struct vop2 * vop2)757604be855SAndy Yan static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
758604be855SAndy Yan {
759604be855SAndy Yan int ret;
760604be855SAndy Yan
761604be855SAndy Yan ret = clk_prepare_enable(vop2->hclk);
762604be855SAndy Yan if (ret < 0) {
763604be855SAndy Yan drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
764604be855SAndy Yan return ret;
765604be855SAndy Yan }
766604be855SAndy Yan
767604be855SAndy Yan ret = clk_prepare_enable(vop2->aclk);
768604be855SAndy Yan if (ret < 0) {
769604be855SAndy Yan drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
770604be855SAndy Yan goto err;
771604be855SAndy Yan }
772604be855SAndy Yan
7735a028e8fSAndy Yan ret = clk_prepare_enable(vop2->pclk);
7745a028e8fSAndy Yan if (ret < 0) {
7755a028e8fSAndy Yan drm_err(vop2->drm, "failed to enable pclk - %d\n", ret);
7765a028e8fSAndy Yan goto err1;
7775a028e8fSAndy Yan }
7785a028e8fSAndy Yan
779604be855SAndy Yan return 0;
7805a028e8fSAndy Yan err1:
7815a028e8fSAndy Yan clk_disable_unprepare(vop2->aclk);
782604be855SAndy Yan err:
783604be855SAndy Yan clk_disable_unprepare(vop2->hclk);
784604be855SAndy Yan
785604be855SAndy Yan return ret;
786604be855SAndy Yan }
787604be855SAndy Yan
rk3588_vop2_power_domain_enable_all(struct vop2 * vop2)7885a028e8fSAndy Yan static void rk3588_vop2_power_domain_enable_all(struct vop2 *vop2)
7895a028e8fSAndy Yan {
7905a028e8fSAndy Yan u32 pd;
7915a028e8fSAndy Yan
7925a028e8fSAndy Yan pd = vop2_readl(vop2, RK3588_SYS_PD_CTRL);
7935a028e8fSAndy Yan pd &= ~(VOP2_PD_CLUSTER0 | VOP2_PD_CLUSTER1 | VOP2_PD_CLUSTER2 |
7945a028e8fSAndy Yan VOP2_PD_CLUSTER3 | VOP2_PD_ESMART);
7955a028e8fSAndy Yan
7965a028e8fSAndy Yan vop2_writel(vop2, RK3588_SYS_PD_CTRL, pd);
7975a028e8fSAndy Yan }
7985a028e8fSAndy Yan
vop2_enable(struct vop2 * vop2)799604be855SAndy Yan static void vop2_enable(struct vop2 *vop2)
800604be855SAndy Yan {
801604be855SAndy Yan int ret;
802301618edSAndy Yan u32 version;
803604be855SAndy Yan
804e3558747SYuan Can ret = pm_runtime_resume_and_get(vop2->dev);
805604be855SAndy Yan if (ret < 0) {
806604be855SAndy Yan drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
807604be855SAndy Yan return;
808604be855SAndy Yan }
809604be855SAndy Yan
810604be855SAndy Yan ret = vop2_core_clks_prepare_enable(vop2);
811604be855SAndy Yan if (ret) {
812604be855SAndy Yan pm_runtime_put_sync(vop2->dev);
813604be855SAndy Yan return;
814604be855SAndy Yan }
815604be855SAndy Yan
816604be855SAndy Yan ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
817604be855SAndy Yan if (ret) {
818604be855SAndy Yan drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
819604be855SAndy Yan return;
820604be855SAndy Yan }
821604be855SAndy Yan
822301618edSAndy Yan version = vop2_readl(vop2, RK3568_VERSION_INFO);
823301618edSAndy Yan if (version != vop2->version) {
824301618edSAndy Yan drm_err(vop2->drm, "Hardware version(0x%08x) mismatch\n", version);
825301618edSAndy Yan return;
826301618edSAndy Yan }
827301618edSAndy Yan
828301618edSAndy Yan /*
829301618edSAndy Yan * rk3566 share the same vop version with rk3568, so
830301618edSAndy Yan * we need to use soc_id for identification here.
831301618edSAndy Yan */
832604be855SAndy Yan if (vop2->data->soc_id == 3566)
833604be855SAndy Yan vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
834604be855SAndy Yan
835301618edSAndy Yan if (vop2->version == VOP_VERSION_RK3588)
8365a028e8fSAndy Yan rk3588_vop2_power_domain_enable_all(vop2);
8375a028e8fSAndy Yan
838*3e89a8c6SAndy Yan if (vop2->version <= VOP_VERSION_RK3588) {
839*3e89a8c6SAndy Yan vop2->old_layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
840*3e89a8c6SAndy Yan vop2->old_port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
841*3e89a8c6SAndy Yan }
842*3e89a8c6SAndy Yan
843604be855SAndy Yan vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
844604be855SAndy Yan
845604be855SAndy Yan /*
846604be855SAndy Yan * Disable auto gating, this is a workaround to
847604be855SAndy Yan * avoid display image shift when a window enabled.
848604be855SAndy Yan */
849604be855SAndy Yan regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
850604be855SAndy Yan RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
851604be855SAndy Yan
852604be855SAndy Yan vop2_writel(vop2, RK3568_SYS0_INT_CLR,
853604be855SAndy Yan VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
854604be855SAndy Yan vop2_writel(vop2, RK3568_SYS0_INT_EN,
855604be855SAndy Yan VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
856604be855SAndy Yan vop2_writel(vop2, RK3568_SYS1_INT_CLR,
857604be855SAndy Yan VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
858604be855SAndy Yan vop2_writel(vop2, RK3568_SYS1_INT_EN,
859604be855SAndy Yan VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
860604be855SAndy Yan }
861604be855SAndy Yan
vop2_disable(struct vop2 * vop2)862604be855SAndy Yan static void vop2_disable(struct vop2 *vop2)
863604be855SAndy Yan {
864604be855SAndy Yan rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
865604be855SAndy Yan
866604be855SAndy Yan pm_runtime_put_sync(vop2->dev);
867604be855SAndy Yan
8683ee348ebSAndy Yan regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
8693ee348ebSAndy Yan
8705a028e8fSAndy Yan clk_disable_unprepare(vop2->pclk);
871604be855SAndy Yan clk_disable_unprepare(vop2->aclk);
872604be855SAndy Yan clk_disable_unprepare(vop2->hclk);
873604be855SAndy Yan }
874604be855SAndy Yan
vop2_vp_dsp_lut_is_enabled(struct vop2_video_port * vp)8754f537776SPiotr Zalewski static bool vop2_vp_dsp_lut_is_enabled(struct vop2_video_port *vp)
8764f537776SPiotr Zalewski {
8774f537776SPiotr Zalewski u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
8784f537776SPiotr Zalewski
8794f537776SPiotr Zalewski return dsp_ctrl & RK3568_VP_DSP_CTRL__DSP_LUT_EN;
8804f537776SPiotr Zalewski }
8814f537776SPiotr Zalewski
vop2_vp_dsp_lut_disable(struct vop2_video_port * vp)8824f537776SPiotr Zalewski static void vop2_vp_dsp_lut_disable(struct vop2_video_port *vp)
8834f537776SPiotr Zalewski {
8844f537776SPiotr Zalewski u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
8854f537776SPiotr Zalewski
8864f537776SPiotr Zalewski dsp_ctrl &= ~RK3568_VP_DSP_CTRL__DSP_LUT_EN;
8874f537776SPiotr Zalewski vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
8884f537776SPiotr Zalewski }
8894f537776SPiotr Zalewski
vop2_vp_dsp_lut_poll_disabled(struct vop2_video_port * vp)8904f537776SPiotr Zalewski static bool vop2_vp_dsp_lut_poll_disabled(struct vop2_video_port *vp)
8914f537776SPiotr Zalewski {
8924f537776SPiotr Zalewski u32 dsp_ctrl;
8934f537776SPiotr Zalewski int ret = readx_poll_timeout(vop2_vp_dsp_lut_is_enabled, vp, dsp_ctrl,
8944f537776SPiotr Zalewski !dsp_ctrl, 5, 30 * 1000);
8954f537776SPiotr Zalewski if (ret) {
8964f537776SPiotr Zalewski drm_err(vp->vop2->drm, "display LUT RAM enable timeout!\n");
8974f537776SPiotr Zalewski return false;
8984f537776SPiotr Zalewski }
8994f537776SPiotr Zalewski
9004f537776SPiotr Zalewski return true;
9014f537776SPiotr Zalewski }
9024f537776SPiotr Zalewski
vop2_vp_dsp_lut_enable(struct vop2_video_port * vp)9034f537776SPiotr Zalewski static void vop2_vp_dsp_lut_enable(struct vop2_video_port *vp)
9044f537776SPiotr Zalewski {
9054f537776SPiotr Zalewski u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
9064f537776SPiotr Zalewski
9074f537776SPiotr Zalewski dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_LUT_EN;
9084f537776SPiotr Zalewski vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
9094f537776SPiotr Zalewski }
9104f537776SPiotr Zalewski
vop2_vp_dsp_lut_update_enable(struct vop2_video_port * vp)9114f537776SPiotr Zalewski static void vop2_vp_dsp_lut_update_enable(struct vop2_video_port *vp)
9124f537776SPiotr Zalewski {
9134f537776SPiotr Zalewski u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
9144f537776SPiotr Zalewski
9154f537776SPiotr Zalewski dsp_ctrl |= RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN;
9164f537776SPiotr Zalewski vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
9174f537776SPiotr Zalewski }
9184f537776SPiotr Zalewski
vop2_supports_seamless_gamma_lut_update(struct vop2 * vop2)9194f537776SPiotr Zalewski static inline bool vop2_supports_seamless_gamma_lut_update(struct vop2 *vop2)
9204f537776SPiotr Zalewski {
921301618edSAndy Yan return vop2->version != VOP_VERSION_RK3568;
9224f537776SPiotr Zalewski }
9234f537776SPiotr Zalewski
vop2_gamma_lut_in_use(struct vop2 * vop2,struct vop2_video_port * vp)9244f537776SPiotr Zalewski static bool vop2_gamma_lut_in_use(struct vop2 *vop2, struct vop2_video_port *vp)
9254f537776SPiotr Zalewski {
9264f537776SPiotr Zalewski const int nr_vps = vop2->data->nr_vps;
9274f537776SPiotr Zalewski int gamma_en_vp_id;
9284f537776SPiotr Zalewski
9294f537776SPiotr Zalewski for (gamma_en_vp_id = 0; gamma_en_vp_id < nr_vps; gamma_en_vp_id++)
9304f537776SPiotr Zalewski if (vop2_vp_dsp_lut_is_enabled(&vop2->vps[gamma_en_vp_id]))
9314f537776SPiotr Zalewski break;
9324f537776SPiotr Zalewski
9334f537776SPiotr Zalewski return gamma_en_vp_id != nr_vps && gamma_en_vp_id != vp->id;
9344f537776SPiotr Zalewski }
9354f537776SPiotr Zalewski
vop2_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)936604be855SAndy Yan static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
937604be855SAndy Yan struct drm_atomic_state *state)
938604be855SAndy Yan {
939604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
940604be855SAndy Yan struct vop2 *vop2 = vp->vop2;
941447fb14bSMichael Tretter struct drm_crtc_state *old_crtc_state;
942604be855SAndy Yan int ret;
943604be855SAndy Yan
944604be855SAndy Yan vop2_lock(vop2);
945604be855SAndy Yan
946447fb14bSMichael Tretter old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
947447fb14bSMichael Tretter drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
948447fb14bSMichael Tretter
949604be855SAndy Yan drm_crtc_vblank_off(crtc);
950604be855SAndy Yan
951604be855SAndy Yan /*
952604be855SAndy Yan * Vop standby will take effect at end of current frame,
953604be855SAndy Yan * if dsp hold valid irq happen, it means standby complete.
954604be855SAndy Yan *
955604be855SAndy Yan * we must wait standby complete when we want to disable aclk,
956604be855SAndy Yan * if not, memory bus maybe dead.
957604be855SAndy Yan */
958604be855SAndy Yan reinit_completion(&vp->dsp_hold_completion);
959604be855SAndy Yan
960604be855SAndy Yan vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
961604be855SAndy Yan
962604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
963604be855SAndy Yan
964604be855SAndy Yan ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
965604be855SAndy Yan msecs_to_jiffies(50));
966604be855SAndy Yan if (!ret)
967604be855SAndy Yan drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
968604be855SAndy Yan
969604be855SAndy Yan vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
970604be855SAndy Yan
9712c1268e7SCristian Ciocaltea if (vp->dclk_src)
9722c1268e7SCristian Ciocaltea clk_set_parent(vp->dclk, vp->dclk_src);
9732c1268e7SCristian Ciocaltea
974604be855SAndy Yan clk_disable_unprepare(vp->dclk);
975604be855SAndy Yan
976604be855SAndy Yan vop2->enable_count--;
977604be855SAndy Yan
978604be855SAndy Yan if (!vop2->enable_count)
979604be855SAndy Yan vop2_disable(vop2);
980604be855SAndy Yan
981604be855SAndy Yan vop2_unlock(vop2);
982604be855SAndy Yan
983604be855SAndy Yan if (crtc->state->event && !crtc->state->active) {
984604be855SAndy Yan spin_lock_irq(&crtc->dev->event_lock);
985604be855SAndy Yan drm_crtc_send_vblank_event(crtc, crtc->state->event);
986604be855SAndy Yan spin_unlock_irq(&crtc->dev->event_lock);
987604be855SAndy Yan
988604be855SAndy Yan crtc->state->event = NULL;
989604be855SAndy Yan }
990604be855SAndy Yan }
991604be855SAndy Yan
vop2_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * astate)992604be855SAndy Yan static int vop2_plane_atomic_check(struct drm_plane *plane,
993604be855SAndy Yan struct drm_atomic_state *astate)
994604be855SAndy Yan {
995604be855SAndy Yan struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
996604be855SAndy Yan struct drm_framebuffer *fb = pstate->fb;
997604be855SAndy Yan struct drm_crtc *crtc = pstate->crtc;
998604be855SAndy Yan struct drm_crtc_state *cstate;
999604be855SAndy Yan struct vop2_video_port *vp;
1000604be855SAndy Yan struct vop2 *vop2;
1001604be855SAndy Yan const struct vop2_data *vop2_data;
1002604be855SAndy Yan struct drm_rect *dest = &pstate->dst;
1003604be855SAndy Yan struct drm_rect *src = &pstate->src;
1004604be855SAndy Yan int min_scale = FRAC_16_16(1, 8);
1005604be855SAndy Yan int max_scale = FRAC_16_16(8, 1);
1006604be855SAndy Yan int format;
1007604be855SAndy Yan int ret;
1008604be855SAndy Yan
1009604be855SAndy Yan if (!crtc)
1010604be855SAndy Yan return 0;
1011604be855SAndy Yan
1012604be855SAndy Yan vp = to_vop2_video_port(crtc);
1013604be855SAndy Yan vop2 = vp->vop2;
1014604be855SAndy Yan vop2_data = vop2->data;
1015604be855SAndy Yan
1016604be855SAndy Yan cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
1017604be855SAndy Yan if (WARN_ON(!cstate))
1018604be855SAndy Yan return -EINVAL;
1019604be855SAndy Yan
1020604be855SAndy Yan ret = drm_atomic_helper_check_plane_state(pstate, cstate,
1021604be855SAndy Yan min_scale, max_scale,
1022604be855SAndy Yan true, true);
1023604be855SAndy Yan if (ret)
1024604be855SAndy Yan return ret;
1025604be855SAndy Yan
1026604be855SAndy Yan if (!pstate->visible)
1027604be855SAndy Yan return 0;
1028604be855SAndy Yan
1029604be855SAndy Yan format = vop2_convert_format(fb->format->format);
1030604be855SAndy Yan if (format < 0)
1031604be855SAndy Yan return format;
1032604be855SAndy Yan
1033604be855SAndy Yan if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
1034604be855SAndy Yan drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
1035604be855SAndy Yan drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
1036604be855SAndy Yan drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
1037604be855SAndy Yan drm_rect_width(dest), drm_rect_height(dest));
1038604be855SAndy Yan pstate->visible = false;
1039604be855SAndy Yan return 0;
1040604be855SAndy Yan }
1041604be855SAndy Yan
1042604be855SAndy Yan if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
1043604be855SAndy Yan drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
1044604be855SAndy Yan drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
1045604be855SAndy Yan drm_rect_width(src) >> 16,
1046604be855SAndy Yan drm_rect_height(src) >> 16,
1047604be855SAndy Yan vop2_data->max_input.width,
1048604be855SAndy Yan vop2_data->max_input.height);
1049604be855SAndy Yan return -EINVAL;
1050604be855SAndy Yan }
1051604be855SAndy Yan
1052604be855SAndy Yan /*
1053604be855SAndy Yan * Src.x1 can be odd when do clip, but yuv plane start point
1054604be855SAndy Yan * need align with 2 pixel.
1055604be855SAndy Yan */
1056604be855SAndy Yan if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
1057604be855SAndy Yan drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
1058604be855SAndy Yan return -EINVAL;
1059604be855SAndy Yan }
1060604be855SAndy Yan
1061604be855SAndy Yan return 0;
1062604be855SAndy Yan }
1063604be855SAndy Yan
vop2_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)1064604be855SAndy Yan static void vop2_plane_atomic_disable(struct drm_plane *plane,
1065604be855SAndy Yan struct drm_atomic_state *state)
1066604be855SAndy Yan {
1067471bf240SMichael Tretter struct drm_plane_state *old_pstate = NULL;
1068604be855SAndy Yan struct vop2_win *win = to_vop2_win(plane);
1069604be855SAndy Yan struct vop2 *vop2 = win->vop2;
1070604be855SAndy Yan
1071604be855SAndy Yan drm_dbg(vop2->drm, "%s disable\n", win->data->name);
1072604be855SAndy Yan
1073471bf240SMichael Tretter if (state)
1074471bf240SMichael Tretter old_pstate = drm_atomic_get_old_plane_state(state, plane);
1075471bf240SMichael Tretter if (old_pstate && !old_pstate->crtc)
1076604be855SAndy Yan return;
1077604be855SAndy Yan
1078604be855SAndy Yan vop2_win_disable(win);
1079604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
1080604be855SAndy Yan }
1081604be855SAndy Yan
1082604be855SAndy Yan /*
1083604be855SAndy Yan * The color key is 10 bit, so all format should
1084604be855SAndy Yan * convert to 10 bit here.
1085604be855SAndy Yan */
vop2_plane_setup_color_key(struct drm_plane * plane,u32 color_key)1086604be855SAndy Yan static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
1087604be855SAndy Yan {
1088604be855SAndy Yan struct drm_plane_state *pstate = plane->state;
1089604be855SAndy Yan struct drm_framebuffer *fb = pstate->fb;
1090604be855SAndy Yan struct vop2_win *win = to_vop2_win(plane);
1091604be855SAndy Yan u32 color_key_en = 0;
1092604be855SAndy Yan u32 r = 0;
1093604be855SAndy Yan u32 g = 0;
1094604be855SAndy Yan u32 b = 0;
1095604be855SAndy Yan
1096604be855SAndy Yan if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
1097604be855SAndy Yan vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
1098604be855SAndy Yan return;
1099604be855SAndy Yan }
1100604be855SAndy Yan
1101604be855SAndy Yan switch (fb->format->format) {
1102604be855SAndy Yan case DRM_FORMAT_RGB565:
1103604be855SAndy Yan case DRM_FORMAT_BGR565:
1104604be855SAndy Yan r = (color_key & 0xf800) >> 11;
1105604be855SAndy Yan g = (color_key & 0x7e0) >> 5;
1106604be855SAndy Yan b = (color_key & 0x1f);
1107604be855SAndy Yan r <<= 5;
1108604be855SAndy Yan g <<= 4;
1109604be855SAndy Yan b <<= 5;
1110604be855SAndy Yan color_key_en = 1;
1111604be855SAndy Yan break;
1112604be855SAndy Yan case DRM_FORMAT_XRGB8888:
1113604be855SAndy Yan case DRM_FORMAT_ARGB8888:
1114604be855SAndy Yan case DRM_FORMAT_XBGR8888:
1115604be855SAndy Yan case DRM_FORMAT_ABGR8888:
1116604be855SAndy Yan case DRM_FORMAT_RGB888:
1117604be855SAndy Yan case DRM_FORMAT_BGR888:
1118604be855SAndy Yan r = (color_key & 0xff0000) >> 16;
1119604be855SAndy Yan g = (color_key & 0xff00) >> 8;
1120604be855SAndy Yan b = (color_key & 0xff);
1121604be855SAndy Yan r <<= 2;
1122604be855SAndy Yan g <<= 2;
1123604be855SAndy Yan b <<= 2;
1124604be855SAndy Yan color_key_en = 1;
1125604be855SAndy Yan break;
1126604be855SAndy Yan }
1127604be855SAndy Yan
1128604be855SAndy Yan vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
1129604be855SAndy Yan vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
1130604be855SAndy Yan }
1131604be855SAndy Yan
vop2_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)1132604be855SAndy Yan static void vop2_plane_atomic_update(struct drm_plane *plane,
1133604be855SAndy Yan struct drm_atomic_state *state)
1134604be855SAndy Yan {
1135604be855SAndy Yan struct drm_plane_state *pstate = plane->state;
1136604be855SAndy Yan struct drm_crtc *crtc = pstate->crtc;
1137604be855SAndy Yan struct vop2_win *win = to_vop2_win(plane);
1138604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
1139604be855SAndy Yan struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
1140604be855SAndy Yan struct vop2 *vop2 = win->vop2;
1141604be855SAndy Yan struct drm_framebuffer *fb = pstate->fb;
114245ad07c7SAndy Yan u32 bpp = vop2_get_bpp(fb->format);
1143604be855SAndy Yan u32 actual_w, actual_h, dsp_w, dsp_h;
1144604be855SAndy Yan u32 act_info, dsp_info;
1145604be855SAndy Yan u32 format;
1146604be855SAndy Yan u32 afbc_format;
1147604be855SAndy Yan u32 rb_swap;
1148604be855SAndy Yan u32 uv_swap;
1149604be855SAndy Yan struct drm_rect *src = &pstate->src;
1150604be855SAndy Yan struct drm_rect *dest = &pstate->dst;
1151604be855SAndy Yan u32 afbc_tile_num;
1152604be855SAndy Yan u32 transform_offset;
1153604be855SAndy Yan bool dither_up;
1154604be855SAndy Yan bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
1155604be855SAndy Yan bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
1156604be855SAndy Yan bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
1157604be855SAndy Yan bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
1158604be855SAndy Yan struct rockchip_gem_object *rk_obj;
1159604be855SAndy Yan unsigned long offset;
1160bebad6bdSAndy Yan bool half_block_en;
1161604be855SAndy Yan bool afbc_en;
1162604be855SAndy Yan dma_addr_t yrgb_mst;
1163604be855SAndy Yan dma_addr_t uv_mst;
1164604be855SAndy Yan
1165604be855SAndy Yan /*
1166604be855SAndy Yan * can't update plane when vop2 is disabled.
1167604be855SAndy Yan */
1168604be855SAndy Yan if (WARN_ON(!crtc))
1169604be855SAndy Yan return;
1170604be855SAndy Yan
1171604be855SAndy Yan if (!pstate->visible) {
1172604be855SAndy Yan vop2_plane_atomic_disable(plane, state);
1173604be855SAndy Yan return;
1174604be855SAndy Yan }
1175604be855SAndy Yan
1176604be855SAndy Yan afbc_en = rockchip_afbc(plane, fb->modifier);
1177604be855SAndy Yan
1178604be855SAndy Yan offset = (src->x1 >> 16) * fb->format->cpp[0];
1179604be855SAndy Yan
1180604be855SAndy Yan /*
1181604be855SAndy Yan * AFBC HDR_PTR must set to the zero offset of the framebuffer.
1182604be855SAndy Yan */
1183604be855SAndy Yan if (afbc_en)
1184604be855SAndy Yan offset = 0;
1185604be855SAndy Yan else if (pstate->rotation & DRM_MODE_REFLECT_Y)
1186604be855SAndy Yan offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
1187604be855SAndy Yan else
1188604be855SAndy Yan offset += (src->y1 >> 16) * fb->pitches[0];
1189604be855SAndy Yan
1190604be855SAndy Yan rk_obj = to_rockchip_obj(fb->obj[0]);
1191604be855SAndy Yan
1192604be855SAndy Yan yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
1193604be855SAndy Yan if (fb->format->is_yuv) {
1194604be855SAndy Yan int hsub = fb->format->hsub;
1195604be855SAndy Yan int vsub = fb->format->vsub;
1196604be855SAndy Yan
1197604be855SAndy Yan offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
1198604be855SAndy Yan offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
1199604be855SAndy Yan
1200604be855SAndy Yan if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
1201604be855SAndy Yan offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
1202604be855SAndy Yan
1203604be855SAndy Yan rk_obj = to_rockchip_obj(fb->obj[0]);
1204604be855SAndy Yan uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
1205604be855SAndy Yan }
1206604be855SAndy Yan
1207604be855SAndy Yan actual_w = drm_rect_width(src) >> 16;
1208604be855SAndy Yan actual_h = drm_rect_height(src) >> 16;
1209604be855SAndy Yan dsp_w = drm_rect_width(dest);
1210604be855SAndy Yan
1211604be855SAndy Yan if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
1212712ec5deSAndy Yan drm_dbg_kms(vop2->drm,
1213712ec5deSAndy Yan "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
1214604be855SAndy Yan vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
1215604be855SAndy Yan dsp_w = adjusted_mode->hdisplay - dest->x1;
1216604be855SAndy Yan if (dsp_w < 4)
1217604be855SAndy Yan dsp_w = 4;
1218604be855SAndy Yan actual_w = dsp_w * actual_w / drm_rect_width(dest);
1219604be855SAndy Yan }
1220604be855SAndy Yan
1221604be855SAndy Yan dsp_h = drm_rect_height(dest);
1222604be855SAndy Yan
1223604be855SAndy Yan if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
1224712ec5deSAndy Yan drm_dbg_kms(vop2->drm,
1225712ec5deSAndy Yan "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
1226604be855SAndy Yan vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
1227604be855SAndy Yan dsp_h = adjusted_mode->vdisplay - dest->y1;
1228604be855SAndy Yan if (dsp_h < 4)
1229604be855SAndy Yan dsp_h = 4;
1230604be855SAndy Yan actual_h = dsp_h * actual_h / drm_rect_height(dest);
1231604be855SAndy Yan }
1232604be855SAndy Yan
1233604be855SAndy Yan /*
1234604be855SAndy Yan * This is workaround solution for IC design:
1235604be855SAndy Yan * esmart can't support scale down when actual_w % 16 == 1.
1236604be855SAndy Yan */
1237604be855SAndy Yan if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
1238604be855SAndy Yan if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
1239712ec5deSAndy Yan drm_dbg_kms(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
1240604be855SAndy Yan vp->id, win->data->name, actual_w);
1241604be855SAndy Yan actual_w -= 1;
1242604be855SAndy Yan }
1243604be855SAndy Yan }
1244604be855SAndy Yan
1245604be855SAndy Yan if (afbc_en && actual_w % 4) {
1246712ec5deSAndy Yan drm_dbg_kms(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
1247604be855SAndy Yan vp->id, win->data->name, actual_w);
1248604be855SAndy Yan actual_w = ALIGN_DOWN(actual_w, 4);
1249604be855SAndy Yan }
1250604be855SAndy Yan
1251604be855SAndy Yan act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
1252604be855SAndy Yan dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
1253604be855SAndy Yan
1254604be855SAndy Yan format = vop2_convert_format(fb->format->format);
1255bebad6bdSAndy Yan half_block_en = vop2_half_block_enable(pstate);
1256604be855SAndy Yan
1257604be855SAndy Yan drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
1258604be855SAndy Yan vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
1259604be855SAndy Yan dest->x1, dest->y1,
1260604be855SAndy Yan &fb->format->format,
1261604be855SAndy Yan afbc_en ? "AFBC" : "", &yrgb_mst);
1262604be855SAndy Yan
1263301618edSAndy Yan if (vop2->version > VOP_VERSION_RK3568) {
12647b256880SAndy Yan vop2_win_write(win, VOP2_WIN_AXI_BUS_ID, win->data->axi_bus_id);
12657b256880SAndy Yan vop2_win_write(win, VOP2_WIN_AXI_YRGB_R_ID, win->data->axi_yrgb_r_id);
12667b256880SAndy Yan vop2_win_write(win, VOP2_WIN_AXI_UV_R_ID, win->data->axi_uv_r_id);
12677b256880SAndy Yan }
12687b256880SAndy Yan
1269944757a4SAndy Yan if (vop2->version >= VOP_VERSION_RK3576)
1270944757a4SAndy Yan vop2_win_write(win, VOP2_WIN_VP_SEL, vp->id);
1271944757a4SAndy Yan
1272bebad6bdSAndy Yan if (vop2_cluster_window(win))
1273bebad6bdSAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en);
1274bebad6bdSAndy Yan
1275604be855SAndy Yan if (afbc_en) {
1276938fbb16SAndy Yan u32 stride, block_w;
1277604be855SAndy Yan
1278938fbb16SAndy Yan /* the afbc superblock is 16 x 16 or 32 x 8 */
1279938fbb16SAndy Yan block_w = fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 ? 32 : 16;
1280938fbb16SAndy Yan
1281604be855SAndy Yan afbc_format = vop2_convert_afbc_format(fb->format->format);
1282604be855SAndy Yan
1283604be855SAndy Yan /* Enable color transform for YTR */
1284604be855SAndy Yan if (fb->modifier & AFBC_FORMAT_MOD_YTR)
1285604be855SAndy Yan afbc_format |= (1 << 4);
1286604be855SAndy Yan
1287938fbb16SAndy Yan afbc_tile_num = ALIGN(actual_w, block_w) / block_w;
1288604be855SAndy Yan
1289604be855SAndy Yan /*
1290604be855SAndy Yan * AFBC pic_vir_width is count by pixel, this is different
1291604be855SAndy Yan * with WIN_VIR_STRIDE.
1292604be855SAndy Yan */
1293604be855SAndy Yan stride = (fb->pitches[0] << 3) / bpp;
1294604be855SAndy Yan if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
1295712ec5deSAndy Yan drm_dbg_kms(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
1296604be855SAndy Yan vp->id, win->data->name, stride);
1297604be855SAndy Yan
1298938fbb16SAndy Yan /* It's for head stride, each head size is 16 byte */
1299938fbb16SAndy Yan stride = ALIGN(stride, block_w) / block_w * 16;
1300938fbb16SAndy Yan
1301604be855SAndy Yan uv_swap = vop2_afbc_uv_swap(fb->format->format);
1302604be855SAndy Yan /*
1303604be855SAndy Yan * This is a workaround for crazy IC design, Cluster
1304604be855SAndy Yan * and Esmart/Smart use different format configuration map:
1305604be855SAndy Yan * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
1306604be855SAndy Yan *
1307604be855SAndy Yan * This is one thing we can make the convert simple:
1308604be855SAndy Yan * AFBCD decode all the YUV data to YUV444. So we just
1309604be855SAndy Yan * set all the yuv 10 bit to YUV444_10.
1310604be855SAndy Yan */
1311604be855SAndy Yan if (fb->format->is_yuv && bpp == 10)
1312604be855SAndy Yan format = VOP2_CLUSTER_YUV444_10;
1313604be855SAndy Yan
1314604be855SAndy Yan if (vop2_cluster_window(win))
1315604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
1316604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
1317604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
13185a028e8fSAndy Yan /*
13195a028e8fSAndy Yan * On rk3566/8, this bit is auto gating enable,
13205a028e8fSAndy Yan * but this function is not work well so we need
13215a028e8fSAndy Yan * to disable it for these two platform.
13225a028e8fSAndy Yan * On rk3588, and the following new soc(rk3528/rk3576),
13235a028e8fSAndy Yan * this bit is gating disable, we should write 1 to
13245a028e8fSAndy Yan * disable gating when enable afbc.
13255a028e8fSAndy Yan */
1326301618edSAndy Yan if (vop2->version == VOP_VERSION_RK3568)
1327604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
13285a028e8fSAndy Yan else
13295a028e8fSAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1);
13305a028e8fSAndy Yan
1331938fbb16SAndy Yan if (fb->modifier & AFBC_FORMAT_MOD_SPLIT)
1332938fbb16SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 1);
1333938fbb16SAndy Yan else
1334604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
1335938fbb16SAndy Yan
1336944757a4SAndy Yan if (vop2->version >= VOP_VERSION_RK3576) {
1337944757a4SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET_EN, 1);
1338944757a4SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET, yrgb_mst);
1339944757a4SAndy Yan }
1340944757a4SAndy Yan
1341bebad6bdSAndy Yan transform_offset = vop2_afbc_transform_offset(pstate, half_block_en);
1342604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
1343604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
1344838a871aSAndy Yan vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, transform_offset);
1345604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
1346604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
1347604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
1348604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
1349604be855SAndy Yan vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
1350604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
1351604be855SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
1352604be855SAndy Yan } else {
135320529a68SAndy Yan if (vop2_cluster_window(win)) {
135420529a68SAndy Yan vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 0);
1355838a871aSAndy Yan vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, 0);
135620529a68SAndy Yan }
135720529a68SAndy Yan
1358604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
1359604be855SAndy Yan }
1360604be855SAndy Yan
1361604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
1362604be855SAndy Yan
1363604be855SAndy Yan if (rotate_90 || rotate_270) {
1364604be855SAndy Yan act_info = swahw32(act_info);
1365604be855SAndy Yan actual_w = drm_rect_height(src) >> 16;
1366604be855SAndy Yan actual_h = drm_rect_width(src) >> 16;
1367604be855SAndy Yan }
1368604be855SAndy Yan
1369604be855SAndy Yan vop2_win_write(win, VOP2_WIN_FORMAT, format);
1370604be855SAndy Yan vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
1371604be855SAndy Yan
1372604be855SAndy Yan rb_swap = vop2_win_rb_swap(fb->format->format);
1373604be855SAndy Yan vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
1374604be855SAndy Yan uv_swap = vop2_win_uv_swap(fb->format->format);
1375604be855SAndy Yan vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
1376604be855SAndy Yan
1377604be855SAndy Yan if (fb->format->is_yuv) {
1378604be855SAndy Yan vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
1379604be855SAndy Yan vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
1380604be855SAndy Yan }
1381604be855SAndy Yan
1382604be855SAndy Yan vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
1383604be855SAndy Yan if (!vop2_cluster_window(win))
1384604be855SAndy Yan vop2_plane_setup_color_key(plane, 0);
1385604be855SAndy Yan vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
1386604be855SAndy Yan vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
1387604be855SAndy Yan vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
1388604be855SAndy Yan
1389604be855SAndy Yan vop2_setup_csc_mode(vp, win, pstate);
1390604be855SAndy Yan
1391604be855SAndy Yan dither_up = vop2_win_dither_up(fb->format->format);
1392604be855SAndy Yan vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
1393604be855SAndy Yan
1394604be855SAndy Yan vop2_win_write(win, VOP2_WIN_ENABLE, 1);
1395604be855SAndy Yan
1396604be855SAndy Yan if (vop2_cluster_window(win)) {
1397604be855SAndy Yan int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
1398604be855SAndy Yan
1399604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
1400604be855SAndy Yan vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
1401604be855SAndy Yan }
1402604be855SAndy Yan }
1403604be855SAndy Yan
1404604be855SAndy Yan static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
1405604be855SAndy Yan .atomic_check = vop2_plane_atomic_check,
1406604be855SAndy Yan .atomic_update = vop2_plane_atomic_update,
1407604be855SAndy Yan .atomic_disable = vop2_plane_atomic_disable,
1408604be855SAndy Yan };
1409604be855SAndy Yan
1410604be855SAndy Yan static const struct drm_plane_funcs vop2_plane_funcs = {
1411604be855SAndy Yan .update_plane = drm_atomic_helper_update_plane,
1412604be855SAndy Yan .disable_plane = drm_atomic_helper_disable_plane,
1413604be855SAndy Yan .destroy = drm_plane_cleanup,
1414604be855SAndy Yan .reset = drm_atomic_helper_plane_reset,
1415604be855SAndy Yan .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1416604be855SAndy Yan .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
1417604be855SAndy Yan .format_mod_supported = rockchip_vop2_mod_supported,
1418604be855SAndy Yan };
1419604be855SAndy Yan
vop2_crtc_enable_vblank(struct drm_crtc * crtc)1420604be855SAndy Yan static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
1421604be855SAndy Yan {
1422604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
1423604be855SAndy Yan
1424604be855SAndy Yan vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
1425604be855SAndy Yan
1426604be855SAndy Yan return 0;
1427604be855SAndy Yan }
1428604be855SAndy Yan
vop2_crtc_disable_vblank(struct drm_crtc * crtc)1429604be855SAndy Yan static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
1430604be855SAndy Yan {
1431604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
1432604be855SAndy Yan
1433604be855SAndy Yan vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
1434604be855SAndy Yan }
1435604be855SAndy Yan
vop2_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adj_mode)1436604be855SAndy Yan static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
1437604be855SAndy Yan const struct drm_display_mode *mode,
1438604be855SAndy Yan struct drm_display_mode *adj_mode)
1439604be855SAndy Yan {
1440604be855SAndy Yan drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
1441604be855SAndy Yan CRTC_STEREO_DOUBLE);
1442604be855SAndy Yan
1443604be855SAndy Yan return true;
1444604be855SAndy Yan }
1445604be855SAndy Yan
vop2_crtc_write_gamma_lut(struct vop2 * vop2,struct drm_crtc * crtc)14464f537776SPiotr Zalewski static void vop2_crtc_write_gamma_lut(struct vop2 *vop2, struct drm_crtc *crtc)
14474f537776SPiotr Zalewski {
14484f537776SPiotr Zalewski const struct vop2_video_port *vp = to_vop2_video_port(crtc);
14494f537776SPiotr Zalewski const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id];
14504f537776SPiotr Zalewski struct drm_color_lut *lut = crtc->state->gamma_lut->data;
14514f537776SPiotr Zalewski unsigned int i, bpc = ilog2(vp_data->gamma_lut_len);
14524f537776SPiotr Zalewski u32 word;
14534f537776SPiotr Zalewski
14544f537776SPiotr Zalewski for (i = 0; i < crtc->gamma_size; i++) {
14554f537776SPiotr Zalewski word = (drm_color_lut_extract(lut[i].blue, bpc) << (2 * bpc)) |
14564f537776SPiotr Zalewski (drm_color_lut_extract(lut[i].green, bpc) << bpc) |
14574f537776SPiotr Zalewski drm_color_lut_extract(lut[i].red, bpc);
14584f537776SPiotr Zalewski
14594f537776SPiotr Zalewski writel(word, vop2->lut_regs + i * 4);
14604f537776SPiotr Zalewski }
14614f537776SPiotr Zalewski }
14624f537776SPiotr Zalewski
vop2_crtc_atomic_set_gamma_seamless(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc)14634f537776SPiotr Zalewski static void vop2_crtc_atomic_set_gamma_seamless(struct vop2 *vop2,
14644f537776SPiotr Zalewski struct vop2_video_port *vp,
14654f537776SPiotr Zalewski struct drm_crtc *crtc)
14664f537776SPiotr Zalewski {
14674f537776SPiotr Zalewski vop2_writel(vop2, RK3568_LUT_PORT_SEL,
14684f537776SPiotr Zalewski FIELD_PREP(RK3588_LUT_PORT_SEL__GAMMA_AHB_WRITE_SEL, vp->id));
14694f537776SPiotr Zalewski vop2_vp_dsp_lut_enable(vp);
14704f537776SPiotr Zalewski vop2_crtc_write_gamma_lut(vop2, crtc);
14714f537776SPiotr Zalewski vop2_vp_dsp_lut_update_enable(vp);
14724f537776SPiotr Zalewski }
14734f537776SPiotr Zalewski
vop2_crtc_atomic_set_gamma_rk356x(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc)14744f537776SPiotr Zalewski static void vop2_crtc_atomic_set_gamma_rk356x(struct vop2 *vop2,
14754f537776SPiotr Zalewski struct vop2_video_port *vp,
14764f537776SPiotr Zalewski struct drm_crtc *crtc)
14774f537776SPiotr Zalewski {
14784f537776SPiotr Zalewski vop2_vp_dsp_lut_disable(vp);
14794f537776SPiotr Zalewski vop2_cfg_done(vp);
14804f537776SPiotr Zalewski if (!vop2_vp_dsp_lut_poll_disabled(vp))
14814f537776SPiotr Zalewski return;
14824f537776SPiotr Zalewski
14834f537776SPiotr Zalewski vop2_writel(vop2, RK3568_LUT_PORT_SEL, vp->id);
14844f537776SPiotr Zalewski vop2_crtc_write_gamma_lut(vop2, crtc);
14854f537776SPiotr Zalewski vop2_vp_dsp_lut_enable(vp);
14864f537776SPiotr Zalewski }
14874f537776SPiotr Zalewski
vop2_crtc_atomic_try_set_gamma(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc,struct drm_crtc_state * crtc_state)14884f537776SPiotr Zalewski static void vop2_crtc_atomic_try_set_gamma(struct vop2 *vop2,
14894f537776SPiotr Zalewski struct vop2_video_port *vp,
14904f537776SPiotr Zalewski struct drm_crtc *crtc,
14914f537776SPiotr Zalewski struct drm_crtc_state *crtc_state)
14924f537776SPiotr Zalewski {
14939c22b6ecSPiotr Zalewski if (!vop2->lut_regs)
14944f537776SPiotr Zalewski return;
14954f537776SPiotr Zalewski
14964f537776SPiotr Zalewski if (!crtc_state->gamma_lut) {
14974f537776SPiotr Zalewski vop2_vp_dsp_lut_disable(vp);
14984f537776SPiotr Zalewski return;
14994f537776SPiotr Zalewski }
15004f537776SPiotr Zalewski
15014f537776SPiotr Zalewski if (vop2_supports_seamless_gamma_lut_update(vop2))
15024f537776SPiotr Zalewski vop2_crtc_atomic_set_gamma_seamless(vop2, vp, crtc);
15034f537776SPiotr Zalewski else
15044f537776SPiotr Zalewski vop2_crtc_atomic_set_gamma_rk356x(vop2, vp, crtc);
15054f537776SPiotr Zalewski }
15064f537776SPiotr Zalewski
vop2_crtc_atomic_try_set_gamma_locked(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc,struct drm_crtc_state * crtc_state)15074f537776SPiotr Zalewski static inline void vop2_crtc_atomic_try_set_gamma_locked(struct vop2 *vop2,
15084f537776SPiotr Zalewski struct vop2_video_port *vp,
15094f537776SPiotr Zalewski struct drm_crtc *crtc,
15104f537776SPiotr Zalewski struct drm_crtc_state *crtc_state)
15114f537776SPiotr Zalewski {
15124f537776SPiotr Zalewski vop2_lock(vop2);
15134f537776SPiotr Zalewski vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
15144f537776SPiotr Zalewski vop2_unlock(vop2);
15154f537776SPiotr Zalewski }
15164f537776SPiotr Zalewski
vop2_dither_setup(struct drm_crtc * crtc,u32 * dsp_ctrl)1517604be855SAndy Yan static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
1518604be855SAndy Yan {
1519604be855SAndy Yan struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
1520604be855SAndy Yan
1521604be855SAndy Yan switch (vcstate->bus_format) {
1522604be855SAndy Yan case MEDIA_BUS_FMT_RGB565_1X16:
1523604be855SAndy Yan *dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
1524604be855SAndy Yan break;
1525604be855SAndy Yan case MEDIA_BUS_FMT_RGB666_1X18:
1526604be855SAndy Yan case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
1527604be855SAndy Yan case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
1528604be855SAndy Yan *dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
1529604be855SAndy Yan *dsp_ctrl |= RGB888_TO_RGB666;
1530604be855SAndy Yan break;
1531604be855SAndy Yan case MEDIA_BUS_FMT_YUV8_1X24:
1532604be855SAndy Yan case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1533604be855SAndy Yan *dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
1534604be855SAndy Yan break;
1535604be855SAndy Yan default:
1536604be855SAndy Yan break;
1537604be855SAndy Yan }
1538604be855SAndy Yan
1539604be855SAndy Yan if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
1540604be855SAndy Yan *dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
1541604be855SAndy Yan
1542604be855SAndy Yan *dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
1543604be855SAndy Yan DITHER_DOWN_ALLEGRO);
1544604be855SAndy Yan }
1545604be855SAndy Yan
vop2_post_config(struct drm_crtc * crtc)1546604be855SAndy Yan static void vop2_post_config(struct drm_crtc *crtc)
1547604be855SAndy Yan {
1548604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
1549328e6885SAndy Yan struct vop2 *vop2 = vp->vop2;
1550604be855SAndy Yan struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1551604be855SAndy Yan u16 vtotal = mode->crtc_vtotal;
1552604be855SAndy Yan u16 hdisplay = mode->crtc_hdisplay;
1553604be855SAndy Yan u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
1554604be855SAndy Yan u16 vdisplay = mode->crtc_vdisplay;
1555604be855SAndy Yan u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
1556604be855SAndy Yan u32 left_margin = 100, right_margin = 100;
1557604be855SAndy Yan u32 top_margin = 100, bottom_margin = 100;
1558604be855SAndy Yan u16 hsize = hdisplay * (left_margin + right_margin) / 200;
1559604be855SAndy Yan u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
1560604be855SAndy Yan u16 hact_end, vact_end;
1561604be855SAndy Yan u32 val;
1562075a5b39SAndy Yan
1563328e6885SAndy Yan vop2->ops->setup_bg_dly(vp);
1564604be855SAndy Yan
1565604be855SAndy Yan vsize = rounddown(vsize, 2);
1566604be855SAndy Yan hsize = rounddown(hsize, 2);
1567604be855SAndy Yan hact_st += hdisplay * (100 - left_margin) / 200;
1568604be855SAndy Yan hact_end = hact_st + hsize;
1569604be855SAndy Yan val = hact_st << 16;
1570604be855SAndy Yan val |= hact_end;
1571604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
1572604be855SAndy Yan vact_st += vdisplay * (100 - top_margin) / 200;
1573604be855SAndy Yan vact_end = vact_st + vsize;
1574604be855SAndy Yan val = vact_st << 16;
1575604be855SAndy Yan val |= vact_end;
1576604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
1577604be855SAndy Yan val = scl_cal_scale2(vdisplay, vsize) << 16;
1578604be855SAndy Yan val |= scl_cal_scale2(hdisplay, hsize);
1579604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
1580604be855SAndy Yan
1581604be855SAndy Yan val = 0;
1582604be855SAndy Yan if (hdisplay != hsize)
1583604be855SAndy Yan val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
1584604be855SAndy Yan if (vdisplay != vsize)
1585604be855SAndy Yan val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
1586604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
1587604be855SAndy Yan
1588604be855SAndy Yan if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1589604be855SAndy Yan u16 vact_st_f1 = vtotal + vact_st + 1;
1590604be855SAndy Yan u16 vact_end_f1 = vact_st_f1 + vsize;
1591604be855SAndy Yan
1592604be855SAndy Yan val = vact_st_f1 << 16 | vact_end_f1;
1593604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
1594604be855SAndy Yan }
1595604be855SAndy Yan
1596604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
1597604be855SAndy Yan }
1598604be855SAndy Yan
us_to_vertical_line(struct drm_display_mode * mode,int us)1599604be855SAndy Yan static int us_to_vertical_line(struct drm_display_mode *mode, int us)
1600604be855SAndy Yan {
1601604be855SAndy Yan return us * mode->clock / mode->htotal / 1000;
1602604be855SAndy Yan }
1603604be855SAndy Yan
vop2_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)1604604be855SAndy Yan static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
1605604be855SAndy Yan struct drm_atomic_state *state)
1606604be855SAndy Yan {
1607604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
1608604be855SAndy Yan struct vop2 *vop2 = vp->vop2;
1609604be855SAndy Yan const struct vop2_data *vop2_data = vop2->data;
1610604be855SAndy Yan const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
1611604be855SAndy Yan struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1612604be855SAndy Yan struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
1613604be855SAndy Yan struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1614604be855SAndy Yan unsigned long clock = mode->crtc_clock * 1000;
1615604be855SAndy Yan u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
1616604be855SAndy Yan u16 hdisplay = mode->crtc_hdisplay;
1617604be855SAndy Yan u16 htotal = mode->crtc_htotal;
1618604be855SAndy Yan u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
1619604be855SAndy Yan u16 hact_end = hact_st + hdisplay;
1620604be855SAndy Yan u16 vdisplay = mode->crtc_vdisplay;
1621604be855SAndy Yan u16 vtotal = mode->crtc_vtotal;
1622604be855SAndy Yan u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
1623604be855SAndy Yan u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
1624604be855SAndy Yan u16 vact_end = vact_st + vdisplay;
1625604be855SAndy Yan u8 out_mode;
1626604be855SAndy Yan u32 dsp_ctrl = 0;
1627604be855SAndy Yan int act_end;
1628604be855SAndy Yan u32 val, polflags;
1629604be855SAndy Yan int ret;
1630604be855SAndy Yan struct drm_encoder *encoder;
1631604be855SAndy Yan
1632604be855SAndy Yan drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
1633604be855SAndy Yan hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
1634604be855SAndy Yan drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
1635604be855SAndy Yan
1636604be855SAndy Yan vop2_lock(vop2);
1637604be855SAndy Yan
1638604be855SAndy Yan ret = clk_prepare_enable(vp->dclk);
1639604be855SAndy Yan if (ret < 0) {
1640604be855SAndy Yan drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
1641604be855SAndy Yan vp->id, ret);
164298526c5bSDan Carpenter vop2_unlock(vop2);
1643604be855SAndy Yan return;
1644604be855SAndy Yan }
1645604be855SAndy Yan
1646604be855SAndy Yan if (!vop2->enable_count)
1647604be855SAndy Yan vop2_enable(vop2);
1648604be855SAndy Yan
1649604be855SAndy Yan vop2->enable_count++;
1650604be855SAndy Yan
1651dd49ee46SAndy Yan vcstate->yuv_overlay = is_yuv_output(vcstate->bus_format);
1652dd49ee46SAndy Yan
1653604be855SAndy Yan vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
1654604be855SAndy Yan
1655604be855SAndy Yan polflags = 0;
1656604be855SAndy Yan if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
1657604be855SAndy Yan polflags |= POLFLAG_DCLK_INV;
1658604be855SAndy Yan if (mode->flags & DRM_MODE_FLAG_PHSYNC)
1659604be855SAndy Yan polflags |= BIT(HSYNC_POSITIVE);
1660604be855SAndy Yan if (mode->flags & DRM_MODE_FLAG_PVSYNC)
1661604be855SAndy Yan polflags |= BIT(VSYNC_POSITIVE);
1662604be855SAndy Yan
1663604be855SAndy Yan drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
1664604be855SAndy Yan struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
1665604be855SAndy Yan
16665a028e8fSAndy Yan /*
16675a028e8fSAndy Yan * for drive a high resolution(4KP120, 8K), vop on rk3588/rk3576 need
16685a028e8fSAndy Yan * process multi(1/2/4/8) pixels per cycle, so the dclk feed by the
16695a028e8fSAndy Yan * system cru may be the 1/2 or 1/4 of mode->clock.
16705a028e8fSAndy Yan */
1671328e6885SAndy Yan clock = vop2->ops->setup_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
1672604be855SAndy Yan }
1673604be855SAndy Yan
1674b6ddaa63SHarshit Mogalapalli if (!clock) {
1675b6ddaa63SHarshit Mogalapalli vop2_unlock(vop2);
16765a028e8fSAndy Yan return;
1677b6ddaa63SHarshit Mogalapalli }
16785a028e8fSAndy Yan
1679604be855SAndy Yan if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
16809d7fe770SAndy Yan !(vp_data->feature & VOP2_VP_FEATURE_OUTPUT_10BIT))
1681604be855SAndy Yan out_mode = ROCKCHIP_OUT_MODE_P888;
1682604be855SAndy Yan else
1683604be855SAndy Yan out_mode = vcstate->output_mode;
1684604be855SAndy Yan
1685604be855SAndy Yan dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
1686604be855SAndy Yan
1687604be855SAndy Yan if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
1688604be855SAndy Yan dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
16895a028e8fSAndy Yan if (vop2_output_rg_swap(vop2, vcstate->bus_format))
16905a028e8fSAndy Yan dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RG_SWAP;
1691604be855SAndy Yan
1692dd49ee46SAndy Yan if (vcstate->yuv_overlay)
1693604be855SAndy Yan dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
1694604be855SAndy Yan
1695604be855SAndy Yan vop2_dither_setup(crtc, &dsp_ctrl);
1696604be855SAndy Yan
1697604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
1698604be855SAndy Yan val = hact_st << 16;
1699604be855SAndy Yan val |= hact_end;
1700604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
1701604be855SAndy Yan
1702604be855SAndy Yan val = vact_st << 16;
1703604be855SAndy Yan val |= vact_end;
1704604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
1705604be855SAndy Yan
1706604be855SAndy Yan if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1707604be855SAndy Yan u16 vact_st_f1 = vtotal + vact_st + 1;
1708604be855SAndy Yan u16 vact_end_f1 = vact_st_f1 + vdisplay;
1709604be855SAndy Yan
1710604be855SAndy Yan val = vact_st_f1 << 16 | vact_end_f1;
1711604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
1712604be855SAndy Yan
1713604be855SAndy Yan val = vtotal << 16 | (vtotal + vsync_len);
1714604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
1715604be855SAndy Yan dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
1716604be855SAndy Yan dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
1717604be855SAndy Yan dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
1718604be855SAndy Yan vtotal += vtotal + 1;
1719604be855SAndy Yan act_end = vact_end_f1;
1720604be855SAndy Yan } else {
1721604be855SAndy Yan act_end = vact_end;
1722604be855SAndy Yan }
1723604be855SAndy Yan
1724604be855SAndy Yan vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
1725604be855SAndy Yan (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
1726604be855SAndy Yan
1727604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
1728604be855SAndy Yan
1729604be855SAndy Yan if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
1730604be855SAndy Yan dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
1731604be855SAndy Yan clock *= 2;
1732604be855SAndy Yan }
1733604be855SAndy Yan
1734604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
1735604be855SAndy Yan
17362c1268e7SCristian Ciocaltea /*
17372c1268e7SCristian Ciocaltea * Switch to HDMI PHY PLL as DCLK source for display modes up
17382c1268e7SCristian Ciocaltea * to 4K@60Hz, if available, otherwise keep using the system CRU.
17392c1268e7SCristian Ciocaltea */
1740f8dd7fc9SCristian Ciocaltea if ((vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) && clock <= VOP2_MAX_DCLK_RATE) {
17412c1268e7SCristian Ciocaltea drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
17422c1268e7SCristian Ciocaltea struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
17432c1268e7SCristian Ciocaltea
17442c1268e7SCristian Ciocaltea if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) {
1745f8dd7fc9SCristian Ciocaltea if (!vop2->pll_hdmiphy0)
1746f8dd7fc9SCristian Ciocaltea break;
1747f8dd7fc9SCristian Ciocaltea
17482c1268e7SCristian Ciocaltea if (!vp->dclk_src)
17492c1268e7SCristian Ciocaltea vp->dclk_src = clk_get_parent(vp->dclk);
17502c1268e7SCristian Ciocaltea
17512c1268e7SCristian Ciocaltea ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0);
17522c1268e7SCristian Ciocaltea if (ret < 0)
17532c1268e7SCristian Ciocaltea drm_warn(vop2->drm,
17542c1268e7SCristian Ciocaltea "Could not switch to HDMI0 PHY PLL: %d\n", ret);
17552c1268e7SCristian Ciocaltea break;
17562c1268e7SCristian Ciocaltea }
1757f8dd7fc9SCristian Ciocaltea
1758f8dd7fc9SCristian Ciocaltea if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) {
1759f8dd7fc9SCristian Ciocaltea if (!vop2->pll_hdmiphy1)
1760f8dd7fc9SCristian Ciocaltea break;
1761f8dd7fc9SCristian Ciocaltea
1762f8dd7fc9SCristian Ciocaltea if (!vp->dclk_src)
1763f8dd7fc9SCristian Ciocaltea vp->dclk_src = clk_get_parent(vp->dclk);
1764f8dd7fc9SCristian Ciocaltea
1765f8dd7fc9SCristian Ciocaltea ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1);
1766f8dd7fc9SCristian Ciocaltea if (ret < 0)
1767f8dd7fc9SCristian Ciocaltea drm_warn(vop2->drm,
1768f8dd7fc9SCristian Ciocaltea "Could not switch to HDMI1 PHY PLL: %d\n", ret);
1769f8dd7fc9SCristian Ciocaltea break;
1770f8dd7fc9SCristian Ciocaltea }
17712c1268e7SCristian Ciocaltea }
17722c1268e7SCristian Ciocaltea }
17732c1268e7SCristian Ciocaltea
1774604be855SAndy Yan clk_set_rate(vp->dclk, clock);
1775604be855SAndy Yan
1776604be855SAndy Yan vop2_post_config(crtc);
1777604be855SAndy Yan
1778604be855SAndy Yan vop2_cfg_done(vp);
1779604be855SAndy Yan
1780604be855SAndy Yan vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
1781604be855SAndy Yan
17824f537776SPiotr Zalewski vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
17834f537776SPiotr Zalewski
1784604be855SAndy Yan drm_crtc_vblank_on(crtc);
1785604be855SAndy Yan
1786604be855SAndy Yan vop2_unlock(vop2);
1787604be855SAndy Yan }
1788604be855SAndy Yan
vop2_crtc_atomic_check_gamma(struct vop2_video_port * vp,struct drm_crtc * crtc,struct drm_atomic_state * state,struct drm_crtc_state * crtc_state)17894f537776SPiotr Zalewski static int vop2_crtc_atomic_check_gamma(struct vop2_video_port *vp,
17904f537776SPiotr Zalewski struct drm_crtc *crtc,
17914f537776SPiotr Zalewski struct drm_atomic_state *state,
17924f537776SPiotr Zalewski struct drm_crtc_state *crtc_state)
17934f537776SPiotr Zalewski {
17944f537776SPiotr Zalewski struct vop2 *vop2 = vp->vop2;
17954f537776SPiotr Zalewski unsigned int len;
17964f537776SPiotr Zalewski
17974f537776SPiotr Zalewski if (!vp->vop2->lut_regs || !crtc_state->color_mgmt_changed ||
17984f537776SPiotr Zalewski !crtc_state->gamma_lut)
17994f537776SPiotr Zalewski return 0;
18004f537776SPiotr Zalewski
18014f537776SPiotr Zalewski len = drm_color_lut_size(crtc_state->gamma_lut);
18024f537776SPiotr Zalewski if (len != crtc->gamma_size) {
18034f537776SPiotr Zalewski drm_dbg(vop2->drm, "Invalid LUT size; got %d, expected %d\n",
18044f537776SPiotr Zalewski len, crtc->gamma_size);
18054f537776SPiotr Zalewski return -EINVAL;
18064f537776SPiotr Zalewski }
18074f537776SPiotr Zalewski
18084f537776SPiotr Zalewski if (!vop2_supports_seamless_gamma_lut_update(vop2) && vop2_gamma_lut_in_use(vop2, vp)) {
18094f537776SPiotr Zalewski drm_info(vop2->drm, "Gamma LUT can be enabled for only one CRTC at a time\n");
18104f537776SPiotr Zalewski return -EINVAL;
18114f537776SPiotr Zalewski }
18124f537776SPiotr Zalewski
18134f537776SPiotr Zalewski return 0;
18144f537776SPiotr Zalewski }
18154f537776SPiotr Zalewski
vop2_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)1816604be855SAndy Yan static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
1817604be855SAndy Yan struct drm_atomic_state *state)
1818604be855SAndy Yan {
1819604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
1820604be855SAndy Yan struct drm_plane *plane;
1821604be855SAndy Yan int nplanes = 0;
1822604be855SAndy Yan struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
18234f537776SPiotr Zalewski int ret;
18244f537776SPiotr Zalewski
18254f537776SPiotr Zalewski ret = vop2_crtc_atomic_check_gamma(vp, crtc, state, crtc_state);
18264f537776SPiotr Zalewski if (ret)
18274f537776SPiotr Zalewski return ret;
1828604be855SAndy Yan
1829604be855SAndy Yan drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
1830604be855SAndy Yan nplanes++;
1831604be855SAndy Yan
1832604be855SAndy Yan if (nplanes > vp->nlayers)
1833604be855SAndy Yan return -EINVAL;
1834604be855SAndy Yan
1835604be855SAndy Yan return 0;
1836604be855SAndy Yan }
1837604be855SAndy Yan
vop2_crtc_atomic_begin(struct drm_crtc * crtc,struct drm_atomic_state * state)1838604be855SAndy Yan static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
1839604be855SAndy Yan struct drm_atomic_state *state)
1840604be855SAndy Yan {
1841604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
1842604be855SAndy Yan struct vop2 *vop2 = vp->vop2;
1843604be855SAndy Yan
1844328e6885SAndy Yan vop2->ops->setup_overlay(vp);
1845604be855SAndy Yan }
1846604be855SAndy Yan
vop2_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)1847604be855SAndy Yan static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
1848604be855SAndy Yan struct drm_atomic_state *state)
1849604be855SAndy Yan {
18504f537776SPiotr Zalewski struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1851604be855SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
18524f537776SPiotr Zalewski struct vop2 *vop2 = vp->vop2;
18534f537776SPiotr Zalewski
18544f537776SPiotr Zalewski /* In case of modeset, gamma lut update already happened in atomic enable */
18559c22b6ecSPiotr Zalewski if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed)
18564f537776SPiotr Zalewski vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state);
1857604be855SAndy Yan
1858604be855SAndy Yan vop2_post_config(crtc);
1859604be855SAndy Yan
1860604be855SAndy Yan vop2_cfg_done(vp);
1861604be855SAndy Yan
1862604be855SAndy Yan spin_lock_irq(&crtc->dev->event_lock);
1863604be855SAndy Yan
1864604be855SAndy Yan if (crtc->state->event) {
1865604be855SAndy Yan WARN_ON(drm_crtc_vblank_get(crtc));
1866604be855SAndy Yan vp->event = crtc->state->event;
1867604be855SAndy Yan crtc->state->event = NULL;
1868604be855SAndy Yan }
1869604be855SAndy Yan
1870604be855SAndy Yan spin_unlock_irq(&crtc->dev->event_lock);
1871604be855SAndy Yan }
1872604be855SAndy Yan
1873604be855SAndy Yan static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
1874604be855SAndy Yan .mode_fixup = vop2_crtc_mode_fixup,
1875604be855SAndy Yan .atomic_check = vop2_crtc_atomic_check,
1876604be855SAndy Yan .atomic_begin = vop2_crtc_atomic_begin,
1877604be855SAndy Yan .atomic_flush = vop2_crtc_atomic_flush,
1878604be855SAndy Yan .atomic_enable = vop2_crtc_atomic_enable,
1879604be855SAndy Yan .atomic_disable = vop2_crtc_atomic_disable,
1880604be855SAndy Yan };
1881604be855SAndy Yan
vop2_dump_connector_on_crtc(struct drm_crtc * crtc,struct seq_file * s)188277996455SAndy Yan static void vop2_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s)
188377996455SAndy Yan {
188477996455SAndy Yan struct drm_connector_list_iter conn_iter;
188577996455SAndy Yan struct drm_connector *connector;
188677996455SAndy Yan
188777996455SAndy Yan drm_connector_list_iter_begin(crtc->dev, &conn_iter);
188877996455SAndy Yan drm_for_each_connector_iter(connector, &conn_iter) {
188977996455SAndy Yan if (crtc->state->connector_mask & drm_connector_mask(connector))
189077996455SAndy Yan seq_printf(s, " Connector: %s\n", connector->name);
189177996455SAndy Yan }
189277996455SAndy Yan drm_connector_list_iter_end(&conn_iter);
189377996455SAndy Yan }
189477996455SAndy Yan
vop2_plane_state_dump(struct seq_file * s,struct drm_plane * plane)189577996455SAndy Yan static int vop2_plane_state_dump(struct seq_file *s, struct drm_plane *plane)
189677996455SAndy Yan {
189777996455SAndy Yan struct vop2_win *win = to_vop2_win(plane);
189877996455SAndy Yan struct drm_plane_state *pstate = plane->state;
189977996455SAndy Yan struct drm_rect *src, *dst;
190077996455SAndy Yan struct drm_framebuffer *fb;
190177996455SAndy Yan struct drm_gem_object *obj;
190277996455SAndy Yan struct rockchip_gem_object *rk_obj;
190377996455SAndy Yan bool xmirror;
190477996455SAndy Yan bool ymirror;
190577996455SAndy Yan bool rotate_270;
190677996455SAndy Yan bool rotate_90;
190777996455SAndy Yan dma_addr_t fb_addr;
190877996455SAndy Yan int i;
190977996455SAndy Yan
191077996455SAndy Yan seq_printf(s, " %s: %s\n", win->data->name, !pstate ?
191177996455SAndy Yan "DISABLED" : pstate->crtc ? "ACTIVE" : "DISABLED");
191277996455SAndy Yan
191377996455SAndy Yan if (!pstate || !pstate->fb)
191477996455SAndy Yan return 0;
191577996455SAndy Yan
191677996455SAndy Yan fb = pstate->fb;
191777996455SAndy Yan src = &pstate->src;
191877996455SAndy Yan dst = &pstate->dst;
191977996455SAndy Yan xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
192077996455SAndy Yan ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
192177996455SAndy Yan rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
192277996455SAndy Yan rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
192377996455SAndy Yan
192477996455SAndy Yan seq_printf(s, "\twin_id: %d\n", win->win_id);
192577996455SAndy Yan
192677996455SAndy Yan seq_printf(s, "\tformat: %p4cc%s glb_alpha[0x%x]\n",
192777996455SAndy Yan &fb->format->format,
192877996455SAndy Yan drm_is_afbc(fb->modifier) ? "[AFBC]" : "",
192977996455SAndy Yan pstate->alpha >> 8);
193077996455SAndy Yan seq_printf(s, "\trotate: xmirror: %d ymirror: %d rotate_90: %d rotate_270: %d\n",
193177996455SAndy Yan xmirror, ymirror, rotate_90, rotate_270);
193277996455SAndy Yan seq_printf(s, "\tzpos: %d\n", pstate->normalized_zpos);
193377996455SAndy Yan seq_printf(s, "\tsrc: pos[%d, %d] rect[%d x %d]\n", src->x1 >> 16,
193477996455SAndy Yan src->y1 >> 16, drm_rect_width(src) >> 16,
193577996455SAndy Yan drm_rect_height(src) >> 16);
193677996455SAndy Yan seq_printf(s, "\tdst: pos[%d, %d] rect[%d x %d]\n", dst->x1, dst->y1,
193777996455SAndy Yan drm_rect_width(dst), drm_rect_height(dst));
193877996455SAndy Yan
193977996455SAndy Yan for (i = 0; i < fb->format->num_planes; i++) {
194077996455SAndy Yan obj = fb->obj[i];
194177996455SAndy Yan rk_obj = to_rockchip_obj(obj);
194277996455SAndy Yan fb_addr = rk_obj->dma_addr + fb->offsets[i];
194377996455SAndy Yan
194477996455SAndy Yan seq_printf(s, "\tbuf[%d]: addr: %pad pitch: %d offset: %d\n",
194577996455SAndy Yan i, &fb_addr, fb->pitches[i], fb->offsets[i]);
194677996455SAndy Yan }
194777996455SAndy Yan
194877996455SAndy Yan return 0;
194977996455SAndy Yan }
195077996455SAndy Yan
vop2_crtc_state_dump(struct drm_crtc * crtc,struct seq_file * s)195177996455SAndy Yan static int vop2_crtc_state_dump(struct drm_crtc *crtc, struct seq_file *s)
195277996455SAndy Yan {
195377996455SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
195477996455SAndy Yan struct drm_crtc_state *cstate = crtc->state;
195577996455SAndy Yan struct rockchip_crtc_state *vcstate;
195677996455SAndy Yan struct drm_display_mode *mode;
195777996455SAndy Yan struct drm_plane *plane;
195877996455SAndy Yan bool interlaced;
195977996455SAndy Yan
196077996455SAndy Yan seq_printf(s, "Video Port%d: %s\n", vp->id, !cstate ?
196177996455SAndy Yan "DISABLED" : cstate->active ? "ACTIVE" : "DISABLED");
196277996455SAndy Yan
196377996455SAndy Yan if (!cstate || !cstate->active)
196477996455SAndy Yan return 0;
196577996455SAndy Yan
196677996455SAndy Yan mode = &crtc->state->adjusted_mode;
196777996455SAndy Yan vcstate = to_rockchip_crtc_state(cstate);
196877996455SAndy Yan interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
196977996455SAndy Yan
197077996455SAndy Yan vop2_dump_connector_on_crtc(crtc, s);
197177996455SAndy Yan seq_printf(s, "\tbus_format[%x]: %s\n", vcstate->bus_format,
197277996455SAndy Yan drm_get_bus_format_name(vcstate->bus_format));
197377996455SAndy Yan seq_printf(s, "\toutput_mode[%x]", vcstate->output_mode);
197477996455SAndy Yan seq_printf(s, " color_space[%d]\n", vcstate->color_space);
197577996455SAndy Yan seq_printf(s, " Display mode: %dx%d%s%d\n",
197677996455SAndy Yan mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p",
197777996455SAndy Yan drm_mode_vrefresh(mode));
197877996455SAndy Yan seq_printf(s, "\tclk[%d] real_clk[%d] type[%x] flag[%x]\n",
197977996455SAndy Yan mode->clock, mode->crtc_clock, mode->type, mode->flags);
198077996455SAndy Yan seq_printf(s, "\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start,
198177996455SAndy Yan mode->hsync_end, mode->htotal);
198277996455SAndy Yan seq_printf(s, "\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start,
198377996455SAndy Yan mode->vsync_end, mode->vtotal);
198477996455SAndy Yan
198577996455SAndy Yan drm_atomic_crtc_for_each_plane(plane, crtc) {
198677996455SAndy Yan vop2_plane_state_dump(s, plane);
198777996455SAndy Yan }
198877996455SAndy Yan
198977996455SAndy Yan return 0;
199077996455SAndy Yan }
199177996455SAndy Yan
vop2_summary_show(struct seq_file * s,void * data)199277996455SAndy Yan static int vop2_summary_show(struct seq_file *s, void *data)
199377996455SAndy Yan {
199477996455SAndy Yan struct drm_info_node *node = s->private;
199577996455SAndy Yan struct drm_minor *minor = node->minor;
199677996455SAndy Yan struct drm_device *drm_dev = minor->dev;
199777996455SAndy Yan struct drm_crtc *crtc;
199877996455SAndy Yan
199977996455SAndy Yan drm_modeset_lock_all(drm_dev);
200077996455SAndy Yan drm_for_each_crtc(crtc, drm_dev) {
200177996455SAndy Yan vop2_crtc_state_dump(crtc, s);
200277996455SAndy Yan }
200377996455SAndy Yan drm_modeset_unlock_all(drm_dev);
200477996455SAndy Yan
200577996455SAndy Yan return 0;
200677996455SAndy Yan }
200777996455SAndy Yan
vop2_regs_print(struct vop2 * vop2,struct seq_file * s,const struct vop2_regs_dump * dump,bool active_only)200877996455SAndy Yan static void vop2_regs_print(struct vop2 *vop2, struct seq_file *s,
200977996455SAndy Yan const struct vop2_regs_dump *dump, bool active_only)
201077996455SAndy Yan {
201177996455SAndy Yan resource_size_t start;
201277996455SAndy Yan u32 val;
201377996455SAndy Yan int i;
201477996455SAndy Yan
201577996455SAndy Yan if (dump->en_mask && active_only) {
201677996455SAndy Yan val = vop2_readl(vop2, dump->base + dump->en_reg);
201777996455SAndy Yan if ((val & dump->en_mask) != dump->en_val)
201877996455SAndy Yan return;
201977996455SAndy Yan }
202077996455SAndy Yan
202177996455SAndy Yan seq_printf(s, "\n%s:\n", dump->name);
202277996455SAndy Yan
202377996455SAndy Yan start = vop2->res->start + dump->base;
202477996455SAndy Yan for (i = 0; i < dump->size >> 2; i += 4) {
202577996455SAndy Yan seq_printf(s, "%08x: %08x %08x %08x %08x\n", (u32)start + i * 4,
202677996455SAndy Yan vop2_readl(vop2, dump->base + (4 * i)),
202777996455SAndy Yan vop2_readl(vop2, dump->base + (4 * (i + 1))),
202877996455SAndy Yan vop2_readl(vop2, dump->base + (4 * (i + 2))),
202977996455SAndy Yan vop2_readl(vop2, dump->base + (4 * (i + 3))));
203077996455SAndy Yan }
203177996455SAndy Yan }
203277996455SAndy Yan
__vop2_regs_dump(struct seq_file * s,bool active_only)203377996455SAndy Yan static void __vop2_regs_dump(struct seq_file *s, bool active_only)
203477996455SAndy Yan {
203577996455SAndy Yan struct drm_info_node *node = s->private;
203677996455SAndy Yan struct vop2 *vop2 = node->info_ent->data;
203777996455SAndy Yan struct drm_minor *minor = node->minor;
203877996455SAndy Yan struct drm_device *drm_dev = minor->dev;
203977996455SAndy Yan const struct vop2_regs_dump *dump;
204077996455SAndy Yan unsigned int i;
204177996455SAndy Yan
204277996455SAndy Yan drm_modeset_lock_all(drm_dev);
204377996455SAndy Yan
204477996455SAndy Yan regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
204577996455SAndy Yan
204677996455SAndy Yan if (vop2->enable_count) {
204777996455SAndy Yan for (i = 0; i < vop2->data->regs_dump_size; i++) {
204877996455SAndy Yan dump = &vop2->data->regs_dump[i];
204977996455SAndy Yan vop2_regs_print(vop2, s, dump, active_only);
205077996455SAndy Yan }
205177996455SAndy Yan } else {
205277996455SAndy Yan seq_puts(s, "VOP disabled\n");
205377996455SAndy Yan }
205477996455SAndy Yan drm_modeset_unlock_all(drm_dev);
205577996455SAndy Yan }
205677996455SAndy Yan
vop2_regs_show(struct seq_file * s,void * arg)205777996455SAndy Yan static int vop2_regs_show(struct seq_file *s, void *arg)
205877996455SAndy Yan {
205977996455SAndy Yan __vop2_regs_dump(s, false);
206077996455SAndy Yan
206177996455SAndy Yan return 0;
206277996455SAndy Yan }
206377996455SAndy Yan
vop2_active_regs_show(struct seq_file * s,void * data)206477996455SAndy Yan static int vop2_active_regs_show(struct seq_file *s, void *data)
206577996455SAndy Yan {
206677996455SAndy Yan __vop2_regs_dump(s, true);
206777996455SAndy Yan
206877996455SAndy Yan return 0;
206977996455SAndy Yan }
207077996455SAndy Yan
207177996455SAndy Yan static struct drm_info_list vop2_debugfs_list[] = {
207277996455SAndy Yan { "summary", vop2_summary_show, 0, NULL },
207377996455SAndy Yan { "active_regs", vop2_active_regs_show, 0, NULL },
207477996455SAndy Yan { "regs", vop2_regs_show, 0, NULL },
207577996455SAndy Yan };
207677996455SAndy Yan
vop2_debugfs_init(struct vop2 * vop2,struct drm_minor * minor)207777996455SAndy Yan static void vop2_debugfs_init(struct vop2 *vop2, struct drm_minor *minor)
207877996455SAndy Yan {
207977996455SAndy Yan struct dentry *root;
208077996455SAndy Yan unsigned int i;
208177996455SAndy Yan
208277996455SAndy Yan root = debugfs_create_dir("vop2", minor->debugfs_root);
208377996455SAndy Yan if (!IS_ERR(root)) {
208477996455SAndy Yan for (i = 0; i < ARRAY_SIZE(vop2_debugfs_list); i++)
208577996455SAndy Yan vop2_debugfs_list[i].data = vop2;
208677996455SAndy Yan
208777996455SAndy Yan drm_debugfs_create_files(vop2_debugfs_list,
208877996455SAndy Yan ARRAY_SIZE(vop2_debugfs_list),
208977996455SAndy Yan root, minor);
209077996455SAndy Yan }
209177996455SAndy Yan }
209277996455SAndy Yan
vop2_crtc_late_register(struct drm_crtc * crtc)209377996455SAndy Yan static int vop2_crtc_late_register(struct drm_crtc *crtc)
209477996455SAndy Yan {
209577996455SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
209677996455SAndy Yan struct vop2 *vop2 = vp->vop2;
209777996455SAndy Yan
209877996455SAndy Yan if (drm_crtc_index(crtc) == 0)
209977996455SAndy Yan vop2_debugfs_init(vop2, crtc->dev->primary);
210077996455SAndy Yan
210177996455SAndy Yan return 0;
210277996455SAndy Yan }
210377996455SAndy Yan
vop2_crtc_duplicate_state(struct drm_crtc * crtc)2104604be855SAndy Yan static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
2105604be855SAndy Yan {
2106342f7e49SJonas Karlman struct rockchip_crtc_state *vcstate;
2107604be855SAndy Yan
2108342f7e49SJonas Karlman if (WARN_ON(!crtc->state))
2109342f7e49SJonas Karlman return NULL;
2110604be855SAndy Yan
2111342f7e49SJonas Karlman vcstate = kmemdup(to_rockchip_crtc_state(crtc->state),
2112342f7e49SJonas Karlman sizeof(*vcstate), GFP_KERNEL);
2113604be855SAndy Yan if (!vcstate)
2114604be855SAndy Yan return NULL;
2115604be855SAndy Yan
2116604be855SAndy Yan __drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
2117604be855SAndy Yan
2118604be855SAndy Yan return &vcstate->base;
2119604be855SAndy Yan }
2120604be855SAndy Yan
vop2_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)2121604be855SAndy Yan static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
2122604be855SAndy Yan struct drm_crtc_state *state)
2123604be855SAndy Yan {
2124604be855SAndy Yan struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
2125604be855SAndy Yan
2126604be855SAndy Yan __drm_atomic_helper_crtc_destroy_state(&vcstate->base);
2127604be855SAndy Yan kfree(vcstate);
2128604be855SAndy Yan }
2129604be855SAndy Yan
vop2_crtc_reset(struct drm_crtc * crtc)21304d49d87bSJonas Karlman static void vop2_crtc_reset(struct drm_crtc *crtc)
21314d49d87bSJonas Karlman {
21324d49d87bSJonas Karlman struct rockchip_crtc_state *vcstate =
21334d49d87bSJonas Karlman kzalloc(sizeof(*vcstate), GFP_KERNEL);
21344d49d87bSJonas Karlman
21354d49d87bSJonas Karlman if (crtc->state)
21364d49d87bSJonas Karlman vop2_crtc_destroy_state(crtc, crtc->state);
21374d49d87bSJonas Karlman
21384d49d87bSJonas Karlman if (vcstate)
21394d49d87bSJonas Karlman __drm_atomic_helper_crtc_reset(crtc, &vcstate->base);
21404d49d87bSJonas Karlman else
21414d49d87bSJonas Karlman __drm_atomic_helper_crtc_reset(crtc, NULL);
21424d49d87bSJonas Karlman }
21434d49d87bSJonas Karlman
2144604be855SAndy Yan static const struct drm_crtc_funcs vop2_crtc_funcs = {
2145604be855SAndy Yan .set_config = drm_atomic_helper_set_config,
2146604be855SAndy Yan .page_flip = drm_atomic_helper_page_flip,
2147604be855SAndy Yan .destroy = drm_crtc_cleanup,
2148604be855SAndy Yan .reset = vop2_crtc_reset,
2149604be855SAndy Yan .atomic_duplicate_state = vop2_crtc_duplicate_state,
2150604be855SAndy Yan .atomic_destroy_state = vop2_crtc_destroy_state,
2151604be855SAndy Yan .enable_vblank = vop2_crtc_enable_vblank,
2152604be855SAndy Yan .disable_vblank = vop2_crtc_disable_vblank,
215377996455SAndy Yan .late_register = vop2_crtc_late_register,
2154604be855SAndy Yan };
2155604be855SAndy Yan
rk3576_vp_isr(int irq,void * data)2156944757a4SAndy Yan static irqreturn_t rk3576_vp_isr(int irq, void *data)
2157944757a4SAndy Yan {
2158944757a4SAndy Yan struct vop2_video_port *vp = data;
2159944757a4SAndy Yan struct vop2 *vop2 = vp->vop2;
2160944757a4SAndy Yan struct drm_crtc *crtc = &vp->crtc;
2161944757a4SAndy Yan uint32_t irqs;
2162944757a4SAndy Yan int ret = IRQ_NONE;
2163944757a4SAndy Yan
2164944757a4SAndy Yan if (!pm_runtime_get_if_in_use(vop2->dev))
2165944757a4SAndy Yan return IRQ_NONE;
2166944757a4SAndy Yan
2167944757a4SAndy Yan irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
2168944757a4SAndy Yan vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
2169944757a4SAndy Yan
2170944757a4SAndy Yan if (irqs & VP_INT_DSP_HOLD_VALID) {
2171944757a4SAndy Yan complete(&vp->dsp_hold_completion);
2172944757a4SAndy Yan ret = IRQ_HANDLED;
2173944757a4SAndy Yan }
2174944757a4SAndy Yan
2175944757a4SAndy Yan if (irqs & VP_INT_FS_FIELD) {
2176944757a4SAndy Yan drm_crtc_handle_vblank(crtc);
2177944757a4SAndy Yan spin_lock(&crtc->dev->event_lock);
2178944757a4SAndy Yan if (vp->event) {
2179944757a4SAndy Yan u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
2180944757a4SAndy Yan
2181944757a4SAndy Yan if (!(val & BIT(vp->id))) {
2182944757a4SAndy Yan drm_crtc_send_vblank_event(crtc, vp->event);
2183944757a4SAndy Yan vp->event = NULL;
2184944757a4SAndy Yan drm_crtc_vblank_put(crtc);
2185944757a4SAndy Yan }
2186944757a4SAndy Yan }
2187944757a4SAndy Yan spin_unlock(&crtc->dev->event_lock);
2188944757a4SAndy Yan
2189944757a4SAndy Yan ret = IRQ_HANDLED;
2190944757a4SAndy Yan }
2191944757a4SAndy Yan
2192944757a4SAndy Yan if (irqs & VP_INT_POST_BUF_EMPTY) {
2193944757a4SAndy Yan drm_err_ratelimited(vop2->drm, "POST_BUF_EMPTY irq err at vp%d\n", vp->id);
2194944757a4SAndy Yan ret = IRQ_HANDLED;
2195944757a4SAndy Yan }
2196944757a4SAndy Yan
2197944757a4SAndy Yan pm_runtime_put(vop2->dev);
2198944757a4SAndy Yan
2199944757a4SAndy Yan return ret;
2200944757a4SAndy Yan }
2201944757a4SAndy Yan
vop2_isr(int irq,void * data)2202604be855SAndy Yan static irqreturn_t vop2_isr(int irq, void *data)
2203604be855SAndy Yan {
2204604be855SAndy Yan struct vop2 *vop2 = data;
2205604be855SAndy Yan const struct vop2_data *vop2_data = vop2->data;
2206604be855SAndy Yan u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
2207604be855SAndy Yan int ret = IRQ_NONE;
2208604be855SAndy Yan int i;
2209604be855SAndy Yan
2210604be855SAndy Yan /*
2211604be855SAndy Yan * The irq is shared with the iommu. If the runtime-pm state of the
2212604be855SAndy Yan * vop2-device is disabled the irq has to be targeted at the iommu.
2213604be855SAndy Yan */
2214604be855SAndy Yan if (!pm_runtime_get_if_in_use(vop2->dev))
2215604be855SAndy Yan return IRQ_NONE;
2216604be855SAndy Yan
2217944757a4SAndy Yan if (vop2->version < VOP_VERSION_RK3576) {
2218604be855SAndy Yan for (i = 0; i < vop2_data->nr_vps; i++) {
2219604be855SAndy Yan struct vop2_video_port *vp = &vop2->vps[i];
2220604be855SAndy Yan struct drm_crtc *crtc = &vp->crtc;
2221604be855SAndy Yan u32 irqs;
2222604be855SAndy Yan
2223604be855SAndy Yan irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
2224604be855SAndy Yan vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
2225604be855SAndy Yan
2226604be855SAndy Yan if (irqs & VP_INT_DSP_HOLD_VALID) {
2227604be855SAndy Yan complete(&vp->dsp_hold_completion);
2228604be855SAndy Yan ret = IRQ_HANDLED;
2229604be855SAndy Yan }
2230604be855SAndy Yan
2231604be855SAndy Yan if (irqs & VP_INT_FS_FIELD) {
2232604be855SAndy Yan drm_crtc_handle_vblank(crtc);
2233604be855SAndy Yan spin_lock(&crtc->dev->event_lock);
2234604be855SAndy Yan if (vp->event) {
2235604be855SAndy Yan u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
2236604be855SAndy Yan
2237604be855SAndy Yan if (!(val & BIT(vp->id))) {
2238604be855SAndy Yan drm_crtc_send_vblank_event(crtc, vp->event);
2239604be855SAndy Yan vp->event = NULL;
2240604be855SAndy Yan drm_crtc_vblank_put(crtc);
2241604be855SAndy Yan }
2242604be855SAndy Yan }
2243604be855SAndy Yan spin_unlock(&crtc->dev->event_lock);
2244604be855SAndy Yan
2245604be855SAndy Yan ret = IRQ_HANDLED;
2246604be855SAndy Yan }
2247604be855SAndy Yan
2248604be855SAndy Yan if (irqs & VP_INT_POST_BUF_EMPTY) {
2249604be855SAndy Yan drm_err_ratelimited(vop2->drm,
2250604be855SAndy Yan "POST_BUF_EMPTY irq err at vp%d\n",
2251604be855SAndy Yan vp->id);
2252604be855SAndy Yan ret = IRQ_HANDLED;
2253604be855SAndy Yan }
2254604be855SAndy Yan }
2255944757a4SAndy Yan }
2256604be855SAndy Yan
2257604be855SAndy Yan axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
2258604be855SAndy Yan vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
2259604be855SAndy Yan axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
2260604be855SAndy Yan vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
2261604be855SAndy Yan
2262604be855SAndy Yan for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
2263604be855SAndy Yan if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
2264604be855SAndy Yan drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
2265604be855SAndy Yan ret = IRQ_HANDLED;
2266604be855SAndy Yan }
2267604be855SAndy Yan }
2268604be855SAndy Yan
2269604be855SAndy Yan pm_runtime_put(vop2->dev);
2270604be855SAndy Yan
2271604be855SAndy Yan return ret;
2272604be855SAndy Yan }
2273604be855SAndy Yan
vop2_plane_init(struct vop2 * vop2,struct vop2_win * win,unsigned long possible_crtcs)2274604be855SAndy Yan static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
2275604be855SAndy Yan unsigned long possible_crtcs)
2276604be855SAndy Yan {
2277604be855SAndy Yan const struct vop2_win_data *win_data = win->data;
2278604be855SAndy Yan unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
2279604be855SAndy Yan BIT(DRM_MODE_BLEND_PREMULTI) |
2280604be855SAndy Yan BIT(DRM_MODE_BLEND_COVERAGE);
2281604be855SAndy Yan int ret;
2282604be855SAndy Yan
2283604be855SAndy Yan ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
2284604be855SAndy Yan &vop2_plane_funcs, win_data->formats,
2285604be855SAndy Yan win_data->nformats,
2286604be855SAndy Yan win_data->format_modifiers,
2287604be855SAndy Yan win->type, win_data->name);
2288604be855SAndy Yan if (ret) {
2289604be855SAndy Yan drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
2290604be855SAndy Yan return ret;
2291604be855SAndy Yan }
2292604be855SAndy Yan
2293604be855SAndy Yan drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
2294604be855SAndy Yan
2295604be855SAndy Yan if (win->data->supported_rotations)
2296604be855SAndy Yan drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
2297604be855SAndy Yan DRM_MODE_ROTATE_0 |
2298604be855SAndy Yan win->data->supported_rotations);
2299604be855SAndy Yan drm_plane_create_alpha_property(&win->base);
2300604be855SAndy Yan drm_plane_create_blend_mode_property(&win->base, blend_caps);
2301604be855SAndy Yan drm_plane_create_zpos_property(&win->base, win->win_id, 0,
2302604be855SAndy Yan vop2->registered_num_wins - 1);
2303604be855SAndy Yan
2304604be855SAndy Yan return 0;
2305604be855SAndy Yan }
2306604be855SAndy Yan
23076fd4f8a2SAndy Yan /*
23086fd4f8a2SAndy Yan * On RK3566 these windows don't have an independent
23096fd4f8a2SAndy Yan * framebuffer. They can only share/mirror the framebuffer
23106fd4f8a2SAndy Yan * with smart0, esmart0 and cluster0 respectively.
23116fd4f8a2SAndy Yan * And RK3566 share the same vop version with Rk3568, so we
23126fd4f8a2SAndy Yan * need to use soc_id for identification here.
23136fd4f8a2SAndy Yan */
vop2_is_mirror_win(struct vop2_win * win)23146fd4f8a2SAndy Yan static bool vop2_is_mirror_win(struct vop2_win *win)
2315604be855SAndy Yan {
23166fd4f8a2SAndy Yan struct vop2 *vop2 = win->vop2;
2317604be855SAndy Yan
23186fd4f8a2SAndy Yan if (vop2->data->soc_id == 3566) {
23196fd4f8a2SAndy Yan switch (win->data->phys_id) {
23206fd4f8a2SAndy Yan case ROCKCHIP_VOP2_SMART1:
23216fd4f8a2SAndy Yan case ROCKCHIP_VOP2_ESMART1:
23226fd4f8a2SAndy Yan case ROCKCHIP_VOP2_CLUSTER1:
23236fd4f8a2SAndy Yan return true;
23246fd4f8a2SAndy Yan default:
23256fd4f8a2SAndy Yan return false;
2326604be855SAndy Yan }
23276fd4f8a2SAndy Yan } else {
23286fd4f8a2SAndy Yan return false;
23296fd4f8a2SAndy Yan }
2330604be855SAndy Yan }
2331604be855SAndy Yan
vop2_create_crtcs(struct vop2 * vop2)2332cddddc06SMichael Riesch static int vop2_create_crtcs(struct vop2 *vop2)
2333604be855SAndy Yan {
2334604be855SAndy Yan const struct vop2_data *vop2_data = vop2->data;
2335604be855SAndy Yan struct drm_device *drm = vop2->drm;
2336604be855SAndy Yan struct device *dev = vop2->dev;
2337604be855SAndy Yan struct drm_plane *plane;
2338604be855SAndy Yan struct device_node *port;
2339604be855SAndy Yan struct vop2_video_port *vp;
23406fd4f8a2SAndy Yan struct vop2_win *win;
23416fd4f8a2SAndy Yan u32 possible_crtcs;
23426fd4f8a2SAndy Yan int i, j, nvp, nvps = 0;
2343604be855SAndy Yan int ret;
2344604be855SAndy Yan
2345604be855SAndy Yan for (i = 0; i < vop2_data->nr_vps; i++) {
2346604be855SAndy Yan const struct vop2_video_port_data *vp_data;
2347604be855SAndy Yan struct device_node *np;
2348604be855SAndy Yan char dclk_name[9];
2349604be855SAndy Yan
2350604be855SAndy Yan vp_data = &vop2_data->vp[i];
2351604be855SAndy Yan vp = &vop2->vps[i];
2352604be855SAndy Yan vp->vop2 = vop2;
2353604be855SAndy Yan vp->id = vp_data->id;
2354604be855SAndy Yan vp->data = vp_data;
2355604be855SAndy Yan
2356604be855SAndy Yan snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
2357604be855SAndy Yan vp->dclk = devm_clk_get(vop2->dev, dclk_name);
2358b06d1ef3SCristian Ciocaltea if (IS_ERR(vp->dclk))
2359b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vp->dclk),
2360b06d1ef3SCristian Ciocaltea "failed to get %s\n", dclk_name);
2361604be855SAndy Yan
2362604be855SAndy Yan np = of_graph_get_remote_node(dev->of_node, i, -1);
2363604be855SAndy Yan if (!np) {
2364604be855SAndy Yan drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
2365604be855SAndy Yan continue;
2366604be855SAndy Yan }
2367604be855SAndy Yan of_node_put(np);
2368604be855SAndy Yan
2369604be855SAndy Yan port = of_graph_get_port_by_id(dev->of_node, i);
2370b06d1ef3SCristian Ciocaltea if (!port)
2371b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, -ENOENT,
2372b06d1ef3SCristian Ciocaltea "no port node found for video_port%d\n", i);
2373604be855SAndy Yan vp->crtc.port = port;
2374604be855SAndy Yan nvps++;
2375604be855SAndy Yan }
2376604be855SAndy Yan
2377604be855SAndy Yan nvp = 0;
23786fd4f8a2SAndy Yan /* Register a primary plane for every crtc */
23796fd4f8a2SAndy Yan for (i = 0; i < vop2_data->nr_vps; i++) {
23806fd4f8a2SAndy Yan vp = &vop2->vps[i];
2381604be855SAndy Yan
23826fd4f8a2SAndy Yan if (!vp->crtc.port)
2383604be855SAndy Yan continue;
23846fd4f8a2SAndy Yan
23856fd4f8a2SAndy Yan for (j = 0; j < vop2->registered_num_wins; j++) {
23866fd4f8a2SAndy Yan win = &vop2->win[j];
23876fd4f8a2SAndy Yan
23886fd4f8a2SAndy Yan /* Aready registered as primary plane */
23896fd4f8a2SAndy Yan if (win->base.type == DRM_PLANE_TYPE_PRIMARY)
23906fd4f8a2SAndy Yan continue;
23916fd4f8a2SAndy Yan
2392b90fa71aSAndy Yan /* If this win can not attached to this VP */
2393b90fa71aSAndy Yan if (!(win->data->possible_vp_mask & BIT(vp->id)))
2394b90fa71aSAndy Yan continue;
2395b90fa71aSAndy Yan
23966fd4f8a2SAndy Yan if (vop2_is_mirror_win(win))
23976fd4f8a2SAndy Yan continue;
2398604be855SAndy Yan
2399604be855SAndy Yan if (win->type == DRM_PLANE_TYPE_PRIMARY) {
2400604be855SAndy Yan possible_crtcs = BIT(nvp);
2401604be855SAndy Yan vp->primary_plane = win;
2402604be855SAndy Yan ret = vop2_plane_init(vop2, win, possible_crtcs);
2403b06d1ef3SCristian Ciocaltea if (ret)
24046fd4f8a2SAndy Yan return dev_err_probe(drm->dev, ret,
24056fd4f8a2SAndy Yan "failed to init primary plane %s\n",
24066fd4f8a2SAndy Yan win->data->name);
24076fd4f8a2SAndy Yan nvp++;
24086fd4f8a2SAndy Yan break;
24096fd4f8a2SAndy Yan }
24106fd4f8a2SAndy Yan }
2411f9f68bf1SHeiko Stuebner
2412f9f68bf1SHeiko Stuebner if (!vp->primary_plane)
2413f9f68bf1SHeiko Stuebner return dev_err_probe(drm->dev, -ENOENT,
2414f9f68bf1SHeiko Stuebner "no primary plane for vp %d\n", i);
24156fd4f8a2SAndy Yan }
24166fd4f8a2SAndy Yan
24176fd4f8a2SAndy Yan /* Register all unused window as overlay plane */
24186fd4f8a2SAndy Yan for (i = 0; i < vop2->registered_num_wins; i++) {
24196fd4f8a2SAndy Yan win = &vop2->win[i];
24206fd4f8a2SAndy Yan
24216fd4f8a2SAndy Yan /* Aready registered as primary plane */
24226fd4f8a2SAndy Yan if (win->base.type == DRM_PLANE_TYPE_PRIMARY)
24236fd4f8a2SAndy Yan continue;
24246fd4f8a2SAndy Yan
24256fd4f8a2SAndy Yan if (vop2_is_mirror_win(win))
24266fd4f8a2SAndy Yan continue;
24276fd4f8a2SAndy Yan
24286fd4f8a2SAndy Yan win->type = DRM_PLANE_TYPE_OVERLAY;
24296fd4f8a2SAndy Yan
2430b90fa71aSAndy Yan possible_crtcs = 0;
2431b90fa71aSAndy Yan nvp = 0;
2432b90fa71aSAndy Yan for (j = 0; j < vop2_data->nr_vps; j++) {
2433b90fa71aSAndy Yan vp = &vop2->vps[j];
2434b90fa71aSAndy Yan
2435b90fa71aSAndy Yan if (!vp->crtc.port)
2436b90fa71aSAndy Yan continue;
2437b90fa71aSAndy Yan
2438b90fa71aSAndy Yan if (win->data->possible_vp_mask & BIT(vp->id))
2439b90fa71aSAndy Yan possible_crtcs |= BIT(nvp);
2440b90fa71aSAndy Yan nvp++;
2441b90fa71aSAndy Yan }
2442b90fa71aSAndy Yan
24436fd4f8a2SAndy Yan ret = vop2_plane_init(vop2, win, possible_crtcs);
24446fd4f8a2SAndy Yan if (ret)
24456fd4f8a2SAndy Yan return dev_err_probe(drm->dev, ret, "failed to init overlay plane %s\n",
2446b06d1ef3SCristian Ciocaltea win->data->name);
2447604be855SAndy Yan }
2448604be855SAndy Yan
2449604be855SAndy Yan for (i = 0; i < vop2_data->nr_vps; i++) {
2450604be855SAndy Yan vp = &vop2->vps[i];
2451604be855SAndy Yan
2452604be855SAndy Yan if (!vp->crtc.port)
2453604be855SAndy Yan continue;
2454604be855SAndy Yan
2455604be855SAndy Yan plane = &vp->primary_plane->base;
2456604be855SAndy Yan
2457604be855SAndy Yan ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
2458604be855SAndy Yan &vop2_crtc_funcs,
2459604be855SAndy Yan "video_port%d", vp->id);
2460b06d1ef3SCristian Ciocaltea if (ret)
2461b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, ret,
2462b06d1ef3SCristian Ciocaltea "crtc init for video_port%d failed\n", i);
2463604be855SAndy Yan
2464604be855SAndy Yan drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
24654f537776SPiotr Zalewski if (vop2->lut_regs) {
24664f537776SPiotr Zalewski const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
2467604be855SAndy Yan
24684f537776SPiotr Zalewski drm_mode_crtc_set_gamma_size(&vp->crtc, vp_data->gamma_lut_len);
24694f537776SPiotr Zalewski drm_crtc_enable_color_mgmt(&vp->crtc, 0, false, vp_data->gamma_lut_len);
24704f537776SPiotr Zalewski }
2471604be855SAndy Yan init_completion(&vp->dsp_hold_completion);
2472604be855SAndy Yan }
2473604be855SAndy Yan
2474604be855SAndy Yan /*
2475604be855SAndy Yan * On the VOP2 it's very hard to change the number of layers on a VP
2476604be855SAndy Yan * during runtime, so we distribute the layers equally over the used
2477604be855SAndy Yan * VPs
2478604be855SAndy Yan */
2479604be855SAndy Yan for (i = 0; i < vop2->data->nr_vps; i++) {
2480604be855SAndy Yan struct vop2_video_port *vp = &vop2->vps[i];
2481604be855SAndy Yan
2482604be855SAndy Yan if (vp->crtc.port)
2483dc00748aSAndy Yan vp->nlayers = vop2_data->win_size / nvps;
2484604be855SAndy Yan }
2485604be855SAndy Yan
2486604be855SAndy Yan return 0;
2487604be855SAndy Yan }
2488604be855SAndy Yan
vop2_destroy_crtcs(struct vop2 * vop2)2489cddddc06SMichael Riesch static void vop2_destroy_crtcs(struct vop2 *vop2)
2490604be855SAndy Yan {
2491cddddc06SMichael Riesch struct drm_device *drm = vop2->drm;
2492cddddc06SMichael Riesch struct list_head *crtc_list = &drm->mode_config.crtc_list;
2493cddddc06SMichael Riesch struct list_head *plane_list = &drm->mode_config.plane_list;
2494cddddc06SMichael Riesch struct drm_crtc *crtc, *tmpc;
2495cddddc06SMichael Riesch struct drm_plane *plane, *tmpp;
2496cddddc06SMichael Riesch
2497cddddc06SMichael Riesch list_for_each_entry_safe(plane, tmpp, plane_list, head)
2498cddddc06SMichael Riesch drm_plane_cleanup(plane);
2499604be855SAndy Yan
2500604be855SAndy Yan /*
2501604be855SAndy Yan * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
2502604be855SAndy Yan * references the CRTC.
2503604be855SAndy Yan */
2504cddddc06SMichael Riesch list_for_each_entry_safe(crtc, tmpc, crtc_list, head) {
2505cddddc06SMichael Riesch of_node_put(crtc->port);
2506604be855SAndy Yan drm_crtc_cleanup(crtc);
2507604be855SAndy Yan }
2508cddddc06SMichael Riesch }
2509604be855SAndy Yan
vop2_find_rgb_encoder(struct vop2 * vop2)2510c66c6d7cSMichael Riesch static int vop2_find_rgb_encoder(struct vop2 *vop2)
2511c66c6d7cSMichael Riesch {
2512c66c6d7cSMichael Riesch struct device_node *node = vop2->dev->of_node;
2513c66c6d7cSMichael Riesch struct device_node *endpoint;
2514c66c6d7cSMichael Riesch int i;
2515c66c6d7cSMichael Riesch
2516c66c6d7cSMichael Riesch for (i = 0; i < vop2->data->nr_vps; i++) {
2517c66c6d7cSMichael Riesch endpoint = of_graph_get_endpoint_by_regs(node, i,
2518c66c6d7cSMichael Riesch ROCKCHIP_VOP2_EP_RGB0);
2519c66c6d7cSMichael Riesch if (!endpoint)
2520c66c6d7cSMichael Riesch continue;
2521c66c6d7cSMichael Riesch
2522c66c6d7cSMichael Riesch of_node_put(endpoint);
2523c66c6d7cSMichael Riesch return i;
2524c66c6d7cSMichael Riesch }
2525c66c6d7cSMichael Riesch
2526c66c6d7cSMichael Riesch return -ENOENT;
2527c66c6d7cSMichael Riesch }
2528604be855SAndy Yan
vop2_regmap_init(struct vop2_win * win,const struct reg_field * regs,int nr_regs)2529145c9b36SAndy Yan static int vop2_regmap_init(struct vop2_win *win, const struct reg_field *regs,
2530145c9b36SAndy Yan int nr_regs)
2531604be855SAndy Yan {
2532604be855SAndy Yan struct vop2 *vop2 = win->vop2;
2533ff0b6c03SAndy Yan int i;
2534604be855SAndy Yan
2535145c9b36SAndy Yan for (i = 0; i < nr_regs; i++) {
2536ff0b6c03SAndy Yan const struct reg_field field = {
2537145c9b36SAndy Yan .reg = (regs[i].reg != 0xffffffff) ?
2538145c9b36SAndy Yan regs[i].reg + win->offset : regs[i].reg,
2539145c9b36SAndy Yan .lsb = regs[i].lsb,
2540145c9b36SAndy Yan .msb = regs[i].msb
2541604be855SAndy Yan };
2542604be855SAndy Yan
2543ff0b6c03SAndy Yan win->reg[i] = devm_regmap_field_alloc(vop2->dev, vop2->map, field);
2544ff0b6c03SAndy Yan if (IS_ERR(win->reg[i]))
2545ff0b6c03SAndy Yan return PTR_ERR(win->reg[i]);
2546ff0b6c03SAndy Yan }
2547ff0b6c03SAndy Yan
2548ff0b6c03SAndy Yan return 0;
2549ff0b6c03SAndy Yan };
2550ff0b6c03SAndy Yan
vop2_win_init(struct vop2 * vop2)2551604be855SAndy Yan static int vop2_win_init(struct vop2 *vop2)
2552604be855SAndy Yan {
2553604be855SAndy Yan const struct vop2_data *vop2_data = vop2->data;
2554604be855SAndy Yan struct vop2_win *win;
2555604be855SAndy Yan int i, ret;
2556604be855SAndy Yan
2557604be855SAndy Yan for (i = 0; i < vop2_data->win_size; i++) {
2558604be855SAndy Yan const struct vop2_win_data *win_data = &vop2_data->win[i];
2559604be855SAndy Yan
2560604be855SAndy Yan win = &vop2->win[i];
2561604be855SAndy Yan win->data = win_data;
2562604be855SAndy Yan win->type = win_data->type;
2563604be855SAndy Yan win->offset = win_data->base;
2564604be855SAndy Yan win->win_id = i;
2565604be855SAndy Yan win->vop2 = vop2;
2566604be855SAndy Yan if (vop2_cluster_window(win))
2567145c9b36SAndy Yan ret = vop2_regmap_init(win, vop2->data->cluster_reg,
2568145c9b36SAndy Yan vop2->data->nr_cluster_regs);
2569604be855SAndy Yan else
2570145c9b36SAndy Yan ret = vop2_regmap_init(win, vop2->data->smart_reg,
2571145c9b36SAndy Yan vop2->data->nr_smart_regs);
2572604be855SAndy Yan if (ret)
2573604be855SAndy Yan return ret;
2574604be855SAndy Yan }
2575604be855SAndy Yan
2576604be855SAndy Yan vop2->registered_num_wins = vop2_data->win_size;
2577604be855SAndy Yan
2578604be855SAndy Yan return 0;
2579604be855SAndy Yan }
2580604be855SAndy Yan
2581604be855SAndy Yan /*
2582604be855SAndy Yan * The window registers are only updated when config done is written.
2583604be855SAndy Yan * Until that they read back the old value. As we read-modify-write
2584604be855SAndy Yan * these registers mark them as non-volatile. This makes sure we read
2585604be855SAndy Yan * the new values from the regmap register cache.
2586604be855SAndy Yan */
2587604be855SAndy Yan static const struct regmap_range vop2_nonvolatile_range[] = {
2588604be855SAndy Yan regmap_reg_range(0x1000, 0x23ff),
2589604be855SAndy Yan };
2590604be855SAndy Yan
2591604be855SAndy Yan static const struct regmap_access_table vop2_volatile_table = {
2592604be855SAndy Yan .no_ranges = vop2_nonvolatile_range,
2593604be855SAndy Yan .n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
2594604be855SAndy Yan };
2595604be855SAndy Yan
2596604be855SAndy Yan static const struct regmap_config vop2_regmap_config = {
2597604be855SAndy Yan .reg_bits = 32,
2598604be855SAndy Yan .val_bits = 32,
2599604be855SAndy Yan .reg_stride = 4,
2600604be855SAndy Yan .max_register = 0x3000,
2601604be855SAndy Yan .name = "vop2",
2602604be855SAndy Yan .volatile_table = &vop2_volatile_table,
26033d59c22bSMark Brown .cache_type = REGCACHE_MAPLE,
2604604be855SAndy Yan };
2605604be855SAndy Yan
vop2_bind(struct device * dev,struct device * master,void * data)2606604be855SAndy Yan static int vop2_bind(struct device *dev, struct device *master, void *data)
2607604be855SAndy Yan {
2608604be855SAndy Yan struct platform_device *pdev = to_platform_device(dev);
2609604be855SAndy Yan const struct vop2_data *vop2_data;
2610604be855SAndy Yan struct drm_device *drm = data;
2611604be855SAndy Yan struct vop2 *vop2;
2612604be855SAndy Yan struct resource *res;
2613604be855SAndy Yan size_t alloc_size;
2614604be855SAndy Yan int ret;
2615604be855SAndy Yan
2616604be855SAndy Yan vop2_data = of_device_get_match_data(dev);
2617604be855SAndy Yan if (!vop2_data)
2618604be855SAndy Yan return -ENODEV;
2619604be855SAndy Yan
2620604be855SAndy Yan /* Allocate vop2 struct and its vop2_win array */
26213b4db36cSJacob Keller alloc_size = struct_size(vop2, win, vop2_data->win_size);
2622604be855SAndy Yan vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
2623604be855SAndy Yan if (!vop2)
2624604be855SAndy Yan return -ENOMEM;
2625604be855SAndy Yan
2626604be855SAndy Yan vop2->dev = dev;
2627604be855SAndy Yan vop2->data = vop2_data;
2628328e6885SAndy Yan vop2->ops = vop2_data->ops;
2629301618edSAndy Yan vop2->version = vop2_data->version;
2630604be855SAndy Yan vop2->drm = drm;
2631604be855SAndy Yan
2632604be855SAndy Yan dev_set_drvdata(dev, vop2);
2633604be855SAndy Yan
26345ee8c8f9SSascha Hauer res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vop");
2635b06d1ef3SCristian Ciocaltea if (!res)
2636b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, -EINVAL,
2637b06d1ef3SCristian Ciocaltea "failed to get vop2 register byname\n");
2638604be855SAndy Yan
263977996455SAndy Yan vop2->res = res;
2640604be855SAndy Yan vop2->regs = devm_ioremap_resource(dev, res);
2641604be855SAndy Yan if (IS_ERR(vop2->regs))
2642604be855SAndy Yan return PTR_ERR(vop2->regs);
2643604be855SAndy Yan vop2->len = resource_size(res);
2644604be855SAndy Yan
2645604be855SAndy Yan vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
26464ab9157cSAlfredo Cruz if (IS_ERR(vop2->map))
26474ab9157cSAlfredo Cruz return PTR_ERR(vop2->map);
2648604be855SAndy Yan
2649604be855SAndy Yan ret = vop2_win_init(vop2);
2650604be855SAndy Yan if (ret)
2651604be855SAndy Yan return ret;
2652604be855SAndy Yan
26535ee8c8f9SSascha Hauer res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma-lut");
2654604be855SAndy Yan if (res) {
2655604be855SAndy Yan vop2->lut_regs = devm_ioremap_resource(dev, res);
2656604be855SAndy Yan if (IS_ERR(vop2->lut_regs))
2657604be855SAndy Yan return PTR_ERR(vop2->lut_regs);
2658604be855SAndy Yan }
26595a028e8fSAndy Yan if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_GRF) {
2660c408af1aSAndy Yan vop2->sys_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
26615a028e8fSAndy Yan if (IS_ERR(vop2->sys_grf))
2662b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_grf),
2663b06d1ef3SCristian Ciocaltea "cannot get sys_grf\n");
26645a028e8fSAndy Yan }
26655a028e8fSAndy Yan
26665a028e8fSAndy Yan if (vop2_data->feature & VOP2_FEATURE_HAS_VOP_GRF) {
26675a028e8fSAndy Yan vop2->vop_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vop-grf");
26685a028e8fSAndy Yan if (IS_ERR(vop2->vop_grf))
2669b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->vop_grf),
2670b06d1ef3SCristian Ciocaltea "cannot get vop_grf\n");
26715a028e8fSAndy Yan }
26725a028e8fSAndy Yan
26735a028e8fSAndy Yan if (vop2_data->feature & VOP2_FEATURE_HAS_VO1_GRF) {
26745a028e8fSAndy Yan vop2->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo1-grf");
26755a028e8fSAndy Yan if (IS_ERR(vop2->vo1_grf))
2676b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->vo1_grf),
2677b06d1ef3SCristian Ciocaltea "cannot get vo1_grf\n");
26785a028e8fSAndy Yan }
26795a028e8fSAndy Yan
26805a028e8fSAndy Yan if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_PMU) {
26815a028e8fSAndy Yan vop2->sys_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pmu");
26825a028e8fSAndy Yan if (IS_ERR(vop2->sys_pmu))
2683b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_pmu),
2684b06d1ef3SCristian Ciocaltea "cannot get sys_pmu\n");
26855a028e8fSAndy Yan }
2686604be855SAndy Yan
2687604be855SAndy Yan vop2->hclk = devm_clk_get(vop2->dev, "hclk");
2688b06d1ef3SCristian Ciocaltea if (IS_ERR(vop2->hclk))
2689b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->hclk),
2690b06d1ef3SCristian Ciocaltea "failed to get hclk source\n");
2691604be855SAndy Yan
2692604be855SAndy Yan vop2->aclk = devm_clk_get(vop2->dev, "aclk");
2693b06d1ef3SCristian Ciocaltea if (IS_ERR(vop2->aclk))
2694b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->aclk),
2695b06d1ef3SCristian Ciocaltea "failed to get aclk source\n");
2696604be855SAndy Yan
26975a028e8fSAndy Yan vop2->pclk = devm_clk_get_optional(vop2->dev, "pclk_vop");
2698b06d1ef3SCristian Ciocaltea if (IS_ERR(vop2->pclk))
2699b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->pclk),
2700b06d1ef3SCristian Ciocaltea "failed to get pclk source\n");
27015a028e8fSAndy Yan
27022c1268e7SCristian Ciocaltea vop2->pll_hdmiphy0 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy0");
2703b06d1ef3SCristian Ciocaltea if (IS_ERR(vop2->pll_hdmiphy0))
2704b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy0),
2705b06d1ef3SCristian Ciocaltea "failed to get pll_hdmiphy0\n");
27062c1268e7SCristian Ciocaltea
2707f8dd7fc9SCristian Ciocaltea vop2->pll_hdmiphy1 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy1");
2708f8dd7fc9SCristian Ciocaltea if (IS_ERR(vop2->pll_hdmiphy1))
2709f8dd7fc9SCristian Ciocaltea return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy1),
2710f8dd7fc9SCristian Ciocaltea "failed to get pll_hdmiphy1\n");
2711f8dd7fc9SCristian Ciocaltea
2712604be855SAndy Yan vop2->irq = platform_get_irq(pdev, 0);
2713b06d1ef3SCristian Ciocaltea if (vop2->irq < 0)
2714b06d1ef3SCristian Ciocaltea return dev_err_probe(drm->dev, vop2->irq, "cannot find irq for vop2\n");
2715604be855SAndy Yan
2716604be855SAndy Yan mutex_init(&vop2->vop2_lock);
2717*3e89a8c6SAndy Yan mutex_init(&vop2->ovl_lock);
2718604be855SAndy Yan
2719604be855SAndy Yan ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
2720604be855SAndy Yan if (ret)
2721604be855SAndy Yan return ret;
2722604be855SAndy Yan
2723cddddc06SMichael Riesch ret = vop2_create_crtcs(vop2);
2724604be855SAndy Yan if (ret)
2725604be855SAndy Yan return ret;
2726604be855SAndy Yan
2727944757a4SAndy Yan if (vop2->version >= VOP_VERSION_RK3576) {
2728944757a4SAndy Yan struct drm_crtc *crtc;
2729944757a4SAndy Yan
2730944757a4SAndy Yan drm_for_each_crtc(crtc, drm) {
2731944757a4SAndy Yan struct vop2_video_port *vp = to_vop2_video_port(crtc);
2732944757a4SAndy Yan int vp_irq;
2733944757a4SAndy Yan const char *irq_name = devm_kasprintf(dev, GFP_KERNEL, "vp%d", vp->id);
2734944757a4SAndy Yan
2735944757a4SAndy Yan if (!irq_name)
2736944757a4SAndy Yan return -ENOMEM;
2737944757a4SAndy Yan
2738944757a4SAndy Yan vp_irq = platform_get_irq_byname(pdev, irq_name);
2739944757a4SAndy Yan if (vp_irq < 0)
2740944757a4SAndy Yan return dev_err_probe(drm->dev, vp_irq,
2741944757a4SAndy Yan "cannot find irq for vop2 vp%d\n", vp->id);
2742944757a4SAndy Yan
2743944757a4SAndy Yan ret = devm_request_irq(dev, vp_irq, rk3576_vp_isr, IRQF_SHARED, irq_name,
2744944757a4SAndy Yan vp);
2745944757a4SAndy Yan if (ret)
2746944757a4SAndy Yan dev_err_probe(drm->dev, ret,
2747944757a4SAndy Yan "request irq for vop2 vp%d failed\n", vp->id);
2748944757a4SAndy Yan }
2749944757a4SAndy Yan }
2750944757a4SAndy Yan
2751c66c6d7cSMichael Riesch ret = vop2_find_rgb_encoder(vop2);
2752c66c6d7cSMichael Riesch if (ret >= 0) {
2753c66c6d7cSMichael Riesch vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc,
2754c66c6d7cSMichael Riesch vop2->drm, ret);
2755c66c6d7cSMichael Riesch if (IS_ERR(vop2->rgb)) {
2756c66c6d7cSMichael Riesch if (PTR_ERR(vop2->rgb) == -EPROBE_DEFER) {
2757c66c6d7cSMichael Riesch ret = PTR_ERR(vop2->rgb);
2758c66c6d7cSMichael Riesch goto err_crtcs;
2759c66c6d7cSMichael Riesch }
2760c66c6d7cSMichael Riesch vop2->rgb = NULL;
2761c66c6d7cSMichael Riesch }
2762c66c6d7cSMichael Riesch }
2763c66c6d7cSMichael Riesch
2764604be855SAndy Yan rockchip_drm_dma_init_device(vop2->drm, vop2->dev);
2765604be855SAndy Yan
2766604be855SAndy Yan pm_runtime_enable(&pdev->dev);
2767604be855SAndy Yan
2768604be855SAndy Yan return 0;
2769c66c6d7cSMichael Riesch
2770c66c6d7cSMichael Riesch err_crtcs:
2771c66c6d7cSMichael Riesch vop2_destroy_crtcs(vop2);
2772c66c6d7cSMichael Riesch
2773c66c6d7cSMichael Riesch return ret;
2774604be855SAndy Yan }
2775604be855SAndy Yan
vop2_unbind(struct device * dev,struct device * master,void * data)2776604be855SAndy Yan static void vop2_unbind(struct device *dev, struct device *master, void *data)
2777604be855SAndy Yan {
2778604be855SAndy Yan struct vop2 *vop2 = dev_get_drvdata(dev);
2779604be855SAndy Yan
2780604be855SAndy Yan pm_runtime_disable(dev);
2781604be855SAndy Yan
2782c66c6d7cSMichael Riesch if (vop2->rgb)
2783c66c6d7cSMichael Riesch rockchip_rgb_fini(vop2->rgb);
2784604be855SAndy Yan
2785cddddc06SMichael Riesch vop2_destroy_crtcs(vop2);
2786604be855SAndy Yan }
2787604be855SAndy Yan
2788604be855SAndy Yan const struct component_ops vop2_component_ops = {
2789604be855SAndy Yan .bind = vop2_bind,
2790604be855SAndy Yan .unbind = vop2_unbind,
2791604be855SAndy Yan };
2792604be855SAndy Yan EXPORT_SYMBOL_GPL(vop2_component_ops);
2793