1186f8572SMark Yao /*
2186f8572SMark Yao * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3186f8572SMark Yao *
4186f8572SMark Yao * SPDX-License-Identifier: GPL-2.0+
5186f8572SMark Yao */
6186f8572SMark Yao
7186f8572SMark Yao #include <config.h>
8186f8572SMark Yao #include <common.h>
9186f8572SMark Yao #include <errno.h>
10186f8572SMark Yao #include <malloc.h>
11186f8572SMark Yao #include <fdtdec.h>
12186f8572SMark Yao #include <fdt_support.h>
13186f8572SMark Yao #include <asm/unaligned.h>
14186f8572SMark Yao #include <asm/io.h>
15186f8572SMark Yao #include <linux/list.h>
16186f8572SMark Yao #include <linux/media-bus-format.h>
170a0e1dcdSDamon Ding #include <linux/iopoll.h>
18186f8572SMark Yao #include <clk.h>
19186f8572SMark Yao #include <asm/arch/clock.h>
20186f8572SMark Yao #include <linux/err.h>
21e2bce6e4SKever Yang #include <dm/device.h>
22e2bce6e4SKever Yang #include <dm/read.h>
233a06149eSSandy Huang #include <syscon.h>
24fc8b0d66SDamon Ding #include <regmap.h>
25186f8572SMark Yao
26186f8572SMark Yao #include "rockchip_display.h"
27186f8572SMark Yao #include "rockchip_crtc.h"
28186f8572SMark Yao #include "rockchip_connector.h"
29186f8572SMark Yao #include "rockchip_vop.h"
30186f8572SMark Yao
us_to_vertical_line(struct drm_display_mode * mode,int us)31186f8572SMark Yao static inline int us_to_vertical_line(struct drm_display_mode *mode, int us)
32186f8572SMark Yao {
33186f8572SMark Yao return us * mode->clock / mode->htotal / 1000;
34186f8572SMark Yao }
354b8c2ef1SMark Yao
set_vop_mcu_rs(struct vop * vop,int v)366b898587SDamon Ding static inline void set_vop_mcu_rs(struct vop *vop, int v)
376b898587SDamon Ding {
386b898587SDamon Ding if (dm_gpio_is_valid(&vop->mcu_rs_gpio))
396b898587SDamon Ding dm_gpio_set_value(&vop->mcu_rs_gpio, v);
406b898587SDamon Ding else
416b898587SDamon Ding VOP_CTRL_SET(vop, mcu_rs, v);
426b898587SDamon Ding }
436b898587SDamon Ding
to_vop_csc_mode(enum drm_color_encoding color_encoding,enum drm_color_range color_range)44df0a5c43SDamon Ding static enum vop_csc_format to_vop_csc_mode(enum drm_color_encoding color_encoding,
45df0a5c43SDamon Ding enum drm_color_range color_range)
4679feefb1SSandy Huang {
47df0a5c43SDamon Ding bool full_range = color_range == DRM_COLOR_YCBCR_FULL_RANGE ? 1 : 0;
48df0a5c43SDamon Ding enum vop_csc_format csc_mode = CSC_BT709L;
49df0a5c43SDamon Ding
50df0a5c43SDamon Ding switch (color_encoding) {
51df0a5c43SDamon Ding case DRM_COLOR_YCBCR_BT601:
52df0a5c43SDamon Ding if (full_range)
53df0a5c43SDamon Ding csc_mode = CSC_BT601F;
54df0a5c43SDamon Ding else
55df0a5c43SDamon Ding csc_mode = CSC_BT601L;
56df0a5c43SDamon Ding break;
57df0a5c43SDamon Ding
58df0a5c43SDamon Ding case DRM_COLOR_YCBCR_BT709:
59df0a5c43SDamon Ding if (full_range) {
60df0a5c43SDamon Ding csc_mode = CSC_BT601F;
61df0a5c43SDamon Ding printf("Unsupported bt709f at 10bit csc depth, use bt601f instead\n");
62df0a5c43SDamon Ding } else {
63df0a5c43SDamon Ding csc_mode = CSC_BT709L;
6479feefb1SSandy Huang }
65df0a5c43SDamon Ding break;
66df0a5c43SDamon Ding
67df0a5c43SDamon Ding case DRM_COLOR_YCBCR_BT2020:
68df0a5c43SDamon Ding if (full_range) {
69df0a5c43SDamon Ding csc_mode = CSC_BT601F;
70df0a5c43SDamon Ding printf("Unsupported bt2020f at 10bit csc depth, use bt601f instead\n");
71df0a5c43SDamon Ding } else {
72df0a5c43SDamon Ding csc_mode = CSC_BT2020L;
73df0a5c43SDamon Ding }
74df0a5c43SDamon Ding break;
75df0a5c43SDamon Ding
76df0a5c43SDamon Ding default:
77df0a5c43SDamon Ding printf("Unsuport color_encoding:%d\n", color_encoding);
78df0a5c43SDamon Ding }
79df0a5c43SDamon Ding
80df0a5c43SDamon Ding return csc_mode;
8179feefb1SSandy Huang }
8279feefb1SSandy Huang
is_yuv_output(uint32_t bus_format)8379feefb1SSandy Huang static bool is_yuv_output(uint32_t bus_format)
8479feefb1SSandy Huang {
8579feefb1SSandy Huang switch (bus_format) {
8679feefb1SSandy Huang case MEDIA_BUS_FMT_YUV8_1X24:
8779feefb1SSandy Huang case MEDIA_BUS_FMT_YUV10_1X30:
8879feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
8979feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
90f721bd04SDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
91f721bd04SDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
92f721bd04SDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8:
93f721bd04SDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8:
94f721bd04SDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16:
95f721bd04SDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16:
96f721bd04SDamon Ding case MEDIA_BUS_FMT_UYVY8_1X16:
97f721bd04SDamon Ding case MEDIA_BUS_FMT_VYUY8_1X16:
9879feefb1SSandy Huang return true;
9979feefb1SSandy Huang default:
10079feefb1SSandy Huang return false;
10179feefb1SSandy Huang }
10279feefb1SSandy Huang }
10379feefb1SSandy Huang
is_uv_swap(uint32_t bus_format,uint32_t output_mode)10479feefb1SSandy Huang static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode)
10579feefb1SSandy Huang {
10679feefb1SSandy Huang /*
10779feefb1SSandy Huang * FIXME:
10879feefb1SSandy Huang *
10979feefb1SSandy Huang * There is no media type for YUV444 output,
11079feefb1SSandy Huang * so when out_mode is AAAA or P888, assume output is YUV444 on
11179feefb1SSandy Huang * yuv format.
11279feefb1SSandy Huang *
11379feefb1SSandy Huang * From H/W testing, YUV444 mode need a rb swap.
11479feefb1SSandy Huang */
11579feefb1SSandy Huang if ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
11679feefb1SSandy Huang bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
11779feefb1SSandy Huang (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
11879feefb1SSandy Huang output_mode == ROCKCHIP_OUT_MODE_P888))
11979feefb1SSandy Huang return true;
12079feefb1SSandy Huang else
12179feefb1SSandy Huang return false;
12279feefb1SSandy Huang }
12379feefb1SSandy Huang
is_rb_swap(uint32_t bus_format,uint32_t output_mode)12480fbe788SDamon Ding static bool is_rb_swap(uint32_t bus_format, uint32_t output_mode)
12580fbe788SDamon Ding {
12680fbe788SDamon Ding /*
12780fbe788SDamon Ding * The default component order of serial rgb3x8 formats
12880fbe788SDamon Ding * is BGR. So it is needed to enable RB swap.
12980fbe788SDamon Ding */
130b7b383ebSDamon Ding if (bus_format == MEDIA_BUS_FMT_RGB888_3X8 ||
131b7b383ebSDamon Ding bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8)
13280fbe788SDamon Ding return true;
13380fbe788SDamon Ding else
13480fbe788SDamon Ding return false;
13580fbe788SDamon Ding }
13680fbe788SDamon Ding
is_yc_swap(uint32_t bus_format)1370c4bb35cSDamon Ding static bool is_yc_swap(uint32_t bus_format)
1380c4bb35cSDamon Ding {
1390c4bb35cSDamon Ding switch (bus_format) {
1400c4bb35cSDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16:
1410c4bb35cSDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16:
1420c4bb35cSDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
1430c4bb35cSDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
1440c4bb35cSDamon Ding return true;
1450c4bb35cSDamon Ding default:
1460c4bb35cSDamon Ding return false;
1470c4bb35cSDamon Ding }
1480c4bb35cSDamon Ding }
1490c4bb35cSDamon Ding
rockchip_vop_init_gamma(struct vop * vop,struct display_state * state)150186f8572SMark Yao static int rockchip_vop_init_gamma(struct vop *vop, struct display_state *state)
151186f8572SMark Yao {
152186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state;
153186f8572SMark Yao struct connector_state *conn_state = &state->conn_state;
154186f8572SMark Yao u32 *lut = conn_state->gamma.lut;
155186f8572SMark Yao fdt_size_t lut_size;
156186f8572SMark Yao int i, lut_len;
157186f8572SMark Yao u32 *lut_regs;
158186f8572SMark Yao
159186f8572SMark Yao if (!conn_state->gamma.lut)
160186f8572SMark Yao return 0;
161186f8572SMark Yao
162e2bce6e4SKever Yang i = dev_read_stringlist_search(crtc_state->dev, "reg-names", "gamma_lut");
163186f8572SMark Yao if (i < 0) {
164186f8572SMark Yao printf("Warning: vop not support gamma\n");
165186f8572SMark Yao return 0;
166186f8572SMark Yao }
167e2bce6e4SKever Yang lut_regs = (u32 *)dev_read_addr_size(crtc_state->dev, "reg", &lut_size);
168186f8572SMark Yao if (lut_regs == (u32 *)FDT_ADDR_T_NONE) {
169186f8572SMark Yao printf("failed to get gamma lut register\n");
170186f8572SMark Yao return 0;
171186f8572SMark Yao }
172186f8572SMark Yao lut_len = lut_size / 4;
173186f8572SMark Yao if (lut_len != 256 && lut_len != 1024) {
174186f8572SMark Yao printf("Warning: unsupport gamma lut table[%d]\n", lut_len);
175186f8572SMark Yao return 0;
176186f8572SMark Yao }
177186f8572SMark Yao
178186f8572SMark Yao if (conn_state->gamma.size != lut_len) {
179186f8572SMark Yao int size = conn_state->gamma.size;
180186f8572SMark Yao u32 j, r, g, b, color;
181186f8572SMark Yao
182186f8572SMark Yao for (i = 0; i < lut_len; i++) {
183186f8572SMark Yao j = i * size / lut_len;
184186f8572SMark Yao r = lut[j] / size / size * lut_len / size;
185186f8572SMark Yao g = lut[j] / size % size * lut_len / size;
186186f8572SMark Yao b = lut[j] % size * lut_len / size;
187186f8572SMark Yao color = r * lut_len * lut_len + g * lut_len + b;
188186f8572SMark Yao
189186f8572SMark Yao writel(color, lut_regs + (i << 2));
190186f8572SMark Yao }
191186f8572SMark Yao } else {
192186f8572SMark Yao for (i = 0; i < lut_len; i++)
193186f8572SMark Yao writel(lut[i], lut_regs + (i << 2));
194186f8572SMark Yao }
195186f8572SMark Yao
196186f8572SMark Yao VOP_CTRL_SET(vop, dsp_lut_en, 1);
197186f8572SMark Yao VOP_CTRL_SET(vop, update_gamma_lut, 1);
198186f8572SMark Yao
199186f8572SMark Yao return 0;
200186f8572SMark Yao }
201186f8572SMark Yao
vop_post_config(struct display_state * state,struct vop * vop)2028a2a3a29SSandy Huang static void vop_post_config(struct display_state *state, struct vop *vop)
2038a2a3a29SSandy Huang {
2048a2a3a29SSandy Huang struct connector_state *conn_state = &state->conn_state;
2058a2a3a29SSandy Huang struct drm_display_mode *mode = &conn_state->mode;
2068a2a3a29SSandy Huang u16 vtotal = mode->crtc_vtotal;
2078a2a3a29SSandy Huang u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
2088a2a3a29SSandy Huang u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
2098a2a3a29SSandy Huang u16 hdisplay = mode->crtc_hdisplay;
2108a2a3a29SSandy Huang u16 vdisplay = mode->crtc_vdisplay;
2118a2a3a29SSandy Huang u16 hsize = hdisplay * (conn_state->overscan.left_margin + conn_state->overscan.right_margin) / 200;
2128a2a3a29SSandy Huang u16 vsize = vdisplay * (conn_state->overscan.top_margin + conn_state->overscan.bottom_margin) / 200;
2138a2a3a29SSandy Huang u16 hact_end, vact_end;
2148a2a3a29SSandy Huang u32 val;
2158a2a3a29SSandy Huang
2168a2a3a29SSandy Huang if (mode->flags & DRM_MODE_FLAG_INTERLACE)
2178a2a3a29SSandy Huang vsize = round_down(vsize, 2);
2188a2a3a29SSandy Huang
2198a2a3a29SSandy Huang hact_st += hdisplay * (100 - conn_state->overscan.left_margin) / 200;
2208a2a3a29SSandy Huang hact_end = hact_st + hsize;
2218a2a3a29SSandy Huang val = hact_st << 16;
2228a2a3a29SSandy Huang val |= hact_end;
2238a2a3a29SSandy Huang
2248a2a3a29SSandy Huang VOP_CTRL_SET(vop, hpost_st_end, val);
2258a2a3a29SSandy Huang vact_st += vdisplay * (100 - conn_state->overscan.top_margin) / 200;
2268a2a3a29SSandy Huang vact_end = vact_st + vsize;
2278a2a3a29SSandy Huang val = vact_st << 16;
2288a2a3a29SSandy Huang val |= vact_end;
2298a2a3a29SSandy Huang VOP_CTRL_SET(vop, vpost_st_end, val);
2308a2a3a29SSandy Huang val = scl_cal_scale2(vdisplay, vsize) << 16;
2318a2a3a29SSandy Huang val |= scl_cal_scale2(hdisplay, hsize);
2328a2a3a29SSandy Huang VOP_CTRL_SET(vop, post_scl_factor, val);
2338a2a3a29SSandy Huang #define POST_HORIZONTAL_SCALEDOWN_EN(x) ((x) << 0)
2348a2a3a29SSandy Huang #define POST_VERTICAL_SCALEDOWN_EN(x) ((x) << 1)
2358a2a3a29SSandy Huang VOP_CTRL_SET(vop, post_scl_ctrl,
2368a2a3a29SSandy Huang POST_HORIZONTAL_SCALEDOWN_EN(hdisplay != hsize) |
2378a2a3a29SSandy Huang POST_VERTICAL_SCALEDOWN_EN(vdisplay != vsize));
2388a2a3a29SSandy Huang if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
2398a2a3a29SSandy Huang u16 vact_st_f1 = vtotal + vact_st + 1;
2408a2a3a29SSandy Huang u16 vact_end_f1 = vact_st_f1 + vsize;
2418a2a3a29SSandy Huang
2428a2a3a29SSandy Huang val = vact_st_f1 << 16 | vact_end_f1;
2438a2a3a29SSandy Huang VOP_CTRL_SET(vop, vpost_st_end_f1, val);
2448a2a3a29SSandy Huang }
2458a2a3a29SSandy Huang }
2468a2a3a29SSandy Huang
vop_mcu_bypass_mode_setup(struct display_state * state,struct vop * vop)24772f233a9SDamon Ding static void vop_mcu_bypass_mode_setup(struct display_state *state, struct vop *vop)
24872f233a9SDamon Ding {
24972f233a9SDamon Ding /*
25072f233a9SDamon Ding * If mcu_hold_mode is 1, set 1 to mcu_frame_st will
25172f233a9SDamon Ding * refresh one frame from ddr. So mcu_frame_st is needed
25272f233a9SDamon Ding * to be initialized as 0.
25372f233a9SDamon Ding */
25472f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_frame_st, 0);
25572f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_clk_sel, 1);
25672f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_type, 1);
25772f233a9SDamon Ding
25872f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_hold_mode, 1);
25972f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_pix_total, 53);
26072f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_cs_pst, 6);
26172f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_cs_pend, 48);
26272f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_rw_pst, 12);
26372f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_rw_pend, 30);
26472f233a9SDamon Ding }
26572f233a9SDamon Ding
vop_mcu_mode_setup(struct display_state * state,struct vop * vop)26672f233a9SDamon Ding static void vop_mcu_mode_setup(struct display_state *state, struct vop *vop)
26767b9012cSSandy Huang {
26867b9012cSSandy Huang struct crtc_state *crtc_state = &state->crtc_state;
26967b9012cSSandy Huang
27072f233a9SDamon Ding /*
27172f233a9SDamon Ding * If mcu_hold_mode is 1, set 1 to mcu_frame_st will
27272f233a9SDamon Ding * refresh one frame from ddr. So mcu_frame_st is needed
27372f233a9SDamon Ding * to be initialized as 0.
27472f233a9SDamon Ding */
27572f233a9SDamon Ding VOP_CTRL_SET(vop, mcu_frame_st, 0);
27667b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_clk_sel, 1);
27767b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_type, 1);
27867b9012cSSandy Huang
27967b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_hold_mode, 1);
28067b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_pix_total, crtc_state->mcu_timing.mcu_pix_total);
28167b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_cs_pst, crtc_state->mcu_timing.mcu_cs_pst);
28267b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_cs_pend, crtc_state->mcu_timing.mcu_cs_pend);
28367b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_pst, crtc_state->mcu_timing.mcu_rw_pst);
28467b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_pend, crtc_state->mcu_timing.mcu_rw_pend);
28567b9012cSSandy Huang }
28667b9012cSSandy Huang
rockchip_vop_preinit(struct display_state * state)287cf53642aSSandy Huang static int rockchip_vop_preinit(struct display_state *state)
288cf53642aSSandy Huang {
289cf53642aSSandy Huang const struct vop_data *vop_data = state->crtc_state.crtc->data;
290cf53642aSSandy Huang
291cf53642aSSandy Huang state->crtc_state.max_output = vop_data->max_output;
292cf53642aSSandy Huang
293cf53642aSSandy Huang return 0;
294cf53642aSSandy Huang }
295cf53642aSSandy Huang
vop_mode_done(struct vop * vop)2960a0e1dcdSDamon Ding static u32 vop_mode_done(struct vop *vop)
2970a0e1dcdSDamon Ding {
2980a0e1dcdSDamon Ding return VOP_CTRL_GET(vop, out_mode);
2990a0e1dcdSDamon Ding }
3000a0e1dcdSDamon Ding
vop_set_out_mode(struct vop * vop,u32 mode)3010a0e1dcdSDamon Ding static void vop_set_out_mode(struct vop *vop, u32 mode)
3020a0e1dcdSDamon Ding {
3030a0e1dcdSDamon Ding int ret;
3040a0e1dcdSDamon Ding u32 val;
3050a0e1dcdSDamon Ding
3060a0e1dcdSDamon Ding VOP_CTRL_SET(vop, out_mode, mode);
3070a0e1dcdSDamon Ding vop_cfg_done(vop);
3080a0e1dcdSDamon Ding ret = readx_poll_timeout(vop_mode_done, vop, val, val == mode, 1000 * 1000);
3090a0e1dcdSDamon Ding if (ret)
3100a0e1dcdSDamon Ding printf("wait for setting mode 0x%x timeout\n", mode);
3110a0e1dcdSDamon Ding }
3120a0e1dcdSDamon Ding
rockchip_vop_init(struct display_state * state)313186f8572SMark Yao static int rockchip_vop_init(struct display_state *state)
314186f8572SMark Yao {
315186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state;
316186f8572SMark Yao struct connector_state *conn_state = &state->conn_state;
317186f8572SMark Yao struct drm_display_mode *mode = &conn_state->mode;
318186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc;
319186f8572SMark Yao const struct vop_data *vop_data = crtc->data;
320186f8572SMark Yao struct vop *vop;
321fc8b0d66SDamon Ding struct regmap *map;
322ccd843b9SSandy Huang u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
323ccd843b9SSandy Huang u16 hdisplay = mode->crtc_hdisplay;
324ccd843b9SSandy Huang u16 htotal = mode->crtc_htotal;
325ccd843b9SSandy Huang u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
326186f8572SMark Yao u16 hact_end = hact_st + hdisplay;
327ccd843b9SSandy Huang u16 vdisplay = mode->crtc_vdisplay;
328ccd843b9SSandy Huang u16 vtotal = mode->crtc_vtotal;
329ccd843b9SSandy Huang u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
330ccd843b9SSandy Huang u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
331186f8572SMark Yao u16 vact_end = vact_st + vdisplay;
332960080c9SSandy Huang u32 val, act_end;
3334b8c2ef1SMark Yao int ret;
33479feefb1SSandy Huang bool yuv_overlay = false, post_r2y_en = false, post_y2r_en = false;
33579feefb1SSandy Huang u16 post_csc_mode;
3360c4bb35cSDamon Ding bool dclk_inv, yc_swap = false;
3372264c88bSDamon Ding char output_type_name[30] = {0};
338186f8572SMark Yao
339186f8572SMark Yao vop = malloc(sizeof(*vop));
340186f8572SMark Yao if (!vop)
341186f8572SMark Yao return -ENOMEM;
342186f8572SMark Yao memset(vop, 0, sizeof(*vop));
343186f8572SMark Yao
344186f8572SMark Yao crtc_state->private = vop;
345fc8b0d66SDamon Ding vop->data = vop_data;
346e2bce6e4SKever Yang vop->regs = dev_read_addr_ptr(crtc_state->dev);
347186f8572SMark Yao vop->regsbak = malloc(vop_data->reg_len);
348186f8572SMark Yao vop->win = vop_data->win;
349186f8572SMark Yao vop->win_offset = vop_data->win_offset;
350186f8572SMark Yao vop->ctrl = vop_data->ctrl;
3513a06149eSSandy Huang
352fc8b0d66SDamon Ding map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,grf");
353e9058b80SDamon Ding if (!IS_ERR_OR_NULL(map)) {
354fc8b0d66SDamon Ding vop->grf_ctrl = regmap_get_range(map, 0);
355fc8b0d66SDamon Ding if (vop->grf_ctrl <= 0)
356fc8b0d66SDamon Ding printf("%s: Get syscon grf failed (ret=%p)\n", __func__, vop->grf_ctrl);
357e9058b80SDamon Ding }
35872f233a9SDamon Ding map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,vo0-grf");
359e9058b80SDamon Ding if (!IS_ERR_OR_NULL(map)) {
36072f233a9SDamon Ding vop->vo0_grf_ctrl = regmap_get_range(map, 0);
36172f233a9SDamon Ding if (vop->vo0_grf_ctrl <= 0)
36272f233a9SDamon Ding printf("%s: Get syscon vo0_grf failed (ret=%p)\n", __func__, vop->vo0_grf_ctrl);
363e9058b80SDamon Ding }
364fc8b0d66SDamon Ding
365186f8572SMark Yao vop->line_flag = vop_data->line_flag;
366b7618fd3SSandy Huang vop->csc_table = vop_data->csc_table;
367b7618fd3SSandy Huang vop->win_csc = vop_data->win_csc;
368186f8572SMark Yao vop->version = vop_data->version;
369186f8572SMark Yao
3702264c88bSDamon Ding printf("VOP:0x%8p update mode to: %dx%d%s%d, type:%s\n",
3712264c88bSDamon Ding vop->regs, mode->crtc_hdisplay, mode->vdisplay,
3722264c88bSDamon Ding mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
373e32f1beaSDamon Ding drm_mode_vrefresh(mode),
3742264c88bSDamon Ding rockchip_get_output_if_name(conn_state->output_if, output_type_name));
3752264c88bSDamon Ding
376cadc8d74SKever Yang /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
377cadc8d74SKever Yang ret = clk_set_defaults(crtc_state->dev);
378cadc8d74SKever Yang if (ret)
379cadc8d74SKever Yang debug("%s clk_set_defaults failed %d\n", __func__, ret);
380186f8572SMark Yao
38172f233a9SDamon Ding ret = clk_get_by_name(crtc_state->dev, "dclk_vop", &crtc_state->dclk);
382186f8572SMark Yao if (!ret)
38372f233a9SDamon Ding ret = clk_set_rate(&crtc_state->dclk, mode->crtc_clock * 1000);
384186f8572SMark Yao if (IS_ERR_VALUE(ret)) {
3854b8c2ef1SMark Yao printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
386186f8572SMark Yao return ret;
387186f8572SMark Yao }
388ddef0aacSDamon Ding printf("VOP:0x%8p set crtc_clock to %dKHz, get %dHz\n", vop->regs, mode->crtc_clock, ret);
389186f8572SMark Yao
390186f8572SMark Yao memcpy(vop->regsbak, vop->regs, vop_data->reg_len);
391186f8572SMark Yao
3924b8c2ef1SMark Yao rockchip_vop_init_gamma(vop, state);
393186f8572SMark Yao
3946b898587SDamon Ding ret = gpio_request_by_name(crtc_state->dev, "mcu-rs-gpios",
3956b898587SDamon Ding 0, &vop->mcu_rs_gpio, GPIOD_IS_OUT);
3966b898587SDamon Ding if (ret && ret != -ENOENT)
3976b898587SDamon Ding printf("%s: Cannot get mcu rs GPIO: %d\n", __func__, ret);
3986b898587SDamon Ding
399186f8572SMark Yao VOP_CTRL_SET(vop, global_regdone_en, 1);
4002b34f307SMark Yao VOP_CTRL_SET(vop, axi_outstanding_max_num, 30);
4012b34f307SMark Yao VOP_CTRL_SET(vop, axi_max_outstanding_en, 1);
402b0dbe9a0SSandy Huang VOP_CTRL_SET(vop, reg_done_frm, 1);
403186f8572SMark Yao VOP_CTRL_SET(vop, win_gate[0], 1);
404186f8572SMark Yao VOP_CTRL_SET(vop, win_gate[1], 1);
4052b34f307SMark Yao VOP_CTRL_SET(vop, win_channel[0], 0x12);
4062b34f307SMark Yao VOP_CTRL_SET(vop, win_channel[1], 0x34);
4072b34f307SMark Yao VOP_CTRL_SET(vop, win_channel[2], 0x56);
408186f8572SMark Yao VOP_CTRL_SET(vop, dsp_blank, 0);
409186f8572SMark Yao
410de3f896fSChaoyi Chen if (vop->version == VOP_VERSION_RK3576_LITE) {
41172f233a9SDamon Ding VOP_GRF_SET(vop, grf_ctrl, grf_vopl_sel, 1);
41272f233a9SDamon Ding VOP_CTRL_SET(vop, enable, 1);
41372f233a9SDamon Ding }
41472f233a9SDamon Ding
41513f658dcSDamon Ding dclk_inv = (conn_state->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0;
41654f7137bSDamon Ding /* For improving signal quality, dclk need to be inverted by default on rv1106. */
417de3f896fSChaoyi Chen if (vop->version == VOP_VERSION_RV1106)
41854f7137bSDamon Ding dclk_inv = !dclk_inv;
4193a06149eSSandy Huang VOP_CTRL_SET(vop, dclk_pol, dclk_inv);
4203a06149eSSandy Huang
421186f8572SMark Yao val = 0x8;
422186f8572SMark Yao val |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
423186f8572SMark Yao val |= (mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
424186f8572SMark Yao VOP_CTRL_SET(vop, pin_pol, val);
425186f8572SMark Yao
426186f8572SMark Yao switch (conn_state->type) {
427186f8572SMark Yao case DRM_MODE_CONNECTOR_LVDS:
428186f8572SMark Yao VOP_CTRL_SET(vop, rgb_en, 1);
429186f8572SMark Yao VOP_CTRL_SET(vop, rgb_pin_pol, val);
4303a06149eSSandy Huang VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv);
4313a06149eSSandy Huang VOP_CTRL_SET(vop, lvds_en, 1);
4323a06149eSSandy Huang VOP_CTRL_SET(vop, lvds_pin_pol, val);
4333a06149eSSandy Huang VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv);
434fc8b0d66SDamon Ding if (!IS_ERR_OR_NULL(vop->grf_ctrl))
435fc8b0d66SDamon Ding VOP_GRF_SET(vop, grf_ctrl, grf_dclk_inv, dclk_inv);
4360c4bb35cSDamon Ding if (conn_state->output_if & VOP_OUTPUT_IF_BT1120) {
4370c4bb35cSDamon Ding VOP_CTRL_SET(vop, bt1120_en, 1);
4380c4bb35cSDamon Ding yc_swap = is_yc_swap(conn_state->bus_format);
4390c4bb35cSDamon Ding VOP_CTRL_SET(vop, bt1120_yc_swap, yc_swap);
4400c4bb35cSDamon Ding VOP_CTRL_SET(vop, yuv_clip, 1);
4410c4bb35cSDamon Ding } else if (conn_state->output_if & VOP_OUTPUT_IF_BT656) {
4420c4bb35cSDamon Ding VOP_CTRL_SET(vop, bt656_en, 1);
4430c4bb35cSDamon Ding yc_swap = is_yc_swap(conn_state->bus_format);
4440c4bb35cSDamon Ding VOP_CTRL_SET(vop, bt1120_yc_swap, yc_swap);
4450c4bb35cSDamon Ding }
446186f8572SMark Yao break;
447186f8572SMark Yao case DRM_MODE_CONNECTOR_eDP:
448186f8572SMark Yao VOP_CTRL_SET(vop, edp_en, 1);
449186f8572SMark Yao VOP_CTRL_SET(vop, edp_pin_pol, val);
4503a06149eSSandy Huang VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv);
45172f233a9SDamon Ding VOP_CTRL_SET(vop, inf_out_en, 1);
45249d669f5SDamon Ding VOP_CTRL_SET(vop, out_dresetn, 1);
45372f233a9SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_edp_ch_sel, 1);
454186f8572SMark Yao break;
455186f8572SMark Yao case DRM_MODE_CONNECTOR_HDMIA:
456186f8572SMark Yao VOP_CTRL_SET(vop, hdmi_en, 1);
457186f8572SMark Yao VOP_CTRL_SET(vop, hdmi_pin_pol, val);
4583a06149eSSandy Huang VOP_CTRL_SET(vop, hdmi_dclk_pol, 1);
45972f233a9SDamon Ding VOP_CTRL_SET(vop, inf_out_en, 1);
46049d669f5SDamon Ding VOP_CTRL_SET(vop, out_dresetn, 1);
46172f233a9SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_ch_sel, 1);
46272f233a9SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_pin_pol, val);
46349d669f5SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_1to4_en, 1);
464186f8572SMark Yao break;
465186f8572SMark Yao case DRM_MODE_CONNECTOR_DSI:
46672f233a9SDamon Ding /*
46772f233a9SDamon Ding * RK3576 DSI CTRL hsync/vsync polarity is positive and can't update,
46872f233a9SDamon Ding * so set VOP hsync/vsync polarity as positive by default.
46972f233a9SDamon Ding */
470de3f896fSChaoyi Chen if (vop->version == VOP_VERSION_RK3576_LITE)
47172f233a9SDamon Ding val = BIT(HSYNC_POSITIVE) | BIT(VSYNC_POSITIVE);
472186f8572SMark Yao VOP_CTRL_SET(vop, mipi_en, 1);
473186f8572SMark Yao VOP_CTRL_SET(vop, mipi_pin_pol, val);
4743a06149eSSandy Huang VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv);
475186f8572SMark Yao VOP_CTRL_SET(vop, mipi_dual_channel_en,
476bee25ee6SGuochun Huang !!(conn_state->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE));
477186f8572SMark Yao VOP_CTRL_SET(vop, data01_swap,
478bee25ee6SGuochun Huang !!(conn_state->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) ||
479289af5f4SSandy Huang crtc_state->dual_channel_swap);
48072f233a9SDamon Ding VOP_CTRL_SET(vop, inf_out_en, 1);
48149d669f5SDamon Ding VOP_CTRL_SET(vop, out_dresetn, 1);
48272f233a9SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_ch_sel, 1);
48349d669f5SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_mode, 0);
48472f233a9SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_pin_pol, val);
48572f233a9SDamon Ding VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_1to4_en, 1);
486186f8572SMark Yao break;
4873a06149eSSandy Huang case DRM_MODE_CONNECTOR_DisplayPort:
4883a06149eSSandy Huang VOP_CTRL_SET(vop, dp_dclk_pol, 0);
4893a06149eSSandy Huang VOP_CTRL_SET(vop, dp_pin_pol, val);
4903a06149eSSandy Huang VOP_CTRL_SET(vop, dp_en, 1);
4913a06149eSSandy Huang break;
49209b01f9eSAlgea Cao case DRM_MODE_CONNECTOR_TV:
49309b01f9eSAlgea Cao if (vdisplay == CVBS_PAL_VDISPLAY)
49409b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_sw_mode, 1);
49509b01f9eSAlgea Cao else
49609b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_sw_mode, 0);
49709b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_dclk_pol, 1);
49809b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_dclk_en, 1);
49909b01f9eSAlgea Cao /* use the same pol reg with hdmi */
50009b01f9eSAlgea Cao VOP_CTRL_SET(vop, hdmi_pin_pol, val);
50109b01f9eSAlgea Cao VOP_CTRL_SET(vop, sw_genlock, 1);
50209b01f9eSAlgea Cao VOP_CTRL_SET(vop, sw_uv_offset_en, 1);
50309b01f9eSAlgea Cao VOP_CTRL_SET(vop, dither_up, 1);
50409b01f9eSAlgea Cao break;
505186f8572SMark Yao default:
506186f8572SMark Yao printf("unsupport connector_type[%d]\n", conn_state->type);
507186f8572SMark Yao }
508186f8572SMark Yao
5090c4bb35cSDamon Ding if ((conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
5100c4bb35cSDamon Ding !(vop_data->feature & VOP_FEATURE_OUTPUT_10BIT)) ||
511de3f896fSChaoyi Chen (vop->version >= VOP_VERSION_RV1106 && vop->version < VOP_VERSION_RK3288 &&
5120c4bb35cSDamon Ding conn_state->output_if & VOP_OUTPUT_IF_BT656))
513186f8572SMark Yao conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
514186f8572SMark Yao
515186f8572SMark Yao switch (conn_state->bus_format) {
516186f8572SMark Yao case MEDIA_BUS_FMT_RGB565_1X16:
517186f8572SMark Yao val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB565);
518186f8572SMark Yao break;
519186f8572SMark Yao case MEDIA_BUS_FMT_RGB666_1X18:
520186f8572SMark Yao case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
5219c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
5229c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:
523186f8572SMark Yao val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB666);
524186f8572SMark Yao break;
52579feefb1SSandy Huang case MEDIA_BUS_FMT_YUV8_1X24:
52679feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
52779feefb1SSandy Huang val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(1);
52879feefb1SSandy Huang break;
52979feefb1SSandy Huang case MEDIA_BUS_FMT_YUV10_1X30:
53079feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
53179feefb1SSandy Huang val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
53279feefb1SSandy Huang break;
533186f8572SMark Yao case MEDIA_BUS_FMT_RGB888_1X24:
5349c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
5359c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
536186f8572SMark Yao default:
537186f8572SMark Yao val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
538186f8572SMark Yao break;
539186f8572SMark Yao }
540186f8572SMark Yao if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA)
541186f8572SMark Yao val |= PRE_DITHER_DOWN_EN(0);
542186f8572SMark Yao else
543186f8572SMark Yao val |= PRE_DITHER_DOWN_EN(1);
544186f8572SMark Yao val |= DITHER_DOWN_MODE_SEL(DITHER_DOWN_ALLEGRO);
545186f8572SMark Yao VOP_CTRL_SET(vop, dither_down, val);
546186f8572SMark Yao
54779feefb1SSandy Huang VOP_CTRL_SET(vop, dclk_ddr,
54879feefb1SSandy Huang conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
54979feefb1SSandy Huang VOP_CTRL_SET(vop, hdmi_dclk_out_en,
55079feefb1SSandy Huang conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
55179feefb1SSandy Huang
55280fbe788SDamon Ding if (is_uv_swap(conn_state->bus_format, conn_state->output_mode) ||
55380fbe788SDamon Ding is_rb_swap(conn_state->bus_format, conn_state->output_mode))
55432328971SDamon Ding VOP_CTRL_SET(vop, dsp_rb_swap, 1);
55579feefb1SSandy Huang else
55679feefb1SSandy Huang VOP_CTRL_SET(vop, dsp_data_swap, 0);
55779feefb1SSandy Huang
558efd10ad6SDamon Ding /*
559efd10ad6SDamon Ding * For RK3576 vopl, rg_swap and rb_swap need to be enabled in
560efd10ad6SDamon Ding * YUV444 bus_format.
561efd10ad6SDamon Ding */
562de3f896fSChaoyi Chen if (vop->version == VOP_VERSION_RK3576_LITE) {
563efd10ad6SDamon Ding if (conn_state->bus_format == MEDIA_BUS_FMT_YUV8_1X24)
564efd10ad6SDamon Ding VOP_CTRL_SET(vop, dsp_data_swap, DSP_RG_SWAP | DSP_RB_SWAP);
565efd10ad6SDamon Ding }
566efd10ad6SDamon Ding
567186f8572SMark Yao VOP_CTRL_SET(vop, out_mode, conn_state->output_mode);
56879feefb1SSandy Huang
56979feefb1SSandy Huang if (VOP_CTRL_SUPPORT(vop, overlay_mode)) {
57079feefb1SSandy Huang yuv_overlay = is_yuv_output(conn_state->bus_format);
57179feefb1SSandy Huang VOP_CTRL_SET(vop, overlay_mode, yuv_overlay);
57279feefb1SSandy Huang }
57379feefb1SSandy Huang /*
57479feefb1SSandy Huang * todo: r2y for win csc
57579feefb1SSandy Huang */
57679feefb1SSandy Huang VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(conn_state->bus_format));
57779feefb1SSandy Huang
57879feefb1SSandy Huang if (yuv_overlay) {
57979feefb1SSandy Huang if (!is_yuv_output(conn_state->bus_format))
58079feefb1SSandy Huang post_y2r_en = true;
58179feefb1SSandy Huang } else {
58279feefb1SSandy Huang if (is_yuv_output(conn_state->bus_format))
58379feefb1SSandy Huang post_r2y_en = true;
58479feefb1SSandy Huang }
58579feefb1SSandy Huang
586b7618fd3SSandy Huang crtc_state->yuv_overlay = yuv_overlay;
587df0a5c43SDamon Ding post_csc_mode = to_vop_csc_mode(conn_state->color_encoding, conn_state->color_range);
58879feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_r2y_en, post_r2y_en);
58979feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_y2r_en, post_y2r_en);
59079feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, post_csc_mode);
59179feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, post_csc_mode);
59279feefb1SSandy Huang
59379feefb1SSandy Huang /*
59479feefb1SSandy Huang * Background color is 10bit depth if vop version >= 3.5
59579feefb1SSandy Huang */
59679feefb1SSandy Huang if (!is_yuv_output(conn_state->bus_format))
59779feefb1SSandy Huang val = 0;
598de3f896fSChaoyi Chen else if (vop->version == VOP_VERSION_RK3576_LITE)
5994fd29a21SDamon Ding val = 0;
600de3f896fSChaoyi Chen else if (vop->version >= VOP_VERSION_RK3399_BIG)
60179feefb1SSandy Huang val = 0x20010200;
60279feefb1SSandy Huang else
60379feefb1SSandy Huang val = 0x801080;
60479feefb1SSandy Huang VOP_CTRL_SET(vop, dsp_background, val);
60579feefb1SSandy Huang
606186f8572SMark Yao VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
607186f8572SMark Yao val = hact_st << 16;
608186f8572SMark Yao val |= hact_end;
609186f8572SMark Yao VOP_CTRL_SET(vop, hact_st_end, val);
610186f8572SMark Yao val = vact_st << 16;
611186f8572SMark Yao val |= vact_end;
612186f8572SMark Yao VOP_CTRL_SET(vop, vact_st_end, val);
613ccd843b9SSandy Huang if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
614ccd843b9SSandy Huang u16 vact_st_f1 = vtotal + vact_st + 1;
615ccd843b9SSandy Huang u16 vact_end_f1 = vact_st_f1 + vdisplay;
616ccd843b9SSandy Huang
617ccd843b9SSandy Huang val = vact_st_f1 << 16 | vact_end_f1;
618ccd843b9SSandy Huang VOP_CTRL_SET(vop, vact_st_end_f1, val);
619ccd843b9SSandy Huang
620ccd843b9SSandy Huang val = vtotal << 16 | (vtotal + vsync_len);
621ccd843b9SSandy Huang VOP_CTRL_SET(vop, vs_st_end_f1, val);
622ccd843b9SSandy Huang VOP_CTRL_SET(vop, dsp_interlace, 1);
623ccd843b9SSandy Huang VOP_CTRL_SET(vop, p2i_en, 1);
624ccd843b9SSandy Huang vtotal += vtotal + 1;
625960080c9SSandy Huang act_end = vact_end_f1;
626ccd843b9SSandy Huang } else {
627ccd843b9SSandy Huang VOP_CTRL_SET(vop, dsp_interlace, 0);
628ccd843b9SSandy Huang VOP_CTRL_SET(vop, p2i_en, 0);
629960080c9SSandy Huang act_end = vact_end;
630ccd843b9SSandy Huang }
631ccd843b9SSandy Huang VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len);
6328a2a3a29SSandy Huang vop_post_config(state, vop);
633ccd843b9SSandy Huang VOP_CTRL_SET(vop, core_dclk_div,
6340c4bb35cSDamon Ding !!(mode->flags & DRM_MODE_FLAG_DBLCLK) ||
6350c4bb35cSDamon Ding conn_state->output_if & VOP_OUTPUT_IF_BT656);
6368a2a3a29SSandy Huang
637960080c9SSandy Huang VOP_LINE_FLAG_SET(vop, line_flag_num[0], act_end - 3);
638186f8572SMark Yao VOP_LINE_FLAG_SET(vop, line_flag_num[1],
639960080c9SSandy Huang act_end - us_to_vertical_line(mode, 1000));
6400a0e1dcdSDamon Ding
6410a0e1dcdSDamon Ding if (state->crtc_state.mcu_timing.mcu_pix_total > 0) {
642de3f896fSChaoyi Chen if (vop->version >= VOP_VERSION_RK3576_LITE) {
6430a0e1dcdSDamon Ding VOP_CTRL_SET(vop, standby, 0);
6440a0e1dcdSDamon Ding vop_set_out_mode(vop, conn_state->output_mode);
6450a0e1dcdSDamon Ding }
64672f233a9SDamon Ding vop_mcu_mode_setup(state, vop);
6470a0e1dcdSDamon Ding }
6480a0e1dcdSDamon Ding
649186f8572SMark Yao vop_cfg_done(vop);
650186f8572SMark Yao
651186f8572SMark Yao return 0;
652186f8572SMark Yao }
653186f8572SMark Yao
scl_vop_cal_scale(enum scale_mode mode,uint32_t src,uint32_t dst,bool is_horizontal,int vsu_mode,int * vskiplines)654186f8572SMark Yao static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
655186f8572SMark Yao uint32_t dst, bool is_horizontal,
656186f8572SMark Yao int vsu_mode, int *vskiplines)
657186f8572SMark Yao {
658186f8572SMark Yao uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
659186f8572SMark Yao
660186f8572SMark Yao if (is_horizontal) {
661186f8572SMark Yao if (mode == SCALE_UP)
662186f8572SMark Yao val = GET_SCL_FT_BIC(src, dst);
663186f8572SMark Yao else if (mode == SCALE_DOWN)
664186f8572SMark Yao val = GET_SCL_FT_BILI_DN(src, dst);
665186f8572SMark Yao } else {
666186f8572SMark Yao if (mode == SCALE_UP) {
667186f8572SMark Yao if (vsu_mode == SCALE_UP_BIL)
668186f8572SMark Yao val = GET_SCL_FT_BILI_UP(src, dst);
669186f8572SMark Yao else
670186f8572SMark Yao val = GET_SCL_FT_BIC(src, dst);
671186f8572SMark Yao } else if (mode == SCALE_DOWN) {
672186f8572SMark Yao if (vskiplines) {
673186f8572SMark Yao *vskiplines = scl_get_vskiplines(src, dst);
674186f8572SMark Yao val = scl_get_bili_dn_vskip(src, dst,
675186f8572SMark Yao *vskiplines);
676186f8572SMark Yao } else {
677186f8572SMark Yao val = GET_SCL_FT_BILI_DN(src, dst);
678186f8572SMark Yao }
679186f8572SMark Yao }
680186f8572SMark Yao }
681186f8572SMark Yao
682186f8572SMark Yao return val;
683186f8572SMark Yao }
684186f8572SMark Yao
scl_vop_cal_scl_fac(struct vop * vop,uint32_t src_w,uint32_t src_h,uint32_t dst_w,uint32_t dst_h,uint32_t pixel_format)685186f8572SMark Yao static void scl_vop_cal_scl_fac(struct vop *vop,
686186f8572SMark Yao uint32_t src_w, uint32_t src_h, uint32_t dst_w,
687186f8572SMark Yao uint32_t dst_h, uint32_t pixel_format)
688186f8572SMark Yao {
689186f8572SMark Yao uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
690186f8572SMark Yao uint16_t cbcr_hor_scl_mode = SCALE_NONE;
691186f8572SMark Yao uint16_t cbcr_ver_scl_mode = SCALE_NONE;
692186f8572SMark Yao int hsub = drm_format_horz_chroma_subsampling(pixel_format);
693186f8572SMark Yao int vsub = drm_format_vert_chroma_subsampling(pixel_format);
694186f8572SMark Yao bool is_yuv = false;
695186f8572SMark Yao uint16_t cbcr_src_w = src_w / hsub;
696186f8572SMark Yao uint16_t cbcr_src_h = src_h / vsub;
697186f8572SMark Yao uint16_t vsu_mode;
698186f8572SMark Yao uint16_t lb_mode;
699186f8572SMark Yao uint32_t val;
700186f8572SMark Yao int vskiplines = 0;
701186f8572SMark Yao
702186f8572SMark Yao if (!vop->win->scl)
703186f8572SMark Yao return;
704186f8572SMark Yao
705186f8572SMark Yao if (!vop->win->scl->ext) {
706186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_x,
707186f8572SMark Yao scl_cal_scale2(src_w, dst_w));
708186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_y,
709186f8572SMark Yao scl_cal_scale2(src_h, dst_h));
710186f8572SMark Yao if (is_yuv) {
711186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_x,
712186f8572SMark Yao scl_cal_scale2(src_w, dst_w));
713186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_y,
714186f8572SMark Yao scl_cal_scale2(src_h, dst_h));
715186f8572SMark Yao }
716186f8572SMark Yao return;
717186f8572SMark Yao }
718186f8572SMark Yao
719186f8572SMark Yao yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
720186f8572SMark Yao yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
721186f8572SMark Yao
722186f8572SMark Yao if (is_yuv) {
723186f8572SMark Yao cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
724186f8572SMark Yao cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
725186f8572SMark Yao if (cbcr_hor_scl_mode == SCALE_DOWN)
726186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(dst_w, true);
727186f8572SMark Yao else
728186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
729186f8572SMark Yao } else {
730186f8572SMark Yao if (yrgb_hor_scl_mode == SCALE_DOWN)
731186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(dst_w, false);
732186f8572SMark Yao else
733186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(src_w, false);
734186f8572SMark Yao }
735186f8572SMark Yao
736186f8572SMark Yao VOP_SCL_SET_EXT(vop, lb_mode, lb_mode);
737186f8572SMark Yao if (lb_mode == LB_RGB_3840X2) {
738186f8572SMark Yao if (yrgb_ver_scl_mode != SCALE_NONE) {
739186f8572SMark Yao printf("ERROR : not allow yrgb ver scale\n");
740186f8572SMark Yao return;
741186f8572SMark Yao }
742186f8572SMark Yao if (cbcr_ver_scl_mode != SCALE_NONE) {
743186f8572SMark Yao printf("ERROR : not allow cbcr ver scale\n");
744186f8572SMark Yao return;
745186f8572SMark Yao }
746186f8572SMark Yao vsu_mode = SCALE_UP_BIL;
747186f8572SMark Yao } else if (lb_mode == LB_RGB_2560X4) {
748186f8572SMark Yao vsu_mode = SCALE_UP_BIL;
749186f8572SMark Yao } else {
750186f8572SMark Yao vsu_mode = SCALE_UP_BIC;
751186f8572SMark Yao }
752186f8572SMark Yao
753186f8572SMark Yao val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
754186f8572SMark Yao true, 0, NULL);
755186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_x, val);
756186f8572SMark Yao val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
757186f8572SMark Yao false, vsu_mode, &vskiplines);
758186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_y, val);
759186f8572SMark Yao
760186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_yrgb_gt4, vskiplines == 4);
761186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_yrgb_gt2, vskiplines == 2);
762186f8572SMark Yao
763186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
764186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
765186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_hsd_mode, SCALE_DOWN_BIL);
766186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_vsd_mode, SCALE_DOWN_BIL);
767186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_vsu_mode, vsu_mode);
768186f8572SMark Yao if (is_yuv) {
769186f8572SMark Yao val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
770186f8572SMark Yao dst_w, true, 0, NULL);
771186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_x, val);
772186f8572SMark Yao val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
773186f8572SMark Yao dst_h, false, vsu_mode, &vskiplines);
774186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_y, val);
775186f8572SMark Yao
776186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_cbcr_gt4, vskiplines == 4);
777186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_cbcr_gt2, vskiplines == 2);
778186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
779186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
780186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_hsd_mode, SCALE_DOWN_BIL);
781186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_vsd_mode, SCALE_DOWN_BIL);
782186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_vsu_mode, vsu_mode);
783186f8572SMark Yao }
784186f8572SMark Yao }
785186f8572SMark Yao
vop_load_csc_table(struct vop * vop,u32 offset,const u32 * table)786b7618fd3SSandy Huang static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table)
787b7618fd3SSandy Huang {
788b7618fd3SSandy Huang int i;
789b7618fd3SSandy Huang
790b7618fd3SSandy Huang /*
791b7618fd3SSandy Huang * so far the csc offset is not 0 and in the feature the csc offset
792b7618fd3SSandy Huang * impossible be 0, so when the offset is 0, should return here.
793b7618fd3SSandy Huang */
794b7618fd3SSandy Huang if (!table || offset == 0)
795b7618fd3SSandy Huang return;
796b7618fd3SSandy Huang
797b7618fd3SSandy Huang for (i = 0; i < 8; i++)
798b7618fd3SSandy Huang vop_writel(vop, offset + i * 4, table[i]);
799b7618fd3SSandy Huang }
800b7618fd3SSandy Huang
rockchip_vop_setup_csc_table(struct display_state * state)801b7618fd3SSandy Huang static int rockchip_vop_setup_csc_table(struct display_state *state)
802b7618fd3SSandy Huang {
803b7618fd3SSandy Huang struct crtc_state *crtc_state = &state->crtc_state;
804b7618fd3SSandy Huang struct connector_state *conn_state = &state->conn_state;
805b7618fd3SSandy Huang struct vop *vop = crtc_state->private;
806b7618fd3SSandy Huang const uint32_t *csc_table = NULL;
807b7618fd3SSandy Huang
808b7618fd3SSandy Huang if (!vop->csc_table || !crtc_state->yuv_overlay)
809b7618fd3SSandy Huang return 0;
810b7618fd3SSandy Huang /* todo: only implement r2y*/
811df0a5c43SDamon Ding switch (conn_state->color_encoding) {
812df0a5c43SDamon Ding case DRM_COLOR_YCBCR_BT601:
813df0a5c43SDamon Ding if (conn_state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
814df0a5c43SDamon Ding csc_table = vop->csc_table->r2y_bt601_12_235; /* bt601 limit */
815df0a5c43SDamon Ding else
816df0a5c43SDamon Ding csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
817b7618fd3SSandy Huang break;
818df0a5c43SDamon Ding case DRM_COLOR_YCBCR_BT709:
819b7618fd3SSandy Huang csc_table = vop->csc_table->r2y_bt709;
820b7618fd3SSandy Huang break;
821df0a5c43SDamon Ding case DRM_COLOR_YCBCR_BT2020:
822b7618fd3SSandy Huang csc_table = vop->csc_table->r2y_bt2020;
823b7618fd3SSandy Huang break;
824b7618fd3SSandy Huang default:
825df0a5c43SDamon Ding csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
826b7618fd3SSandy Huang break;
827b7618fd3SSandy Huang }
828b7618fd3SSandy Huang
829b7618fd3SSandy Huang vop_load_csc_table(vop, vop->win_csc->r2y_offset, csc_table);
830b7618fd3SSandy Huang VOP_WIN_CSC_SET(vop, r2y_en, 1);
831b7618fd3SSandy Huang
832b7618fd3SSandy Huang return 0;
833b7618fd3SSandy Huang }
834b7618fd3SSandy Huang
rockchip_vop_set_plane(struct display_state * state)835186f8572SMark Yao static int rockchip_vop_set_plane(struct display_state *state)
836186f8572SMark Yao {
837186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state;
838d4a9114aSShixiang Zheng const struct rockchip_crtc *crtc = crtc_state->crtc;
839d4a9114aSShixiang Zheng const struct vop_data *vop_data = crtc->data;
840186f8572SMark Yao struct connector_state *conn_state = &state->conn_state;
841186f8572SMark Yao struct drm_display_mode *mode = &conn_state->mode;
842186f8572SMark Yao u32 act_info, dsp_info, dsp_st, dsp_stx, dsp_sty;
843186f8572SMark Yao struct vop *vop = crtc_state->private;
844ee01dbb2SDamon Ding int src_w = crtc_state->src_rect.w;
845ee01dbb2SDamon Ding int src_h = crtc_state->src_rect.h;
846ee01dbb2SDamon Ding int crtc_x = crtc_state->crtc_rect.x;
847ee01dbb2SDamon Ding int crtc_y = crtc_state->crtc_rect.y;
848ee01dbb2SDamon Ding int crtc_w = crtc_state->crtc_rect.w;
849ee01dbb2SDamon Ding int crtc_h = crtc_state->crtc_rect.h;
850186f8572SMark Yao int xvir = crtc_state->xvir;
851ffa55e18SShixiang Zheng int x_mirror = 0, y_mirror = 0;
852186f8572SMark Yao
853eee28ceaSSandy Huang if (crtc_w > crtc_state->max_output.width) {
854eee28ceaSSandy Huang printf("ERROR: output w[%d] exceeded max width[%d]\n",
855eee28ceaSSandy Huang crtc_w, crtc_state->max_output.width);
856cf53642aSSandy Huang return -EINVAL;
857cf53642aSSandy Huang }
858cf53642aSSandy Huang
859de3f896fSChaoyi Chen if ((vop->version == VOP_VERSION_RK3036 || vop->version >= VOP_VERSION_RK3576_LITE) &&
86072f233a9SDamon Ding (mode->flags & DRM_MODE_FLAG_INTERLACE))
86172f233a9SDamon Ding crtc_h = crtc_h / 2;
86272f233a9SDamon Ding
863186f8572SMark Yao act_info = (src_h - 1) << 16;
864186f8572SMark Yao act_info |= (src_w - 1) & 0xffff;
865186f8572SMark Yao
866186f8572SMark Yao dsp_info = (crtc_h - 1) << 16;
867186f8572SMark Yao dsp_info |= (crtc_w - 1) & 0xffff;
868186f8572SMark Yao
86944006808SAlgea Cao dsp_stx = crtc_x + mode->crtc_htotal - mode->crtc_hsync_start;
87044006808SAlgea Cao dsp_sty = crtc_y + mode->crtc_vtotal - mode->crtc_vsync_start;
871de3f896fSChaoyi Chen if ((vop->version == VOP_VERSION_RK3036 || vop->version >= VOP_VERSION_RK3576_LITE) &&
87272f233a9SDamon Ding (mode->flags & DRM_MODE_FLAG_INTERLACE))
87372f233a9SDamon Ding dsp_sty = crtc_y / 2 + mode->crtc_vtotal - mode->crtc_vsync_start;
874186f8572SMark Yao dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
875d4a9114aSShixiang Zheng /*
876f0e8414bSSandy Huang * vop full need to treats rgb888 as bgr888 so we reverse the rb swap to workaround
877d4a9114aSShixiang Zheng */
878f0e8414bSSandy Huang if (crtc_state->format == ROCKCHIP_FMT_RGB888 && VOP_MAJOR(vop_data->version) == 3)
879d4a9114aSShixiang Zheng crtc_state->rb_swap = !crtc_state->rb_swap;
880186f8572SMark Yao
881ffa55e18SShixiang Zheng if (mode->flags & DRM_MODE_FLAG_YMIRROR)
882ffa55e18SShixiang Zheng y_mirror = 1;
883ffa55e18SShixiang Zheng else
884ffa55e18SShixiang Zheng y_mirror = 0;
885ffa55e18SShixiang Zheng if (mode->flags & DRM_MODE_FLAG_XMIRROR)
886ffa55e18SShixiang Zheng x_mirror = 1;
887ffa55e18SShixiang Zheng else
888ffa55e18SShixiang Zheng x_mirror = 0;
889ffa55e18SShixiang Zheng if (crtc_state->ymirror ^ y_mirror)
890ffa55e18SShixiang Zheng y_mirror = 1;
891ffa55e18SShixiang Zheng else
892ffa55e18SShixiang Zheng y_mirror = 0;
893ffa55e18SShixiang Zheng if (y_mirror) {
894ffa55e18SShixiang Zheng if (VOP_CTRL_SUPPORT(vop, ymirror))
895186f8572SMark Yao crtc_state->dma_addr += (src_h - 1) * xvir * 4;
896620af6a3SSandy Huang else
897ffa55e18SShixiang Zheng y_mirror = 0;
898620af6a3SSandy Huang }
899ffa55e18SShixiang Zheng VOP_CTRL_SET(vop, ymirror, y_mirror);
900ffa55e18SShixiang Zheng VOP_CTRL_SET(vop, xmirror, x_mirror);
901ffa55e18SShixiang Zheng
902186f8572SMark Yao VOP_WIN_SET(vop, format, crtc_state->format);
9030c4bb35cSDamon Ding
9040c4bb35cSDamon Ding VOP_WIN_SET(vop, interlace_read, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
9050c4bb35cSDamon Ding
906186f8572SMark Yao VOP_WIN_SET(vop, yrgb_vir, xvir);
907186f8572SMark Yao VOP_WIN_SET(vop, yrgb_mst, crtc_state->dma_addr);
908186f8572SMark Yao
909186f8572SMark Yao scl_vop_cal_scl_fac(vop, src_w, src_h, crtc_w, crtc_h,
910186f8572SMark Yao crtc_state->format);
911186f8572SMark Yao
912186f8572SMark Yao VOP_WIN_SET(vop, act_info, act_info);
913186f8572SMark Yao VOP_WIN_SET(vop, dsp_info, dsp_info);
914186f8572SMark Yao VOP_WIN_SET(vop, dsp_st, dsp_st);
915186f8572SMark Yao VOP_WIN_SET(vop, rb_swap, crtc_state->rb_swap);
916186f8572SMark Yao
917186f8572SMark Yao VOP_WIN_SET(vop, src_alpha_ctl, 0);
918186f8572SMark Yao
919b7618fd3SSandy Huang rockchip_vop_setup_csc_table(state);
920186f8572SMark Yao VOP_WIN_SET(vop, enable, 1);
921c33e1feaSAndy Yan VOP_WIN_SET(vop, gate, 1);
922186f8572SMark Yao vop_cfg_done(vop);
923186f8572SMark Yao
924224ad886SDamon Ding printf("VOP:0x%8p set plane [%dx%d->%dx%d@%dx%d] fmt[%d] addr[0x%x]\n",
925224ad886SDamon Ding vop->regs, crtc_state->src_rect.w, crtc_state->src_rect.h,
926224ad886SDamon Ding crtc_state->crtc_rect.w, crtc_state->crtc_rect.h,
927224ad886SDamon Ding crtc_state->crtc_rect.x, crtc_state->crtc_rect.y,
928224ad886SDamon Ding crtc_state->format, crtc_state->dma_addr);
929224ad886SDamon Ding
930186f8572SMark Yao return 0;
931186f8572SMark Yao }
932186f8572SMark Yao
rockchip_vop_prepare(struct display_state * state)933186f8572SMark Yao static int rockchip_vop_prepare(struct display_state *state)
934186f8572SMark Yao {
935186f8572SMark Yao return 0;
936186f8572SMark Yao }
937186f8572SMark Yao
rockchip_vop_enable(struct display_state * state)938186f8572SMark Yao static int rockchip_vop_enable(struct display_state *state)
939186f8572SMark Yao {
940186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state;
941186f8572SMark Yao struct vop *vop = crtc_state->private;
942186f8572SMark Yao
943186f8572SMark Yao VOP_CTRL_SET(vop, standby, 0);
944186f8572SMark Yao vop_cfg_done(vop);
94567b9012cSSandy Huang if (crtc_state->mcu_timing.mcu_pix_total > 0)
94667b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_hold_mode, 0);
947186f8572SMark Yao
948186f8572SMark Yao return 0;
949186f8572SMark Yao }
950186f8572SMark Yao
rockchip_vop_disable(struct display_state * state)951186f8572SMark Yao static int rockchip_vop_disable(struct display_state *state)
952186f8572SMark Yao {
953186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state;
954186f8572SMark Yao struct vop *vop = crtc_state->private;
955186f8572SMark Yao
956186f8572SMark Yao VOP_CTRL_SET(vop, standby, 1);
957186f8572SMark Yao vop_cfg_done(vop);
958186f8572SMark Yao return 0;
959186f8572SMark Yao }
960186f8572SMark Yao
rockchip_vop_fixup_dts(struct display_state * state,void * blob)961186f8572SMark Yao static int rockchip_vop_fixup_dts(struct display_state *state, void *blob)
962186f8572SMark Yao {
963e2bce6e4SKever Yang #if 0
964186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state;
965186f8572SMark Yao struct panel_state *pstate = &state->panel_state;
966186f8572SMark Yao uint32_t phandle;
967186f8572SMark Yao char path[100];
968186f8572SMark Yao int ret, dsp_lut_node;
969186f8572SMark Yao
970e2bce6e4SKever Yang if (!ofnode_valid(pstate->dsp_lut_node))
971186f8572SMark Yao return 0;
972186f8572SMark Yao ret = fdt_get_path(state->blob, pstate->dsp_lut_node, path, sizeof(path));
973186f8572SMark Yao if (ret < 0) {
974186f8572SMark Yao printf("failed to get dsp_lut path[%s], ret=%d\n",
975186f8572SMark Yao path, ret);
976186f8572SMark Yao return ret;
977186f8572SMark Yao }
978186f8572SMark Yao
979186f8572SMark Yao dsp_lut_node = fdt_path_offset(blob, path);
980186f8572SMark Yao phandle = fdt_get_phandle(blob, dsp_lut_node);
981186f8572SMark Yao if (!phandle) {
982186f8572SMark Yao phandle = fdt_alloc_phandle(blob);
983186f8572SMark Yao if (!phandle) {
984186f8572SMark Yao printf("failed to alloc phandle\n");
985186f8572SMark Yao return -ENOMEM;
986186f8572SMark Yao }
987186f8572SMark Yao
988186f8572SMark Yao fdt_set_phandle(blob, dsp_lut_node, phandle);
989186f8572SMark Yao }
990186f8572SMark Yao
991186f8572SMark Yao ret = fdt_get_path(state->blob, crtc_state->node, path, sizeof(path));
992186f8572SMark Yao if (ret < 0) {
993186f8572SMark Yao printf("failed to get route path[%s], ret=%d\n",
994186f8572SMark Yao path, ret);
995186f8572SMark Yao return ret;
996186f8572SMark Yao }
997186f8572SMark Yao
998186f8572SMark Yao do_fixup_by_path_u32(blob, path, "dsp-lut", phandle, 1);
999e2bce6e4SKever Yang #endif
1000186f8572SMark Yao return 0;
1001186f8572SMark Yao }
1002186f8572SMark Yao
rockchip_vop_send_mcu_cmd(struct display_state * state,u32 type,u32 value)100372f233a9SDamon Ding static int rockchip_vop_send_mcu_cmd(struct display_state *state, u32 type, u32 value)
100467b9012cSSandy Huang {
100567b9012cSSandy Huang struct crtc_state *crtc_state = &state->crtc_state;
100672f233a9SDamon Ding struct connector_state *conn_state = &state->conn_state;
100772f233a9SDamon Ding struct drm_display_mode *mode = &conn_state->mode;
100867b9012cSSandy Huang struct vop *vop = crtc_state->private;
100972f233a9SDamon Ding int ret;
101072f233a9SDamon Ding
1011de3f896fSChaoyi Chen if (vop->version >= VOP_VERSION_RK3576_LITE) {
101272f233a9SDamon Ding /*
101372f233a9SDamon Ding * 1.set mcu bypass mode timing.
101472f233a9SDamon Ding * 2.set dclk rate to 150M.
101572f233a9SDamon Ding */
101672f233a9SDamon Ding if ((type == MCU_SETBYPASS) && value) {
101772f233a9SDamon Ding vop_mcu_bypass_mode_setup(state, vop);
101872f233a9SDamon Ding ret = clk_set_rate(&crtc_state->dclk, 150000000);
101972f233a9SDamon Ding if (IS_ERR_VALUE(ret)) {
102072f233a9SDamon Ding printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
102172f233a9SDamon Ding return ret;
102272f233a9SDamon Ding }
102372f233a9SDamon Ding }
102472f233a9SDamon Ding }
102567b9012cSSandy Huang
102667b9012cSSandy Huang if (vop) {
102767b9012cSSandy Huang switch (type) {
102867b9012cSSandy Huang case MCU_WRCMD:
1029*b899f9ccSChaoyi Chen VOP_CTRL_SET(vop, mcu_force_rdn, 1);
10306b898587SDamon Ding set_vop_mcu_rs(vop, 0);
103167b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
10326b898587SDamon Ding set_vop_mcu_rs(vop, 1);
103367b9012cSSandy Huang break;
103467b9012cSSandy Huang case MCU_WRDATA:
1035*b899f9ccSChaoyi Chen VOP_CTRL_SET(vop, mcu_force_rdn, 1);
10366b898587SDamon Ding set_vop_mcu_rs(vop, 1);
103767b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
103867b9012cSSandy Huang break;
103967b9012cSSandy Huang case MCU_SETBYPASS:
104067b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
1041*b899f9ccSChaoyi Chen if (!value)
1042*b899f9ccSChaoyi Chen VOP_CTRL_SET(vop, mcu_force_rdn, 1);
104367b9012cSSandy Huang break;
104467b9012cSSandy Huang default:
104567b9012cSSandy Huang break;
104667b9012cSSandy Huang }
104767b9012cSSandy Huang }
104867b9012cSSandy Huang
1049de3f896fSChaoyi Chen if (vop->version >= VOP_VERSION_RK3576_LITE) {
105072f233a9SDamon Ding /*
105172f233a9SDamon Ding * 1.restore mcu data mode timing.
105272f233a9SDamon Ding * 2.restore dclk rate to crtc_clock.
105372f233a9SDamon Ding */
105472f233a9SDamon Ding if ((type == MCU_SETBYPASS) && !value) {
105572f233a9SDamon Ding vop_mcu_mode_setup(state, vop);
105672f233a9SDamon Ding ret = clk_set_rate(&crtc_state->dclk, mode->crtc_clock * 1000);
105772f233a9SDamon Ding if (IS_ERR_VALUE(ret)) {
105872f233a9SDamon Ding printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
105972f233a9SDamon Ding return ret;
106072f233a9SDamon Ding }
106172f233a9SDamon Ding }
106272f233a9SDamon Ding }
106372f233a9SDamon Ding
106467b9012cSSandy Huang return 0;
106567b9012cSSandy Huang }
106667b9012cSSandy Huang
rockchip_vop_mode_valid(struct display_state * state)106722007755SDamon Ding static int rockchip_vop_mode_valid(struct display_state *state)
106822007755SDamon Ding {
106922007755SDamon Ding struct connector_state *conn_state = &state->conn_state;
107022007755SDamon Ding struct drm_display_mode *mode = &conn_state->mode;
107122007755SDamon Ding struct videomode vm;
107222007755SDamon Ding
107322007755SDamon Ding drm_display_mode_to_videomode(mode, &vm);
107422007755SDamon Ding
107522007755SDamon Ding if (vm.hactive < 32 || vm.vactive < 32 ||
107622007755SDamon Ding (vm.hfront_porch * vm.hsync_len * vm.hback_porch *
107722007755SDamon Ding vm.vfront_porch * vm.vsync_len * vm.vback_porch == 0)) {
107822007755SDamon Ding printf("ERROR: unsupported display timing\n");
107922007755SDamon Ding return -EINVAL;
108022007755SDamon Ding }
108122007755SDamon Ding
108222007755SDamon Ding return 0;
108322007755SDamon Ding }
108422007755SDamon Ding
rockchip_vop_plane_check(struct display_state * state)10854c765862SDamon Ding static int rockchip_vop_plane_check(struct display_state *state)
10864c765862SDamon Ding {
10874c765862SDamon Ding struct crtc_state *crtc_state = &state->crtc_state;
10884c765862SDamon Ding const struct rockchip_crtc *crtc = crtc_state->crtc;
10894c765862SDamon Ding const struct vop_data *vop_data = crtc->data;
10904c765862SDamon Ding const struct vop_win *win = vop_data->win;
10914c765862SDamon Ding struct display_rect *src = &crtc_state->src_rect;
10924c765862SDamon Ding struct display_rect *dst = &crtc_state->crtc_rect;
10934c765862SDamon Ding int min_scale, max_scale;
10944c765862SDamon Ding int hscale, vscale;
10954c765862SDamon Ding
10964c765862SDamon Ding min_scale = win->scl ? FRAC_16_16(1, 8) : VOP_PLANE_NO_SCALING;
10974c765862SDamon Ding max_scale = win->scl ? FRAC_16_16(8, 1) : VOP_PLANE_NO_SCALING;
10984c765862SDamon Ding
10994c765862SDamon Ding hscale = display_rect_calc_hscale(src, dst, min_scale, max_scale);
11004c765862SDamon Ding vscale = display_rect_calc_vscale(src, dst, min_scale, max_scale);
11014c765862SDamon Ding if (hscale < 0 || vscale < 0) {
11024c765862SDamon Ding printf("ERROR: scale factor is out of range\n");
11034c765862SDamon Ding return -ERANGE;
11044c765862SDamon Ding }
11054c765862SDamon Ding
11064c765862SDamon Ding return 0;
11074c765862SDamon Ding }
11084c765862SDamon Ding
rockchip_vop_mode_fixup(struct display_state * state)11091b5811e7SDamon Ding static int rockchip_vop_mode_fixup(struct display_state *state)
11101b5811e7SDamon Ding {
11114d64cedbSDamon Ding struct crtc_state *crtc_state = &state->crtc_state;
11120c4bb35cSDamon Ding const struct rockchip_crtc *crtc = crtc_state->crtc;
11130c4bb35cSDamon Ding const struct vop_data *vop_data = crtc->data;
11141b5811e7SDamon Ding struct connector_state *conn_state = &state->conn_state;
11151b5811e7SDamon Ding struct drm_display_mode *mode = &conn_state->mode;
11161b5811e7SDamon Ding
11171b5811e7SDamon Ding drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
11181b5811e7SDamon Ding
11190c4bb35cSDamon Ding /*
11200c4bb35cSDamon Ding * Dclk need to be double if BT656 interface and vop version >= 2.12.
11210c4bb35cSDamon Ding */
11220c4bb35cSDamon Ding if (mode->flags & DRM_MODE_FLAG_DBLCLK ||
1123de3f896fSChaoyi Chen (vop_data->version >= VOP_VERSION_RV1106 && vop_data->version < VOP_VERSION_RK3288 &&
11240c4bb35cSDamon Ding conn_state->output_if & VOP_OUTPUT_IF_BT656))
11250c4bb35cSDamon Ding mode->crtc_clock *= 2;
11260c4bb35cSDamon Ding
11274d64cedbSDamon Ding mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(conn_state->bus_format);
11284d64cedbSDamon Ding if (crtc_state->mcu_timing.mcu_pix_total)
11294d64cedbSDamon Ding mode->crtc_clock *= crtc_state->mcu_timing.mcu_pix_total + 1;
11304d64cedbSDamon Ding
11311b5811e7SDamon Ding return 0;
11321b5811e7SDamon Ding }
11331b5811e7SDamon Ding
1134186f8572SMark Yao const struct rockchip_crtc_funcs rockchip_vop_funcs = {
1135cf53642aSSandy Huang .preinit = rockchip_vop_preinit,
1136186f8572SMark Yao .init = rockchip_vop_init,
1137186f8572SMark Yao .set_plane = rockchip_vop_set_plane,
1138186f8572SMark Yao .prepare = rockchip_vop_prepare,
1139186f8572SMark Yao .enable = rockchip_vop_enable,
1140186f8572SMark Yao .disable = rockchip_vop_disable,
1141186f8572SMark Yao .fixup_dts = rockchip_vop_fixup_dts,
114267b9012cSSandy Huang .send_mcu_cmd = rockchip_vop_send_mcu_cmd,
114322007755SDamon Ding .mode_valid = rockchip_vop_mode_valid,
11444c765862SDamon Ding .plane_check = rockchip_vop_plane_check,
11451b5811e7SDamon Ding .mode_fixup = rockchip_vop_mode_fixup,
1146186f8572SMark Yao };
1147