diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip/rockchip_drm_vop2.c')
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 622 |
1 files changed, 578 insertions, 44 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index fdd768bbd487..17a98845fd31 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -24,16 +24,17 @@ #include <drm/drm_atomic_uapi.h> #include <drm/drm_blend.h> #include <drm/drm_crtc.h> +#include <linux/debugfs.h> #include <drm/drm_debugfs.h> #include <drm/drm_flip_work.h> #include <drm/drm_framebuffer.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> #include <uapi/linux/videodev2.h> #include <dt-bindings/soc/rockchip,vop2.h> -#include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" #include "rockchip_drm_vop2.h" #include "rockchip_rgb.h" @@ -186,6 +187,7 @@ struct vop2 { */ u32 registered_num_wins; + struct resource *res; void __iomem *regs; struct regmap *map; @@ -237,6 +239,37 @@ struct vop2 { #define vop2_output_if_is_dpi(x) ((x) == ROCKCHIP_VOP2_EP_RGB0) +/* + * bus-format types. + */ +struct drm_bus_format_enum_list { + int type; + const char *name; +}; + +static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = { + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, + { MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" }, + { MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" }, + { MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" }, + { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" }, + { MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" }, + { MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" }, + { MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" }, + { MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" }, + { MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" }, + { MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" }, + { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" }, + { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" }, + { 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" }, + { MEDIA_BUS_FMT_YUYV10_1X20, "YUYV10_1X20" }, +}; + +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) @@ -278,6 +311,15 @@ static u32 vop2_readl(struct vop2 *vop2, u32 offset) 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); @@ -550,6 +592,25 @@ 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_cluster_window(win)) { + if (modifier == DRM_FORMAT_MOD_LINEAR) { + drm_dbg_kms(vop2->drm, + "Cluster window only supports format with afbc\n"); + return false; + } + } + } + + if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) { + if (vop2->data->soc_id == 3588) { + if (!rockchip_afbc(plane, modifier)) { + drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n"); + return false; + } + } + } + if (modifier == DRM_FORMAT_MOD_LINEAR) return true; @@ -706,6 +767,8 @@ static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win, const struct drm_format_info *info; u16 hor_scl_mode, ver_scl_mode; u16 hscl_filter_mode, vscl_filter_mode; + uint16_t cbcr_src_w = src_w; + uint16_t cbcr_src_h = src_h; u8 gt2 = 0; u8 gt4 = 0; u32 val; @@ -763,27 +826,27 @@ static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win, vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode); if (info->is_yuv) { - src_w /= info->hsub; - src_h /= info->vsub; + cbcr_src_w /= info->hsub; + cbcr_src_h /= info->vsub; gt4 = 0; gt2 = 0; - if (src_h >= (4 * dst_h)) { + if (cbcr_src_h >= (4 * dst_h)) { gt4 = 1; - src_h >>= 2; - } else if (src_h >= (2 * dst_h)) { + cbcr_src_h >>= 2; + } else if (cbcr_src_h >= (2 * dst_h)) { gt2 = 1; - src_h >>= 1; + cbcr_src_h >>= 1; } - hor_scl_mode = scl_get_scl_mode(src_w, dst_w); - ver_scl_mode = scl_get_scl_mode(src_h, dst_h); + hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w); + ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h); - val = vop2_scale_factor(src_w, dst_w); + val = vop2_scale_factor(cbcr_src_w, dst_w); vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val); - val = vop2_scale_factor(src_h, dst_h); + val = vop2_scale_factor(cbcr_src_h, dst_h); vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val); vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4); @@ -996,6 +1059,67 @@ static void vop2_disable(struct vop2 *vop2) clk_disable_unprepare(vop2->hclk); } +static bool vop2_vp_dsp_lut_is_enabled(struct vop2_video_port *vp) +{ + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); + + return dsp_ctrl & RK3568_VP_DSP_CTRL__DSP_LUT_EN; +} + +static void vop2_vp_dsp_lut_disable(struct vop2_video_port *vp) +{ + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); + + dsp_ctrl &= ~RK3568_VP_DSP_CTRL__DSP_LUT_EN; + vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); +} + +static bool vop2_vp_dsp_lut_poll_disabled(struct vop2_video_port *vp) +{ + u32 dsp_ctrl; + int ret = readx_poll_timeout(vop2_vp_dsp_lut_is_enabled, vp, dsp_ctrl, + !dsp_ctrl, 5, 30 * 1000); + if (ret) { + drm_err(vp->vop2->drm, "display LUT RAM enable timeout!\n"); + return false; + } + + return true; +} + +static void vop2_vp_dsp_lut_enable(struct vop2_video_port *vp) +{ + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); + + dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_LUT_EN; + vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); +} + +static void vop2_vp_dsp_lut_update_enable(struct vop2_video_port *vp) +{ + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); + + dsp_ctrl |= RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN; + vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); +} + +static inline bool vop2_supports_seamless_gamma_lut_update(struct vop2 *vop2) +{ + return (vop2->data->soc_id != 3566 && vop2->data->soc_id != 3568); +} + +static bool vop2_gamma_lut_in_use(struct vop2 *vop2, struct vop2_video_port *vp) +{ + const int nr_vps = vop2->data->nr_vps; + int gamma_en_vp_id; + + for (gamma_en_vp_id = 0; gamma_en_vp_id < nr_vps; gamma_en_vp_id++) + if (vop2_vp_dsp_lut_is_enabled(&vop2->vps[gamma_en_vp_id])) + break; + + return gamma_en_vp_id != nr_vps && gamma_en_vp_id != vp->id; +} + static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -1269,8 +1393,9 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, dsp_w = drm_rect_width(dest); if (dest->x1 + dsp_w > adjusted_mode->hdisplay) { - drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n", - vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay); + drm_dbg_kms(vop2->drm, + "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n", + vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay); dsp_w = adjusted_mode->hdisplay - dest->x1; if (dsp_w < 4) dsp_w = 4; @@ -1280,8 +1405,9 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, dsp_h = drm_rect_height(dest); if (dest->y1 + dsp_h > adjusted_mode->vdisplay) { - drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n", - vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay); + drm_dbg_kms(vop2->drm, + "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n", + vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay); dsp_h = adjusted_mode->vdisplay - dest->y1; if (dsp_h < 4) dsp_h = 4; @@ -1294,15 +1420,15 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, */ if (!(win->data->feature & WIN_FEATURE_AFBDC)) { if (actual_w > dsp_w && (actual_w & 0xf) == 1) { - drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n", - vp->id, win->data->name, actual_w); + drm_dbg_kms(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n", + vp->id, win->data->name, actual_w); actual_w -= 1; } } if (afbc_en && actual_w % 4) { - drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n", - vp->id, win->data->name, actual_w); + drm_dbg_kms(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n", + vp->id, win->data->name, actual_w); actual_w = ALIGN_DOWN(actual_w, 4); } @@ -1318,20 +1444,28 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, &fb->format->format, afbc_en ? "AFBC" : "", &yrgb_mst); + if (vop2->data->soc_id > 3568) { + 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_cluster_window(win)) vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en); if (afbc_en) { - u32 stride; + u32 stride, block_w; + + /* the afbc superblock is 16 x 16 or 32 x 8 */ + block_w = fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 ? 32 : 16; - /* the afbc superblock is 16 x 16 */ afbc_format = vop2_convert_afbc_format(fb->format->format); /* Enable color transform for YTR */ if (fb->modifier & AFBC_FORMAT_MOD_YTR) afbc_format |= (1 << 4); - afbc_tile_num = ALIGN(actual_w, 16) >> 4; + afbc_tile_num = ALIGN(actual_w, block_w) / block_w; /* * AFBC pic_vir_width is count by pixel, this is different @@ -1339,8 +1473,11 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, */ stride = (fb->pitches[0] << 3) / bpp; if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270)) - drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n", - vp->id, win->data->name, stride); + drm_dbg_kms(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n", + vp->id, win->data->name, stride); + + /* It's for head stride, each head size is 16 byte */ + stride = ALIGN(stride, block_w) / block_w * 16; uv_swap = vop2_afbc_uv_swap(fb->format->format); /* @@ -1372,7 +1509,11 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, else vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1); - vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0); + if (fb->modifier & AFBC_FORMAT_MOD_SPLIT) + vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 1); + else + vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0); + 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); @@ -1480,6 +1621,77 @@ static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc, return true; } +static void vop2_crtc_write_gamma_lut(struct vop2 *vop2, struct drm_crtc *crtc) +{ + const struct vop2_video_port *vp = to_vop2_video_port(crtc); + const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id]; + struct drm_color_lut *lut = crtc->state->gamma_lut->data; + unsigned int i, bpc = ilog2(vp_data->gamma_lut_len); + u32 word; + + for (i = 0; i < crtc->gamma_size; i++) { + word = (drm_color_lut_extract(lut[i].blue, bpc) << (2 * bpc)) | + (drm_color_lut_extract(lut[i].green, bpc) << bpc) | + drm_color_lut_extract(lut[i].red, bpc); + + writel(word, vop2->lut_regs + i * 4); + } +} + +static void vop2_crtc_atomic_set_gamma_seamless(struct vop2 *vop2, + struct vop2_video_port *vp, + struct drm_crtc *crtc) +{ + vop2_writel(vop2, RK3568_LUT_PORT_SEL, + FIELD_PREP(RK3588_LUT_PORT_SEL__GAMMA_AHB_WRITE_SEL, vp->id)); + vop2_vp_dsp_lut_enable(vp); + vop2_crtc_write_gamma_lut(vop2, crtc); + vop2_vp_dsp_lut_update_enable(vp); +} + +static void vop2_crtc_atomic_set_gamma_rk356x(struct vop2 *vop2, + struct vop2_video_port *vp, + struct drm_crtc *crtc) +{ + vop2_vp_dsp_lut_disable(vp); + vop2_cfg_done(vp); + if (!vop2_vp_dsp_lut_poll_disabled(vp)) + return; + + vop2_writel(vop2, RK3568_LUT_PORT_SEL, vp->id); + vop2_crtc_write_gamma_lut(vop2, crtc); + vop2_vp_dsp_lut_enable(vp); +} + +static void vop2_crtc_atomic_try_set_gamma(struct vop2 *vop2, + struct vop2_video_port *vp, + struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + if (!vop2->lut_regs) + return; + + if (!crtc_state->gamma_lut) { + vop2_vp_dsp_lut_disable(vp); + return; + } + + if (vop2_supports_seamless_gamma_lut_update(vop2)) + vop2_crtc_atomic_set_gamma_seamless(vop2, vp, crtc); + else + vop2_crtc_atomic_set_gamma_rk356x(vop2, vp, crtc); +} + +static inline void vop2_crtc_atomic_try_set_gamma_locked(struct vop2 *vop2, + struct vop2_video_port *vp, + struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + vop2_lock(vop2); + vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state); + vop2_unlock(vop2); +} + static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl) { struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); @@ -1719,9 +1931,9 @@ static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id, else dclk_out_rate = v_pixclk >> 2; - dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000); + 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 KHZ\n", + drm_err(vop2->drm, "DP dclk_out_rate out of range, dclk_out_rate: %ld Hz\n", dclk_out_rate); return 0; } @@ -1736,9 +1948,9 @@ static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id, * dclk_rate = N * dclk_core_rate N = (1,2,4 ), * we get a little factor here */ - dclk_rate = rk3588_calc_dclk(dclk_out_rate, 600000); + 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 KHZ\n", + drm_err(vop2->drm, "MIPI dclk out of range, dclk_out_rate: %ld Hz\n", dclk_out_rate); return 0; } @@ -2055,11 +2267,40 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); + vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state); + drm_crtc_vblank_on(crtc); vop2_unlock(vop2); } +static int vop2_crtc_atomic_check_gamma(struct vop2_video_port *vp, + struct drm_crtc *crtc, + struct drm_atomic_state *state, + struct drm_crtc_state *crtc_state) +{ + struct vop2 *vop2 = vp->vop2; + unsigned int len; + + if (!vp->vop2->lut_regs || !crtc_state->color_mgmt_changed || + !crtc_state->gamma_lut) + return 0; + + len = drm_color_lut_size(crtc_state->gamma_lut); + if (len != crtc->gamma_size) { + drm_dbg(vop2->drm, "Invalid LUT size; got %d, expected %d\n", + len, crtc->gamma_size); + return -EINVAL; + } + + if (!vop2_supports_seamless_gamma_lut_update(vop2) && vop2_gamma_lut_in_use(vop2, vp)) { + drm_info(vop2->drm, "Gamma LUT can be enabled for only one CRTC at a time\n"); + return -EINVAL; + } + + return 0; +} + static int vop2_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -2067,6 +2308,11 @@ static int vop2_crtc_atomic_check(struct drm_crtc *crtc, struct drm_plane *plane; int nplanes = 0; struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + int ret; + + ret = vop2_crtc_atomic_check_gamma(vp, crtc, state, crtc_state); + if (ret) + return ret; drm_atomic_crtc_state_for_each_plane(plane, crtc_state) nplanes++; @@ -2157,7 +2403,6 @@ static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id) static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win) { - u32 offset = (main_win->data->phys_id * 0x10); struct vop2_alpha_config alpha_config; struct vop2_alpha alpha; struct drm_plane_state *bottom_win_pstate; @@ -2165,6 +2410,7 @@ static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_wi 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; @@ -2183,6 +2429,22 @@ static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_wi 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, @@ -2230,6 +2492,12 @@ static void vop2_setup_alpha(struct vop2_video_port *vp) 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 @@ -2306,7 +2574,10 @@ static void vop2_setup_layer_mixer(struct vop2_video_port *vp) struct drm_plane *plane; u32 layer_sel = 0; u32 port_sel; - unsigned int nlayer, ofs; + 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]; @@ -2342,7 +2613,7 @@ static void vop2_setup_layer_mixer(struct vop2_video_port *vp) 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__PORT1_MUX, 8); + port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX, 8); layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL); @@ -2350,9 +2621,30 @@ static void vop2_setup_layer_mixer(struct vop2_video_port *vp) for (i = 0; i < vp->id; i++) ofs += vop2->vps[i].nlayers; - nlayer = 0; 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: @@ -2397,17 +2689,14 @@ static void vop2_setup_layer_mixer(struct vop2_video_port *vp) break; } - layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs, - 0x7); - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs, - win->data->layer_sel_id); - nlayer++; - } - - /* configure unused layers to 0x5 (reserved) */ - for (; nlayer < vp->nlayers; nlayer++) { - layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7); - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5); + 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); @@ -2442,9 +2731,11 @@ static void vop2_setup_dly_for_windows(struct vop2 *vop2) 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; } @@ -2485,7 +2776,13 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + /* In case of modeset, gamma lut update already happened in atomic enable */ + if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed) + vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state); vop2_post_config(crtc); @@ -2511,6 +2808,228 @@ static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { .atomic_disable = vop2_crtc_atomic_disable, }; +static void vop2_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s) +{ + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; + + drm_connector_list_iter_begin(crtc->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (crtc->state->connector_mask & drm_connector_mask(connector)) + seq_printf(s, " Connector: %s\n", connector->name); + } + drm_connector_list_iter_end(&conn_iter); +} + +static int vop2_plane_state_dump(struct seq_file *s, struct drm_plane *plane) +{ + struct vop2_win *win = to_vop2_win(plane); + struct drm_plane_state *pstate = plane->state; + struct drm_rect *src, *dst; + struct drm_framebuffer *fb; + struct drm_gem_object *obj; + struct rockchip_gem_object *rk_obj; + bool xmirror; + bool ymirror; + bool rotate_270; + bool rotate_90; + dma_addr_t fb_addr; + int i; + + seq_printf(s, " %s: %s\n", win->data->name, !pstate ? + "DISABLED" : pstate->crtc ? "ACTIVE" : "DISABLED"); + + if (!pstate || !pstate->fb) + return 0; + + fb = pstate->fb; + src = &pstate->src; + dst = &pstate->dst; + xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false; + ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false; + rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270; + rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90; + + seq_printf(s, "\twin_id: %d\n", win->win_id); + + seq_printf(s, "\tformat: %p4cc%s glb_alpha[0x%x]\n", + &fb->format->format, + drm_is_afbc(fb->modifier) ? "[AFBC]" : "", + pstate->alpha >> 8); + seq_printf(s, "\trotate: xmirror: %d ymirror: %d rotate_90: %d rotate_270: %d\n", + xmirror, ymirror, rotate_90, rotate_270); + seq_printf(s, "\tzpos: %d\n", pstate->normalized_zpos); + seq_printf(s, "\tsrc: pos[%d, %d] rect[%d x %d]\n", src->x1 >> 16, + src->y1 >> 16, drm_rect_width(src) >> 16, + drm_rect_height(src) >> 16); + seq_printf(s, "\tdst: pos[%d, %d] rect[%d x %d]\n", dst->x1, dst->y1, + drm_rect_width(dst), drm_rect_height(dst)); + + for (i = 0; i < fb->format->num_planes; i++) { + obj = fb->obj[i]; + rk_obj = to_rockchip_obj(obj); + fb_addr = rk_obj->dma_addr + fb->offsets[i]; + + seq_printf(s, "\tbuf[%d]: addr: %pad pitch: %d offset: %d\n", + i, &fb_addr, fb->pitches[i], fb->offsets[i]); + } + + return 0; +} + +static int vop2_crtc_state_dump(struct drm_crtc *crtc, struct seq_file *s) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_crtc_state *cstate = crtc->state; + struct rockchip_crtc_state *vcstate; + struct drm_display_mode *mode; + struct drm_plane *plane; + bool interlaced; + + seq_printf(s, "Video Port%d: %s\n", vp->id, !cstate ? + "DISABLED" : cstate->active ? "ACTIVE" : "DISABLED"); + + if (!cstate || !cstate->active) + return 0; + + mode = &crtc->state->adjusted_mode; + vcstate = to_rockchip_crtc_state(cstate); + interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + + vop2_dump_connector_on_crtc(crtc, s); + seq_printf(s, "\tbus_format[%x]: %s\n", vcstate->bus_format, + drm_get_bus_format_name(vcstate->bus_format)); + seq_printf(s, "\toutput_mode[%x]", vcstate->output_mode); + seq_printf(s, " color_space[%d]\n", vcstate->color_space); + seq_printf(s, " Display mode: %dx%d%s%d\n", + mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p", + drm_mode_vrefresh(mode)); + seq_printf(s, "\tclk[%d] real_clk[%d] type[%x] flag[%x]\n", + mode->clock, mode->crtc_clock, mode->type, mode->flags); + seq_printf(s, "\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal); + seq_printf(s, "\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal); + + drm_atomic_crtc_for_each_plane(plane, crtc) { + vop2_plane_state_dump(s, plane); + } + + return 0; +} + +static int vop2_summary_show(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct drm_minor *minor = node->minor; + struct drm_device *drm_dev = minor->dev; + struct drm_crtc *crtc; + + drm_modeset_lock_all(drm_dev); + drm_for_each_crtc(crtc, drm_dev) { + vop2_crtc_state_dump(crtc, s); + } + drm_modeset_unlock_all(drm_dev); + + return 0; +} + +static void vop2_regs_print(struct vop2 *vop2, struct seq_file *s, + const struct vop2_regs_dump *dump, bool active_only) +{ + resource_size_t start; + u32 val; + int i; + + if (dump->en_mask && active_only) { + val = vop2_readl(vop2, dump->base + dump->en_reg); + if ((val & dump->en_mask) != dump->en_val) + return; + } + + seq_printf(s, "\n%s:\n", dump->name); + + start = vop2->res->start + dump->base; + for (i = 0; i < dump->size >> 2; i += 4) { + seq_printf(s, "%08x: %08x %08x %08x %08x\n", (u32)start + i * 4, + vop2_readl(vop2, dump->base + (4 * i)), + vop2_readl(vop2, dump->base + (4 * (i + 1))), + vop2_readl(vop2, dump->base + (4 * (i + 2))), + vop2_readl(vop2, dump->base + (4 * (i + 3)))); + } +} + +static void __vop2_regs_dump(struct seq_file *s, bool active_only) +{ + struct drm_info_node *node = s->private; + struct vop2 *vop2 = node->info_ent->data; + struct drm_minor *minor = node->minor; + struct drm_device *drm_dev = minor->dev; + const struct vop2_regs_dump *dump; + unsigned int i; + + drm_modeset_lock_all(drm_dev); + + regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register); + + if (vop2->enable_count) { + for (i = 0; i < vop2->data->regs_dump_size; i++) { + dump = &vop2->data->regs_dump[i]; + vop2_regs_print(vop2, s, dump, active_only); + } + } else { + seq_puts(s, "VOP disabled\n"); + } + drm_modeset_unlock_all(drm_dev); +} + +static int vop2_regs_show(struct seq_file *s, void *arg) +{ + __vop2_regs_dump(s, false); + + return 0; +} + +static int vop2_active_regs_show(struct seq_file *s, void *data) +{ + __vop2_regs_dump(s, true); + + return 0; +} + +static struct drm_info_list vop2_debugfs_list[] = { + { "summary", vop2_summary_show, 0, NULL }, + { "active_regs", vop2_active_regs_show, 0, NULL }, + { "regs", vop2_regs_show, 0, NULL }, +}; + +static void vop2_debugfs_init(struct vop2 *vop2, struct drm_minor *minor) +{ + struct dentry *root; + unsigned int i; + + root = debugfs_create_dir("vop2", minor->debugfs_root); + if (!IS_ERR(root)) { + for (i = 0; i < ARRAY_SIZE(vop2_debugfs_list); i++) + vop2_debugfs_list[i].data = vop2; + + drm_debugfs_create_files(vop2_debugfs_list, + ARRAY_SIZE(vop2_debugfs_list), + root, minor); + } +} + +static int vop2_crtc_late_register(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + if (drm_crtc_index(crtc) == 0) + vop2_debugfs_init(vop2, crtc->dev->primary); + + return 0; +} + static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc) { struct rockchip_crtc_state *vcstate; @@ -2560,6 +3079,7 @@ static const struct drm_crtc_funcs vop2_crtc_funcs = { .atomic_destroy_state = vop2_crtc_destroy_state, .enable_vblank = vop2_crtc_enable_vblank, .disable_vblank = vop2_crtc_disable_vblank, + .late_register = vop2_crtc_late_register, }; static irqreturn_t vop2_isr(int irq, void *data) @@ -2788,7 +3308,12 @@ static int vop2_create_crtcs(struct vop2 *vop2) } drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs); + if (vop2->lut_regs) { + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + drm_mode_crtc_set_gamma_size(&vp->crtc, vp_data->gamma_lut_len); + drm_crtc_enable_color_mgmt(&vp->crtc, 0, false, vp_data->gamma_lut_len); + } init_completion(&vp->dsp_hold_completion); } @@ -2863,6 +3388,10 @@ static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = { [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), @@ -2955,6 +3484,10 @@ static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = { [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), @@ -3104,6 +3637,7 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) return -EINVAL; } + vop2->res = res; vop2->regs = devm_ioremap_resource(dev, res); if (IS_ERR(vop2->regs)) return PTR_ERR(vop2->regs); |