Re: [PATCH v2 08/10] drm/ingenic: Add support for OSD mode
From: Sam Ravnborg
Date: Tue Jun 30 2020 - 08:05:27 EST
Hi Paul.
On Tue, Jun 30, 2020 at 01:52:08AM +0200, Paul Cercueil wrote:
> All Ingenic SoCs starting from the JZ4725B support OSD mode.
>
> In this mode, two separate planes can be used. They can have different
> positions and sizes, and one can be overlayed on top of the other.
>
> Signed-off-by: Paul Cercueil <paul@xxxxxxxxxxxxxxx>
OSD? On screen Display?
Some random comments in the following - from my attempt to follow the
code.
Sam
> ---
>
> Notes:
> v2: Use fallthrough; instead of /* fall-through */
>
> drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 271 +++++++++++++++++-----
> drivers/gpu/drm/ingenic/ingenic-drm.h | 35 +++
> 2 files changed, 254 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> index 6590b61cb915..a8573da1d577 100644
> --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> @@ -43,12 +43,13 @@ struct ingenic_dma_hwdesc {
>
> struct jz_soc_info {
> bool needs_dev_clk;
> + bool has_osd;
> unsigned int max_width, max_height;
> };
>
> struct ingenic_drm {
> struct drm_device drm;
> - struct drm_plane primary;
> + struct drm_plane f0, f1;
A small comment about when f0 and f1 is used and how they are the
primary plane would be good here.
> struct drm_crtc crtc;
> struct drm_encoder encoder;
>
> @@ -57,8 +58,8 @@ struct ingenic_drm {
> struct clk *lcd_clk, *pix_clk;
> const struct jz_soc_info *soc_info;
>
> - struct ingenic_dma_hwdesc *dma_hwdesc;
> - dma_addr_t dma_hwdesc_phys;
> + struct ingenic_dma_hwdesc *dma_hwdesc[2];
> + dma_addr_t dma_hwdesc_phys[2];
>
> bool panel_is_sharp;
> };
> @@ -90,7 +91,7 @@ static const struct regmap_config ingenic_drm_regmap_config = {
> .val_bits = 32,
> .reg_stride = 4,
>
> - .max_register = JZ_REG_LCD_CMD1,
> + .max_register = JZ_REG_LCD_SIZE1,
> .writeable_reg = ingenic_drm_writeable_reg,
> };
>
> @@ -110,11 +111,6 @@ drm_encoder_get_priv(struct drm_encoder *encoder)
> return container_of(encoder, struct ingenic_drm, encoder);
> }
>
> -static inline struct ingenic_drm *drm_plane_get_priv(struct drm_plane *plane)
> -{
> - return container_of(plane, struct ingenic_drm, primary);
> -}
> -
> static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
> struct drm_crtc_state *state)
> {
> @@ -185,34 +181,17 @@ static void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv,
> regmap_write(priv->map, JZ_REG_LCD_SPL, hpe << 16 | (hpe + 1));
> regmap_write(priv->map, JZ_REG_LCD_REV, mode->htotal << 16);
> }
> -}
> -
> -static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv,
> - const struct drm_format_info *finfo)
> -{
> - unsigned int ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16;
> -
> - switch (finfo->format) {
> - case DRM_FORMAT_XRGB1555:
> - ctrl |= JZ_LCD_CTRL_RGB555;
> - /* fall-through */
> - case DRM_FORMAT_RGB565:
> - ctrl |= JZ_LCD_CTRL_BPP_15_16;
> - break;
> - case DRM_FORMAT_XRGB8888:
> - ctrl |= JZ_LCD_CTRL_BPP_18_24;
> - break;
> - }
>
> regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
> - JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16 |
> - JZ_LCD_CTRL_BPP_MASK, ctrl);
> + JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16,
> + JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16);
It looks like regmap_update_bits() is no longer the best choice here.
> }
>
> static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
> struct drm_crtc_state *state)
> {
> struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
> + struct drm_plane_state *f1_state, *f0_state;
> long rate;
>
> if (!drm_atomic_crtc_needs_modeset(state))
> @@ -227,6 +206,15 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
> if (rate < 0)
> return rate;
>
> + if (priv->soc_info->has_osd) {
> + f1_state = drm_atomic_get_plane_state(state->state, &priv->f1);
> + f0_state = drm_atomic_get_plane_state(state->state, &priv->f0);
> +
> + /* If all the planes are disabled, we won't get a VBLANK IRQ */
> + if (!f1_state->fb && !f0_state->fb)
> + state->no_vblank = true;
> + }
> +
> return 0;
> }
>
> @@ -236,14 +224,9 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
> struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
> struct drm_crtc_state *state = crtc->state;
> struct drm_pending_vblank_event *event = state->event;
> - struct drm_framebuffer *drm_fb = crtc->primary->state->fb;
> - const struct drm_format_info *finfo;
>
> if (drm_atomic_crtc_needs_modeset(state)) {
> - finfo = drm_format_info(drm_fb->format->format);
> -
> ingenic_drm_crtc_update_timings(priv, &state->mode);
> - ingenic_drm_crtc_update_ctrl(priv, finfo);
>
> clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000);
> }
> @@ -260,12 +243,149 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
> }
> }
>
> +static int ingenic_drm_plane_atomic_check(struct drm_plane *plane,
> + struct drm_plane_state *state)
> +{
> + struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
> + struct drm_crtc_state *crtc_state;
> + struct drm_crtc *crtc = state->crtc ?: plane->state->crtc;
> + int ret;
> +
> + if (!crtc)
> + return 0;
> +
> + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
> + if (WARN_ON(!crtc_state))
> + return -EINVAL;
> +
> + ret = drm_atomic_helper_check_plane_state(state, crtc_state,
> + DRM_PLANE_HELPER_NO_SCALING,
> + DRM_PLANE_HELPER_NO_SCALING,
> + priv->soc_info->has_osd,
> + true);
> + if (ret)
> + return ret;
> +
> + if (!priv->soc_info->has_osd &&
> + (state->src_x != 0 ||
> + (state->src_w >> 16) != state->crtc_w ||
> + (state->src_h >> 16) != state->crtc_h))
> + return -EINVAL;
This check could bebefit from a small comment.
I cannot see the purpose of " >> 16" on the src_* fields...
> +
> + /*
> + * Require full modeset if if enabling or disabling a plane, or changing
Too many ifs?
> + * its position, size or depth.
> + */
> + if (priv->soc_info->has_osd &&
> + (!plane->state->fb || !state->fb ||
> + plane->state->crtc_x != state->crtc_x ||
> + plane->state->crtc_y != state->crtc_y ||
> + plane->state->crtc_w != state->crtc_w ||
> + plane->state->crtc_h != state->crtc_h ||
> + plane->state->fb->format->format != state->fb->format->format))
> + crtc_state->mode_changed = true;
> +
> + return 0;
> +}
> +
> +static void ingenic_drm_plane_enable(struct ingenic_drm *priv,
> + struct drm_plane *plane)
> +{
> + unsigned int en_bit;
> +
> + if (priv->soc_info->has_osd) {
> + if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> + en_bit = JZ_LCD_OSDC_F1EN;
> + else
> + en_bit = JZ_LCD_OSDC_F0EN;
> +
> + regmap_update_bits(priv->map, JZ_REG_LCD_OSDC, en_bit, en_bit);
I think you can use a more direct way to do the assignment.
Like before where the same pattern was used (last two arguments the
same).
> + }
> +}
> +
> +static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
> + unsigned int en_bit;
> +
> + if (priv->soc_info->has_osd) {
> + if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> + en_bit = JZ_LCD_OSDC_F1EN;
> + else
> + en_bit = JZ_LCD_OSDC_F0EN;
> +
> + regmap_update_bits(priv->map, JZ_REG_LCD_OSDC, en_bit, 0);
> + }
> +}
> +
> +static void ingenic_drm_plane_config(struct ingenic_drm *priv,
> + struct drm_plane *plane, u32 fourcc)
> +{
> + struct drm_plane_state *state = plane->state;
> + unsigned int xy_reg, size_reg;
> + unsigned int ctrl = 0;
> +
> + ingenic_drm_plane_enable(priv, plane);
> +
> + if (priv->soc_info->has_osd &&
> + plane->type == DRM_PLANE_TYPE_PRIMARY) {
> + switch (fourcc) {
> + case DRM_FORMAT_XRGB1555:
> + ctrl |= JZ_LCD_OSDCTRL_RGB555;
> + fallthrough;
> + case DRM_FORMAT_RGB565:
> + ctrl |= JZ_LCD_OSDCTRL_BPP_15_16;
> + break;
> + case DRM_FORMAT_XRGB8888:
> + ctrl |= JZ_LCD_OSDCTRL_BPP_18_24;
> + break;
> + }
> +
> + regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL,
> + JZ_LCD_OSDCTRL_BPP_MASK, ctrl);
> + } else {
> + switch (fourcc) {
> + case DRM_FORMAT_XRGB1555:
> + ctrl |= JZ_LCD_CTRL_RGB555;
> + fallthrough;
> + case DRM_FORMAT_RGB565:
> + ctrl |= JZ_LCD_CTRL_BPP_15_16;
> + break;
> + case DRM_FORMAT_XRGB8888:
> + ctrl |= JZ_LCD_CTRL_BPP_18_24;
> + break;
> + }
> +
> + regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
> + JZ_LCD_CTRL_BPP_MASK, ctrl);
> + }
> +
> + if (priv->soc_info->has_osd) {
> + if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> + xy_reg = JZ_REG_LCD_XYP1;
> + size_reg = JZ_REG_LCD_SIZE1;
> + } else {
> + xy_reg = JZ_REG_LCD_XYP0;
> + size_reg = JZ_REG_LCD_SIZE0;
> + }
> +
> + regmap_write(priv->map, xy_reg,
> + state->crtc_x << JZ_LCD_XYP01_XPOS_LSB |
> + state->crtc_y << JZ_LCD_XYP01_YPOS_LSB);
> + regmap_write(priv->map, size_reg,
> + state->crtc_w << JZ_LCD_SIZE01_WIDTH_LSB |
> + state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB);
> + }
> +}
> +
> static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
> struct drm_plane_state *oldstate)
> {
> - struct ingenic_drm *priv = drm_plane_get_priv(plane);
> + struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
> struct drm_plane_state *state = plane->state;
> unsigned int width, height, cpp;
> + unsigned int hwdesc_idx;
> dma_addr_t addr;
>
> if (state && state->fb) {
> @@ -274,9 +394,18 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
> height = state->src_h >> 16;
> cpp = state->fb->format->cpp[0];
>
> - priv->dma_hwdesc->addr = addr;
> - priv->dma_hwdesc->cmd = width * height * cpp / 4;
> - priv->dma_hwdesc->cmd |= JZ_LCD_CMD_EOF_IRQ;
> + if (!priv->soc_info->has_osd)
> + hwdesc_idx = 0;
> + else
> + hwdesc_idx = plane->type == DRM_PLANE_TYPE_PRIMARY;
This looks ugly.
"plane->type == DRM_PLANE_TYPE_PRIMARY" evaluates to a boolean.
This is then ocnverted to an int used as index to an array later.
Something a bit more explicit would be more readable.
> +
> + priv->dma_hwdesc[hwdesc_idx]->addr = addr;
> + priv->dma_hwdesc[hwdesc_idx]->cmd = width * height * cpp / 4;
> + priv->dma_hwdesc[hwdesc_idx]->cmd |= JZ_LCD_CMD_EOF_IRQ;
> +
> + if (drm_atomic_crtc_needs_modeset(state->crtc->state))
> + ingenic_drm_plane_config(priv, plane,
> + state->fb->format->format);
> }
> }
>
> @@ -437,6 +566,8 @@ static const struct drm_crtc_funcs ingenic_drm_crtc_funcs = {
>
> static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = {
> .atomic_update = ingenic_drm_plane_atomic_update,
> + .atomic_check = ingenic_drm_plane_atomic_check,
> + .atomic_disable = ingenic_drm_plane_atomic_disable,
> .prepare_fb = drm_gem_fb_prepare_fb,
> };
>
> @@ -463,8 +594,10 @@ static void ingenic_drm_free_dma_hwdesc(void *d)
> {
> struct ingenic_drm *priv = d;
>
> - dma_free_coherent(priv->dev, sizeof(*priv->dma_hwdesc),
> - priv->dma_hwdesc, priv->dma_hwdesc_phys);
> + dma_free_coherent(priv->dev, sizeof(*priv->dma_hwdesc[0]),
> + priv->dma_hwdesc[0], priv->dma_hwdesc_phys[0]);
> + dma_free_coherent(priv->dev, sizeof(*priv->dma_hwdesc[1]),
> + priv->dma_hwdesc[1], priv->dma_hwdesc_phys[1]);
> }
>
> static int ingenic_drm_probe(struct platform_device *pdev)
> @@ -549,23 +682,32 @@ static int ingenic_drm_probe(struct platform_device *pdev)
> bridge = devm_drm_panel_bridge_add_typed(dev, panel,
> DRM_MODE_CONNECTOR_DPI);
>
> - priv->dma_hwdesc = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc),
> - &priv->dma_hwdesc_phys,
> - GFP_KERNEL);
> - if (!priv->dma_hwdesc)
> + priv->dma_hwdesc[0] = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc[0]),
> + &priv->dma_hwdesc_phys[0],
> + GFP_KERNEL);
> + if (!priv->dma_hwdesc[0])
> + return -ENOMEM;
> +
> + priv->dma_hwdesc[0]->next = priv->dma_hwdesc_phys[0];
> + priv->dma_hwdesc[0]->id = 0xdeafbead;
> +
> + priv->dma_hwdesc[1] = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc[1]),
> + &priv->dma_hwdesc_phys[1],
> + GFP_KERNEL);
> + if (!priv->dma_hwdesc[1])
> return -ENOMEM;
Here you should undo the first allocation??
I think the code could benefit from using dmam_alloc_coherent().
>
> + priv->dma_hwdesc[1]->next = priv->dma_hwdesc_phys[1];
> + priv->dma_hwdesc[1]->id = 0xdeadbabe;
> +
> ret = devm_add_action_or_reset(dev, ingenic_drm_free_dma_hwdesc, priv);
> if (ret)
> return ret;
>
> - priv->dma_hwdesc->next = priv->dma_hwdesc_phys;
> - priv->dma_hwdesc->id = 0xdeafbead;
> -
> - drm_plane_helper_add(&priv->primary, &ingenic_drm_plane_helper_funcs);
> + drm_plane_helper_add(&priv->f1, &ingenic_drm_plane_helper_funcs);
>
> - ret = drm_universal_plane_init(drm, &priv->primary,
> - 0, &ingenic_drm_primary_plane_funcs,
> + ret = drm_universal_plane_init(drm, &priv->f1, 1,
> + &ingenic_drm_primary_plane_funcs,
> ingenic_drm_primary_formats,
> ARRAY_SIZE(ingenic_drm_primary_formats),
> NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
> @@ -576,13 +718,30 @@ static int ingenic_drm_probe(struct platform_device *pdev)
>
> drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs);
>
> - ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->primary,
> + ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->f1,
> NULL, &ingenic_drm_crtc_funcs, NULL);
> if (ret) {
> dev_err(dev, "Failed to init CRTC: %i\n", ret);
> return ret;
> }
>
> + if (soc_info->has_osd) {
> + drm_plane_helper_add(&priv->f0,
> + &ingenic_drm_plane_helper_funcs);
> +
> + ret = drm_universal_plane_init(drm, &priv->f0, 1,
> + &ingenic_drm_primary_plane_funcs,
> + ingenic_drm_primary_formats,
> + ARRAY_SIZE(ingenic_drm_primary_formats),
> + NULL, DRM_PLANE_TYPE_OVERLAY,
> + NULL);
> + if (ret) {
> + dev_err(dev, "Failed to register overlay plane: %i\n",
> + ret);
> + return ret;
> + }
> + }
> +
> priv->encoder.possible_crtcs = 1;
>
> drm_encoder_helper_add(&priv->encoder,
> @@ -644,7 +803,12 @@ static int ingenic_drm_probe(struct platform_device *pdev)
> }
>
> /* Set address of our DMA descriptor chain */
> - regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys);
> + regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys[0]);
> + regmap_write(priv->map, JZ_REG_LCD_DA1, priv->dma_hwdesc_phys[1]);
> +
> + /* Enable OSD if available */
> + if (soc_info->has_osd)
> + regmap_write(priv->map, JZ_REG_LCD_OSDC, JZ_LCD_OSDC_OSDEN);
>
> ret = drm_dev_register(drm, 0);
> if (ret) {
> @@ -680,18 +844,21 @@ static int ingenic_drm_remove(struct platform_device *pdev)
>
> static const struct jz_soc_info jz4740_soc_info = {
> .needs_dev_clk = true,
> + .has_osd = false,
> .max_width = 800,
> .max_height = 600,
> };
>
> static const struct jz_soc_info jz4725b_soc_info = {
> .needs_dev_clk = false,
> + .has_osd = true,
> .max_width = 800,
> .max_height = 600,
> };
>
> static const struct jz_soc_info jz4770_soc_info = {
> .needs_dev_clk = false,
> + .has_osd = true,
> .max_width = 1280,
> .max_height = 720,
> };
> diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.h b/drivers/gpu/drm/ingenic/ingenic-drm.h
> index cb578cff7bb1..d0b827a9fe83 100644
> --- a/drivers/gpu/drm/ingenic/ingenic-drm.h
> +++ b/drivers/gpu/drm/ingenic/ingenic-drm.h
> @@ -30,6 +30,18 @@
> #define JZ_REG_LCD_SA1 0x54
> #define JZ_REG_LCD_FID1 0x58
> #define JZ_REG_LCD_CMD1 0x5C
> +#define JZ_REG_LCD_OSDC 0x100
> +#define JZ_REG_LCD_OSDCTRL 0x104
> +#define JZ_REG_LCD_OSDS 0x108
> +#define JZ_REG_LCD_BGC 0x10c
> +#define JZ_REG_LCD_KEY0 0x110
> +#define JZ_REG_LCD_KEY1 0x114
> +#define JZ_REG_LCD_ALPHA 0x118
> +#define JZ_REG_LCD_IPUR 0x11c
> +#define JZ_REG_LCD_XYP0 0x120
> +#define JZ_REG_LCD_XYP1 0x124
> +#define JZ_REG_LCD_SIZE0 0x128
> +#define JZ_REG_LCD_SIZE1 0x12c
>
> #define JZ_LCD_CFG_SLCD BIT(31)
> #define JZ_LCD_CFG_PS_DISABLE BIT(23)
> @@ -123,4 +135,27 @@
> #define JZ_LCD_STATE_SOF_IRQ BIT(4)
> #define JZ_LCD_STATE_DISABLED BIT(0)
>
> +#define JZ_LCD_OSDC_OSDEN BIT(0)
> +#define JZ_LCD_OSDC_F0EN BIT(3)
> +#define JZ_LCD_OSDC_F1EN BIT(4)
> +
> +#define JZ_LCD_OSDCTRL_IPU BIT(15)
> +#define JZ_LCD_OSDCTRL_RGB555 BIT(4)
> +#define JZ_LCD_OSDCTRL_CHANGE BIT(3)
> +#define JZ_LCD_OSDCTRL_BPP_15_16 0x4
> +#define JZ_LCD_OSDCTRL_BPP_18_24 0x5
> +#define JZ_LCD_OSDCTRL_BPP_30 0x7
> +#define JZ_LCD_OSDCTRL_BPP_MASK (JZ_LCD_OSDCTRL_RGB555 | 0x7)
> +
> +#define JZ_LCD_OSDS_READY BIT(0)
> +
> +#define JZ_LCD_IPUR_IPUREN BIT(31)
> +#define JZ_LCD_IPUR_IPUR_LSB 0
> +
> +#define JZ_LCD_XYP01_XPOS_LSB 0
> +#define JZ_LCD_XYP01_YPOS_LSB 16
> +
> +#define JZ_LCD_SIZE01_WIDTH_LSB 0
> +#define JZ_LCD_SIZE01_HEIGHT_LSB 16
> +
> #endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */
> --
> 2.27.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@xxxxxxxxxxxxxxxxxxxxx
> https://lists.freedesktop.org/mailman/listinfo/dri-devel