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> 17186f8572SMark Yao #include <clk.h> 18186f8572SMark Yao #include <asm/arch/clock.h> 19186f8572SMark Yao #include <linux/err.h> 20e2bce6e4SKever Yang #include <dm/device.h> 21e2bce6e4SKever Yang #include <dm/read.h> 223a06149eSSandy Huang #include <syscon.h> 23*fc8b0d66SDamon Ding #include <regmap.h> 24186f8572SMark Yao 25186f8572SMark Yao #include "rockchip_display.h" 26186f8572SMark Yao #include "rockchip_crtc.h" 27186f8572SMark Yao #include "rockchip_connector.h" 28186f8572SMark Yao #include "rockchip_vop.h" 29186f8572SMark Yao 30186f8572SMark Yao static inline int us_to_vertical_line(struct drm_display_mode *mode, int us) 31186f8572SMark Yao { 32186f8572SMark Yao return us * mode->clock / mode->htotal / 1000; 33186f8572SMark Yao } 344b8c2ef1SMark Yao 356b898587SDamon Ding static inline void set_vop_mcu_rs(struct vop *vop, int v) 366b898587SDamon Ding { 376b898587SDamon Ding if (dm_gpio_is_valid(&vop->mcu_rs_gpio)) 386b898587SDamon Ding dm_gpio_set_value(&vop->mcu_rs_gpio, v); 396b898587SDamon Ding else 406b898587SDamon Ding VOP_CTRL_SET(vop, mcu_rs, v); 416b898587SDamon Ding } 426b898587SDamon Ding 4379feefb1SSandy Huang static int to_vop_csc_mode(int csc_mode) 4479feefb1SSandy Huang { 4579feefb1SSandy Huang switch (csc_mode) { 4679feefb1SSandy Huang case V4L2_COLORSPACE_SMPTE170M: 4779feefb1SSandy Huang return CSC_BT601L; 4879feefb1SSandy Huang case V4L2_COLORSPACE_REC709: 4979feefb1SSandy Huang case V4L2_COLORSPACE_DEFAULT: 5079feefb1SSandy Huang return CSC_BT709L; 5179feefb1SSandy Huang case V4L2_COLORSPACE_JPEG: 5279feefb1SSandy Huang return CSC_BT601F; 5379feefb1SSandy Huang case V4L2_COLORSPACE_BT2020: 5479feefb1SSandy Huang return CSC_BT2020; 5579feefb1SSandy Huang default: 5679feefb1SSandy Huang return CSC_BT709L; 5779feefb1SSandy Huang } 5879feefb1SSandy Huang } 5979feefb1SSandy Huang 6079feefb1SSandy Huang static bool is_yuv_output(uint32_t bus_format) 6179feefb1SSandy Huang { 6279feefb1SSandy Huang switch (bus_format) { 6379feefb1SSandy Huang case MEDIA_BUS_FMT_YUV8_1X24: 6479feefb1SSandy Huang case MEDIA_BUS_FMT_YUV10_1X30: 6579feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 6679feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 67f721bd04SDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8: 68f721bd04SDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8: 69f721bd04SDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8: 70f721bd04SDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8: 71f721bd04SDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16: 72f721bd04SDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16: 73f721bd04SDamon Ding case MEDIA_BUS_FMT_UYVY8_1X16: 74f721bd04SDamon Ding case MEDIA_BUS_FMT_VYUY8_1X16: 7579feefb1SSandy Huang return true; 7679feefb1SSandy Huang default: 7779feefb1SSandy Huang return false; 7879feefb1SSandy Huang } 7979feefb1SSandy Huang } 8079feefb1SSandy Huang 8179feefb1SSandy Huang static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode) 8279feefb1SSandy Huang { 8379feefb1SSandy Huang /* 8479feefb1SSandy Huang * FIXME: 8579feefb1SSandy Huang * 8679feefb1SSandy Huang * There is no media type for YUV444 output, 8779feefb1SSandy Huang * so when out_mode is AAAA or P888, assume output is YUV444 on 8879feefb1SSandy Huang * yuv format. 8979feefb1SSandy Huang * 9079feefb1SSandy Huang * From H/W testing, YUV444 mode need a rb swap. 9179feefb1SSandy Huang */ 9279feefb1SSandy Huang if ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 || 9379feefb1SSandy Huang bus_format == MEDIA_BUS_FMT_YUV10_1X30) && 9479feefb1SSandy Huang (output_mode == ROCKCHIP_OUT_MODE_AAAA || 9579feefb1SSandy Huang output_mode == ROCKCHIP_OUT_MODE_P888)) 9679feefb1SSandy Huang return true; 9779feefb1SSandy Huang else 9879feefb1SSandy Huang return false; 9979feefb1SSandy Huang } 10079feefb1SSandy Huang 10180fbe788SDamon Ding static bool is_rb_swap(uint32_t bus_format, uint32_t output_mode) 10280fbe788SDamon Ding { 10380fbe788SDamon Ding /* 10480fbe788SDamon Ding * The default component order of serial rgb3x8 formats 10580fbe788SDamon Ding * is BGR. So it is needed to enable RB swap. 10680fbe788SDamon Ding */ 107b7b383ebSDamon Ding if (bus_format == MEDIA_BUS_FMT_RGB888_3X8 || 108b7b383ebSDamon Ding bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8) 10980fbe788SDamon Ding return true; 11080fbe788SDamon Ding else 11180fbe788SDamon Ding return false; 11280fbe788SDamon Ding } 11380fbe788SDamon Ding 114186f8572SMark Yao static int rockchip_vop_init_gamma(struct vop *vop, struct display_state *state) 115186f8572SMark Yao { 116186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 117186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 118186f8572SMark Yao u32 *lut = conn_state->gamma.lut; 119186f8572SMark Yao fdt_size_t lut_size; 120186f8572SMark Yao int i, lut_len; 121186f8572SMark Yao u32 *lut_regs; 122186f8572SMark Yao 123186f8572SMark Yao if (!conn_state->gamma.lut) 124186f8572SMark Yao return 0; 125186f8572SMark Yao 126e2bce6e4SKever Yang i = dev_read_stringlist_search(crtc_state->dev, "reg-names", "gamma_lut"); 127186f8572SMark Yao if (i < 0) { 128186f8572SMark Yao printf("Warning: vop not support gamma\n"); 129186f8572SMark Yao return 0; 130186f8572SMark Yao } 131e2bce6e4SKever Yang lut_regs = (u32 *)dev_read_addr_size(crtc_state->dev, "reg", &lut_size); 132186f8572SMark Yao if (lut_regs == (u32 *)FDT_ADDR_T_NONE) { 133186f8572SMark Yao printf("failed to get gamma lut register\n"); 134186f8572SMark Yao return 0; 135186f8572SMark Yao } 136186f8572SMark Yao lut_len = lut_size / 4; 137186f8572SMark Yao if (lut_len != 256 && lut_len != 1024) { 138186f8572SMark Yao printf("Warning: unsupport gamma lut table[%d]\n", lut_len); 139186f8572SMark Yao return 0; 140186f8572SMark Yao } 141186f8572SMark Yao 142186f8572SMark Yao if (conn_state->gamma.size != lut_len) { 143186f8572SMark Yao int size = conn_state->gamma.size; 144186f8572SMark Yao u32 j, r, g, b, color; 145186f8572SMark Yao 146186f8572SMark Yao for (i = 0; i < lut_len; i++) { 147186f8572SMark Yao j = i * size / lut_len; 148186f8572SMark Yao r = lut[j] / size / size * lut_len / size; 149186f8572SMark Yao g = lut[j] / size % size * lut_len / size; 150186f8572SMark Yao b = lut[j] % size * lut_len / size; 151186f8572SMark Yao color = r * lut_len * lut_len + g * lut_len + b; 152186f8572SMark Yao 153186f8572SMark Yao writel(color, lut_regs + (i << 2)); 154186f8572SMark Yao } 155186f8572SMark Yao } else { 156186f8572SMark Yao for (i = 0; i < lut_len; i++) 157186f8572SMark Yao writel(lut[i], lut_regs + (i << 2)); 158186f8572SMark Yao } 159186f8572SMark Yao 160186f8572SMark Yao VOP_CTRL_SET(vop, dsp_lut_en, 1); 161186f8572SMark Yao VOP_CTRL_SET(vop, update_gamma_lut, 1); 162186f8572SMark Yao 163186f8572SMark Yao return 0; 164186f8572SMark Yao } 165186f8572SMark Yao 1668a2a3a29SSandy Huang static void vop_post_config(struct display_state *state, struct vop *vop) 1678a2a3a29SSandy Huang { 1688a2a3a29SSandy Huang struct connector_state *conn_state = &state->conn_state; 1698a2a3a29SSandy Huang struct drm_display_mode *mode = &conn_state->mode; 1708a2a3a29SSandy Huang u16 vtotal = mode->crtc_vtotal; 1718a2a3a29SSandy Huang u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; 1728a2a3a29SSandy Huang u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start; 1738a2a3a29SSandy Huang u16 hdisplay = mode->crtc_hdisplay; 1748a2a3a29SSandy Huang u16 vdisplay = mode->crtc_vdisplay; 1758a2a3a29SSandy Huang u16 hsize = hdisplay * (conn_state->overscan.left_margin + conn_state->overscan.right_margin) / 200; 1768a2a3a29SSandy Huang u16 vsize = vdisplay * (conn_state->overscan.top_margin + conn_state->overscan.bottom_margin) / 200; 1778a2a3a29SSandy Huang u16 hact_end, vact_end; 1788a2a3a29SSandy Huang u32 val; 1798a2a3a29SSandy Huang 1808a2a3a29SSandy Huang if (mode->flags & DRM_MODE_FLAG_INTERLACE) 1818a2a3a29SSandy Huang vsize = round_down(vsize, 2); 1828a2a3a29SSandy Huang 1838a2a3a29SSandy Huang hact_st += hdisplay * (100 - conn_state->overscan.left_margin) / 200; 1848a2a3a29SSandy Huang hact_end = hact_st + hsize; 1858a2a3a29SSandy Huang val = hact_st << 16; 1868a2a3a29SSandy Huang val |= hact_end; 1878a2a3a29SSandy Huang 1888a2a3a29SSandy Huang VOP_CTRL_SET(vop, hpost_st_end, val); 1898a2a3a29SSandy Huang vact_st += vdisplay * (100 - conn_state->overscan.top_margin) / 200; 1908a2a3a29SSandy Huang vact_end = vact_st + vsize; 1918a2a3a29SSandy Huang val = vact_st << 16; 1928a2a3a29SSandy Huang val |= vact_end; 1938a2a3a29SSandy Huang VOP_CTRL_SET(vop, vpost_st_end, val); 1948a2a3a29SSandy Huang val = scl_cal_scale2(vdisplay, vsize) << 16; 1958a2a3a29SSandy Huang val |= scl_cal_scale2(hdisplay, hsize); 1968a2a3a29SSandy Huang VOP_CTRL_SET(vop, post_scl_factor, val); 1978a2a3a29SSandy Huang #define POST_HORIZONTAL_SCALEDOWN_EN(x) ((x) << 0) 1988a2a3a29SSandy Huang #define POST_VERTICAL_SCALEDOWN_EN(x) ((x) << 1) 1998a2a3a29SSandy Huang VOP_CTRL_SET(vop, post_scl_ctrl, 2008a2a3a29SSandy Huang POST_HORIZONTAL_SCALEDOWN_EN(hdisplay != hsize) | 2018a2a3a29SSandy Huang POST_VERTICAL_SCALEDOWN_EN(vdisplay != vsize)); 2028a2a3a29SSandy Huang if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 2038a2a3a29SSandy Huang u16 vact_st_f1 = vtotal + vact_st + 1; 2048a2a3a29SSandy Huang u16 vact_end_f1 = vact_st_f1 + vsize; 2058a2a3a29SSandy Huang 2068a2a3a29SSandy Huang val = vact_st_f1 << 16 | vact_end_f1; 2078a2a3a29SSandy Huang VOP_CTRL_SET(vop, vpost_st_end_f1, val); 2088a2a3a29SSandy Huang } 2098a2a3a29SSandy Huang } 2108a2a3a29SSandy Huang 21167b9012cSSandy Huang static void vop_mcu_mode(struct display_state *state, struct vop *vop) 21267b9012cSSandy Huang { 21367b9012cSSandy Huang struct crtc_state *crtc_state = &state->crtc_state; 21467b9012cSSandy Huang 21567b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_clk_sel, 1); 21667b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_type, 1); 21767b9012cSSandy Huang 21867b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_hold_mode, 1); 21967b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_pix_total, crtc_state->mcu_timing.mcu_pix_total); 22067b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_cs_pst, crtc_state->mcu_timing.mcu_cs_pst); 22167b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_cs_pend, crtc_state->mcu_timing.mcu_cs_pend); 22267b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_pst, crtc_state->mcu_timing.mcu_rw_pst); 22367b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_pend, crtc_state->mcu_timing.mcu_rw_pend); 22467b9012cSSandy Huang } 22567b9012cSSandy Huang 226cf53642aSSandy Huang static int rockchip_vop_preinit(struct display_state *state) 227cf53642aSSandy Huang { 228cf53642aSSandy Huang const struct vop_data *vop_data = state->crtc_state.crtc->data; 229cf53642aSSandy Huang 230cf53642aSSandy Huang state->crtc_state.max_output = vop_data->max_output; 231cf53642aSSandy Huang 232cf53642aSSandy Huang return 0; 233cf53642aSSandy Huang } 234cf53642aSSandy Huang 235186f8572SMark Yao static int rockchip_vop_init(struct display_state *state) 236186f8572SMark Yao { 237186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 238186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 239186f8572SMark Yao struct drm_display_mode *mode = &conn_state->mode; 240186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 241186f8572SMark Yao const struct vop_data *vop_data = crtc->data; 242186f8572SMark Yao struct vop *vop; 243*fc8b0d66SDamon Ding struct regmap *map; 244ccd843b9SSandy Huang u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 245ccd843b9SSandy Huang u16 hdisplay = mode->crtc_hdisplay; 246ccd843b9SSandy Huang u16 htotal = mode->crtc_htotal; 247ccd843b9SSandy Huang u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; 248186f8572SMark Yao u16 hact_end = hact_st + hdisplay; 249ccd843b9SSandy Huang u16 vdisplay = mode->crtc_vdisplay; 250ccd843b9SSandy Huang u16 vtotal = mode->crtc_vtotal; 251ccd843b9SSandy Huang u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 252ccd843b9SSandy Huang u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start; 253186f8572SMark Yao u16 vact_end = vact_st + vdisplay; 254504d9922SSandy Huang struct clk dclk; 255960080c9SSandy Huang u32 val, act_end; 2564b8c2ef1SMark Yao int ret; 25779feefb1SSandy Huang bool yuv_overlay = false, post_r2y_en = false, post_y2r_en = false; 25879feefb1SSandy Huang u16 post_csc_mode; 2593a06149eSSandy Huang bool dclk_inv; 2602264c88bSDamon Ding char output_type_name[30] = {0}; 261186f8572SMark Yao 262186f8572SMark Yao vop = malloc(sizeof(*vop)); 263186f8572SMark Yao if (!vop) 264186f8572SMark Yao return -ENOMEM; 265186f8572SMark Yao memset(vop, 0, sizeof(*vop)); 266186f8572SMark Yao 267186f8572SMark Yao crtc_state->private = vop; 268*fc8b0d66SDamon Ding vop->data = vop_data; 269e2bce6e4SKever Yang vop->regs = dev_read_addr_ptr(crtc_state->dev); 270186f8572SMark Yao vop->regsbak = malloc(vop_data->reg_len); 271186f8572SMark Yao vop->win = vop_data->win; 272186f8572SMark Yao vop->win_offset = vop_data->win_offset; 273186f8572SMark Yao vop->ctrl = vop_data->ctrl; 2743a06149eSSandy Huang 275*fc8b0d66SDamon Ding map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,grf"); 276*fc8b0d66SDamon Ding vop->grf_ctrl = regmap_get_range(map, 0); 277*fc8b0d66SDamon Ding if (vop->grf_ctrl <= 0) 278*fc8b0d66SDamon Ding printf("%s: Get syscon grf failed (ret=%p)\n", __func__, vop->grf_ctrl); 279*fc8b0d66SDamon Ding 280186f8572SMark Yao vop->line_flag = vop_data->line_flag; 281b7618fd3SSandy Huang vop->csc_table = vop_data->csc_table; 282b7618fd3SSandy Huang vop->win_csc = vop_data->win_csc; 283186f8572SMark Yao vop->version = vop_data->version; 284186f8572SMark Yao 2852264c88bSDamon Ding printf("VOP:0x%8p update mode to: %dx%d%s%d, type:%s\n", 2862264c88bSDamon Ding vop->regs, mode->crtc_hdisplay, mode->vdisplay, 2872264c88bSDamon Ding mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p", 2882264c88bSDamon Ding mode->vrefresh, 2892264c88bSDamon Ding rockchip_get_output_if_name(conn_state->output_if, output_type_name)); 2902264c88bSDamon Ding 291cadc8d74SKever Yang /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ 292cadc8d74SKever Yang ret = clk_set_defaults(crtc_state->dev); 293cadc8d74SKever Yang if (ret) 294cadc8d74SKever Yang debug("%s clk_set_defaults failed %d\n", __func__, ret); 295186f8572SMark Yao 296186f8572SMark Yao ret = clk_get_by_name(crtc_state->dev, "dclk_vop", &dclk); 297186f8572SMark Yao if (!ret) 2984d64cedbSDamon Ding ret = clk_set_rate(&dclk, mode->crtc_clock * 1000); 299186f8572SMark Yao if (IS_ERR_VALUE(ret)) { 3004b8c2ef1SMark Yao printf("%s: Failed to set dclk: ret=%d\n", __func__, ret); 301186f8572SMark Yao return ret; 302186f8572SMark Yao } 3032264c88bSDamon Ding printf("VOP:0x%8p set crtc_clock to %dKHz\n", vop->regs, mode->crtc_clock); 304186f8572SMark Yao 305186f8572SMark Yao memcpy(vop->regsbak, vop->regs, vop_data->reg_len); 306186f8572SMark Yao 3074b8c2ef1SMark Yao rockchip_vop_init_gamma(vop, state); 308186f8572SMark Yao 3096b898587SDamon Ding ret = gpio_request_by_name(crtc_state->dev, "mcu-rs-gpios", 3106b898587SDamon Ding 0, &vop->mcu_rs_gpio, GPIOD_IS_OUT); 3116b898587SDamon Ding if (ret && ret != -ENOENT) 3126b898587SDamon Ding printf("%s: Cannot get mcu rs GPIO: %d\n", __func__, ret); 3136b898587SDamon Ding 314186f8572SMark Yao VOP_CTRL_SET(vop, global_regdone_en, 1); 3152b34f307SMark Yao VOP_CTRL_SET(vop, axi_outstanding_max_num, 30); 3162b34f307SMark Yao VOP_CTRL_SET(vop, axi_max_outstanding_en, 1); 317b0dbe9a0SSandy Huang VOP_CTRL_SET(vop, reg_done_frm, 1); 318186f8572SMark Yao VOP_CTRL_SET(vop, win_gate[0], 1); 319186f8572SMark Yao VOP_CTRL_SET(vop, win_gate[1], 1); 3202b34f307SMark Yao VOP_CTRL_SET(vop, win_channel[0], 0x12); 3212b34f307SMark Yao VOP_CTRL_SET(vop, win_channel[1], 0x34); 3222b34f307SMark Yao VOP_CTRL_SET(vop, win_channel[2], 0x56); 323186f8572SMark Yao VOP_CTRL_SET(vop, dsp_blank, 0); 324186f8572SMark Yao 32513f658dcSDamon Ding dclk_inv = (conn_state->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0; 32654f7137bSDamon Ding /* For improving signal quality, dclk need to be inverted by default on rv1106. */ 32754f7137bSDamon Ding if ((VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 12)) 32854f7137bSDamon Ding dclk_inv = !dclk_inv; 3293a06149eSSandy Huang VOP_CTRL_SET(vop, dclk_pol, dclk_inv); 3303a06149eSSandy Huang 331186f8572SMark Yao val = 0x8; 332186f8572SMark Yao val |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1; 333186f8572SMark Yao val |= (mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1); 334186f8572SMark Yao VOP_CTRL_SET(vop, pin_pol, val); 335186f8572SMark Yao 336186f8572SMark Yao switch (conn_state->type) { 337186f8572SMark Yao case DRM_MODE_CONNECTOR_LVDS: 338186f8572SMark Yao VOP_CTRL_SET(vop, rgb_en, 1); 339186f8572SMark Yao VOP_CTRL_SET(vop, rgb_pin_pol, val); 3403a06149eSSandy Huang VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv); 3413a06149eSSandy Huang VOP_CTRL_SET(vop, lvds_en, 1); 3423a06149eSSandy Huang VOP_CTRL_SET(vop, lvds_pin_pol, val); 3433a06149eSSandy Huang VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv); 344*fc8b0d66SDamon Ding if (!IS_ERR_OR_NULL(vop->grf_ctrl)) 345*fc8b0d66SDamon Ding VOP_GRF_SET(vop, grf_ctrl, grf_dclk_inv, dclk_inv); 346186f8572SMark Yao break; 347186f8572SMark Yao case DRM_MODE_CONNECTOR_eDP: 348186f8572SMark Yao VOP_CTRL_SET(vop, edp_en, 1); 349186f8572SMark Yao VOP_CTRL_SET(vop, edp_pin_pol, val); 3503a06149eSSandy Huang VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv); 351186f8572SMark Yao break; 352186f8572SMark Yao case DRM_MODE_CONNECTOR_HDMIA: 353186f8572SMark Yao VOP_CTRL_SET(vop, hdmi_en, 1); 354186f8572SMark Yao VOP_CTRL_SET(vop, hdmi_pin_pol, val); 3553a06149eSSandy Huang VOP_CTRL_SET(vop, hdmi_dclk_pol, 1); 356186f8572SMark Yao break; 357186f8572SMark Yao case DRM_MODE_CONNECTOR_DSI: 358186f8572SMark Yao VOP_CTRL_SET(vop, mipi_en, 1); 359186f8572SMark Yao VOP_CTRL_SET(vop, mipi_pin_pol, val); 3603a06149eSSandy Huang VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv); 361186f8572SMark Yao VOP_CTRL_SET(vop, mipi_dual_channel_en, 362bee25ee6SGuochun Huang !!(conn_state->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE)); 363186f8572SMark Yao VOP_CTRL_SET(vop, data01_swap, 364bee25ee6SGuochun Huang !!(conn_state->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) || 365289af5f4SSandy Huang crtc_state->dual_channel_swap); 366186f8572SMark Yao break; 3673a06149eSSandy Huang case DRM_MODE_CONNECTOR_DisplayPort: 3683a06149eSSandy Huang VOP_CTRL_SET(vop, dp_dclk_pol, 0); 3693a06149eSSandy Huang VOP_CTRL_SET(vop, dp_pin_pol, val); 3703a06149eSSandy Huang VOP_CTRL_SET(vop, dp_en, 1); 3713a06149eSSandy Huang break; 37209b01f9eSAlgea Cao case DRM_MODE_CONNECTOR_TV: 37309b01f9eSAlgea Cao if (vdisplay == CVBS_PAL_VDISPLAY) 37409b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_sw_mode, 1); 37509b01f9eSAlgea Cao else 37609b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_sw_mode, 0); 37709b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_dclk_pol, 1); 37809b01f9eSAlgea Cao VOP_CTRL_SET(vop, tve_dclk_en, 1); 37909b01f9eSAlgea Cao /* use the same pol reg with hdmi */ 38009b01f9eSAlgea Cao VOP_CTRL_SET(vop, hdmi_pin_pol, val); 38109b01f9eSAlgea Cao VOP_CTRL_SET(vop, sw_genlock, 1); 38209b01f9eSAlgea Cao VOP_CTRL_SET(vop, sw_uv_offset_en, 1); 38309b01f9eSAlgea Cao VOP_CTRL_SET(vop, dither_up, 1); 38409b01f9eSAlgea Cao break; 385186f8572SMark Yao default: 386186f8572SMark Yao printf("unsupport connector_type[%d]\n", conn_state->type); 387186f8572SMark Yao } 388186f8572SMark Yao 389186f8572SMark Yao if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA && 390186f8572SMark Yao !(vop_data->feature & VOP_FEATURE_OUTPUT_10BIT)) 391186f8572SMark Yao conn_state->output_mode = ROCKCHIP_OUT_MODE_P888; 392186f8572SMark Yao 393186f8572SMark Yao switch (conn_state->bus_format) { 394186f8572SMark Yao case MEDIA_BUS_FMT_RGB565_1X16: 395186f8572SMark Yao val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB565); 396186f8572SMark Yao break; 397186f8572SMark Yao case MEDIA_BUS_FMT_RGB666_1X18: 398186f8572SMark Yao case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: 3999c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: 4009c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA: 401186f8572SMark Yao val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB666); 402186f8572SMark Yao break; 40379feefb1SSandy Huang case MEDIA_BUS_FMT_YUV8_1X24: 40479feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 40579feefb1SSandy Huang val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(1); 40679feefb1SSandy Huang break; 40779feefb1SSandy Huang case MEDIA_BUS_FMT_YUV10_1X30: 40879feefb1SSandy Huang case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 40979feefb1SSandy Huang val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0); 41079feefb1SSandy Huang break; 411186f8572SMark Yao case MEDIA_BUS_FMT_RGB888_1X24: 4129c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: 4139c5e1148SWyon Bi case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: 414186f8572SMark Yao default: 415186f8572SMark Yao val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0); 416186f8572SMark Yao break; 417186f8572SMark Yao } 418186f8572SMark Yao if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA) 419186f8572SMark Yao val |= PRE_DITHER_DOWN_EN(0); 420186f8572SMark Yao else 421186f8572SMark Yao val |= PRE_DITHER_DOWN_EN(1); 422186f8572SMark Yao val |= DITHER_DOWN_MODE_SEL(DITHER_DOWN_ALLEGRO); 423186f8572SMark Yao VOP_CTRL_SET(vop, dither_down, val); 424186f8572SMark Yao 42579feefb1SSandy Huang VOP_CTRL_SET(vop, dclk_ddr, 42679feefb1SSandy Huang conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0); 42779feefb1SSandy Huang VOP_CTRL_SET(vop, hdmi_dclk_out_en, 42879feefb1SSandy Huang conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0); 42979feefb1SSandy Huang 43080fbe788SDamon Ding if (is_uv_swap(conn_state->bus_format, conn_state->output_mode) || 43180fbe788SDamon Ding is_rb_swap(conn_state->bus_format, conn_state->output_mode)) 43232328971SDamon Ding VOP_CTRL_SET(vop, dsp_rb_swap, 1); 43379feefb1SSandy Huang else 43479feefb1SSandy Huang VOP_CTRL_SET(vop, dsp_data_swap, 0); 43579feefb1SSandy Huang 436186f8572SMark Yao VOP_CTRL_SET(vop, out_mode, conn_state->output_mode); 43779feefb1SSandy Huang 43879feefb1SSandy Huang if (VOP_CTRL_SUPPORT(vop, overlay_mode)) { 43979feefb1SSandy Huang yuv_overlay = is_yuv_output(conn_state->bus_format); 44079feefb1SSandy Huang VOP_CTRL_SET(vop, overlay_mode, yuv_overlay); 44179feefb1SSandy Huang } 44279feefb1SSandy Huang /* 44379feefb1SSandy Huang * todo: r2y for win csc 44479feefb1SSandy Huang */ 44579feefb1SSandy Huang VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(conn_state->bus_format)); 44679feefb1SSandy Huang 44779feefb1SSandy Huang if (yuv_overlay) { 44879feefb1SSandy Huang if (!is_yuv_output(conn_state->bus_format)) 44979feefb1SSandy Huang post_y2r_en = true; 45079feefb1SSandy Huang } else { 45179feefb1SSandy Huang if (is_yuv_output(conn_state->bus_format)) 45279feefb1SSandy Huang post_r2y_en = true; 45379feefb1SSandy Huang } 45479feefb1SSandy Huang 455b7618fd3SSandy Huang crtc_state->yuv_overlay = yuv_overlay; 45679feefb1SSandy Huang post_csc_mode = to_vop_csc_mode(conn_state->color_space); 45779feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_r2y_en, post_r2y_en); 45879feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_y2r_en, post_y2r_en); 45979feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, post_csc_mode); 46079feefb1SSandy Huang VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, post_csc_mode); 46179feefb1SSandy Huang 46279feefb1SSandy Huang /* 46379feefb1SSandy Huang * Background color is 10bit depth if vop version >= 3.5 46479feefb1SSandy Huang */ 46579feefb1SSandy Huang if (!is_yuv_output(conn_state->bus_format)) 46679feefb1SSandy Huang val = 0; 46779feefb1SSandy Huang else if (VOP_MAJOR(vop->version) == 3 && 46879feefb1SSandy Huang VOP_MINOR(vop->version) >= 5) 46979feefb1SSandy Huang val = 0x20010200; 47079feefb1SSandy Huang else 47179feefb1SSandy Huang val = 0x801080; 47279feefb1SSandy Huang VOP_CTRL_SET(vop, dsp_background, val); 47379feefb1SSandy Huang 474186f8572SMark Yao VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len); 475186f8572SMark Yao val = hact_st << 16; 476186f8572SMark Yao val |= hact_end; 477186f8572SMark Yao VOP_CTRL_SET(vop, hact_st_end, val); 478186f8572SMark Yao val = vact_st << 16; 479186f8572SMark Yao val |= vact_end; 480186f8572SMark Yao VOP_CTRL_SET(vop, vact_st_end, val); 481ccd843b9SSandy Huang if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 482ccd843b9SSandy Huang u16 vact_st_f1 = vtotal + vact_st + 1; 483ccd843b9SSandy Huang u16 vact_end_f1 = vact_st_f1 + vdisplay; 484ccd843b9SSandy Huang 485ccd843b9SSandy Huang val = vact_st_f1 << 16 | vact_end_f1; 486ccd843b9SSandy Huang VOP_CTRL_SET(vop, vact_st_end_f1, val); 487ccd843b9SSandy Huang 488ccd843b9SSandy Huang val = vtotal << 16 | (vtotal + vsync_len); 489ccd843b9SSandy Huang VOP_CTRL_SET(vop, vs_st_end_f1, val); 490ccd843b9SSandy Huang VOP_CTRL_SET(vop, dsp_interlace, 1); 491ccd843b9SSandy Huang VOP_CTRL_SET(vop, p2i_en, 1); 492ccd843b9SSandy Huang vtotal += vtotal + 1; 493960080c9SSandy Huang act_end = vact_end_f1; 494ccd843b9SSandy Huang } else { 495ccd843b9SSandy Huang VOP_CTRL_SET(vop, dsp_interlace, 0); 496ccd843b9SSandy Huang VOP_CTRL_SET(vop, p2i_en, 0); 497960080c9SSandy Huang act_end = vact_end; 498ccd843b9SSandy Huang } 499ccd843b9SSandy Huang VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len); 5008a2a3a29SSandy Huang vop_post_config(state, vop); 501ccd843b9SSandy Huang VOP_CTRL_SET(vop, core_dclk_div, 502ccd843b9SSandy Huang !!(mode->flags & DRM_MODE_FLAG_DBLCLK)); 5038a2a3a29SSandy Huang 504960080c9SSandy Huang VOP_LINE_FLAG_SET(vop, line_flag_num[0], act_end - 3); 505186f8572SMark Yao VOP_LINE_FLAG_SET(vop, line_flag_num[1], 506960080c9SSandy Huang act_end - us_to_vertical_line(mode, 1000)); 50767b9012cSSandy Huang if (state->crtc_state.mcu_timing.mcu_pix_total > 0) 50867b9012cSSandy Huang vop_mcu_mode(state, vop); 509186f8572SMark Yao vop_cfg_done(vop); 510186f8572SMark Yao 511186f8572SMark Yao return 0; 512186f8572SMark Yao } 513186f8572SMark Yao 514186f8572SMark Yao static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src, 515186f8572SMark Yao uint32_t dst, bool is_horizontal, 516186f8572SMark Yao int vsu_mode, int *vskiplines) 517186f8572SMark Yao { 518186f8572SMark Yao uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT; 519186f8572SMark Yao 520186f8572SMark Yao if (is_horizontal) { 521186f8572SMark Yao if (mode == SCALE_UP) 522186f8572SMark Yao val = GET_SCL_FT_BIC(src, dst); 523186f8572SMark Yao else if (mode == SCALE_DOWN) 524186f8572SMark Yao val = GET_SCL_FT_BILI_DN(src, dst); 525186f8572SMark Yao } else { 526186f8572SMark Yao if (mode == SCALE_UP) { 527186f8572SMark Yao if (vsu_mode == SCALE_UP_BIL) 528186f8572SMark Yao val = GET_SCL_FT_BILI_UP(src, dst); 529186f8572SMark Yao else 530186f8572SMark Yao val = GET_SCL_FT_BIC(src, dst); 531186f8572SMark Yao } else if (mode == SCALE_DOWN) { 532186f8572SMark Yao if (vskiplines) { 533186f8572SMark Yao *vskiplines = scl_get_vskiplines(src, dst); 534186f8572SMark Yao val = scl_get_bili_dn_vskip(src, dst, 535186f8572SMark Yao *vskiplines); 536186f8572SMark Yao } else { 537186f8572SMark Yao val = GET_SCL_FT_BILI_DN(src, dst); 538186f8572SMark Yao } 539186f8572SMark Yao } 540186f8572SMark Yao } 541186f8572SMark Yao 542186f8572SMark Yao return val; 543186f8572SMark Yao } 544186f8572SMark Yao 545186f8572SMark Yao static void scl_vop_cal_scl_fac(struct vop *vop, 546186f8572SMark Yao uint32_t src_w, uint32_t src_h, uint32_t dst_w, 547186f8572SMark Yao uint32_t dst_h, uint32_t pixel_format) 548186f8572SMark Yao { 549186f8572SMark Yao uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode; 550186f8572SMark Yao uint16_t cbcr_hor_scl_mode = SCALE_NONE; 551186f8572SMark Yao uint16_t cbcr_ver_scl_mode = SCALE_NONE; 552186f8572SMark Yao int hsub = drm_format_horz_chroma_subsampling(pixel_format); 553186f8572SMark Yao int vsub = drm_format_vert_chroma_subsampling(pixel_format); 554186f8572SMark Yao bool is_yuv = false; 555186f8572SMark Yao uint16_t cbcr_src_w = src_w / hsub; 556186f8572SMark Yao uint16_t cbcr_src_h = src_h / vsub; 557186f8572SMark Yao uint16_t vsu_mode; 558186f8572SMark Yao uint16_t lb_mode; 559186f8572SMark Yao uint32_t val; 560186f8572SMark Yao int vskiplines = 0; 561186f8572SMark Yao 562186f8572SMark Yao if (!vop->win->scl) 563186f8572SMark Yao return; 564186f8572SMark Yao 565186f8572SMark Yao if (!vop->win->scl->ext) { 566186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_x, 567186f8572SMark Yao scl_cal_scale2(src_w, dst_w)); 568186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_y, 569186f8572SMark Yao scl_cal_scale2(src_h, dst_h)); 570186f8572SMark Yao if (is_yuv) { 571186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_x, 572186f8572SMark Yao scl_cal_scale2(src_w, dst_w)); 573186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_y, 574186f8572SMark Yao scl_cal_scale2(src_h, dst_h)); 575186f8572SMark Yao } 576186f8572SMark Yao return; 577186f8572SMark Yao } 578186f8572SMark Yao 579186f8572SMark Yao yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w); 580186f8572SMark Yao yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h); 581186f8572SMark Yao 582186f8572SMark Yao if (is_yuv) { 583186f8572SMark Yao cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w); 584186f8572SMark Yao cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h); 585186f8572SMark Yao if (cbcr_hor_scl_mode == SCALE_DOWN) 586186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(dst_w, true); 587186f8572SMark Yao else 588186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true); 589186f8572SMark Yao } else { 590186f8572SMark Yao if (yrgb_hor_scl_mode == SCALE_DOWN) 591186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(dst_w, false); 592186f8572SMark Yao else 593186f8572SMark Yao lb_mode = scl_vop_cal_lb_mode(src_w, false); 594186f8572SMark Yao } 595186f8572SMark Yao 596186f8572SMark Yao VOP_SCL_SET_EXT(vop, lb_mode, lb_mode); 597186f8572SMark Yao if (lb_mode == LB_RGB_3840X2) { 598186f8572SMark Yao if (yrgb_ver_scl_mode != SCALE_NONE) { 599186f8572SMark Yao printf("ERROR : not allow yrgb ver scale\n"); 600186f8572SMark Yao return; 601186f8572SMark Yao } 602186f8572SMark Yao if (cbcr_ver_scl_mode != SCALE_NONE) { 603186f8572SMark Yao printf("ERROR : not allow cbcr ver scale\n"); 604186f8572SMark Yao return; 605186f8572SMark Yao } 606186f8572SMark Yao vsu_mode = SCALE_UP_BIL; 607186f8572SMark Yao } else if (lb_mode == LB_RGB_2560X4) { 608186f8572SMark Yao vsu_mode = SCALE_UP_BIL; 609186f8572SMark Yao } else { 610186f8572SMark Yao vsu_mode = SCALE_UP_BIC; 611186f8572SMark Yao } 612186f8572SMark Yao 613186f8572SMark Yao val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w, 614186f8572SMark Yao true, 0, NULL); 615186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_x, val); 616186f8572SMark Yao val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h, 617186f8572SMark Yao false, vsu_mode, &vskiplines); 618186f8572SMark Yao VOP_SCL_SET(vop, scale_yrgb_y, val); 619186f8572SMark Yao 620186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_yrgb_gt4, vskiplines == 4); 621186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_yrgb_gt2, vskiplines == 2); 622186f8572SMark Yao 623186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_hor_scl_mode, yrgb_hor_scl_mode); 624186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_ver_scl_mode, yrgb_ver_scl_mode); 625186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_hsd_mode, SCALE_DOWN_BIL); 626186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_vsd_mode, SCALE_DOWN_BIL); 627186f8572SMark Yao VOP_SCL_SET_EXT(vop, yrgb_vsu_mode, vsu_mode); 628186f8572SMark Yao if (is_yuv) { 629186f8572SMark Yao val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w, 630186f8572SMark Yao dst_w, true, 0, NULL); 631186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_x, val); 632186f8572SMark Yao val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h, 633186f8572SMark Yao dst_h, false, vsu_mode, &vskiplines); 634186f8572SMark Yao VOP_SCL_SET(vop, scale_cbcr_y, val); 635186f8572SMark Yao 636186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_cbcr_gt4, vskiplines == 4); 637186f8572SMark Yao VOP_SCL_SET_EXT(vop, vsd_cbcr_gt2, vskiplines == 2); 638186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_hor_scl_mode, cbcr_hor_scl_mode); 639186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_ver_scl_mode, cbcr_ver_scl_mode); 640186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_hsd_mode, SCALE_DOWN_BIL); 641186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_vsd_mode, SCALE_DOWN_BIL); 642186f8572SMark Yao VOP_SCL_SET_EXT(vop, cbcr_vsu_mode, vsu_mode); 643186f8572SMark Yao } 644186f8572SMark Yao } 645186f8572SMark Yao 646b7618fd3SSandy Huang static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table) 647b7618fd3SSandy Huang { 648b7618fd3SSandy Huang int i; 649b7618fd3SSandy Huang 650b7618fd3SSandy Huang /* 651b7618fd3SSandy Huang * so far the csc offset is not 0 and in the feature the csc offset 652b7618fd3SSandy Huang * impossible be 0, so when the offset is 0, should return here. 653b7618fd3SSandy Huang */ 654b7618fd3SSandy Huang if (!table || offset == 0) 655b7618fd3SSandy Huang return; 656b7618fd3SSandy Huang 657b7618fd3SSandy Huang for (i = 0; i < 8; i++) 658b7618fd3SSandy Huang vop_writel(vop, offset + i * 4, table[i]); 659b7618fd3SSandy Huang } 660b7618fd3SSandy Huang 661b7618fd3SSandy Huang static int rockchip_vop_setup_csc_table(struct display_state *state) 662b7618fd3SSandy Huang { 663b7618fd3SSandy Huang struct crtc_state *crtc_state = &state->crtc_state; 664b7618fd3SSandy Huang struct connector_state *conn_state = &state->conn_state; 665b7618fd3SSandy Huang struct vop *vop = crtc_state->private; 666b7618fd3SSandy Huang const uint32_t *csc_table = NULL; 667b7618fd3SSandy Huang 668b7618fd3SSandy Huang if (!vop->csc_table || !crtc_state->yuv_overlay) 669b7618fd3SSandy Huang return 0; 670b7618fd3SSandy Huang /* todo: only implement r2y*/ 671b7618fd3SSandy Huang switch (conn_state->color_space) { 672b7618fd3SSandy Huang case V4L2_COLORSPACE_SMPTE170M: 673b7618fd3SSandy Huang csc_table = vop->csc_table->r2y_bt601_12_235; 674b7618fd3SSandy Huang break; 675b7618fd3SSandy Huang case V4L2_COLORSPACE_REC709: 676b7618fd3SSandy Huang case V4L2_COLORSPACE_DEFAULT: 677b7618fd3SSandy Huang case V4L2_COLORSPACE_JPEG: 678b7618fd3SSandy Huang csc_table = vop->csc_table->r2y_bt709; 679b7618fd3SSandy Huang break; 680b7618fd3SSandy Huang case V4L2_COLORSPACE_BT2020: 681b7618fd3SSandy Huang csc_table = vop->csc_table->r2y_bt2020; 682b7618fd3SSandy Huang break; 683b7618fd3SSandy Huang default: 684b7618fd3SSandy Huang csc_table = vop->csc_table->r2y_bt601; 685b7618fd3SSandy Huang break; 686b7618fd3SSandy Huang } 687b7618fd3SSandy Huang 688b7618fd3SSandy Huang vop_load_csc_table(vop, vop->win_csc->r2y_offset, csc_table); 689b7618fd3SSandy Huang VOP_WIN_CSC_SET(vop, r2y_en, 1); 690b7618fd3SSandy Huang 691b7618fd3SSandy Huang return 0; 692b7618fd3SSandy Huang } 693b7618fd3SSandy Huang 694186f8572SMark Yao static int rockchip_vop_set_plane(struct display_state *state) 695186f8572SMark Yao { 696186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 697d4a9114aSShixiang Zheng const struct rockchip_crtc *crtc = crtc_state->crtc; 698d4a9114aSShixiang Zheng const struct vop_data *vop_data = crtc->data; 699186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 700186f8572SMark Yao struct drm_display_mode *mode = &conn_state->mode; 701186f8572SMark Yao u32 act_info, dsp_info, dsp_st, dsp_stx, dsp_sty; 702186f8572SMark Yao struct vop *vop = crtc_state->private; 703ee01dbb2SDamon Ding int src_w = crtc_state->src_rect.w; 704ee01dbb2SDamon Ding int src_h = crtc_state->src_rect.h; 705ee01dbb2SDamon Ding int crtc_x = crtc_state->crtc_rect.x; 706ee01dbb2SDamon Ding int crtc_y = crtc_state->crtc_rect.y; 707ee01dbb2SDamon Ding int crtc_w = crtc_state->crtc_rect.w; 708ee01dbb2SDamon Ding int crtc_h = crtc_state->crtc_rect.h; 709186f8572SMark Yao int xvir = crtc_state->xvir; 710ffa55e18SShixiang Zheng int x_mirror = 0, y_mirror = 0; 711186f8572SMark Yao 712eee28ceaSSandy Huang if (crtc_w > crtc_state->max_output.width) { 713eee28ceaSSandy Huang printf("ERROR: output w[%d] exceeded max width[%d]\n", 714eee28ceaSSandy Huang crtc_w, crtc_state->max_output.width); 715cf53642aSSandy Huang return -EINVAL; 716cf53642aSSandy Huang } 717cf53642aSSandy Huang 718186f8572SMark Yao act_info = (src_h - 1) << 16; 719186f8572SMark Yao act_info |= (src_w - 1) & 0xffff; 720186f8572SMark Yao 721186f8572SMark Yao dsp_info = (crtc_h - 1) << 16; 722186f8572SMark Yao dsp_info |= (crtc_w - 1) & 0xffff; 723186f8572SMark Yao 72444006808SAlgea Cao dsp_stx = crtc_x + mode->crtc_htotal - mode->crtc_hsync_start; 72544006808SAlgea Cao dsp_sty = crtc_y + mode->crtc_vtotal - mode->crtc_vsync_start; 726186f8572SMark Yao dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); 727d4a9114aSShixiang Zheng /* 728f0e8414bSSandy Huang * vop full need to treats rgb888 as bgr888 so we reverse the rb swap to workaround 729d4a9114aSShixiang Zheng */ 730f0e8414bSSandy Huang if (crtc_state->format == ROCKCHIP_FMT_RGB888 && VOP_MAJOR(vop_data->version) == 3) 731d4a9114aSShixiang Zheng crtc_state->rb_swap = !crtc_state->rb_swap; 732186f8572SMark Yao 733ffa55e18SShixiang Zheng if (mode->flags & DRM_MODE_FLAG_YMIRROR) 734ffa55e18SShixiang Zheng y_mirror = 1; 735ffa55e18SShixiang Zheng else 736ffa55e18SShixiang Zheng y_mirror = 0; 737ffa55e18SShixiang Zheng if (mode->flags & DRM_MODE_FLAG_XMIRROR) 738ffa55e18SShixiang Zheng x_mirror = 1; 739ffa55e18SShixiang Zheng else 740ffa55e18SShixiang Zheng x_mirror = 0; 741ffa55e18SShixiang Zheng if (crtc_state->ymirror ^ y_mirror) 742ffa55e18SShixiang Zheng y_mirror = 1; 743ffa55e18SShixiang Zheng else 744ffa55e18SShixiang Zheng y_mirror = 0; 745ffa55e18SShixiang Zheng if (y_mirror) { 746ffa55e18SShixiang Zheng if (VOP_CTRL_SUPPORT(vop, ymirror)) 747186f8572SMark Yao crtc_state->dma_addr += (src_h - 1) * xvir * 4; 748620af6a3SSandy Huang else 749ffa55e18SShixiang Zheng y_mirror = 0; 750620af6a3SSandy Huang } 751ffa55e18SShixiang Zheng VOP_CTRL_SET(vop, ymirror, y_mirror); 752ffa55e18SShixiang Zheng VOP_CTRL_SET(vop, xmirror, x_mirror); 753ffa55e18SShixiang Zheng 754186f8572SMark Yao VOP_WIN_SET(vop, format, crtc_state->format); 755186f8572SMark Yao VOP_WIN_SET(vop, yrgb_vir, xvir); 756186f8572SMark Yao VOP_WIN_SET(vop, yrgb_mst, crtc_state->dma_addr); 757186f8572SMark Yao 758186f8572SMark Yao scl_vop_cal_scl_fac(vop, src_w, src_h, crtc_w, crtc_h, 759186f8572SMark Yao crtc_state->format); 760186f8572SMark Yao 761186f8572SMark Yao VOP_WIN_SET(vop, act_info, act_info); 762186f8572SMark Yao VOP_WIN_SET(vop, dsp_info, dsp_info); 763186f8572SMark Yao VOP_WIN_SET(vop, dsp_st, dsp_st); 764186f8572SMark Yao VOP_WIN_SET(vop, rb_swap, crtc_state->rb_swap); 765186f8572SMark Yao 766186f8572SMark Yao VOP_WIN_SET(vop, src_alpha_ctl, 0); 767186f8572SMark Yao 768b7618fd3SSandy Huang rockchip_vop_setup_csc_table(state); 769186f8572SMark Yao VOP_WIN_SET(vop, enable, 1); 770c33e1feaSAndy Yan VOP_WIN_SET(vop, gate, 1); 771186f8572SMark Yao vop_cfg_done(vop); 772186f8572SMark Yao 773186f8572SMark Yao return 0; 774186f8572SMark Yao } 775186f8572SMark Yao 776186f8572SMark Yao static int rockchip_vop_prepare(struct display_state *state) 777186f8572SMark Yao { 778186f8572SMark Yao return 0; 779186f8572SMark Yao } 780186f8572SMark Yao 781186f8572SMark Yao static int rockchip_vop_enable(struct display_state *state) 782186f8572SMark Yao { 783186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 784186f8572SMark Yao struct vop *vop = crtc_state->private; 785186f8572SMark Yao 786186f8572SMark Yao VOP_CTRL_SET(vop, standby, 0); 787186f8572SMark Yao vop_cfg_done(vop); 78867b9012cSSandy Huang if (crtc_state->mcu_timing.mcu_pix_total > 0) 78967b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_hold_mode, 0); 790186f8572SMark Yao 791186f8572SMark Yao return 0; 792186f8572SMark Yao } 793186f8572SMark Yao 794186f8572SMark Yao static int rockchip_vop_disable(struct display_state *state) 795186f8572SMark Yao { 796186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 797186f8572SMark Yao struct vop *vop = crtc_state->private; 798186f8572SMark Yao 799186f8572SMark Yao VOP_CTRL_SET(vop, standby, 1); 800186f8572SMark Yao vop_cfg_done(vop); 801186f8572SMark Yao return 0; 802186f8572SMark Yao } 803186f8572SMark Yao 804186f8572SMark Yao static int rockchip_vop_fixup_dts(struct display_state *state, void *blob) 805186f8572SMark Yao { 806e2bce6e4SKever Yang #if 0 807186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 808186f8572SMark Yao struct panel_state *pstate = &state->panel_state; 809186f8572SMark Yao uint32_t phandle; 810186f8572SMark Yao char path[100]; 811186f8572SMark Yao int ret, dsp_lut_node; 812186f8572SMark Yao 813e2bce6e4SKever Yang if (!ofnode_valid(pstate->dsp_lut_node)) 814186f8572SMark Yao return 0; 815186f8572SMark Yao ret = fdt_get_path(state->blob, pstate->dsp_lut_node, path, sizeof(path)); 816186f8572SMark Yao if (ret < 0) { 817186f8572SMark Yao printf("failed to get dsp_lut path[%s], ret=%d\n", 818186f8572SMark Yao path, ret); 819186f8572SMark Yao return ret; 820186f8572SMark Yao } 821186f8572SMark Yao 822186f8572SMark Yao dsp_lut_node = fdt_path_offset(blob, path); 823186f8572SMark Yao phandle = fdt_get_phandle(blob, dsp_lut_node); 824186f8572SMark Yao if (!phandle) { 825186f8572SMark Yao phandle = fdt_alloc_phandle(blob); 826186f8572SMark Yao if (!phandle) { 827186f8572SMark Yao printf("failed to alloc phandle\n"); 828186f8572SMark Yao return -ENOMEM; 829186f8572SMark Yao } 830186f8572SMark Yao 831186f8572SMark Yao fdt_set_phandle(blob, dsp_lut_node, phandle); 832186f8572SMark Yao } 833186f8572SMark Yao 834186f8572SMark Yao ret = fdt_get_path(state->blob, crtc_state->node, path, sizeof(path)); 835186f8572SMark Yao if (ret < 0) { 836186f8572SMark Yao printf("failed to get route path[%s], ret=%d\n", 837186f8572SMark Yao path, ret); 838186f8572SMark Yao return ret; 839186f8572SMark Yao } 840186f8572SMark Yao 841186f8572SMark Yao do_fixup_by_path_u32(blob, path, "dsp-lut", phandle, 1); 842e2bce6e4SKever Yang #endif 843186f8572SMark Yao return 0; 844186f8572SMark Yao } 845186f8572SMark Yao 84667b9012cSSandy Huang static int rockchip_vop_send_mcu_cmd(struct display_state *state, 84767b9012cSSandy Huang u32 type, u32 value) 84867b9012cSSandy Huang { 84967b9012cSSandy Huang struct crtc_state *crtc_state = &state->crtc_state; 85067b9012cSSandy Huang struct vop *vop = crtc_state->private; 85167b9012cSSandy Huang 85267b9012cSSandy Huang if (vop) { 85367b9012cSSandy Huang switch (type) { 85467b9012cSSandy Huang case MCU_WRCMD: 8556b898587SDamon Ding set_vop_mcu_rs(vop, 0); 85667b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_bypass_port, value); 8576b898587SDamon Ding set_vop_mcu_rs(vop, 1); 85867b9012cSSandy Huang break; 85967b9012cSSandy Huang case MCU_WRDATA: 8606b898587SDamon Ding set_vop_mcu_rs(vop, 1); 86167b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_rw_bypass_port, value); 86267b9012cSSandy Huang break; 86367b9012cSSandy Huang case MCU_SETBYPASS: 86467b9012cSSandy Huang VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0); 86567b9012cSSandy Huang break; 86667b9012cSSandy Huang default: 86767b9012cSSandy Huang break; 86867b9012cSSandy Huang } 86967b9012cSSandy Huang } 87067b9012cSSandy Huang 87167b9012cSSandy Huang return 0; 87267b9012cSSandy Huang } 87367b9012cSSandy Huang 87422007755SDamon Ding static int rockchip_vop_mode_valid(struct display_state *state) 87522007755SDamon Ding { 87622007755SDamon Ding struct connector_state *conn_state = &state->conn_state; 87722007755SDamon Ding struct drm_display_mode *mode = &conn_state->mode; 87822007755SDamon Ding struct videomode vm; 87922007755SDamon Ding 88022007755SDamon Ding drm_display_mode_to_videomode(mode, &vm); 88122007755SDamon Ding 88222007755SDamon Ding if (vm.hactive < 32 || vm.vactive < 32 || 88322007755SDamon Ding (vm.hfront_porch * vm.hsync_len * vm.hback_porch * 88422007755SDamon Ding vm.vfront_porch * vm.vsync_len * vm.vback_porch == 0)) { 88522007755SDamon Ding printf("ERROR: unsupported display timing\n"); 88622007755SDamon Ding return -EINVAL; 88722007755SDamon Ding } 88822007755SDamon Ding 88922007755SDamon Ding return 0; 89022007755SDamon Ding } 89122007755SDamon Ding 8924c765862SDamon Ding static int rockchip_vop_plane_check(struct display_state *state) 8934c765862SDamon Ding { 8944c765862SDamon Ding struct crtc_state *crtc_state = &state->crtc_state; 8954c765862SDamon Ding const struct rockchip_crtc *crtc = crtc_state->crtc; 8964c765862SDamon Ding const struct vop_data *vop_data = crtc->data; 8974c765862SDamon Ding const struct vop_win *win = vop_data->win; 8984c765862SDamon Ding struct display_rect *src = &crtc_state->src_rect; 8994c765862SDamon Ding struct display_rect *dst = &crtc_state->crtc_rect; 9004c765862SDamon Ding int min_scale, max_scale; 9014c765862SDamon Ding int hscale, vscale; 9024c765862SDamon Ding 9034c765862SDamon Ding min_scale = win->scl ? FRAC_16_16(1, 8) : VOP_PLANE_NO_SCALING; 9044c765862SDamon Ding max_scale = win->scl ? FRAC_16_16(8, 1) : VOP_PLANE_NO_SCALING; 9054c765862SDamon Ding 9064c765862SDamon Ding hscale = display_rect_calc_hscale(src, dst, min_scale, max_scale); 9074c765862SDamon Ding vscale = display_rect_calc_vscale(src, dst, min_scale, max_scale); 9084c765862SDamon Ding if (hscale < 0 || vscale < 0) { 9094c765862SDamon Ding printf("ERROR: scale factor is out of range\n"); 9104c765862SDamon Ding return -ERANGE; 9114c765862SDamon Ding } 9124c765862SDamon Ding 9134c765862SDamon Ding return 0; 9144c765862SDamon Ding } 9154c765862SDamon Ding 9161b5811e7SDamon Ding static int rockchip_vop_mode_fixup(struct display_state *state) 9171b5811e7SDamon Ding { 9184d64cedbSDamon Ding struct crtc_state *crtc_state = &state->crtc_state; 9191b5811e7SDamon Ding struct connector_state *conn_state = &state->conn_state; 9201b5811e7SDamon Ding struct drm_display_mode *mode = &conn_state->mode; 9211b5811e7SDamon Ding 9221b5811e7SDamon Ding drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); 9231b5811e7SDamon Ding 9244d64cedbSDamon Ding mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(conn_state->bus_format); 9254d64cedbSDamon Ding if (crtc_state->mcu_timing.mcu_pix_total) 9264d64cedbSDamon Ding mode->crtc_clock *= crtc_state->mcu_timing.mcu_pix_total + 1; 9274d64cedbSDamon Ding 9281b5811e7SDamon Ding return 0; 9291b5811e7SDamon Ding } 9301b5811e7SDamon Ding 931186f8572SMark Yao const struct rockchip_crtc_funcs rockchip_vop_funcs = { 932cf53642aSSandy Huang .preinit = rockchip_vop_preinit, 933186f8572SMark Yao .init = rockchip_vop_init, 934186f8572SMark Yao .set_plane = rockchip_vop_set_plane, 935186f8572SMark Yao .prepare = rockchip_vop_prepare, 936186f8572SMark Yao .enable = rockchip_vop_enable, 937186f8572SMark Yao .disable = rockchip_vop_disable, 938186f8572SMark Yao .fixup_dts = rockchip_vop_fixup_dts, 93967b9012cSSandy Huang .send_mcu_cmd = rockchip_vop_send_mcu_cmd, 94022007755SDamon Ding .mode_valid = rockchip_vop_mode_valid, 9414c765862SDamon Ding .plane_check = rockchip_vop_plane_check, 9421b5811e7SDamon Ding .mode_fixup = rockchip_vop_mode_fixup, 943186f8572SMark Yao }; 944