The Amlogic AXG SoC family has a downgraded VPU with the following changes : - Only a single OSD plane, no overlay video plane - The primary plane doesn't support HW scaling - The pixels are read directly from DDR without any Canvas module - Doesn't support HDMI or CVBS - Ouputs only with ENCL encoder to a DPI-to-DSI Synopsys DW-MIPI-DSI transceiver Signed-off-by: Neil Armstrong <narmstrong@xxxxxxxxxxxx> --- drivers/gpu/drm/meson/meson_crtc.c | 8 +- drivers/gpu/drm/meson/meson_drv.c | 115 ++++++++++++++++-------- drivers/gpu/drm/meson/meson_drv.h | 10 ++- drivers/gpu/drm/meson/meson_plane.c | 74 +++++++++++++-- drivers/gpu/drm/meson/meson_registers.h | 1 + drivers/gpu/drm/meson/meson_viu.c | 50 ++++++++++- drivers/gpu/drm/meson/meson_vpp.c | 6 +- 7 files changed, 215 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index 2854272dc2d9..430599caa5a0 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -366,7 +366,13 @@ void meson_crtc_irq(struct meson_drm *priv) writel_relaxed(priv->viu.osd_sc_v_ctrl0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); - if (!priv->viu.osd1_afbcd) + /* AXG doesn't use CANVAS since it support a single plane */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) { + writel_relaxed(priv->viu.osd1_addr, + priv->io_base + _REG(VIU_OSD1_BLK1_CFG_W4)); + writel_relaxed(priv->viu.osd1_blk2_cfg4, + priv->io_base + _REG(VIU_OSD1_BLK2_CFG_W4)); + } else if (!priv->viu.osd1_afbcd) meson_canvas_config(priv->canvas, priv->canvas_id_osd1, priv->viu.osd1_addr, priv->viu.osd1_stride, diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 8b9c8dd788c4..92346653223f 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -223,6 +223,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) drm->dev_private = priv; priv->drm = drm; priv->dev = dev; + priv->data = match; priv->compat = match->compat; priv->afbcd.ops = match->afbcd_ops; @@ -255,32 +256,34 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) goto free_drm; } - priv->canvas = meson_canvas_get(dev); - if (IS_ERR(priv->canvas)) { - ret = PTR_ERR(priv->canvas); - goto free_drm; - } + if (priv->data->requires_canvas) { + priv->canvas = meson_canvas_get(dev); + if (IS_ERR(priv->canvas)) { + ret = PTR_ERR(priv->canvas); + goto free_drm; + } - ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); - if (ret) - goto free_drm; - ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); - if (ret) { - meson_canvas_free(priv->canvas, priv->canvas_id_osd1); - goto free_drm; - } - ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); - if (ret) { - meson_canvas_free(priv->canvas, priv->canvas_id_osd1); - meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); - goto free_drm; - } - ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); - if (ret) { - meson_canvas_free(priv->canvas, priv->canvas_id_osd1); - meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); - meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); - goto free_drm; + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); + if (ret) + goto free_drm; + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + goto free_drm; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + goto free_drm; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); + goto free_drm; + } } priv->vsync_irq = platform_get_irq(pdev, 0); @@ -303,8 +306,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) ret = drmm_mode_config_init(drm); if (ret) goto free_drm; - drm->mode_config.max_width = 3840; - drm->mode_config.max_height = 2160; + drm->mode_config.max_width = priv->data->max_width; + drm->mode_config.max_height = priv->data->max_height; drm->mode_config.funcs = &meson_mode_config_funcs; drm->mode_config.helper_private = &meson_mode_config_helpers; @@ -322,9 +325,11 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) /* Encoder Initialization */ - ret = meson_venc_cvbs_create(priv); - if (ret) - goto free_drm; + if (priv->data->provides_cvbs) { + ret = meson_venc_cvbs_create(priv); + if (ret) + goto free_drm; + } if (has_components) { ret = component_bind_all(drm->dev, drm); @@ -334,13 +339,17 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) } } - ret = meson_plane_create(priv); - if (ret) - goto free_drm; + if (priv->data->osd_count) { + ret = meson_plane_create(priv); + if (ret) + goto free_drm; + } - ret = meson_overlay_create(priv); - if (ret) - goto free_drm; + if (priv->data->vd_count) { + ret = meson_overlay_create(priv); + if (ret) + goto free_drm; + } ret = meson_crtc_create(priv); if (ret) @@ -516,20 +525,52 @@ static int meson_drv_probe(struct platform_device *pdev) static struct meson_drm_match_data meson_drm_gxbb_data = { .compat = VPU_COMPATIBLE_GXBB, + .requires_canvas = true, + .provides_cvbs = true, + .osd_count = 2, + .vd_count = 2, + .max_width = 3840, + .max_height = 2160, }; static struct meson_drm_match_data meson_drm_gxl_data = { .compat = VPU_COMPATIBLE_GXL, + .requires_canvas = true, + .provides_cvbs = true, + .osd_count = 2, + .vd_count = 2, + .max_width = 3840, + .max_height = 2160, }; static struct meson_drm_match_data meson_drm_gxm_data = { .compat = VPU_COMPATIBLE_GXM, .afbcd_ops = &meson_afbcd_gxm_ops, + .requires_canvas = true, + .provides_cvbs = true, + .osd_count = 2, + .vd_count = 2, + .max_width = 3840, + .max_height = 2160, +}; + +static struct meson_drm_match_data meson_drm_axg_data = { + .compat = VPU_COMPATIBLE_AXG, + .osd_count = 1, + .vd_count = 0, + .max_width = 1920, + .max_height = 1080, }; static struct meson_drm_match_data meson_drm_g12a_data = { .compat = VPU_COMPATIBLE_G12A, .afbcd_ops = &meson_afbcd_g12a_ops, + .requires_canvas = true, + .provides_cvbs = true, + .osd_count = 4, + .vd_count = 2, + .max_width = 3840, + .max_height = 2160, }; static const struct of_device_id dt_match[] = { @@ -539,6 +580,8 @@ static const struct of_device_id dt_match[] = { .data = (void *)&meson_drm_gxl_data }, { .compatible = "amlogic,meson-gxm-vpu", .data = (void *)&meson_drm_gxm_data }, + { .compatible = "amlogic,meson-axg-vpu", + .data = (void *)&meson_drm_axg_data }, { .compatible = "amlogic,meson-g12a-vpu", .data = (void *)&meson_drm_g12a_data }, {} diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h index 177dac3ca3be..5d67f97ec298 100644 --- a/drivers/gpu/drm/meson/meson_drv.h +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -22,12 +22,19 @@ enum vpu_compatible { VPU_COMPATIBLE_GXBB = 0, VPU_COMPATIBLE_GXL = 1, VPU_COMPATIBLE_GXM = 2, - VPU_COMPATIBLE_G12A = 3, + VPU_COMPATIBLE_AXG = 3, + VPU_COMPATIBLE_G12A = 4, }; struct meson_drm_match_data { enum vpu_compatible compat; struct meson_afbcd_ops *afbcd_ops; + bool requires_canvas; + bool provides_cvbs; + unsigned int osd_count; + unsigned int vd_count; + unsigned int max_width; + unsigned int max_height; }; struct meson_drm_soc_limits { @@ -52,6 +59,7 @@ struct meson_drm { struct drm_plane *primary_plane; struct drm_plane *overlay_plane; + const struct meson_drm_match_data *data; const struct meson_drm_soc_limits *limits; /* Components Data */ diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 35338ed18209..9111b3540bdf 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -93,6 +93,25 @@ static int meson_plane_atomic_check(struct drm_plane *plane, false, true); } +static int meson_plane_atomic_check_axg(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + + if (!state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + /* AXG VPU OSD plane doesn't support scaling */ + return drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); +} + #define MESON_MOD_AFBC_VALID_BITS (AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | \ AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | \ AFBC_FORMAT_MOD_YTR | \ @@ -125,6 +144,29 @@ static u32 meson_g12a_afbcd_line_stride(struct meson_drm *priv) return ((line_stride + 1) >> 1) << 1; } +static u32 meson_axg_line_stride(struct meson_drm *priv, u32 format) +{ + u32 line_stride = 0; + u32 bwidth; + + switch (format) { + case DRM_FORMAT_RGB565: + bwidth = priv->viu.osd1_stride >> 1; + line_stride = ((bwidth << 4) + 127) >> 7; + break; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + bwidth = priv->viu.osd1_stride >> 2; + line_stride = ((bwidth << 5) + 127) >> 7; + break; + } + + return ((line_stride + 1) >> 1) << 1; +} + static void meson_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { @@ -161,15 +203,20 @@ static void meson_plane_atomic_update(struct drm_plane *plane, else priv->viu.osd1_afbcd = false; - /* Enable OSD and BLK0, set max global alpha */ - priv->viu.osd1_ctrl_stat = OSD_ENABLE | - (0xFF << OSD_GLOBAL_ALPHA_SHIFT) | - OSD_BLK0_ENABLE; + priv->viu.osd1_ctrl_stat = OSD_ENABLE | OSD_BLK0_ENABLE; + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) + priv->viu.osd1_ctrl_stat |= 0x100 << OSD_GLOBAL_ALPHA_SHIFT; + else + priv->viu.osd1_ctrl_stat |= 0xFF << OSD_GLOBAL_ALPHA_SHIFT; priv->viu.osd1_ctrl_stat2 = readl(priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); - canvas_id_osd1 = priv->canvas_id_osd1; + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) + canvas_id_osd1 = 0x40; + else + canvas_id_osd1 = priv->canvas_id_osd1; /* Set up BLK0 to point to the right canvas */ priv->viu.osd1_blk0_cfg[0] = canvas_id_osd1 << OSD_CANVAS_SEL; @@ -366,7 +413,10 @@ static void meson_plane_atomic_update(struct drm_plane *plane, priv->viu.osd1_height = fb->height; priv->viu.osd1_width = fb->width; - if (priv->viu.osd1_afbcd) { + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) + priv->viu.osd1_blk2_cfg4 = meson_axg_line_stride(priv, + fb->format->format); + else if (priv->viu.osd1_afbcd) { priv->afbcd.modifier = fb->modifier; priv->afbcd.format = fb->format->format; @@ -413,6 +463,13 @@ static void meson_plane_atomic_disable(struct drm_plane *plane, priv->viu.osd1_enabled = false; } +static const struct drm_plane_helper_funcs meson_plane_helper_funcs_axg = { + .atomic_check = meson_plane_atomic_check_axg, + .atomic_disable = meson_plane_atomic_disable, + .atomic_update = meson_plane_atomic_update, + .prepare_fb = drm_gem_fb_prepare_fb, +}; + static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { .atomic_check = meson_plane_atomic_check, .atomic_disable = meson_plane_atomic_disable, @@ -550,7 +607,10 @@ int meson_plane_create(struct meson_drm *priv) format_modifiers, DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane"); - drm_plane_helper_add(plane, &meson_plane_helper_funcs); + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) + drm_plane_helper_add(plane, &meson_plane_helper_funcs_axg); + else + drm_plane_helper_add(plane, &meson_plane_helper_funcs); /* For now, OSD Primary plane is always on the front */ drm_plane_create_zpos_immutable_property(plane, 1); diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h index 446e7961da48..18396b59e6cb 100644 --- a/drivers/gpu/drm/meson/meson_registers.h +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -588,6 +588,7 @@ #define VPP_OSD_SCALE_COEF_IDX 0x1dcc #define VPP_OSD_SCALE_COEF 0x1dcd #define VPP_INT_LINE_NUM 0x1dce +#define VPP_MATRIX_CLIP 0x1dde #define VPP_WRAP_OSD1_MATRIX_COEF00_01 0x3d60 #define VPP_WRAP_OSD1_MATRIX_COEF02_10 0x3d61 diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c index aede0c67a57f..9b644e598211 100644 --- a/drivers/gpu/drm/meson/meson_viu.c +++ b/drivers/gpu/drm/meson/meson_viu.c @@ -423,19 +423,63 @@ void meson_viu_init(struct meson_drm *priv) /* On GXL/GXM, Use the 10bit HDR conversion matrix */ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || - meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) meson_viu_load_matrix(priv); else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff, true); + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) { + writel_bits_relaxed(BIT(0), BIT(0), + priv->io_base + _REG(VPP_MATRIX_CTRL)); + writel_bits_relaxed(0x3 << 8, 0, + priv->io_base + _REG(VPP_MATRIX_CTRL)); + + writel_relaxed(0x0fc00e00, + priv->io_base + _REG(VPP_MATRIX_PRE_OFFSET0_1)); + writel_relaxed(0x00000e00, + priv->io_base + _REG(VPP_MATRIX_PRE_OFFSET2)); + + /* + * ycbcr limit range, 709 to RGB + * -16 1.164 0 1.793 0 + * -128 1.164 -0.213 -0.534 0 + * -128 1.164 2.115 0 0 + */ + writel_relaxed(0x04a80000, + priv->io_base + _REG(VPP_MATRIX_COEF00_01)); + writel_relaxed(0x072c04a8, + priv->io_base + _REG(VPP_MATRIX_COEF02_10)); + writel_relaxed(0x1f261ddd, + priv->io_base + _REG(VPP_MATRIX_COEF11_12)); + writel_relaxed(0x04a80876, + priv->io_base + _REG(VPP_MATRIX_COEF20_21)); + writel_relaxed(0x0, priv->io_base + _REG(VPP_MATRIX_COEF22)); + writel_relaxed(0x0, priv->io_base + _REG(VPP_MATRIX_OFFSET0_1)); + writel_relaxed(0x0, priv->io_base + _REG(VPP_MATRIX_OFFSET2)); + + writel_bits_relaxed(0x1f << 3, 0, + priv->io_base + _REG(VPP_MATRIX_CLIP)); + } + /* Initialize OSD1 fifo control register */ reg = VIU_OSD_DDR_PRIORITY_URGENT | - VIU_OSD_HOLD_FIFO_LINES(31) | - VIU_OSD_FIFO_DEPTH_VAL(32) | /* fifo_depth_val: 32*8=256 */ VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */ VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) + reg |= VIU_OSD_HOLD_FIFO_LINES(24); + else + reg |= VIU_OSD_HOLD_FIFO_LINES(31); + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) + reg |= VIU_OSD_FIFO_DEPTH_VAL(32); /* fifo_depth_val: 32*8=256 */ + else + reg |= VIU_OSD_FIFO_DEPTH_VAL(64); /* fifo_depth_val: 64*8=512 */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) reg |= VIU_OSD_BURST_LENGTH_32; else diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c index 154837688ab0..069f527d42c6 100644 --- a/drivers/gpu/drm/meson/meson_vpp.c +++ b/drivers/gpu/drm/meson/meson_vpp.c @@ -91,7 +91,8 @@ static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_drm *priv, void meson_vpp_init(struct meson_drm *priv) { /* set dummy data default YUV black */ - if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1)); else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) { writel_bits_relaxed(0xff << 16, 0xff << 16, @@ -107,6 +108,9 @@ void meson_vpp_init(struct meson_drm *priv) if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) writel_relaxed(VPP_OFIFO_SIZE_DEFAULT, priv->io_base + _REG(VPP_OFIFO_SIZE)); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_AXG)) + writel_bits_relaxed(VPP_OFIFO_SIZE_MASK, 0x400, + priv->io_base + _REG(VPP_OFIFO_SIZE)); else writel_bits_relaxed(VPP_OFIFO_SIZE_MASK, 0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE)); -- 2.22.0 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel