From e3e12f52b214121840b44c91de5b3e5af5d3eb84 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 06 Nov 2023 03:04:41 +0000 Subject: [PATCH] rk3568 rt init --- kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 1176 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 1,063 insertions(+), 113 deletions(-) diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 35128d4..7757b07 100644 --- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -35,6 +35,8 @@ #include <linux/swab.h> #include <linux/sort.h> #include <linux/rockchip/cpu.h> +#include <linux/workqueue.h> +#include <linux/types.h> #include <soc/rockchip/rockchip_dmc.h> #include <soc/rockchip/rockchip-system-status.h> #include <uapi/linux/videodev2.h> @@ -47,6 +49,7 @@ #include "rockchip_drm_psr.h" #include "rockchip_drm_vop.h" #include "rockchip_vop_reg.h" +#include "rockchip_post_csc.h" #define _REG_SET(vop2, name, off, reg, mask, v, relaxed) \ vop2_mask_write(vop2, off + reg.offset, mask, reg.shift, v, reg.write_mask, relaxed) @@ -78,6 +81,8 @@ #define VOP_CTRL_SET(x, name, v) \ REG_SET(x, name, 0, (x)->data->ctrl->name, v, false) + +#define VOP_CTRL_GET(x, name) vop2_read_reg(x, 0, &(x)->data->ctrl->name) #define VOP_INTR_GET(vop2, name) \ vop2_read_reg(vop2, 0, &vop2->data->ctrl->name) @@ -477,6 +482,8 @@ uint8_t id; bool layer_sel_update; bool xmirror_en; + bool need_reset_p2i_flag; + atomic_t post_buf_empty_flag; const struct vop2_video_port_regs *regs; struct completion dsp_hold_completion; @@ -555,6 +562,11 @@ bool gamma_lut_active; /** + * @lut_dma_rid: lut dma id + */ + u16 lut_dma_rid; + + /** * @gamma_lut: atomic gamma look up table */ struct drm_color_lut *gamma_lut; @@ -568,6 +580,11 @@ * @cubic_lut_gem_obj: gem obj to store cubic lut */ struct rockchip_gem_object *cubic_lut_gem_obj; + + /** + * @hdr_lut_gem_obj: gem obj to store hdr lut + */ + struct rockchip_gem_object *hdr_lut_gem_obj; /** * @cubic_lut: cubic look up table @@ -591,10 +608,29 @@ struct drm_property *plane_mask_prop; /** + * @hdr_ext_data_prop: hdr extend data interaction with userspace + */ + struct drm_property *hdr_ext_data_prop; + + int hdrvivid_mode; + + /** + * @acm_lut_data_prop: acm lut data interaction with userspace + */ + struct drm_property *acm_lut_data_prop; + /** + * @post_csc_data_prop: post csc data interaction with userspace + */ + struct drm_property *post_csc_data_prop; + + /** * @primary_plane_phy_id: vp primary plane phy id, the primary plane * will be used to show uboot logo and kernel logo */ enum vop2_layer_phy_id primary_plane_phy_id; + + struct post_acm acm_info; + struct post_csc csc_info; }; struct vop2 { @@ -652,6 +688,7 @@ */ uint32_t registered_num_wins; uint8_t used_mixers; + uint8_t esmart_lb_mode; /** * @active_vp_mask: Bitmask of active video ports; */ @@ -684,6 +721,8 @@ unsigned int enable_count; struct clk *hclk; struct clk *aclk; + struct work_struct post_buf_empty_work; + struct workqueue_struct *workqueue; struct vop2_layer layers[ROCKCHIP_MAX_LAYER]; /* must put at the end of the struct */ @@ -717,6 +756,7 @@ { MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" }, { MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" }, { MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" }, + { MEDIA_BUS_FMT_RGB101010_1X30, "RGB101010_1x30" }, }; static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list) @@ -991,8 +1031,23 @@ static uint32_t vop2_read_vcnt(struct vop2_video_port *vp) { uint32_t offset = RK3568_SYS_STATUS0 + (vp->id << 2); + uint32_t vcnt0, vcnt1; + int i = 0; - return vop2_readl(vp->vop2, offset) >> 16; + for (i = 0; i < 10; i++) { + vcnt0 = vop2_readl(vp->vop2, offset) >> 16; + vcnt1 = vop2_readl(vp->vop2, offset) >> 16; + + if ((vcnt1 - vcnt0) <= 1) + break; + } + + if (i == 10) { + DRM_DEV_ERROR(vp->vop2->dev, "read VP%d vcnt error: %d %d\n", vp->id, vcnt0, vcnt1); + vcnt1 = vop2_readl(vp->vop2, offset) >> 16; + } + + return vcnt1; } static void vop2_wait_for_irq_handler(struct drm_crtc *crtc) @@ -1864,7 +1919,7 @@ #define VOP2_COMMON_SCL_FAC_CHECK(src, dst, fac) \ (fac * (dst - 1) >> 16 < (src - 1)) #define VOP3_COMMON_HOR_SCL_FAC_CHECK(src, dst, fac) \ - (fac * (dst - 1) >> 16 <= (src - 1)) + (fac * (dst - 1) >> 16 < (src - 1)) static uint16_t vop2_scale_factor(enum scale_mode mode, int32_t filter_mode, @@ -1983,12 +2038,30 @@ } } - if (src_h >= (4 * dst_h)) { - ygt4 = 1; - src_h >>= 2; - } else if (src_h >= (2 * dst_h)) { - ygt2 = 1; - src_h >>= 1; + /** + * The rk3528 is processed as 2 pixel/cycle, + * so ygt2/ygt4 needs to be triggered in advance to improve performance + * when src_w is bigger than 1920. + * dst_h / src_h is at [1, 0.65) ygt2=0; ygt4=0; + * dst_h / src_h is at [0.65, 0.35) ygt2=1; ygt4=0; + * dst_h / src_h is at [0.35, 0) ygt2=0; ygt4=1; + */ + if (vop2->version == VOP_VERSION_RK3528 && src_w > 1920) { + if (src_h >= (100 * dst_h / 35)) { + ygt4 = 1; + src_h >>= 2; + } else if ((src_h >= 100 * dst_h / 65) && (src_h < 100 * dst_h / 35)) { + ygt2 = 1; + src_h >>= 1; + } + } else { + if (src_h >= (4 * dst_h)) { + ygt4 = 1; + src_h >>= 2; + } else if (src_h >= (2 * dst_h)) { + ygt2 = 1; + src_h >>= 1; + } } yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w); @@ -2065,10 +2138,17 @@ if (!is_vop3(vop2) || (!vpstate->afbc_en && !vpstate->tiled_en) || win_data->vsd_pre_filter_mode == VOP3_PRE_SCALE_DOWN_GT) { - if (cbcr_src_h >= (4 * dst_h)) - ygt4 = 1; - else if (cbcr_src_h >= (2 * dst_h)) - ygt2 = 1; + if (vop2->version == VOP_VERSION_RK3528 && src_w > 1920) { + if (cbcr_src_h >= (100 * dst_h / 35)) + ygt4 = 1; + else if ((cbcr_src_h >= 100 * dst_h / 65) && (cbcr_src_h < 100 * dst_h / 35)) + ygt2 = 1; + } else { + if (cbcr_src_h >= (4 * dst_h)) + ygt4 = 1; + else if (cbcr_src_h >= (2 * dst_h)) + ygt2 = 1; + } if (ygt4) cbcr_src_h >>= 2; @@ -2230,29 +2310,49 @@ vpstate->r2y_en = 0; vpstate->csc_mode = 0; - /* hdr2sdr and sdr2hdr will do csc itself */ - if (vpstate->hdr2sdr_en) { - /* - * This is hdr2sdr enabled plane - * If it's RGB layer do hdr2sdr, we need to do r2y before send to hdr2sdr, - * because hdr2sdr only support yuv input. - */ - if (!is_input_yuv) { - vpstate->r2y_en = 1; - vpstate->csc_mode = vop2_convert_csc_mode(output_csc, CSC_10BIT_DEPTH); + if (is_vop3(vp->vop2)) { + if (vpstate->hdr_in) { + if (is_input_yuv) { + vpstate->y2r_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(input_csc, + CSC_13BIT_DEPTH); + } + return; + } else if (vp->sdr2hdr_en) { + if (is_input_yuv) { + vpstate->y2r_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(input_csc, + csc_y2r_bit_depth); + } + return; } - return; - } else if (!vpstate->hdr_in && vp->sdr2hdr_en) { - /* - * This is sdr2hdr enabled plane - * If it's YUV layer do sdr2hdr, we need to do y2r before send to sdr2hdr, - * because sdr2hdr only support rgb input. - */ - if (is_input_yuv) { - vpstate->y2r_en = 1; - vpstate->csc_mode = vop2_convert_csc_mode(input_csc, csc_y2r_bit_depth); + } else { + /* hdr2sdr and sdr2hdr will do csc itself */ + if (vpstate->hdr2sdr_en) { + /* + * This is hdr2sdr enabled plane + * If it's RGB layer do hdr2sdr, we need to do r2y before send to hdr2sdr, + * because hdr2sdr only support yuv input. + */ + if (!is_input_yuv) { + vpstate->r2y_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(output_csc, + CSC_10BIT_DEPTH); + } + return; + } else if (!vpstate->hdr_in && vp->sdr2hdr_en) { + /* + * This is sdr2hdr enabled plane + * If it's YUV layer do sdr2hdr, we need to do y2r before send to sdr2hdr, + * because sdr2hdr only support rgb input. + */ + if (is_input_yuv) { + vpstate->y2r_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(input_csc, + csc_y2r_bit_depth); + } + return; } - return; } if (is_input_yuv && !is_output_yuv) { @@ -2657,7 +2757,7 @@ fifo_throd = fb->pitches[0] >> 4; if (fifo_throd >= vop2->data->wb->fifo_depth) fifo_throd = vop2->data->wb->fifo_depth; - r2y = fb->format->is_yuv && (!is_yuv_output(vcstate->bus_format)); + r2y = !vcstate->yuv_overlay && fb->format->is_yuv; /* * the vp_id register config done immediately @@ -2963,25 +3063,16 @@ */ static void vop3_layer_map_initial(struct vop2 *vop2, uint32_t current_vp_id) { - struct vop2_video_port *vp; - struct vop2_win *win; - unsigned long win_mask; uint16_t vp_id; - int phys_id; - int i; + struct drm_plane *plane = NULL; - for (i = 0; i < vop2->data->nr_vps; i++) { - vp_id = i; - vp = &vop2->vps[vp_id]; - vp->win_mask = vp->plane_mask; - win_mask = vp->win_mask; - for_each_set_bit(phys_id, &win_mask, ROCKCHIP_MAX_LAYER) { - win = vop2_find_win_by_phys_id(vop2, phys_id); - VOP_CTRL_SET(vop2, win_vp_id[phys_id], vp_id); - win->vp_mask = BIT(vp_id); - win->old_vp_mask = win->vp_mask; - DRM_DEV_DEBUG(vop2->dev, "%s attach to vp%d\n", win->name, vp_id); - } + drm_for_each_plane(plane, vop2->drm_dev) { + struct vop2_win *win = to_vop2_win(plane); + + vp_id = VOP_CTRL_GET(vop2, win_vp_id[win->phys_id]); + win->vp_mask = BIT(vp_id); + win->old_vp_mask = win->vp_mask; + vop2->vps[vp_id].win_mask |= BIT(win->phys_id); } } @@ -3081,8 +3172,17 @@ VOP_MODULE_SET(vop2, wb, axi_uv_id, 0xe); vop2_wb_cfg_done(vp); - if (is_vop3(vop2)) - VOP_CTRL_SET(vop2, esmart_lb_mode, vop2->data->esmart_lb_mode); + if (is_vop3(vop2)) { + VOP_CTRL_SET(vop2, dsp_vs_t_sel, 0); + VOP_CTRL_SET(vop2, esmart_lb_mode, vop2->esmart_lb_mode); + } + + /* + * This is unused and error init value for rk3528 vp1, if less of this config, + * vp1 can't display normally. + */ + if (vop2->version == VOP_VERSION_RK3528) + vop2_mask_write(vop2, 0x700, 0x3, 4, 0, 0, true); VOP_CTRL_SET(vop2, cfg_done_en, 1); /* @@ -3151,6 +3251,7 @@ { struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; + const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id]; int ret; WARN_ON(vp->event); @@ -3163,6 +3264,8 @@ VOP_MODULE_SET(vop2, vp, cubic_lut_en, 0); } + if (vp_data->feature & VOP_FEATURE_VIVID_HDR) + VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 0); vop2_disable_all_planes_for_crtc(crtc); /* @@ -3188,6 +3291,7 @@ vop2_dsp_hold_valid_irq_disable(crtc); vop2_disable(crtc); + memset(&vp->active_tv_state, 0, sizeof(vp->active_tv_state)); vop2_unlock(vop2); vop2->active_vp_mask &= ~BIT(vp->id); @@ -3500,7 +3604,7 @@ uint32_t actual_w, actual_h, dsp_w, dsp_h; uint32_t dsp_stx, dsp_sty; uint32_t act_info, dsp_info, dsp_st; - uint32_t format; + uint32_t format, check_size; uint32_t afbc_format; uint32_t rb_swap; uint32_t uv_swap; @@ -3569,18 +3673,19 @@ actual_w = drm_rect_width(src) >> 16; actual_h = drm_rect_height(src) >> 16; dsp_w = drm_rect_width(dest); - if (dest->x1 + dsp_w > adjusted_mode->hdisplay) { + if (dest->x1 + dsp_w > adjusted_mode->crtc_hdisplay) { DRM_ERROR("vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n", - vp->id, win->name, dest->x1, dsp_w, adjusted_mode->hdisplay); + vp->id, win->name, dest->x1, dsp_w, adjusted_mode->crtc_hdisplay); dsp_w = adjusted_mode->hdisplay - dest->x1; if (dsp_w < 4) dsp_w = 4; actual_w = dsp_w * actual_w / drm_rect_width(dest); } dsp_h = drm_rect_height(dest); - if (dest->y1 + dsp_h > adjusted_mode->vdisplay) { + check_size = adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE ? adjusted_mode->vdisplay : adjusted_mode->crtc_vdisplay; + if (dest->y1 + dsp_h > check_size) { DRM_ERROR("vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n", - vp->id, win->name, dest->y1, dsp_h, adjusted_mode->vdisplay); + vp->id, win->name, dest->y1, dsp_h, adjusted_mode->crtc_vdisplay); dsp_h = adjusted_mode->vdisplay - dest->y1; if (dsp_h < 4) dsp_h = 4; @@ -3588,20 +3693,25 @@ } /* - * This is workaround solution for IC design: - * esmart can't support scale down when actual_w % 16 == 1. + * Workaround only for rk3568 vop */ - if (!(win->feature & WIN_FEATURE_AFBDC)) { - if (actual_w > dsp_w && (actual_w & 0xf) == 1) { - DRM_WARN("vp%d %s act_w[%d] MODE 16 == 1\n", vp->id, win->name, actual_w); - actual_w -= 1; + if (vop2->version == VOP_VERSION_RK3568) { + /* + * This is workaround solution for IC design: + * esmart can't support scale down when actual_w % 16 == 1. + */ + if (!(win->feature & WIN_FEATURE_AFBDC)) { + if (actual_w > dsp_w && (actual_w & 0xf) == 1) { + DRM_WARN("vp%d %s act_w[%d] MODE 16 == 1\n", vp->id, win->name, actual_w); + actual_w -= 1; + } } - } - if (vpstate->afbc_en && actual_w % 4) { - DRM_ERROR("vp%d %s actual_w[%d] should align as 4 pixel when enable afbc\n", - vp->id, win->name, actual_w); - actual_w = ALIGN_DOWN(actual_w, 4); + if (vpstate->afbc_en && actual_w % 4) { + DRM_ERROR("vp%d %s actual_w[%d] should align as 4 pixel when enable afbc\n", + vp->id, win->name, actual_w); + actual_w = ALIGN_DOWN(actual_w, 4); + } } act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); @@ -3633,7 +3743,7 @@ if (vop2->version != VOP_VERSION_RK3568) rk3588_vop2_win_cfg_axi(win); - if (is_vop3(vop2) && !vop2_cluster_window(win)) + if (is_vop3(vop2) && !vop2_cluster_window(win) && !win->parent) VOP_WIN_SET(vop2, win, scale_engine_num, win->scale_engine_num); if (vpstate->afbc_en) { @@ -3765,7 +3875,9 @@ if (vop2_cluster_window(win)) { lb_mode = vop2_get_cluster_lb_mode(win, vpstate); VOP_CLUSTER_SET(vop2, win, lb_mode, lb_mode); + VOP_CLUSTER_SET(vop2, win, scl_lb_mode, lb_mode == 1 ? 3 : 0); VOP_CLUSTER_SET(vop2, win, enable, 1); + VOP_CLUSTER_SET(vop2, win, frm_reset_en, 1); } if (vcstate->output_if & VOP_OUTPUT_IF_BT1120 || vcstate->output_if & VOP_OUTPUT_IF_BT656) @@ -4736,6 +4848,27 @@ struct drm_display_mode *adj_mode) { struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + /* + * For RK3568 and RK3588, the hactive of video timing must + * be 4-pixel aligned. + */ + if (vop2->version == VOP_VERSION_RK3568 || vop2->version == VOP_VERSION_RK3588) { + if (adj_mode->hdisplay % 4) { + u16 old_hdisplay = adj_mode->hdisplay; + u16 align; + + align = 4 - (adj_mode->hdisplay % 4); + adj_mode->hdisplay += align; + adj_mode->hsync_start += align; + adj_mode->hsync_end += align; + adj_mode->htotal += align; + + DRM_WARN("VP%d: hactive need to be aligned with 4-pixel, %d -> %d\n", + vp->id, old_hdisplay, adj_mode->hdisplay); + } + } drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); @@ -4799,6 +4932,8 @@ to_rockchip_crtc_state(crtc->state); struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; struct drm_display_mode *mode = &crtc->state->adjusted_mode; u16 vtotal = mode->crtc_vtotal; u16 hdisplay = mode->crtc_hdisplay; @@ -4838,8 +4973,16 @@ val = vact_st_f1 << 16 | vact_end_f1; VOP_MODULE_SET(vop2, vp, vpost_st_end_f1, val); } - VOP_MODULE_SET(vop2, vp, post_dsp_out_r2y, - is_yuv_output(vcstate->bus_format)); + + /* + * BCSH[R2Y] -> POST Linebuffer[post scale] -> the background R2Y will be deal by post_dsp_out_r2y + * + * POST Linebuffer[post scale] -> ACM[R2Y] -> the background R2Y will be deal by ACM[R2Y] + */ + if (vp_data->feature & VOP_FEATURE_POST_ACM) + VOP_MODULE_SET(vop2, vp, post_dsp_out_r2y, vcstate->yuv_overlay); + else + VOP_MODULE_SET(vop2, vp, post_dsp_out_r2y, is_yuv_output(vcstate->bus_format)); } /* @@ -4880,6 +5023,107 @@ return false; } +/* + * For vop3 video port0, if hdr_vivid is not enable, the pipe delay time as follow: + * win_dly + config_win_dly + layer_mix_dly + sdr2hdr_dly + * hdr_mix_dly = config_bg_dly + * + * if hdr_vivid is enable, the hdr layer's pipe delay time as follow: + * win_dly + config_win_dly +hdrvivid_dly + hdr_mix_dly = config_bg_dly + * + * If hdrvivid and sdr2hdr bot enable, the time arrivr hdr_mix should be the same: + * win_dly + config_win_dly0 + hdrvivid_dly = win_dly + config_win_dly1 + laer_mix_dly + + * sdr2hdr_dly + * + * For vop3 video port1, the pipe delay time as follow: + * win_dly + config_win_dly + layer_mix_dly = config_bg_dly + * + * Here, win_dly, layer_mix_dly, sdr2hdr_dly, hdr_mix_dly, hdrvivid_dly is the hardware + * delay cycles. Config_win_dly and config_bg_dly is the register value that we can config. + * Different hdr vivid mode have different hdrvivid_dly. For sdr2hdr_dly, only sde2hdr + * enable, it will delay, otherwise, the sdr2hdr_dly is 0. + * + * For default, the config_win_dly will be 0, it just user to make the pipe to arrive + * hdr_mix at the same time. + */ +static void vop3_setup_pipe_dly(struct vop2_video_port *vp, const struct vop2_zpos *vop2_zpos) +{ + struct vop2 *vop2 = vp->vop2; + struct drm_crtc *crtc = &vp->crtc; + const struct vop2_zpos *zpos; + struct drm_plane *plane; + struct vop2_plane_state *vpstate; + struct vop2_win *win; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + u16 hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + u16 hdisplay = adjusted_mode->crtc_hdisplay; + int bg_dly = 0x0; + int dly = 0x0; + int hdr_win_dly; + int sdr_win_dly; + int sdr2hdr_dly; + int pre_scan_dly; + int i; + + /** + * config bg dly, select the max delay num of hdrvivid and sdr2hdr module + * as the increase value of bg delay num. If hdrvivid and sdr2hdr is not + * work, the default bg_dly is 0x10. and the default win delay num is 0. + */ + if ((vp->hdr_en || vp->sdr2hdr_en) && + (vp->hdrvivid_mode >= 0 && vp->hdrvivid_mode <= SDR2HLG)) { + /* set sdr2hdr_dly to 0 if sdr2hdr is disable */ + sdr2hdr_dly = vp->sdr2hdr_en ? vp_data->sdr2hdr_dly : 0; + + /* set the max delay pipe's config_win_dly as 0 */ + if (vp_data->hdrvivid_dly[vp->hdrvivid_mode] >= + sdr2hdr_dly + vp_data->layer_mix_dly) { + bg_dly = vp_data->win_dly + vp_data->hdrvivid_dly[vp->hdrvivid_mode] + + vp_data->hdr_mix_dly; + hdr_win_dly = 0; + sdr_win_dly = vp_data->hdrvivid_dly[vp->hdrvivid_mode] - + vp_data->layer_mix_dly - sdr2hdr_dly; + } else { + bg_dly = vp_data->win_dly + vp_data->layer_mix_dly + sdr2hdr_dly + + vp_data->hdr_mix_dly; + hdr_win_dly = sdr2hdr_dly + vp_data->layer_mix_dly - + vp_data->hdrvivid_dly[vp->hdrvivid_mode]; + sdr_win_dly = 0; + } + } else { + bg_dly = vp_data->win_dly + vp_data->layer_mix_dly + vp_data->hdr_mix_dly; + sdr_win_dly = 0; + } + + pre_scan_dly = bg_dly + (hdisplay >> 1) - 1; + pre_scan_dly = (pre_scan_dly << 16) | hsync_len; + VOP_MODULE_SET(vop2, vp, bg_dly, bg_dly); + VOP_MODULE_SET(vop2, vp, pre_scan_htiming, pre_scan_dly); + + /** + * config win dly + */ + if (!vop2_zpos) + return; + + for (i = 0; i < vp->nr_layers; i++) { + zpos = &vop2_zpos[i]; + win = vop2_find_win_by_phys_id(vop2, zpos->win_phys_id); + plane = &win->base; + vpstate = to_vop2_plane_state(plane->state); + + if ((vp->hdr_en || vp->sdr2hdr_en) && + (vp->hdrvivid_mode >= 0 && vp->hdrvivid_mode <= SDR2HLG)) { + dly = vpstate->hdr_in ? hdr_win_dly : sdr_win_dly; + } + if (vop2_cluster_window(win)) + dly |= dly << 8; + + VOP_CTRL_SET(vop2, win_dly[win->phys_id], dly); + } +} + static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct vop2_video_port *vp = to_vop2_video_port(crtc); @@ -4900,7 +5144,6 @@ u16 vact_st = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start; u16 vact_end = vact_st + vdisplay; bool interlaced = !!(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE); - uint8_t out_mode; bool dclk_inv, yc_swap = false; int act_end; uint32_t val; @@ -4925,6 +5168,7 @@ if (vcstate->output_if & VOP_OUTPUT_IF_RGB) { VOP_CTRL_SET(vop2, rgb_en, 1); VOP_CTRL_SET(vop2, rgb_mux, vp_data->id); + VOP_CTRL_SET(vop2, rgb_pin_pol, val); VOP_GRF_SET(vop2, grf_dclk_inv, dclk_inv); } @@ -5038,21 +5282,6 @@ VOP_CTRL_SET(vop2, hdmi_dclk_pol, 1); } - if ((vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA && - !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT)) || - vcstate->output_if & VOP_OUTPUT_IF_BT656) - out_mode = ROCKCHIP_OUT_MODE_P888; - else - out_mode = vcstate->output_mode; - VOP_MODULE_SET(vop2, vp, out_mode, out_mode); - - if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode)) - VOP_MODULE_SET(vop2, vp, dsp_data_swap, DSP_RB_SWAP); - else - VOP_MODULE_SET(vop2, vp, dsp_data_swap, 0); - - vop2_dither_setup(crtc); - VOP_MODULE_SET(vop2, vp, htotal_pw, (htotal << 16) | hsync_len); val = hact_st << 16; val |= hact_end; @@ -5105,9 +5334,20 @@ VOP_MODULE_SET(vop2, vp, dclk_div2_phase_lock, 0); } - clk_set_rate(vp->dclk, adjusted_mode->crtc_clock * 1000); + /* + * For RK3528, the path of CVBS output is like: + * VOP BT656 ENCODER -> CVBS BT656 DECODER -> CVBS ENCODER -> CVBS VDAC + * The vop2 dclk should be four times crtc_clock for CVBS sampling clock needs. + */ + if (vop2->version == VOP_VERSION_RK3528 && vcstate->output_if & VOP_OUTPUT_IF_BT656) + clk_set_rate(vp->dclk, 4 * adjusted_mode->crtc_clock * 1000); + else + clk_set_rate(vp->dclk, adjusted_mode->crtc_clock * 1000); vop2_post_config(crtc); + + if (is_vop3(vop2)) + vop3_setup_pipe_dly(vp, NULL); vop2_cfg_done(crtc); @@ -5154,6 +5394,224 @@ struct drm_crtc_state *crtc_state) { return 0; +} + +static void vop3_disable_dynamic_hdr(struct vop2_video_port *vp, uint8_t win_phys_id) +{ + struct vop2 *vop2 = vp->vop2; + struct vop2_win *win = vop2_find_win_by_phys_id(vop2, win_phys_id); + struct drm_plane *plane = &win->base; + struct drm_plane_state *pstate = plane->state; + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + + VOP_MODULE_SET(vop2, vp, hdr10_en, 0); + VOP_MODULE_SET(vop2, vp, hdr_vivid_en, 0); + VOP_MODULE_SET(vop2, vp, hdr_vivid_bypass_en, 0); + VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 0); + VOP_MODULE_SET(vop2, vp, sdr2hdr_en, 0); + VOP_MODULE_SET(vop2, vp, sdr2hdr_path_en, 0); + VOP_MODULE_SET(vop2, vp, sdr2hdr_auto_gating_en, 1); + + vp->hdr_en = false; + vp->hdr_in = false; + vp->hdr_out = false; + vp->sdr2hdr_en = false; + vpstate->hdr_in = false; + vpstate->hdr2sdr_en = false; +} + +static void vop3_setup_hdrvivid(struct vop2_video_port *vp, uint8_t win_phys_id) +{ + struct vop2 *vop2 = vp->vop2; + struct vop2_win *win = vop2_find_win_by_phys_id(vop2, win_phys_id); + struct drm_plane *plane = &win->base; + struct drm_plane_state *pstate = plane->state; + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + struct drm_crtc_state *cstate = vp->crtc.state; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate); + unsigned long win_mask = vp->win_mask; + int phys_id; + struct hdrvivid_regs *hdrvivid_data; + struct hdr_extend *hdr_data; + bool have_sdr_layer = false; + uint32_t hdr_mode; + int i; + u32 *tone_lut_kvaddr; + dma_addr_t tone_lut_mst; + + vp->hdr_en = false; + vp->hdr_in = false; + vp->hdr_out = false; + vp->sdr2hdr_en = false; + vpstate->hdr_in = false; + vpstate->hdr2sdr_en = false; + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + hdrvivid_data = &hdr_data->hdrvivid_data; + + hdr_mode = hdrvivid_data->hdr_mode; + + if (hdr_mode > SDR2HLG && hdr_mode != SDR2HDR10_USERSPACE && + hdr_mode != SDR2HLG_USERSPACE) { + DRM_ERROR("Invalid HDR mode:%d, beyond the mode range\n", hdr_mode); + return; + } + + /* adjust userspace hdr mode value to kernel value */ + if (hdr_mode == SDR2HDR10_USERSPACE) + hdr_mode = SDR2HDR10; + if (hdr_mode == SDR2HLG_USERSPACE) + hdr_mode = SDR2HLG; + + if (hdr_mode <= HDR102SDR && vpstate->eotf != SMPTE_ST2084 && vpstate->eotf != HLG) { + DRM_ERROR("Invalid HDR mode:%d, mismatch plane eotf:%d\n", hdr_mode, + vpstate->eotf); + return; + } + + vp->hdrvivid_mode = hdr_mode; + vcstate->yuv_overlay = false; + + if (hdr_mode <= HDR102SDR) { + vp->hdr_en = true; + vp->hdr_in = true; + vpstate->hdr_in = true; + } else { + vp->sdr2hdr_en = true; + } + + /* + * To confirm whether need to enable sdr2hdr. + */ + for_each_set_bit(phys_id, &win_mask, ROCKCHIP_MAX_LAYER) { + win = vop2_find_win_by_phys_id(vop2, phys_id); + plane = &win->base; + pstate = plane->state; + vpstate = to_vop2_plane_state(pstate); + + /* skip inactive plane */ + if (!vop2_plane_active(pstate)) + continue; + + if (vpstate->eotf != SMPTE_ST2084 && vpstate->eotf != HLG) { + have_sdr_layer = true; + break; + } + } + + if (hdr_mode == PQHDR2SDR_WITH_DYNAMIC || hdr_mode == HLG2SDR_WITH_DYNAMIC || + hdr_mode == HLG2SDR_WITHOUT_DYNAMIC || hdr_mode == HDR102SDR) { + vpstate->hdr2sdr_en = true; + } else { + vp->hdr_out = true; + if (have_sdr_layer) + vp->sdr2hdr_en = true; + } + + /** + * Config hdr ctrl registers + */ + vop2_writel(vop2, RK3528_SDR2HDR_CTRL, hdrvivid_data->sdr2hdr_ctrl); + vop2_writel(vop2, RK3528_HDRVIVID_CTRL, hdrvivid_data->hdrvivid_ctrl); + + VOP_MODULE_SET(vop2, vp, hdr10_en, vp->hdr_en); + if (vp->hdr_en) { + VOP_MODULE_SET(vop2, vp, hdr_vivid_en, (hdr_mode == HDR_BYPASS) ? 0 : 1); + VOP_MODULE_SET(vop2, vp, hdr_vivid_path_mode, + (hdr_mode == HDR102SDR) ? PQHDR2SDR_WITH_DYNAMIC : hdr_mode); + VOP_MODULE_SET(vop2, vp, hdr_vivid_bypass_en, (hdr_mode == HDR_BYPASS) ? 1 : 0); + } else { + VOP_MODULE_SET(vop2, vp, hdr_vivid_en, 0); + } + VOP_MODULE_SET(vop2, vp, sdr2hdr_en, vp->sdr2hdr_en); + VOP_MODULE_SET(vop2, vp, sdr2hdr_path_en, vp->sdr2hdr_en); + VOP_MODULE_SET(vop2, vp, sdr2hdr_auto_gating_en, vp->sdr2hdr_en ? 0 : 1); + + vop2_writel(vop2, RK3528_SDR_CFG_COE0, hdrvivid_data->sdr2hdr_coe0); + vop2_writel(vop2, RK3528_SDR_CFG_COE1, hdrvivid_data->sdr2hdr_coe1); + vop2_writel(vop2, RK3528_SDR_CSC_COE00_01, hdrvivid_data->sdr2hdr_csc_coe00_01); + vop2_writel(vop2, RK3528_SDR_CSC_COE02_10, hdrvivid_data->sdr2hdr_csc_coe02_10); + vop2_writel(vop2, RK3528_SDR_CSC_COE11_12, hdrvivid_data->sdr2hdr_csc_coe11_12); + vop2_writel(vop2, RK3528_SDR_CSC_COE20_21, hdrvivid_data->sdr2hdr_csc_coe20_21); + vop2_writel(vop2, RK3528_SDR_CSC_COE22, hdrvivid_data->sdr2hdr_csc_coe22); + + vop2_writel(vop2, RK3528_HDR_PQ_GAMMA, hdrvivid_data->hdr_pq_gamma); + vop2_writel(vop2, RK3528_HLG_RFIX_SCALEFAC, hdrvivid_data->hlg_rfix_scalefac); + vop2_writel(vop2, RK3528_HLG_MAXLUMA, hdrvivid_data->hlg_maxluma); + vop2_writel(vop2, RK3528_HLG_R_TM_LIN2NON, hdrvivid_data->hlg_r_tm_lin2non); + + vop2_writel(vop2, RK3528_HDR_CSC_COE00_01, hdrvivid_data->hdr_csc_coe00_01); + vop2_writel(vop2, RK3528_HDR_CSC_COE02_10, hdrvivid_data->hdr_csc_coe02_10); + vop2_writel(vop2, RK3528_HDR_CSC_COE11_12, hdrvivid_data->hdr_csc_coe11_12); + vop2_writel(vop2, RK3528_HDR_CSC_COE20_21, hdrvivid_data->hdr_csc_coe20_21); + vop2_writel(vop2, RK3528_HDR_CSC_COE22, hdrvivid_data->hdr_csc_coe22); + + tone_lut_kvaddr = (u32 *)vp->hdr_lut_gem_obj->kvaddr; + tone_lut_mst = vp->hdr_lut_gem_obj->dma_addr; + + for (i = 0; i < RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH; i++) + *tone_lut_kvaddr++ = hdrvivid_data->tone_sca_axi_tab[i]; + + VOP_MODULE_SET(vop2, vp, lut_dma_rid, vp->lut_dma_rid - vp->id); + VOP_MODULE_SET(vop2, vp, hdr_lut_mode, 1); + VOP_MODULE_SET(vop2, vp, hdr_lut_mst, tone_lut_mst); + VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 1); + VOP_CTRL_SET(vop2, lut_dma_en, 1); + + for (i = 0; i < RK_HDRVIVID_GAMMA_CURVE_LENGTH; i++) + vop2_writel(vop2, RK3528_HDRGAMMA_CURVE + i * 4, hdrvivid_data->hdrgamma_curve[i]); + + for (i = 0; i < RK_HDRVIVID_GAMMA_MDFVALUE_LENGTH; i++) + vop2_writel(vop2, RK3528_HDRGAMMA_MDFVALUE + i * 4, + hdrvivid_data->hdrgamma_mdfvalue[i]); + + for (i = 0; i < RK_SDR2HDR_INVGAMMA_CURVE_LENGTH; i++) + vop2_writel(vop2, RK3528_SDRINVGAMMA_CURVE + i * 4, + hdrvivid_data->sdrinvgamma_curve[i]); + + for (i = 0; i < RK_SDR2HDR_INVGAMMA_S_IDX_LENGTH; i++) + vop2_writel(vop2, RK3528_SDRINVGAMMA_STARTIDX + i * 4, + hdrvivid_data->sdrinvgamma_startidx[i]); + + for (i = 0; i < RK_SDR2HDR_INVGAMMA_C_IDX_LENGTH; i++) + vop2_writel(vop2, RK3528_SDRINVGAMMA_CHANGEIDX + i * 4, + hdrvivid_data->sdrinvgamma_changeidx[i]); + + for (i = 0; i < RK_SDR2HDR_SMGAIN_LENGTH; i++) + vop2_writel(vop2, RK3528_SDR_SMGAIN + i * 4, hdrvivid_data->sdr_smgain[i]); +} + +static void vop3_setup_dynamic_hdr(struct vop2_video_port *vp, uint8_t win_phys_id) +{ + struct drm_crtc_state *cstate = vp->crtc.state; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate); + struct hdr_extend *hdr_data; + uint32_t hdr_format; + + /* If hdr extend data is null, exit hdr mode */ + if (!vcstate->hdr_ext_data) { + vop3_disable_dynamic_hdr(vp, win_phys_id); + return; + } + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + hdr_format = hdr_data->hdr_type; + + switch (hdr_format) { + case HDR_NONE: + case HDR_HDR10: + case HDR_HLGSTATIC: + case HDR_HDRVIVID: + /* + * hdr module support hdr10, hlg, vividhdr + * sdr2hdr module support hdrnone for sdr2hdr + */ + vop3_setup_hdrvivid(vp, win_phys_id); + break; + default: + DRM_DEBUG("unsupprot hdr format:%u\n", hdr_format); + break; + } } static void vop2_setup_hdr10(struct vop2_video_port *vp, uint8_t win_phys_id) @@ -5648,7 +6106,7 @@ vop2_writel(vop2, dst_alpha_ctrl_offset + offset, alpha.dst_alpha_ctrl.val); } - if (vp_data->feature & VOP_FEATURE_HDR10) { + if (vp_data->feature & (VOP_FEATURE_HDR10 | VOP_FEATURE_VIVID_HDR)) { src_color_ctrl_offset = ovl_regs->hdr_mix_regs->src_color_ctrl.offset; dst_color_ctrl_offset = ovl_regs->hdr_mix_regs->dst_color_ctrl.offset; src_alpha_ctrl_offset = ovl_regs->hdr_mix_regs->src_alpha_ctrl.offset; @@ -5923,6 +6381,7 @@ struct vop2_cluster cluster; uint8_t nr_layers = 0; struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id]; vcstate->yuv_overlay = is_yuv_output(vcstate->bus_format); vop2_zpos = kmalloc_array(vop2->data->win_size, sizeof(*vop2_zpos), GFP_KERNEL); @@ -5982,21 +6441,26 @@ sort(vop2_zpos, nr_layers, sizeof(vop2_zpos[0]), vop2_zpos_cmp, NULL); - if (is_vop3(vop2)) + if (is_vop3(vop2)) { vop3_setup_layer_sel_for_vp(vp, vop2_zpos); - else - vop2_setup_layer_mixer_for_vp(vp, vop2_zpos); - vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id); - if (is_vop3(vop2)) + if (vp_data->feature & VOP_FEATURE_VIVID_HDR) + vop3_setup_dynamic_hdr(vp, vop2_zpos[0].win_phys_id); vop3_setup_alpha(vp, vop2_zpos); - else + vop3_setup_pipe_dly(vp, vop2_zpos); + } else { + vop2_setup_layer_mixer_for_vp(vp, vop2_zpos); + vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id); vop2_setup_alpha(vp, vop2_zpos); - vop2_setup_dly_for_vp(vp); - vop2_setup_dly_for_window(vp, vop2_zpos); + vop2_setup_dly_for_vp(vp); + vop2_setup_dly_for_window(vp, vop2_zpos); + } } else { - if (!is_vop3(vop2)) + if (!is_vop3(vop2)) { vop2_calc_bg_ovl_and_port_mux(vp); - vop2_setup_dly_for_vp(vp); + vop2_setup_dly_for_vp(vp); + } else { + vop3_setup_pipe_dly(vp, NULL); + } } /* The pre alpha overlay of Cluster still need process in one win mode. */ @@ -6124,16 +6588,193 @@ vop2_bcsh_reg_update(vcstate, vp, &bcsh_state); } +static void vop3_post_csc_config(struct drm_crtc *crtc, struct post_acm *acm, struct post_csc *csc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop2 *vop2 = vp->vop2; + struct post_csc_coef csc_coef; + bool acm_enable; + bool is_input_yuv = false; + bool is_output_yuv = false; + bool post_r2y_en = false; + bool post_csc_en = false; + int range_type; + + if (!acm) + acm_enable = false; + else + acm_enable = acm->acm_enable; + + if (acm_enable) { + if (!vcstate->yuv_overlay) + post_r2y_en = true; + + /* do y2r in csc module */ + if (!is_yuv_output(vcstate->bus_format)) + post_csc_en = true; + } else { + if (!vcstate->yuv_overlay && is_yuv_output(vcstate->bus_format)) + post_r2y_en = true; + + /* do y2r in csc module */ + if (vcstate->yuv_overlay && !is_yuv_output(vcstate->bus_format)) + post_csc_en = true; + } + + if (csc && csc->csc_enable) + post_csc_en = true; + + if (vcstate->yuv_overlay || post_r2y_en) + is_input_yuv = true; + + if (is_yuv_output(vcstate->bus_format)) + is_output_yuv = true; + + vcstate->post_csc_mode = vop2_convert_csc_mode(vcstate->color_space, CSC_13BIT_DEPTH); + + if (post_csc_en) { + rockchip_calc_post_csc(csc, &csc_coef, vcstate->post_csc_mode, is_input_yuv, + is_output_yuv); + + VOP_MODULE_SET(vop2, vp, csc_coe00, csc_coef.csc_coef00); + VOP_MODULE_SET(vop2, vp, csc_coe01, csc_coef.csc_coef01); + VOP_MODULE_SET(vop2, vp, csc_coe02, csc_coef.csc_coef02); + VOP_MODULE_SET(vop2, vp, csc_coe10, csc_coef.csc_coef10); + VOP_MODULE_SET(vop2, vp, csc_coe11, csc_coef.csc_coef11); + VOP_MODULE_SET(vop2, vp, csc_coe12, csc_coef.csc_coef12); + VOP_MODULE_SET(vop2, vp, csc_coe20, csc_coef.csc_coef20); + VOP_MODULE_SET(vop2, vp, csc_coe21, csc_coef.csc_coef21); + VOP_MODULE_SET(vop2, vp, csc_coe22, csc_coef.csc_coef22); + VOP_MODULE_SET(vop2, vp, csc_offset0, csc_coef.csc_dc0); + VOP_MODULE_SET(vop2, vp, csc_offset1, csc_coef.csc_dc1); + VOP_MODULE_SET(vop2, vp, csc_offset2, csc_coef.csc_dc2); + + range_type = csc_coef.range_type ? 0 : 1; + range_type <<= is_input_yuv ? 0 : 1; + VOP_MODULE_SET(vop2, vp, csc_mode, range_type); + } + + VOP_MODULE_SET(vop2, vp, acm_r2y_en, post_r2y_en ? 1 : 0); + VOP_MODULE_SET(vop2, vp, csc_en, post_csc_en ? 1 : 0); + VOP_MODULE_SET(vop2, vp, acm_r2y_mode, vcstate->post_csc_mode); +} + +static void vop3_post_acm_config(struct drm_crtc *crtc, struct post_acm *acm) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + s16 *lut_y; + s16 *lut_h; + s16 *lut_s; + u32 value; + int i; + + writel(0, vop2->acm_regs + RK3528_ACM_CTRL); + VOP_MODULE_SET(vop2, vp, acm_bypass_en, 0); + + if (!acm || !acm->acm_enable) + return; + + /* + * If acm update parameters, it need disable acm in the first frame, + * then update parameters and enable acm in second frame. + */ + vop2_cfg_done(crtc); + readx_poll_timeout(readl, vop2->acm_regs + RK3528_ACM_CTRL, value, !value, 200, 50000); + + value = RK3528_ACM_ENABLE + ((adjusted_mode->hdisplay & 0xfff) << 8) + + ((adjusted_mode->vdisplay & 0xfff) << 20); + writel(value, vop2->acm_regs + RK3528_ACM_CTRL); + + + writel(1, vop2->acm_regs + RK3528_ACM_FETCH_START); + + value = (acm->y_gain & 0x3ff) + ((acm->h_gain << 10) & 0xffc00) + + ((acm->s_gain << 20) & 0x3ff00000); + writel(value, vop2->acm_regs + RK3528_ACM_DELTA_RANGE); + + lut_y = &acm->gain_lut_hy[0]; + lut_h = &acm->gain_lut_hy[ACM_GAIN_LUT_HY_LENGTH]; + lut_s = &acm->gain_lut_hy[ACM_GAIN_LUT_HY_LENGTH * 2]; + for (i = 0; i < ACM_GAIN_LUT_HY_LENGTH; i++) { + value = (lut_y[i] & 0xff) + ((lut_h[i] << 8) & 0xff00) + + ((lut_s[i] << 16) & 0xff0000); + writel(value, vop2->acm_regs + RK3528_ACM_YHS_DEL_HY_SEG0 + (i << 2)); + } + + lut_y = &acm->gain_lut_hs[0]; + lut_h = &acm->gain_lut_hs[ACM_GAIN_LUT_HS_LENGTH]; + lut_s = &acm->gain_lut_hs[ACM_GAIN_LUT_HS_LENGTH * 2]; + for (i = 0; i < ACM_GAIN_LUT_HS_LENGTH; i++) { + value = (lut_y[i] & 0xff) + ((lut_h[i] << 8) & 0xff00) + + ((lut_s[i] << 16) & 0xff0000); + writel(value, vop2->acm_regs + RK3528_ACM_YHS_DEL_HS_SEG0 + (i << 2)); + } + + lut_y = &acm->delta_lut_h[0]; + lut_h = &acm->delta_lut_h[ACM_DELTA_LUT_H_LENGTH]; + lut_s = &acm->delta_lut_h[ACM_DELTA_LUT_H_LENGTH * 2]; + for (i = 0; i < ACM_DELTA_LUT_H_LENGTH; i++) { + value = (lut_y[i] & 0x3ff) + ((lut_h[i] << 12) & 0xff000) + + ((lut_s[i] << 20) & 0x3ff00000); + writel(value, vop2->acm_regs + RK3528_ACM_YHS_DEL_HGAIN_SEG0 + (i << 2)); + } + + writel(1, vop2->acm_regs + RK3528_ACM_FETCH_DONE); +} + +static void vop3_post_config(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct post_acm *acm; + struct post_csc *csc; + + csc = vcstate->post_csc_data ? (struct post_csc *)vcstate->post_csc_data->data : NULL; + if (csc && memcmp(&vp->csc_info, csc, sizeof(struct post_csc))) + memcpy(&vp->csc_info, csc, sizeof(struct post_csc)); + vop3_post_csc_config(crtc, &vp->acm_info, &vp->csc_info); + + acm = vcstate->acm_lut_data ? (struct post_acm *)vcstate->acm_lut_data->data : NULL; + + if (acm && memcmp(&vp->acm_info, acm, sizeof(struct post_acm))) { + memcpy(&vp->acm_info, acm, sizeof(struct post_acm)); + vop3_post_acm_config(crtc, &vp->acm_info); + } else if (crtc->state->active_changed) { + vop3_post_acm_config(crtc, &vp->acm_info); + } +} + static void vop2_cfg_update(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct vop2_video_port *vp = to_vop2_video_port(crtc); struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; uint32_t val; uint32_t r, g, b; + uint8_t out_mode; spin_lock(&vop2->reg_lock); + + if ((vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA && + !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT)) || + vcstate->output_if & VOP_OUTPUT_IF_BT656) + out_mode = ROCKCHIP_OUT_MODE_P888; + else + out_mode = vcstate->output_mode; + VOP_MODULE_SET(vop2, vp, out_mode, out_mode); + + if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode)) + VOP_MODULE_SET(vop2, vp, dsp_data_swap, DSP_RB_SWAP); + else + VOP_MODULE_SET(vop2, vp, dsp_data_swap, 0); + + vop2_dither_setup(crtc); VOP_MODULE_SET(vop2, vp, overlay_mode, vcstate->yuv_overlay); @@ -6162,6 +6803,9 @@ vop2_post_config(crtc); spin_unlock(&vop2->reg_lock); + + if (vp_data->feature & (VOP_FEATURE_POST_ACM | VOP_FEATURE_POST_CSC)) + vop3_post_config(crtc); } static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_cstate) @@ -6304,6 +6948,13 @@ return NULL; vcstate->vp_id = vp->id; + if (vcstate->hdr_ext_data) + drm_property_blob_get(vcstate->hdr_ext_data); + if (vcstate->acm_lut_data) + drm_property_blob_get(vcstate->acm_lut_data); + if (vcstate->post_csc_data) + drm_property_blob_get(vcstate->post_csc_data); + __drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base); return &vcstate->base; } @@ -6314,6 +6965,9 @@ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state); __drm_atomic_helper_crtc_destroy_state(&vcstate->base); + drm_property_blob_put(vcstate->hdr_ext_data); + drm_property_blob_put(vcstate->acm_lut_data); + drm_property_blob_put(vcstate->post_csc_data); kfree(vcstate); } @@ -6436,9 +7090,52 @@ return 0; } + if (property == vp->hdr_ext_data_prop) + return 0; + + if (property == vp->acm_lut_data_prop) + return 0; + + if (property == vp->post_csc_data_prop) + return 0; + DRM_ERROR("failed to get vop2 crtc property: %s\n", property->name); return -EINVAL; +} + +/* copied from drm_atomic.c */ +static int +vop2_atomic_replace_property_blob_from_id(struct drm_device *dev, + struct drm_property_blob **blob, + uint64_t blob_id, + ssize_t expected_size, + ssize_t expected_elem_size, + bool *replaced) +{ + struct drm_property_blob *new_blob = NULL; + + if (blob_id != 0) { + new_blob = drm_property_lookup_blob(dev, blob_id); + if (new_blob == NULL) + return -EINVAL; + + if (expected_size > 0 && + new_blob->length != expected_size) { + drm_property_blob_put(new_blob); + return -EINVAL; + } + if (expected_elem_size > 0 && + new_blob->length % expected_elem_size != 0) { + drm_property_blob_put(new_blob); + return -EINVAL; + } + } + + *replaced |= drm_property_replace_blob(blob, new_blob); + drm_property_blob_put(new_blob); + + return 0; } static int vop2_crtc_atomic_set_property(struct drm_crtc *crtc, @@ -6451,6 +7148,8 @@ struct drm_mode_config *mode_config = &drm_dev->mode_config; struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; + bool replaced = false; + int ret; if (property == mode_config->tv_left_margin_property) { vcstate->left_margin = val; @@ -6481,6 +7180,33 @@ if (property == vop2->line_flag_prop) { vcstate->line_flag = val; return 0; + } + + if (property == vp->hdr_ext_data_prop) { + ret = vop2_atomic_replace_property_blob_from_id(drm_dev, + &vcstate->hdr_ext_data, + val, + -1, -1, + &replaced); + return ret; + } + + if (property == vp->acm_lut_data_prop) { + ret = vop2_atomic_replace_property_blob_from_id(drm_dev, + &vcstate->acm_lut_data, + val, + sizeof(struct post_acm), -1, + &replaced); + return ret; + } + + if (property == vp->post_csc_data_prop) { + ret = vop2_atomic_replace_property_blob_from_id(drm_dev, + &vcstate->post_csc_data, + val, + sizeof(struct post_csc), -1, + &replaced); + return ret; } DRM_ERROR("failed to set vop2 crtc property %s\n", property->name); @@ -6694,6 +7420,16 @@ ret = IRQ_HANDLED; } + if (vop2->version == VOP_VERSION_RK3528 && vp->id == 1) { + if (active_irqs & POST_BUF_EMPTY_INTR) + atomic_inc(&vp->post_buf_empty_flag); + + if (active_irqs & FS_FIELD_INTR && + (atomic_read(&vp->post_buf_empty_flag) > 0 || + vp->need_reset_p2i_flag == true)) + queue_work(vop2->workqueue, &vop2->post_buf_empty_work); + } + if (active_irqs & FS_FIELD_INTR) { vop2_wb_handler(vp); if (likely(!vp->skip_vsync) || (vp->layer_sel_update == false)) { @@ -6784,6 +7520,51 @@ return 0; } +static bool vop3_ignore_plane(struct vop2 *vop2, struct vop2_win *win) +{ + if (!is_vop3(vop2)) + return false; + + if (vop2->esmart_lb_mode == VOP3_ESMART_8K_MODE && + win->phys_id != ROCKCHIP_VOP2_ESMART0) + return true; + else if (vop2->esmart_lb_mode == VOP3_ESMART_4K_4K_MODE && + (win->phys_id == ROCKCHIP_VOP2_ESMART1 || win->phys_id == ROCKCHIP_VOP2_ESMART3)) + return true; + else if (vop2->esmart_lb_mode == VOP3_ESMART_4K_2K_2K_MODE && + win->phys_id == ROCKCHIP_VOP2_ESMART1) + return true; + else + return false; +} + +static u32 vop3_esmart_linebuffer_size(struct vop2 *vop2, struct vop2_win *win) +{ + if (!is_vop3(vop2) || vop2_cluster_window(win)) + return vop2->data->max_output.width; + + if (vop2->esmart_lb_mode == VOP3_ESMART_2K_2K_2K_2K_MODE || + (vop2->esmart_lb_mode == VOP3_ESMART_4K_2K_2K_MODE && win->phys_id != ROCKCHIP_VOP2_ESMART0)) + return vop2->data->max_output.width / 2; + else + return vop2->data->max_output.width; +} + +static void vop3_init_esmart_scale_engine(struct vop2 *vop2) +{ + u8 scale_engine_num = 0; + struct drm_plane *plane = NULL; + + drm_for_each_plane(plane, vop2->drm_dev) { + struct vop2_win *win = to_vop2_win(plane); + + if (win->parent || vop2_cluster_window(win)) + continue; + + win->scale_engine_num = scale_engine_num++; + } +} + static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, unsigned long possible_crtcs) { struct rockchip_drm_private *private = vop2->drm_dev->dev_private; @@ -6807,6 +7588,10 @@ if (win->feature & WIN_FEATURE_CLUSTER_SUB) return -EACCES; } + + /* ignore some plane register according vop3 esmart lb mode */ + if (vop3_ignore_plane(vop2, win)) + return -EACCES; ret = drm_universal_plane_init(vop2->drm_dev, &win->base, possible_crtcs, &vop2_plane_funcs, win->formats, win->nformats, @@ -6847,7 +7632,7 @@ "INPUT_WIDTH", 0, max_width); win->input_height_prop = drm_property_create_range(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, "INPUT_HEIGHT", 0, max_height); - max_width = vop2->data->max_output.width; + max_width = vop3_esmart_linebuffer_size(vop2, win); max_height = vop2->data->max_output.height; if (win->feature & WIN_FEATURE_CLUSTER_SUB) max_width >>= 1; @@ -6883,15 +7668,27 @@ return 0; } -static struct drm_plane *vop2_cursor_plane_init(struct vop2_video_port *vp, - unsigned long possible_crtcs) +static struct drm_plane *vop2_cursor_plane_init(struct vop2_video_port *vp) { struct vop2 *vop2 = vp->vop2; struct drm_plane *cursor = NULL; struct vop2_win *win; + unsigned long possible_crtcs = 0; win = vop2_find_win_by_phys_id(vop2, vp->cursor_win_id); if (win) { + if (vop2->disable_win_move) { + const struct vop2_data *vop2_data = vop2->data; + struct drm_crtc *crtc = vop2_find_crtc_by_plane_mask(vop2, win->phys_id); + + if (crtc) + possible_crtcs = drm_crtc_mask(crtc); + else + possible_crtcs = (1 << vop2_data->nr_vps) - 1; + } + + if (win->possible_crtcs) + possible_crtcs = win->possible_crtcs; win->type = DRM_PLANE_TYPE_CURSOR; win->zpos = vop2->registered_num_wins - 1; if (!vop2_plane_init(vop2, win, possible_crtcs)) @@ -6925,6 +7722,7 @@ if (!lut_len) continue; vp->gamma_lut_len = vp_data->gamma_lut_len; + vp->lut_dma_rid = vp_data->lut_dma_rid; vp->lut = devm_kmalloc_array(dev, lut_len, sizeof(*vp->lut), GFP_KERNEL); if (!vp->lut) @@ -7009,6 +7807,53 @@ return 0; } +static int vop2_crtc_create_hdr_property(struct vop2 *vop2, struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_property *prop; + + prop = drm_property_create(vop2->drm_dev, DRM_MODE_PROP_BLOB, "HDR_EXT_DATA", 0); + if (!prop) { + DRM_DEV_ERROR(vop2->dev, "create hdr ext data prop for vp%d failed\n", vp->id); + return -ENOMEM; + } + vp->hdr_ext_data_prop = prop; + drm_object_attach_property(&crtc->base, vp->hdr_ext_data_prop, 0); + + return 0; +} + +static int vop2_crtc_create_post_acm_property(struct vop2 *vop2, struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_property *prop; + + prop = drm_property_create(vop2->drm_dev, DRM_MODE_PROP_BLOB, "ACM_LUT_DATA", 0); + if (!prop) { + DRM_DEV_ERROR(vop2->dev, "create acm lut data prop for vp%d failed\n", vp->id); + return -ENOMEM; + } + vp->acm_lut_data_prop = prop; + drm_object_attach_property(&crtc->base, vp->acm_lut_data_prop, 0); + + return 0; +} + +static int vop2_crtc_create_post_csc_property(struct vop2 *vop2, struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_property *prop; + + prop = drm_property_create(vop2->drm_dev, DRM_MODE_PROP_BLOB, "POST_CSC_DATA", 0); + if (!prop) { + DRM_DEV_ERROR(vop2->dev, "create post csc data prop for vp%d failed\n", vp->id); + return -ENOMEM; + } + vp->post_csc_data_prop = prop; + drm_object_attach_property(&crtc->base, vp->post_csc_data_prop, 0); + + return 0; +} #define RK3566_MIRROR_PLANE_MASK (BIT(ROCKCHIP_VOP2_CLUSTER1) | BIT(ROCKCHIP_VOP2_ESMART1) | \ BIT(ROCKCHIP_VOP2_SMART1)) @@ -7021,7 +7866,7 @@ const struct vop2_data *vop2_data = vop2->data; struct drm_device *drm_dev = vop2->drm_dev; struct device *dev = vop2->dev; - struct drm_plane *plane; + struct drm_plane *primary; struct drm_plane *cursor = NULL; struct drm_crtc *crtc; struct device_node *port; @@ -7069,6 +7914,9 @@ vp->id = vp_data->id; vp->regs = vp_data->regs; vp->cursor_win_id = -1; + primary = NULL; + cursor = NULL; + if (vop2->disable_win_move) possible_crtcs = BIT(registered_num_crtcs); @@ -7116,6 +7964,7 @@ win->type = DRM_PLANE_TYPE_PRIMARY; } } else { + j = 0; while (j < vop2->registered_num_wins) { be_used_for_primary_plane = false; win = &vop2->win[j]; @@ -7157,24 +8006,43 @@ DRM_DEV_ERROR(vop2->dev, "failed to init primary plane\n"); break; } - plane = &win->base; + primary = &win->base; } /* some times we want a cursor window for some vp */ + if (vp->cursor_win_id < 0) { + bool be_used_for_cursor_plane = false; + + j = 0; + while (j < vop2->registered_num_wins) { + win = &vop2->win[j++]; + + if (win->parent || (win->feature & WIN_FEATURE_CLUSTER_SUB)) + continue; + + if (win->type != DRM_PLANE_TYPE_CURSOR) + continue; + + for (k = 0; k < vop2_data->nr_vps; k++) { + if (vop2->vps[k].cursor_win_id == win->phys_id) + be_used_for_cursor_plane = true; + } + if (be_used_for_cursor_plane) + continue; + vp->cursor_win_id = win->phys_id; + } + } + if (vp->cursor_win_id >= 0) { - if (win->possible_crtcs) - possible_crtcs = win->possible_crtcs; - cursor = vop2_cursor_plane_init(vp, possible_crtcs); + cursor = vop2_cursor_plane_init(vp); if (!cursor) DRM_WARN("failed to init cursor plane for vp%d\n", vp->id); else DRM_DEV_INFO(vop2->dev, "%s as cursor plane for vp%d\n", cursor->name, vp->id); - } else { - cursor = NULL; } - ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, cursor, &vop2_crtc_funcs, + ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, &vop2_crtc_funcs, "video_port%d", vp->id); if (ret) { DRM_DEV_ERROR(vop2->dev, "crtc init for video_port%d failed\n", i); @@ -7202,7 +8070,23 @@ drm_dev->mode_config.tv_top_margin_property, 100); drm_object_attach_property(&crtc->base, drm_dev->mode_config.tv_bottom_margin_property, 100); - vop2_crtc_create_plane_mask_property(vop2, crtc, plane_mask); + if (plane_mask) + vop2_crtc_create_plane_mask_property(vop2, crtc, plane_mask); + + if (vp_data->feature & VOP_FEATURE_VIVID_HDR) { + vop2_crtc_create_hdr_property(vop2, crtc); + vp->hdr_lut_gem_obj = rockchip_gem_create_object(vop2->drm_dev, + RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH * 4, true, 0); + if (IS_ERR(vp->hdr_lut_gem_obj)) { + DRM_ERROR("create hdr lut obj failed\n"); + return -ENOMEM; + } + } + if (vp_data->feature & VOP_FEATURE_POST_ACM) + vop2_crtc_create_post_acm_property(vop2, crtc); + if (vp_data->feature & VOP_FEATURE_POST_CSC) + vop2_crtc_create_post_csc_property(vop2, crtc); + registered_num_crtcs++; } @@ -7260,12 +8144,18 @@ DRM_WARN("failed to init overlay plane %s, ret:%d\n", win->name, ret); } + if (is_vop3(vop2)) + vop3_init_esmart_scale_engine(vop2); + return registered_num_crtcs; } static void vop2_destroy_crtc(struct drm_crtc *crtc) { struct vop2_video_port *vp = to_vop2_video_port(crtc); + + if (vp->hdr_lut_gem_obj) + rockchip_gem_free_object(&vp->hdr_lut_gem_obj->base); of_node_put(crtc->port); @@ -7322,7 +8212,6 @@ win->axi_id = win_data->axi_id; win->axi_yrgb_id = win_data->axi_yrgb_id; win->axi_uv_id = win_data->axi_uv_id; - win->scale_engine_num = win_data->scale_engine_num; win->possible_crtcs = win_data->possible_crtcs; num_wins++; @@ -7351,6 +8240,7 @@ area->vsd_filter_mode = win_data->vsd_filter_mode; area->hsd_pre_filter_mode = win_data->hsd_pre_filter_mode; area->vsd_pre_filter_mode = win_data->vsd_pre_filter_mode; + area->possible_crtcs = win->possible_crtcs; area->vop2 = vop2; area->win_id = i; @@ -7414,6 +8304,43 @@ return 0; } +static void post_buf_empty_work_event(struct work_struct *work) +{ + struct vop2 *vop2 = container_of(work, struct vop2, post_buf_empty_work); + struct rockchip_drm_private *private = vop2->drm_dev->dev_private; + struct vop2_video_port *vp = &vop2->vps[1]; + + /* + * For RK3528, VP1 only supports NTSC and PAL mode(both interlace). If + * POST_BUF_EMPTY_INTR comes, it is needed to reset the p2i_en bit, in + * order to update the line parity flag, which ensures the correct order + * of odd and even lines. + */ + if (vop2->version == VOP_VERSION_RK3528) { + if (atomic_read(&vp->post_buf_empty_flag) > 0) { + atomic_set(&vp->post_buf_empty_flag, 0); + + mutex_lock(&private->ovl_lock); + vop2_wait_for_fs_by_done_bit_status(vp); + VOP_MODULE_SET(vop2, vp, p2i_en, 0); + vop2_cfg_done(&vp->crtc); + vop2_wait_for_fs_by_done_bit_status(vp); + mutex_unlock(&private->ovl_lock); + + vp->need_reset_p2i_flag = true; + } else if (vp->need_reset_p2i_flag == true) { + mutex_lock(&private->ovl_lock); + vop2_wait_for_fs_by_done_bit_status(vp); + VOP_MODULE_SET(vop2, vp, p2i_en, 1); + vop2_cfg_done(&vp->crtc); + vop2_wait_for_fs_by_done_bit_status(vp); + mutex_unlock(&private->ovl_lock); + + vp->need_reset_p2i_flag = false; + } + } +} + static int vop2_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -7454,6 +8381,23 @@ vop2->disable_afbc_win = of_property_read_bool(dev->of_node, "disable-afbc-win"); vop2->disable_win_move = of_property_read_bool(dev->of_node, "disable-win-move"); vop2->skip_ref_fb = of_property_read_bool(dev->of_node, "skip-ref-fb"); + + /* + * esmart lb mode default config at vop2_reg.c vop2_data.esmart_lb_mode, + * you can rewrite at dts vop node: + * + * VOP3_ESMART_8K_MODE = 0, + * VOP3_ESMART_4K_4K_MODE = 1, + * VOP3_ESMART_4K_2K_2K_MODE = 2, + * VOP3_ESMART_2K_2K_2K_2K_MODE = 3, + * + * &vop { + * esmart_lb_mode = /bits/ 8 <2>; + * }; + */ + ret = of_property_read_u8(dev->of_node, "esmart_lb_mode", &vop2->esmart_lb_mode); + if (ret < 0) + vop2->esmart_lb_mode = vop2->data->esmart_lb_mode; ret = vop2_win_init(vop2); if (ret) @@ -7538,6 +8482,12 @@ spin_lock_init(&vop2->irq_lock); mutex_init(&vop2->vop2_lock); + if (vop2->version == VOP_VERSION_RK3528) { + atomic_set(&vop2->vps[1].post_buf_empty_flag, 0); + vop2->workqueue = create_workqueue("post_buf_empty_wq"); + INIT_WORK(&vop2->post_buf_empty_work, post_buf_empty_work_event); + } + ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2); if (ret) return ret; -- Gitblit v1.6.2