diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip/rockchip_drm_vop2.c')
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 1592 |
1 files changed, 317 insertions, 1275 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 17a98845fd31..d0f5fea15e21 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -33,7 +33,6 @@ #include <drm/drm_vblank.h> #include <uapi/linux/videodev2.h> -#include <dt-bindings/soc/rockchip,vop2.h> #include "rockchip_drm_gem.h" #include "rockchip_drm_vop2.h" @@ -102,142 +101,7 @@ enum vop2_afbc_format { VOP2_AFBC_FMT_INVALID = -1, }; -union vop2_alpha_ctrl { - u32 val; - struct { - /* [0:1] */ - u32 color_mode:1; - u32 alpha_mode:1; - /* [2:3] */ - u32 blend_mode:2; - u32 alpha_cal_mode:1; - /* [5:7] */ - u32 factor_mode:3; - /* [8:9] */ - u32 alpha_en:1; - u32 src_dst_swap:1; - u32 reserved:6; - /* [16:23] */ - u32 glb_alpha:8; - } bits; -}; - -struct vop2_alpha { - union vop2_alpha_ctrl src_color_ctrl; - union vop2_alpha_ctrl dst_color_ctrl; - union vop2_alpha_ctrl src_alpha_ctrl; - union vop2_alpha_ctrl dst_alpha_ctrl; -}; - -struct vop2_alpha_config { - bool src_premulti_en; - bool dst_premulti_en; - bool src_pixel_alpha_en; - bool dst_pixel_alpha_en; - u16 src_glb_alpha_value; - u16 dst_glb_alpha_value; -}; - -struct vop2_win { - struct vop2 *vop2; - struct drm_plane base; - const struct vop2_win_data *data; - struct regmap_field *reg[VOP2_WIN_MAX_REG]; - - /** - * @win_id: graphic window id, a cluster may be split into two - * graphics windows. - */ - u8 win_id; - u8 delay; - u32 offset; - - enum drm_plane_type type; -}; - -struct vop2_video_port { - struct drm_crtc crtc; - struct vop2 *vop2; - struct clk *dclk; - unsigned int id; - const struct vop2_video_port_data *data; - - struct completion dsp_hold_completion; - - /** - * @win_mask: Bitmask of windows attached to the video port; - */ - u32 win_mask; - - struct vop2_win *primary_plane; - struct drm_pending_vblank_event *event; - - unsigned int nlayers; -}; - -struct vop2 { - struct device *dev; - struct drm_device *drm; - struct vop2_video_port vps[ROCKCHIP_MAX_CRTC]; - - const struct vop2_data *data; - /* - * Number of windows that are registered as plane, may be less than the - * total number of hardware windows. - */ - u32 registered_num_wins; - - struct resource *res; - void __iomem *regs; - struct regmap *map; - - struct regmap *sys_grf; - struct regmap *vop_grf; - struct regmap *vo1_grf; - struct regmap *sys_pmu; - - /* physical map length of vop2 register */ - u32 len; - - void __iomem *lut_regs; - - /* protects crtc enable/disable */ - struct mutex vop2_lock; - - int irq; - - /* - * Some global resources are shared between all video ports(crtcs), so - * we need a ref counter here. - */ - unsigned int enable_count; - struct clk *hclk; - struct clk *aclk; - struct clk *pclk; - - /* optional internal rgb encoder */ - struct rockchip_rgb *rgb; - - /* must be put at the end of the struct */ - struct vop2_win win[]; -}; - -#define vop2_output_if_is_hdmi(x) ((x) == ROCKCHIP_VOP2_EP_HDMI0 || \ - (x) == ROCKCHIP_VOP2_EP_HDMI1) - -#define vop2_output_if_is_dp(x) ((x) == ROCKCHIP_VOP2_EP_DP0 || \ - (x) == ROCKCHIP_VOP2_EP_DP1) - -#define vop2_output_if_is_edp(x) ((x) == ROCKCHIP_VOP2_EP_EDP0 || \ - (x) == ROCKCHIP_VOP2_EP_EDP1) - -#define vop2_output_if_is_mipi(x) ((x) == ROCKCHIP_VOP2_EP_MIPI0 || \ - (x) == ROCKCHIP_VOP2_EP_MIPI1) - -#define vop2_output_if_is_lvds(x) ((x) == ROCKCHIP_VOP2_EP_LVDS0 || \ - (x) == ROCKCHIP_VOP2_EP_LVDS1) - -#define vop2_output_if_is_dpi(x) ((x) == ROCKCHIP_VOP2_EP_RGB0) +#define VOP2_MAX_DCLK_RATE 600000000 /* * bus-format types. @@ -272,16 +136,6 @@ static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list) static const struct regmap_config vop2_regmap_config; -static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc) -{ - return container_of(crtc, struct vop2_video_port, crtc); -} - -static struct vop2_win *to_vop2_win(struct drm_plane *p) -{ - return container_of(p, struct vop2_win, base); -} - static void vop2_lock(struct vop2 *vop2) { mutex_lock(&vop2->vop2_lock); @@ -292,44 +146,6 @@ static void vop2_unlock(struct vop2 *vop2) mutex_unlock(&vop2->vop2_lock); } -static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v) -{ - regmap_write(vop2->map, offset, v); -} - -static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v) -{ - regmap_write(vp->vop2->map, vp->data->offset + offset, v); -} - -static u32 vop2_readl(struct vop2 *vop2, u32 offset) -{ - u32 val; - - regmap_read(vop2->map, offset, &val); - - return val; -} - -static u32 vop2_vp_read(struct vop2_video_port *vp, u32 offset) -{ - u32 val; - - regmap_read(vp->vop2->map, vp->data->offset + offset, &val); - - return val; -} - -static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v) -{ - regmap_field_write(win->reg[reg], v); -} - -static bool vop2_cluster_window(const struct vop2_win *win) -{ - return win->data->feature & WIN_FEATURE_CLUSTER; -} - /* * Note: * The write mask function is documented but missing on rk3566/8, writes @@ -539,7 +355,7 @@ static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode) static bool vop2_output_rg_swap(struct vop2 *vop2, u32 bus_format) { - if (vop2->data->soc_id == 3588) { + if (vop2->version == VOP_VERSION_RK3588) { if (bus_format == MEDIA_BUS_FMT_YUV8_1X24 || bus_format == MEDIA_BUS_FMT_YUV10_1X30) return true; @@ -592,7 +408,7 @@ static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format, if (modifier == DRM_FORMAT_MOD_INVALID) return false; - if (vop2->data->soc_id == 3568 || vop2->data->soc_id == 3566) { + if (vop2->version == VOP_VERSION_RK3568) { if (vop2_cluster_window(win)) { if (modifier == DRM_FORMAT_MOD_LINEAR) { drm_dbg_kms(vop2->drm, @@ -603,7 +419,7 @@ static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format, } if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) { - if (vop2->data->soc_id == 3588) { + if (vop2->version == VOP_VERSION_RK3588) { if (!rockchip_afbc(plane, modifier)) { drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n"); return false; @@ -1002,6 +818,7 @@ static void rk3588_vop2_power_domain_enable_all(struct vop2 *vop2) static void vop2_enable(struct vop2 *vop2) { int ret; + u32 version; ret = pm_runtime_resume_and_get(vop2->dev); if (ret < 0) { @@ -1021,10 +838,20 @@ static void vop2_enable(struct vop2 *vop2) return; } + version = vop2_readl(vop2, RK3568_VERSION_INFO); + if (version != vop2->version) { + drm_err(vop2->drm, "Hardware version(0x%08x) mismatch\n", version); + return; + } + + /* + * rk3566 share the same vop version with rk3568, so + * we need to use soc_id for identification here. + */ if (vop2->data->soc_id == 3566) vop2_writel(vop2, RK3568_OTP_WIN_EN, 1); - if (vop2->data->soc_id == 3588) + if (vop2->version == VOP_VERSION_RK3588) rk3588_vop2_power_domain_enable_all(vop2); vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); @@ -1105,7 +932,7 @@ static void vop2_vp_dsp_lut_update_enable(struct vop2_video_port *vp) static inline bool vop2_supports_seamless_gamma_lut_update(struct vop2 *vop2) { - return (vop2->data->soc_id != 3566 && vop2->data->soc_id != 3568); + return vop2->version != VOP_VERSION_RK3568; } static bool vop2_gamma_lut_in_use(struct vop2 *vop2, struct vop2_video_port *vp) @@ -1155,6 +982,9 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID); + if (vp->dclk_src) + clk_set_parent(vp->dclk, vp->dclk_src); + clk_disable_unprepare(vp->dclk); vop2->enable_count--; @@ -1444,12 +1274,15 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, &fb->format->format, afbc_en ? "AFBC" : "", &yrgb_mst); - if (vop2->data->soc_id > 3568) { + if (vop2->version > VOP_VERSION_RK3568) { vop2_win_write(win, VOP2_WIN_AXI_BUS_ID, win->data->axi_bus_id); vop2_win_write(win, VOP2_WIN_AXI_YRGB_R_ID, win->data->axi_yrgb_r_id); vop2_win_write(win, VOP2_WIN_AXI_UV_R_ID, win->data->axi_uv_r_id); } + if (vop2->version >= VOP_VERSION_RK3576) + vop2_win_write(win, VOP2_WIN_VP_SEL, vp->id); + if (vop2_cluster_window(win)) vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en); @@ -1504,7 +1337,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, * this bit is gating disable, we should write 1 to * disable gating when enable afbc. */ - if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) + if (vop2->version == VOP_VERSION_RK3568) vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); else vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1); @@ -1514,10 +1347,15 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, else vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0); + if (vop2->version >= VOP_VERSION_RK3576) { + vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET_EN, 1); + vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET, yrgb_mst); + } + transform_offset = vop2_afbc_transform_offset(pstate, half_block_en); vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst); vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info); - vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset); + vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, transform_offset); vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1)); vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16))); vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride); @@ -1528,7 +1366,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, } else { if (vop2_cluster_window(win)) { vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 0); - vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, 0); + vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, 0); } vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4)); @@ -1547,10 +1385,8 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, rb_swap = vop2_win_rb_swap(fb->format->format); vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap); - if (!vop2_cluster_window(win)) { - uv_swap = vop2_win_uv_swap(fb->format->format); - vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap); - } + uv_swap = vop2_win_uv_swap(fb->format->format); + vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap); if (fb->format->is_yuv) { vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4)); @@ -1724,6 +1560,7 @@ static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl) static void vop2_post_config(struct drm_crtc *crtc) { struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; struct drm_display_mode *mode = &crtc->state->adjusted_mode; u16 vtotal = mode->crtc_vtotal; u16 hdisplay = mode->crtc_hdisplay; @@ -1734,18 +1571,10 @@ static void vop2_post_config(struct drm_crtc *crtc) u32 top_margin = 100, bottom_margin = 100; u16 hsize = hdisplay * (left_margin + right_margin) / 200; u16 vsize = vdisplay * (top_margin + bottom_margin) / 200; - u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; u16 hact_end, vact_end; u32 val; - u32 bg_dly; - u32 pre_scan_dly; - - bg_dly = vp->data->pre_scan_max_dly[3]; - vop2_writel(vp->vop2, RK3568_VP_BG_MIX_CTRL(vp->id), - FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly)); - pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len; - vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly); + vop2->ops->setup_bg_dly(vp); vsize = rounddown(vsize, 2); hsize = rounddown(hsize, 2); @@ -1781,347 +1610,6 @@ static void vop2_post_config(struct drm_crtc *crtc) vop2_vp_write(vp, RK3568_VP_DSP_BG, 0); } -static unsigned long rk3568_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags) -{ - struct vop2 *vop2 = vp->vop2; - struct drm_crtc *crtc = &vp->crtc; - u32 die, dip; - - die = vop2_readl(vop2, RK3568_DSP_IF_EN); - dip = vop2_readl(vop2, RK3568_DSP_IF_POL); - - switch (id) { - case ROCKCHIP_VOP2_EP_RGB0: - die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX; - die |= RK3568_SYS_DSP_INFACE_EN_RGB | - FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id); - dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; - dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); - if (polflags & POLFLAG_DCLK_INV) - regmap_write(vop2->sys_grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3)); - else - regmap_write(vop2->sys_grf, RK3568_GRF_VO_CON1, BIT(3 + 16)); - break; - case ROCKCHIP_VOP2_EP_HDMI0: - die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX; - die |= RK3568_SYS_DSP_INFACE_EN_HDMI | - FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id); - dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL; - dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags); - break; - case ROCKCHIP_VOP2_EP_EDP0: - die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX; - die |= RK3568_SYS_DSP_INFACE_EN_EDP | - FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id); - dip &= ~RK3568_DSP_IF_POL__EDP_PIN_POL; - dip |= FIELD_PREP(RK3568_DSP_IF_POL__EDP_PIN_POL, polflags); - break; - case ROCKCHIP_VOP2_EP_MIPI0: - die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX; - die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 | - FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id); - dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL; - dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags); - break; - case ROCKCHIP_VOP2_EP_MIPI1: - die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX; - die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 | - FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id); - dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL; - dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags); - break; - case ROCKCHIP_VOP2_EP_LVDS0: - die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX; - die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 | - FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id); - dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; - dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); - break; - case ROCKCHIP_VOP2_EP_LVDS1: - die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX; - die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 | - FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id); - dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; - dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); - break; - default: - drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id); - return 0; - } - - dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD; - - vop2_writel(vop2, RK3568_DSP_IF_EN, die); - vop2_writel(vop2, RK3568_DSP_IF_POL, dip); - - return crtc->state->adjusted_mode.crtc_clock * 1000LL; -} - -/* - * calc the dclk on rk3588 - * the available div of dclk is 1, 2, 4 - */ -static unsigned long rk3588_calc_dclk(unsigned long child_clk, unsigned long max_dclk) -{ - if (child_clk * 4 <= max_dclk) - return child_clk * 4; - else if (child_clk * 2 <= max_dclk) - return child_clk * 2; - else if (child_clk <= max_dclk) - return child_clk; - else - return 0; -} - -/* - * 4 pixclk/cycle on rk3588 - * RGB/eDP/HDMI: if_pixclk >= dclk_core - * DP: dp_pixclk = dclk_out <= dclk_core - * DSI: mipi_pixclk <= dclk_out <= dclk_core - */ -static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id, - int *dclk_core_div, int *dclk_out_div, - int *if_pixclk_div, int *if_dclk_div) -{ - struct vop2 *vop2 = vp->vop2; - struct drm_crtc *crtc = &vp->crtc; - struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; - struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); - int output_mode = vcstate->output_mode; - unsigned long v_pixclk = adjusted_mode->crtc_clock * 1000LL; /* video timing pixclk */ - unsigned long dclk_core_rate = v_pixclk >> 2; - unsigned long dclk_rate = v_pixclk; - unsigned long dclk_out_rate; - unsigned long if_pixclk_rate; - int K = 1; - - if (vop2_output_if_is_hdmi(id)) { - /* - * K = 2: dclk_core = if_pixclk_rate > if_dclk_rate - * K = 1: dclk_core = hdmie_edp_dclk > if_pixclk_rate - */ - if (output_mode == ROCKCHIP_OUT_MODE_YUV420) { - dclk_rate = dclk_rate >> 1; - K = 2; - } - - if_pixclk_rate = (dclk_core_rate << 1) / K; - /* - * if_dclk_rate = dclk_core_rate / K; - * *if_pixclk_div = dclk_rate / if_pixclk_rate; - * *if_dclk_div = dclk_rate / if_dclk_rate; - */ - *if_pixclk_div = 2; - *if_dclk_div = 4; - } else if (vop2_output_if_is_edp(id)) { - /* - * edp_pixclk = edp_dclk > dclk_core - */ - if_pixclk_rate = v_pixclk / K; - dclk_rate = if_pixclk_rate * K; - /* - * *if_pixclk_div = dclk_rate / if_pixclk_rate; - * *if_dclk_div = *if_pixclk_div; - */ - *if_pixclk_div = K; - *if_dclk_div = K; - } else if (vop2_output_if_is_dp(id)) { - if (output_mode == ROCKCHIP_OUT_MODE_YUV420) - dclk_out_rate = v_pixclk >> 3; - else - dclk_out_rate = v_pixclk >> 2; - - dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000000); - if (!dclk_rate) { - drm_err(vop2->drm, "DP dclk_out_rate out of range, dclk_out_rate: %ld Hz\n", - dclk_out_rate); - return 0; - } - *dclk_out_div = dclk_rate / dclk_out_rate; - } else if (vop2_output_if_is_mipi(id)) { - if_pixclk_rate = dclk_core_rate / K; - /* - * dclk_core = dclk_out * K = if_pixclk * K = v_pixclk / 4 - */ - dclk_out_rate = if_pixclk_rate; - /* - * dclk_rate = N * dclk_core_rate N = (1,2,4 ), - * we get a little factor here - */ - dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000000); - if (!dclk_rate) { - drm_err(vop2->drm, "MIPI dclk out of range, dclk_out_rate: %ld Hz\n", - dclk_out_rate); - return 0; - } - *dclk_out_div = dclk_rate / dclk_out_rate; - /* - * mipi pixclk == dclk_out - */ - *if_pixclk_div = 1; - } else if (vop2_output_if_is_dpi(id)) { - dclk_rate = v_pixclk; - } - - *dclk_core_div = dclk_rate / dclk_core_rate; - *if_pixclk_div = ilog2(*if_pixclk_div); - *if_dclk_div = ilog2(*if_dclk_div); - *dclk_core_div = ilog2(*dclk_core_div); - *dclk_out_div = ilog2(*dclk_out_div); - - drm_dbg(vop2->drm, "dclk: %ld, pixclk_div: %d, dclk_div: %d\n", - dclk_rate, *if_pixclk_div, *if_dclk_div); - - return dclk_rate; -} - -/* - * MIPI port mux on rk3588: - * 0: Video Port2 - * 1: Video Port3 - * 3: Video Port 1(MIPI1 only) - */ -static u32 rk3588_get_mipi_port_mux(int vp_id) -{ - if (vp_id == 1) - return 3; - else if (vp_id == 3) - return 1; - else - return 0; -} - -static u32 rk3588_get_hdmi_pol(u32 flags) -{ - u32 val; - - val = (flags & DRM_MODE_FLAG_NHSYNC) ? BIT(HSYNC_POSITIVE) : 0; - val |= (flags & DRM_MODE_FLAG_NVSYNC) ? BIT(VSYNC_POSITIVE) : 0; - - return val; -} - -static unsigned long rk3588_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags) -{ - struct vop2 *vop2 = vp->vop2; - int dclk_core_div, dclk_out_div, if_pixclk_div, if_dclk_div; - unsigned long clock; - u32 die, dip, div, vp_clk_div, val; - - clock = rk3588_calc_cru_cfg(vp, id, &dclk_core_div, &dclk_out_div, - &if_pixclk_div, &if_dclk_div); - if (!clock) - return 0; - - vp_clk_div = FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_CORE_DIV, dclk_core_div); - vp_clk_div |= FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_OUT_DIV, dclk_out_div); - - die = vop2_readl(vop2, RK3568_DSP_IF_EN); - dip = vop2_readl(vop2, RK3568_DSP_IF_POL); - div = vop2_readl(vop2, RK3568_DSP_IF_CTRL); - - switch (id) { - case ROCKCHIP_VOP2_EP_HDMI0: - div &= ~RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV; - div &= ~RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV; - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); - die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX; - die |= RK3588_SYS_DSP_INFACE_EN_HDMI0 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id); - val = rk3588_get_hdmi_pol(polflags); - regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 1, 1)); - regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 6, 5)); - break; - case ROCKCHIP_VOP2_EP_HDMI1: - div &= ~RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV; - div &= ~RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV; - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV, if_dclk_div); - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV, if_pixclk_div); - die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX; - die |= RK3588_SYS_DSP_INFACE_EN_HDMI1 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id); - val = rk3588_get_hdmi_pol(polflags); - regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 4, 4)); - regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 8, 7)); - break; - case ROCKCHIP_VOP2_EP_EDP0: - div &= ~RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV; - div &= ~RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV; - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); - die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX; - die |= RK3588_SYS_DSP_INFACE_EN_EDP0 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id); - regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 0, 0)); - break; - case ROCKCHIP_VOP2_EP_EDP1: - div &= ~RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV; - div &= ~RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV; - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); - div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); - die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX; - die |= RK3588_SYS_DSP_INFACE_EN_EDP1 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id); - regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 3, 3)); - break; - case ROCKCHIP_VOP2_EP_MIPI0: - div &= ~RK3588_DSP_IF_MIPI0_PCLK_DIV; - div |= FIELD_PREP(RK3588_DSP_IF_MIPI0_PCLK_DIV, if_pixclk_div); - die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX; - val = rk3588_get_mipi_port_mux(vp->id); - die |= RK3588_SYS_DSP_INFACE_EN_MIPI0 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX, !!val); - break; - case ROCKCHIP_VOP2_EP_MIPI1: - div &= ~RK3588_DSP_IF_MIPI1_PCLK_DIV; - div |= FIELD_PREP(RK3588_DSP_IF_MIPI1_PCLK_DIV, if_pixclk_div); - die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX; - val = rk3588_get_mipi_port_mux(vp->id); - die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, val); - break; - case ROCKCHIP_VOP2_EP_DP0: - die &= ~RK3588_SYS_DSP_INFACE_EN_DP0_MUX; - die |= RK3588_SYS_DSP_INFACE_EN_DP0 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_DP0_MUX, vp->id); - dip &= ~RK3588_DSP_IF_POL__DP0_PIN_POL; - dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP0_PIN_POL, polflags); - break; - case ROCKCHIP_VOP2_EP_DP1: - die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX; - die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 | - FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id); - dip &= ~RK3588_DSP_IF_POL__DP1_PIN_POL; - dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP1_PIN_POL, polflags); - break; - default: - drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id); - return 0; - } - - dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD; - - vop2_vp_write(vp, RK3588_VP_CLK_CTRL, vp_clk_div); - vop2_writel(vop2, RK3568_DSP_IF_EN, die); - vop2_writel(vop2, RK3568_DSP_IF_CTRL, div); - vop2_writel(vop2, RK3568_DSP_IF_POL, dip); - - return clock; -} - -static unsigned long vop2_set_intf_mux(struct vop2_video_port *vp, int ep_id, u32 polflags) -{ - struct vop2 *vop2 = vp->vop2; - - if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) - return rk3568_set_intf_mux(vp, ep_id, polflags); - else if (vop2->data->soc_id == 3588) - return rk3588_set_intf_mux(vp, ep_id, polflags); - else - return 0; -} - static int us_to_vertical_line(struct drm_display_mode *mode, int us) { return us * mode->clock / mode->htotal / 1000; @@ -2194,7 +1682,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, * process multi(1/2/4/8) pixels per cycle, so the dclk feed by the * system cru may be the 1/2 or 1/4 of mode->clock. */ - clock = vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); + clock = vop2->ops->setup_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); } if (!clock) { @@ -2259,6 +1747,44 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0); + /* + * Switch to HDMI PHY PLL as DCLK source for display modes up + * to 4K@60Hz, if available, otherwise keep using the system CRU. + */ + if ((vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) && clock <= VOP2_MAX_DCLK_RATE) { + drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); + + if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) { + if (!vop2->pll_hdmiphy0) + break; + + if (!vp->dclk_src) + vp->dclk_src = clk_get_parent(vp->dclk); + + ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0); + if (ret < 0) + drm_warn(vop2->drm, + "Could not switch to HDMI0 PHY PLL: %d\n", ret); + break; + } + + if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) { + if (!vop2->pll_hdmiphy1) + break; + + if (!vp->dclk_src) + vp->dclk_src = clk_get_parent(vp->dclk); + + ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1); + if (ret < 0) + drm_warn(vop2->drm, + "Could not switch to HDMI1 PHY PLL: %d\n", ret); + break; + } + } + } + clk_set_rate(vp->dclk, clock); vop2_post_config(crtc); @@ -2323,454 +1849,13 @@ static int vop2_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -static bool is_opaque(u16 alpha) -{ - return (alpha >> 8) == 0xff; -} - -static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config, - struct vop2_alpha *alpha) -{ - int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1; - int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1; - int src_color_mode = alpha_config->src_premulti_en ? - ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL; - int dst_color_mode = alpha_config->dst_premulti_en ? - ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL; - - alpha->src_color_ctrl.val = 0; - alpha->dst_color_ctrl.val = 0; - alpha->src_alpha_ctrl.val = 0; - alpha->dst_alpha_ctrl.val = 0; - - if (!alpha_config->src_pixel_alpha_en) - alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; - else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en) - alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX; - else - alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL; - - alpha->src_color_ctrl.bits.alpha_en = 1; - - if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) { - alpha->src_color_ctrl.bits.color_mode = src_color_mode; - alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL; - } else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) { - alpha->src_color_ctrl.bits.color_mode = src_color_mode; - alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE; - } else { - alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL; - alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL; - } - alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8; - alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; - alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; - - alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; - alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; - alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; - alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8; - alpha->dst_color_ctrl.bits.color_mode = dst_color_mode; - alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; - - alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; - alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode; - alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; - alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE; - - alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; - if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en) - alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX; - else - alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL; - alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION; - alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; -} - -static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id) -{ - struct vop2_video_port *vp; - int used_layer = 0; - int i; - - for (i = 0; i < port_id; i++) { - vp = &vop2->vps[i]; - used_layer += hweight32(vp->win_mask); - } - - return used_layer; -} - -static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win) -{ - struct vop2_alpha_config alpha_config; - struct vop2_alpha alpha; - struct drm_plane_state *bottom_win_pstate; - bool src_pixel_alpha_en = false; - u16 src_glb_alpha_val, dst_glb_alpha_val; - bool premulti_en = false; - bool swap = false; - u32 offset = 0; - - /* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */ - bottom_win_pstate = main_win->base.state; - src_glb_alpha_val = 0; - dst_glb_alpha_val = main_win->base.state->alpha; - - if (!bottom_win_pstate->fb) - return; - - alpha_config.src_premulti_en = premulti_en; - alpha_config.dst_premulti_en = false; - alpha_config.src_pixel_alpha_en = src_pixel_alpha_en; - alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */ - alpha_config.src_glb_alpha_value = src_glb_alpha_val; - alpha_config.dst_glb_alpha_value = dst_glb_alpha_val; - vop2_parse_alpha(&alpha_config, &alpha); - - alpha.src_color_ctrl.bits.src_dst_swap = swap; - - switch (main_win->data->phys_id) { - case ROCKCHIP_VOP2_CLUSTER0: - offset = 0x0; - break; - case ROCKCHIP_VOP2_CLUSTER1: - offset = 0x10; - break; - case ROCKCHIP_VOP2_CLUSTER2: - offset = 0x20; - break; - case ROCKCHIP_VOP2_CLUSTER3: - offset = 0x30; - break; - } - - vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset, - alpha.src_color_ctrl.val); - vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset, - alpha.dst_color_ctrl.val); - vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset, - alpha.src_alpha_ctrl.val); - vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset, - alpha.dst_alpha_ctrl.val); -} - -static void vop2_setup_alpha(struct vop2_video_port *vp) -{ - struct vop2 *vop2 = vp->vop2; - struct drm_framebuffer *fb; - struct vop2_alpha_config alpha_config; - struct vop2_alpha alpha; - struct drm_plane *plane; - int pixel_alpha_en; - int premulti_en, gpremulti_en = 0; - int mixer_id; - u32 offset; - bool bottom_layer_alpha_en = false; - u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE; - - mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id); - alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */ - - drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { - struct vop2_win *win = to_vop2_win(plane); - - if (plane->state->normalized_zpos == 0 && - !is_opaque(plane->state->alpha) && - !vop2_cluster_window(win)) { - /* - * If bottom layer have global alpha effect [except cluster layer, - * because cluster have deal with bottom layer global alpha value - * at cluster mix], bottom layer mix need deal with global alpha. - */ - bottom_layer_alpha_en = true; - dst_global_alpha = plane->state->alpha; - } - } - - drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { - struct vop2_win *win = to_vop2_win(plane); - int zpos = plane->state->normalized_zpos; - - /* - * Need to configure alpha from second layer. - */ - if (zpos == 0) - continue; - - if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) - premulti_en = 1; - else - premulti_en = 0; - - plane = &win->base; - fb = plane->state->fb; - - pixel_alpha_en = fb->format->has_alpha; - - alpha_config.src_premulti_en = premulti_en; - - if (bottom_layer_alpha_en && zpos == 1) { - gpremulti_en = premulti_en; - /* Cd = Cs + (1 - As) * Cd * Agd */ - alpha_config.dst_premulti_en = false; - alpha_config.src_pixel_alpha_en = pixel_alpha_en; - alpha_config.src_glb_alpha_value = plane->state->alpha; - alpha_config.dst_glb_alpha_value = dst_global_alpha; - } else if (vop2_cluster_window(win)) { - /* Mix output data only have pixel alpha */ - alpha_config.dst_premulti_en = true; - alpha_config.src_pixel_alpha_en = true; - alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; - alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; - } else { - /* Cd = Cs + (1 - As) * Cd */ - alpha_config.dst_premulti_en = true; - alpha_config.src_pixel_alpha_en = pixel_alpha_en; - alpha_config.src_glb_alpha_value = plane->state->alpha; - alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; - } - - vop2_parse_alpha(&alpha_config, &alpha); - - offset = (mixer_id + zpos - 1) * 0x10; - vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset, - alpha.src_color_ctrl.val); - vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset, - alpha.dst_color_ctrl.val); - vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset, - alpha.src_alpha_ctrl.val); - vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset, - alpha.dst_alpha_ctrl.val); - } - - if (vp->id == 0) { - if (bottom_layer_alpha_en) { - /* Transfer pixel alpha to hdr mix */ - alpha_config.src_premulti_en = gpremulti_en; - alpha_config.dst_premulti_en = true; - alpha_config.src_pixel_alpha_en = true; - alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; - alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; - vop2_parse_alpha(&alpha_config, &alpha); - - vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, - alpha.src_color_ctrl.val); - vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL, - alpha.dst_color_ctrl.val); - vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL, - alpha.src_alpha_ctrl.val); - vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL, - alpha.dst_alpha_ctrl.val); - } else { - vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0); - } - } -} - -static void vop2_setup_layer_mixer(struct vop2_video_port *vp) -{ - struct vop2 *vop2 = vp->vop2; - struct drm_plane *plane; - u32 layer_sel = 0; - u32 port_sel; - u8 layer_id; - u8 old_layer_id; - u8 layer_sel_id; - unsigned int ofs; - u32 ovl_ctrl; - int i; - struct vop2_video_port *vp0 = &vop2->vps[0]; - struct vop2_video_port *vp1 = &vop2->vps[1]; - struct vop2_video_port *vp2 = &vop2->vps[2]; - struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state); - - ovl_ctrl = vop2_readl(vop2, RK3568_OVL_CTRL); - ovl_ctrl |= RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD; - if (vcstate->yuv_overlay) - ovl_ctrl |= RK3568_OVL_CTRL__YUV_MODE(vp->id); - else - ovl_ctrl &= ~RK3568_OVL_CTRL__YUV_MODE(vp->id); - - vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl); - - port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL); - port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT; - - if (vp0->nlayers) - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, - vp0->nlayers - 1); - else - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8); - - if (vp1->nlayers) - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, - (vp0->nlayers + vp1->nlayers - 1)); - else - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8); - - if (vp2->nlayers) - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX, - (vp2->nlayers + vp1->nlayers + vp0->nlayers - 1)); - else - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX, 8); - - layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL); - - ofs = 0; - for (i = 0; i < vp->id; i++) - ofs += vop2->vps[i].nlayers; - - drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { - struct vop2_win *win = to_vop2_win(plane); - struct vop2_win *old_win; - - layer_id = (u8)(plane->state->normalized_zpos + ofs); - - /* - * Find the layer this win bind in old state. - */ - for (old_layer_id = 0; old_layer_id < vop2->data->win_size; old_layer_id++) { - layer_sel_id = (layer_sel >> (4 * old_layer_id)) & 0xf; - if (layer_sel_id == win->data->layer_sel_id) - break; - } - - /* - * Find the win bind to this layer in old state - */ - for (i = 0; i < vop2->data->win_size; i++) { - old_win = &vop2->win[i]; - layer_sel_id = (layer_sel >> (4 * layer_id)) & 0xf; - if (layer_sel_id == old_win->data->layer_sel_id) - break; - } - - switch (win->data->phys_id) { - case ROCKCHIP_VOP2_CLUSTER0: - port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0; - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id); - break; - case ROCKCHIP_VOP2_CLUSTER1: - port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1; - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id); - break; - case ROCKCHIP_VOP2_CLUSTER2: - port_sel &= ~RK3588_OVL_PORT_SEL__CLUSTER2; - port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__CLUSTER2, vp->id); - break; - case ROCKCHIP_VOP2_CLUSTER3: - port_sel &= ~RK3588_OVL_PORT_SEL__CLUSTER3; - port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__CLUSTER3, vp->id); - break; - case ROCKCHIP_VOP2_ESMART0: - port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0; - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id); - break; - case ROCKCHIP_VOP2_ESMART1: - port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1; - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id); - break; - case ROCKCHIP_VOP2_ESMART2: - port_sel &= ~RK3588_OVL_PORT_SEL__ESMART2; - port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__ESMART2, vp->id); - break; - case ROCKCHIP_VOP2_ESMART3: - port_sel &= ~RK3588_OVL_PORT_SEL__ESMART3; - port_sel |= FIELD_PREP(RK3588_OVL_PORT_SEL__ESMART3, vp->id); - break; - case ROCKCHIP_VOP2_SMART0: - port_sel &= ~RK3568_OVL_PORT_SEL__SMART0; - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id); - break; - case ROCKCHIP_VOP2_SMART1: - port_sel &= ~RK3568_OVL_PORT_SEL__SMART1; - port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id); - break; - } - - layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(layer_id, 0x7); - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(layer_id, win->data->layer_sel_id); - /* - * When we bind a window from layerM to layerN, we also need to move the old - * window on layerN to layerM to avoid one window selected by two or more layers. - */ - layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, 0x7); - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, old_win->data->layer_sel_id); - } - - vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel); - vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel); -} - -static void vop2_setup_dly_for_windows(struct vop2 *vop2) -{ - struct vop2_win *win; - int i = 0; - u32 cdly = 0, sdly = 0; - - for (i = 0; i < vop2->data->win_size; i++) { - u32 dly; - - win = &vop2->win[i]; - dly = win->delay; - - switch (win->data->phys_id) { - case ROCKCHIP_VOP2_CLUSTER0: - cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly); - cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly); - break; - case ROCKCHIP_VOP2_CLUSTER1: - cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly); - cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly); - break; - case ROCKCHIP_VOP2_ESMART0: - sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly); - break; - case ROCKCHIP_VOP2_ESMART1: - sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly); - break; - case ROCKCHIP_VOP2_SMART0: - case ROCKCHIP_VOP2_ESMART2: - sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly); - break; - case ROCKCHIP_VOP2_SMART1: - case ROCKCHIP_VOP2_ESMART3: - sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly); - break; - } - } - - vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly); - vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly); -} - static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; - struct drm_plane *plane; - vp->win_mask = 0; - - drm_atomic_crtc_for_each_plane(plane, crtc) { - struct vop2_win *win = to_vop2_win(plane); - - win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT]; - - vp->win_mask |= BIT(win->data->phys_id); - - if (vop2_cluster_window(win)) - vop2_setup_cluster_alpha(vop2, win); - } - - if (!vp->win_mask) - return; - - vop2_setup_layer_mixer(vp); - vop2_setup_alpha(vp); - vop2_setup_dly_for_windows(vop2); + vop2->ops->setup_overlay(vp); } static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, @@ -3082,6 +2167,52 @@ static const struct drm_crtc_funcs vop2_crtc_funcs = { .late_register = vop2_crtc_late_register, }; +static irqreturn_t rk3576_vp_isr(int irq, void *data) +{ + struct vop2_video_port *vp = data; + struct vop2 *vop2 = vp->vop2; + struct drm_crtc *crtc = &vp->crtc; + uint32_t irqs; + int ret = IRQ_NONE; + + if (!pm_runtime_get_if_in_use(vop2->dev)) + return IRQ_NONE; + + irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id)); + vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs); + + if (irqs & VP_INT_DSP_HOLD_VALID) { + complete(&vp->dsp_hold_completion); + ret = IRQ_HANDLED; + } + + if (irqs & VP_INT_FS_FIELD) { + drm_crtc_handle_vblank(crtc); + spin_lock(&crtc->dev->event_lock); + if (vp->event) { + u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE); + + if (!(val & BIT(vp->id))) { + drm_crtc_send_vblank_event(crtc, vp->event); + vp->event = NULL; + drm_crtc_vblank_put(crtc); + } + } + spin_unlock(&crtc->dev->event_lock); + + ret = IRQ_HANDLED; + } + + if (irqs & VP_INT_POST_BUF_EMPTY) { + drm_err_ratelimited(vop2->drm, "POST_BUF_EMPTY irq err at vp%d\n", vp->id); + ret = IRQ_HANDLED; + } + + pm_runtime_put(vop2->dev); + + return ret; +} + static irqreturn_t vop2_isr(int irq, void *data) { struct vop2 *vop2 = data; @@ -3097,41 +2228,43 @@ static irqreturn_t vop2_isr(int irq, void *data) if (!pm_runtime_get_if_in_use(vop2->dev)) return IRQ_NONE; - for (i = 0; i < vop2_data->nr_vps; i++) { - struct vop2_video_port *vp = &vop2->vps[i]; - struct drm_crtc *crtc = &vp->crtc; - u32 irqs; - - irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id)); - vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs); + if (vop2->version < VOP_VERSION_RK3576) { + for (i = 0; i < vop2_data->nr_vps; i++) { + struct vop2_video_port *vp = &vop2->vps[i]; + struct drm_crtc *crtc = &vp->crtc; + u32 irqs; - if (irqs & VP_INT_DSP_HOLD_VALID) { - complete(&vp->dsp_hold_completion); - ret = IRQ_HANDLED; - } + irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id)); + vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs); - if (irqs & VP_INT_FS_FIELD) { - drm_crtc_handle_vblank(crtc); - spin_lock(&crtc->dev->event_lock); - if (vp->event) { - u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE); + if (irqs & VP_INT_DSP_HOLD_VALID) { + complete(&vp->dsp_hold_completion); + ret = IRQ_HANDLED; + } - if (!(val & BIT(vp->id))) { - drm_crtc_send_vblank_event(crtc, vp->event); - vp->event = NULL; - drm_crtc_vblank_put(crtc); + if (irqs & VP_INT_FS_FIELD) { + drm_crtc_handle_vblank(crtc); + spin_lock(&crtc->dev->event_lock); + if (vp->event) { + u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE); + + if (!(val & BIT(vp->id))) { + drm_crtc_send_vblank_event(crtc, vp->event); + vp->event = NULL; + drm_crtc_vblank_put(crtc); + } } - } - spin_unlock(&crtc->dev->event_lock); + spin_unlock(&crtc->dev->event_lock); - ret = IRQ_HANDLED; - } + ret = IRQ_HANDLED; + } - if (irqs & VP_INT_POST_BUF_EMPTY) { - drm_err_ratelimited(vop2->drm, - "POST_BUF_EMPTY irq err at vp%d\n", - vp->id); - ret = IRQ_HANDLED; + if (irqs & VP_INT_POST_BUF_EMPTY) { + drm_err_ratelimited(vop2->drm, + "POST_BUF_EMPTY irq err at vp%d\n", + vp->id); + ret = IRQ_HANDLED; + } } } @@ -3185,22 +2318,29 @@ static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, return 0; } -static struct vop2_video_port *find_vp_without_primary(struct vop2 *vop2) +/* + * On RK3566 these windows don't have an independent + * framebuffer. They can only share/mirror the framebuffer + * with smart0, esmart0 and cluster0 respectively. + * And RK3566 share the same vop version with Rk3568, so we + * need to use soc_id for identification here. + */ +static bool vop2_is_mirror_win(struct vop2_win *win) { - int i; - - for (i = 0; i < vop2->data->nr_vps; i++) { - struct vop2_video_port *vp = &vop2->vps[i]; - - if (!vp->crtc.port) - continue; - if (vp->primary_plane) - continue; + struct vop2 *vop2 = win->vop2; - return vp; + if (vop2->data->soc_id == 3566) { + switch (win->data->phys_id) { + case ROCKCHIP_VOP2_SMART1: + case ROCKCHIP_VOP2_ESMART1: + case ROCKCHIP_VOP2_CLUSTER1: + return true; + default: + return false; + } + } else { + return false; } - - return NULL; } static int vop2_create_crtcs(struct vop2 *vop2) @@ -3211,7 +2351,9 @@ static int vop2_create_crtcs(struct vop2 *vop2) struct drm_plane *plane; struct device_node *port; struct vop2_video_port *vp; - int i, nvp, nvps = 0; + struct vop2_win *win; + u32 possible_crtcs; + int i, j, nvp, nvps = 0; int ret; for (i = 0; i < vop2_data->nr_vps; i++) { @@ -3227,10 +2369,9 @@ static int vop2_create_crtcs(struct vop2 *vop2) snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id); vp->dclk = devm_clk_get(vop2->dev, dclk_name); - if (IS_ERR(vp->dclk)) { - drm_err(vop2->drm, "failed to get %s\n", dclk_name); - return PTR_ERR(vp->dclk); - } + if (IS_ERR(vp->dclk)) + return dev_err_probe(drm->dev, PTR_ERR(vp->dclk), + "failed to get %s\n", dclk_name); np = of_graph_get_remote_node(dev->of_node, i, -1); if (!np) { @@ -3240,55 +2381,79 @@ static int vop2_create_crtcs(struct vop2 *vop2) of_node_put(np); port = of_graph_get_port_by_id(dev->of_node, i); - if (!port) { - drm_err(vop2->drm, "no port node found for video_port%d\n", i); - return -ENOENT; - } - + if (!port) + return dev_err_probe(drm->dev, -ENOENT, + "no port node found for video_port%d\n", i); vp->crtc.port = port; nvps++; } nvp = 0; - for (i = 0; i < vop2->registered_num_wins; i++) { - struct vop2_win *win = &vop2->win[i]; - u32 possible_crtcs = 0; - - if (vop2->data->soc_id == 3566) { - /* - * On RK3566 these windows don't have an independent - * framebuffer. They share the framebuffer with smart0, - * esmart0 and cluster0 respectively. - */ - switch (win->data->phys_id) { - case ROCKCHIP_VOP2_SMART1: - case ROCKCHIP_VOP2_ESMART1: - case ROCKCHIP_VOP2_CLUSTER1: + /* Register a primary plane for every crtc */ + for (i = 0; i < vop2_data->nr_vps; i++) { + vp = &vop2->vps[i]; + + if (!vp->crtc.port) + continue; + + for (j = 0; j < vop2->registered_num_wins; j++) { + win = &vop2->win[j]; + + /* Aready registered as primary plane */ + if (win->base.type == DRM_PLANE_TYPE_PRIMARY) + continue; + + /* If this win can not attached to this VP */ + if (!(win->data->possible_vp_mask & BIT(vp->id))) continue; - } - } - if (win->type == DRM_PLANE_TYPE_PRIMARY) { - vp = find_vp_without_primary(vop2); - if (vp) { + if (vop2_is_mirror_win(win)) + continue; + + if (win->type == DRM_PLANE_TYPE_PRIMARY) { possible_crtcs = BIT(nvp); vp->primary_plane = win; + ret = vop2_plane_init(vop2, win, possible_crtcs); + if (ret) + return dev_err_probe(drm->dev, ret, + "failed to init primary plane %s\n", + win->data->name); nvp++; - } else { - /* change the unused primary window to overlay window */ - win->type = DRM_PLANE_TYPE_OVERLAY; + break; } } + } - if (win->type == DRM_PLANE_TYPE_OVERLAY) - possible_crtcs = (1 << nvps) - 1; + /* Register all unused window as overlay plane */ + for (i = 0; i < vop2->registered_num_wins; i++) { + win = &vop2->win[i]; - ret = vop2_plane_init(vop2, win, possible_crtcs); - if (ret) { - drm_err(vop2->drm, "failed to init plane %s: %d\n", - win->data->name, ret); - return ret; + /* Aready registered as primary plane */ + if (win->base.type == DRM_PLANE_TYPE_PRIMARY) + continue; + + if (vop2_is_mirror_win(win)) + continue; + + win->type = DRM_PLANE_TYPE_OVERLAY; + + possible_crtcs = 0; + nvp = 0; + for (j = 0; j < vop2_data->nr_vps; j++) { + vp = &vop2->vps[j]; + + if (!vp->crtc.port) + continue; + + if (win->data->possible_vp_mask & BIT(vp->id)) + possible_crtcs |= BIT(nvp); + nvp++; } + + ret = vop2_plane_init(vop2, win, possible_crtcs); + if (ret) + return dev_err_probe(drm->dev, ret, "failed to init overlay plane %s\n", + win->data->name); } for (i = 0; i < vop2_data->nr_vps; i++) { @@ -3302,10 +2467,9 @@ static int vop2_create_crtcs(struct vop2 *vop2) ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL, &vop2_crtc_funcs, "video_port%d", vp->id); - if (ret) { - drm_err(vop2->drm, "crtc init for video_port%d failed\n", i); - return ret; - } + if (ret) + return dev_err_probe(drm->dev, ret, + "crtc init for video_port%d failed\n", i); drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs); if (vop2->lut_regs) { @@ -3372,184 +2536,26 @@ static int vop2_find_rgb_encoder(struct vop2 *vop2) return -ENOENT; } -static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = { - [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0), - [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5), - [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14), - [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18), - [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31), - [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31), - [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31), - [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31), - [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31), - [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19), - [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15), - [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31), - [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8), - [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9), - [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11), - [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 0, 3), - [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 5, 8), - /* RK3588 only, reserved bit on rk3568*/ - [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3568_CLUSTER_CTRL, 13, 13), - - /* Scale */ - [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15), - [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31), - [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15), - [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13), - [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3), - [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28), - [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29), - - /* cluster regs */ - [VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1), - [VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0), - [VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7), - - /* afbc regs */ - [VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6), - [VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9), - [VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10), - [VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4), - [VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7), - [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8), - [VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31), - [VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31), - [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15), - [VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31), - [VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31), - [VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31), - [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31), - [VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0), - [VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1), - [VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2), - [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3), - [VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff }, - [VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff }, - [VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff }, - [VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff }, - [VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff }, - [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff }, - [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff }, - [VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff }, - [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff }, - [VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff }, - [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff }, - [VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff }, - [VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff }, -}; - -static int vop2_cluster_init(struct vop2_win *win) +static int vop2_regmap_init(struct vop2_win *win, const struct reg_field *regs, + int nr_regs) { struct vop2 *vop2 = win->vop2; - struct reg_field *cluster_regs; - int ret, i; - - cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs), - GFP_KERNEL); - if (!cluster_regs) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++) - if (cluster_regs[i].reg != 0xffffffff) - cluster_regs[i].reg += win->offset; - - ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg, - cluster_regs, - ARRAY_SIZE(vop2_cluster_regs)); - - kfree(cluster_regs); - - return ret; -}; - -static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = { - [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0), - [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5), - [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12), - [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14), - [VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16), - [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31), - [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31), - [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28), - [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31), - [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31), - [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17), - [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15), - [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31), - [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0), - [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1), - [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3), - [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31), - [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29), - [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31), - [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 4, 8), - [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 12, 16), - /* RK3588 only, reserved register on rk3568 */ - [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3588_SMART_AXI_CTRL, 1, 1), - - /* Scale */ - [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15), - [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31), - [VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15), - [VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31), - [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1), - [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3), - [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5), - [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7), - [VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9), - [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11), - [VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13), - [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15), - [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17), - [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8), - [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9), - [VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10), - [VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11), - [VOP2_WIN_XMIRROR] = { .reg = 0xffffffff }, - [VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff }, - [VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff }, - [VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff }, -}; - -static int vop2_esmart_init(struct vop2_win *win) -{ - struct vop2 *vop2 = win->vop2; - struct reg_field *esmart_regs; - int ret, i; - - esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs), - GFP_KERNEL); - if (!esmart_regs) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++) - if (esmart_regs[i].reg != 0xffffffff) - esmart_regs[i].reg += win->offset; + int i; - ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg, - esmart_regs, - ARRAY_SIZE(vop2_esmart_regs)); + for (i = 0; i < nr_regs; i++) { + const struct reg_field field = { + .reg = (regs[i].reg != 0xffffffff) ? + regs[i].reg + win->offset : regs[i].reg, + .lsb = regs[i].lsb, + .msb = regs[i].msb + }; - kfree(esmart_regs); + win->reg[i] = devm_regmap_field_alloc(vop2->dev, vop2->map, field); + if (IS_ERR(win->reg[i])) + return PTR_ERR(win->reg[i]); + } - return ret; + return 0; }; static int vop2_win_init(struct vop2 *vop2) @@ -3568,9 +2574,11 @@ static int vop2_win_init(struct vop2 *vop2) win->win_id = i; win->vop2 = vop2; if (vop2_cluster_window(win)) - ret = vop2_cluster_init(win); + ret = vop2_regmap_init(win, vop2->data->cluster_reg, + vop2->data->nr_cluster_regs); else - ret = vop2_esmart_init(win); + ret = vop2_regmap_init(win, vop2->data->smart_reg, + vop2->data->nr_smart_regs); if (ret) return ret; } @@ -3627,15 +2635,16 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) vop2->dev = dev; vop2->data = vop2_data; + vop2->ops = vop2_data->ops; + vop2->version = vop2_data->version; vop2->drm = drm; dev_set_drvdata(dev, vop2); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vop"); - if (!res) { - drm_err(vop2->drm, "failed to get vop2 register byname\n"); - return -EINVAL; - } + if (!res) + return dev_err_probe(drm->dev, -EINVAL, + "failed to get vop2 register byname\n"); vop2->res = res; vop2->regs = devm_ioremap_resource(dev, res); @@ -3660,50 +2669,59 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_GRF) { vop2->sys_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(vop2->sys_grf)) - return dev_err_probe(dev, PTR_ERR(vop2->sys_grf), "cannot get sys_grf"); + return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_grf), + "cannot get sys_grf\n"); } if (vop2_data->feature & VOP2_FEATURE_HAS_VOP_GRF) { vop2->vop_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vop-grf"); if (IS_ERR(vop2->vop_grf)) - return dev_err_probe(dev, PTR_ERR(vop2->vop_grf), "cannot get vop_grf"); + return dev_err_probe(drm->dev, PTR_ERR(vop2->vop_grf), + "cannot get vop_grf\n"); } if (vop2_data->feature & VOP2_FEATURE_HAS_VO1_GRF) { vop2->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo1-grf"); if (IS_ERR(vop2->vo1_grf)) - return dev_err_probe(dev, PTR_ERR(vop2->vo1_grf), "cannot get vo1_grf"); + return dev_err_probe(drm->dev, PTR_ERR(vop2->vo1_grf), + "cannot get vo1_grf\n"); } if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_PMU) { vop2->sys_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pmu"); if (IS_ERR(vop2->sys_pmu)) - return dev_err_probe(dev, PTR_ERR(vop2->sys_pmu), "cannot get sys_pmu"); + return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_pmu), + "cannot get sys_pmu\n"); } vop2->hclk = devm_clk_get(vop2->dev, "hclk"); - if (IS_ERR(vop2->hclk)) { - drm_err(vop2->drm, "failed to get hclk source\n"); - return PTR_ERR(vop2->hclk); - } + if (IS_ERR(vop2->hclk)) + return dev_err_probe(drm->dev, PTR_ERR(vop2->hclk), + "failed to get hclk source\n"); vop2->aclk = devm_clk_get(vop2->dev, "aclk"); - if (IS_ERR(vop2->aclk)) { - drm_err(vop2->drm, "failed to get aclk source\n"); - return PTR_ERR(vop2->aclk); - } + if (IS_ERR(vop2->aclk)) + return dev_err_probe(drm->dev, PTR_ERR(vop2->aclk), + "failed to get aclk source\n"); vop2->pclk = devm_clk_get_optional(vop2->dev, "pclk_vop"); - if (IS_ERR(vop2->pclk)) { - drm_err(vop2->drm, "failed to get pclk source\n"); - return PTR_ERR(vop2->pclk); - } + if (IS_ERR(vop2->pclk)) + return dev_err_probe(drm->dev, PTR_ERR(vop2->pclk), + "failed to get pclk source\n"); + + vop2->pll_hdmiphy0 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy0"); + if (IS_ERR(vop2->pll_hdmiphy0)) + return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy0), + "failed to get pll_hdmiphy0\n"); + + vop2->pll_hdmiphy1 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy1"); + if (IS_ERR(vop2->pll_hdmiphy1)) + return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy1), + "failed to get pll_hdmiphy1\n"); vop2->irq = platform_get_irq(pdev, 0); - if (vop2->irq < 0) { - drm_err(vop2->drm, "cannot find irq for vop2\n"); - return vop2->irq; - } + if (vop2->irq < 0) + return dev_err_probe(drm->dev, vop2->irq, "cannot find irq for vop2\n"); mutex_init(&vop2->vop2_lock); @@ -3715,6 +2733,30 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; + if (vop2->version >= VOP_VERSION_RK3576) { + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, drm) { + struct vop2_video_port *vp = to_vop2_video_port(crtc); + int vp_irq; + const char *irq_name = devm_kasprintf(dev, GFP_KERNEL, "vp%d", vp->id); + + if (!irq_name) + return -ENOMEM; + + vp_irq = platform_get_irq_byname(pdev, irq_name); + if (vp_irq < 0) + return dev_err_probe(drm->dev, vp_irq, + "cannot find irq for vop2 vp%d\n", vp->id); + + ret = devm_request_irq(dev, vp_irq, rk3576_vp_isr, IRQF_SHARED, irq_name, + vp); + if (ret) + dev_err_probe(drm->dev, ret, + "request irq for vop2 vp%d failed\n", vp->id); + } + } + ret = vop2_find_rgb_encoder(vop2); if (ret >= 0) { vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc, |