[PATCH 3/6] drm/meson: add support for VPU found in AXG SoCs

From: Neil Armstrong
Date: Mon Sep 07 2020 - 04:19:16 EST


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