drm/imx: ipuv3-plane: add support for separate alpha planes
authorPhilipp Zabel <p.zabel@pengutronix.de>
Fri, 22 Jul 2016 10:02:45 +0000 (12:02 +0200)
committerPhilipp Zabel <p.zabel@pengutronix.de>
Wed, 15 Mar 2017 14:42:33 +0000 (15:42 +0100)
The IPUv3 can read 8-bit alpha values from a separate plane buffer using
a companion IDMAC channel driven by the Alpha Transparency Controller
(ATC) for the graphics channels. The conditional read mechanism allows
to reduce memory bandwidth by skipping reads of color data for
completely transparent bursts.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
drivers/gpu/drm/imx/ipuv3-plane.c
drivers/gpu/drm/imx/ipuv3-plane.h

index 53eceff09d1790d569b5870f08694c98a33dbf46..cecb8eba5c320b42a11c797f64e26be5ab93e146 100644 (file)
@@ -57,6 +57,12 @@ static const uint32_t ipu_plane_formats[] = {
        DRM_FORMAT_NV12,
        DRM_FORMAT_NV16,
        DRM_FORMAT_RGB565,
+       DRM_FORMAT_RGB565_A8,
+       DRM_FORMAT_BGR565_A8,
+       DRM_FORMAT_RGB888_A8,
+       DRM_FORMAT_BGR888_A8,
+       DRM_FORMAT_RGBX8888_A8,
+       DRM_FORMAT_BGRX8888_A8,
 };
 
 int ipu_plane_irq(struct ipu_plane *ipu_plane)
@@ -126,11 +132,14 @@ void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
                ipu_dmfc_put(ipu_plane->dmfc);
        if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
                ipu_idmac_put(ipu_plane->ipu_ch);
+       if (!IS_ERR_OR_NULL(ipu_plane->alpha_ch))
+               ipu_idmac_put(ipu_plane->alpha_ch);
 }
 
 int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
 {
        int ret;
+       int alpha_ch;
 
        ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
        if (IS_ERR(ipu_plane->ipu_ch)) {
@@ -139,6 +148,17 @@ int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
                return ret;
        }
 
+       alpha_ch = ipu_channel_alpha_channel(ipu_plane->dma);
+       if (alpha_ch >= 0) {
+               ipu_plane->alpha_ch = ipu_idmac_get(ipu_plane->ipu, alpha_ch);
+               if (IS_ERR(ipu_plane->alpha_ch)) {
+                       ret = PTR_ERR(ipu_plane->alpha_ch);
+                       DRM_ERROR("failed to get alpha idmac channel %d: %d\n",
+                                 alpha_ch, ret);
+                       return ret;
+               }
+       }
+
        ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
        if (IS_ERR(ipu_plane->dmfc)) {
                ret = PTR_ERR(ipu_plane->dmfc);
@@ -162,12 +182,29 @@ err_out:
        return ret;
 }
 
+static bool ipu_plane_separate_alpha(struct ipu_plane *ipu_plane)
+{
+       switch (ipu_plane->base.state->fb->format->format) {
+       case DRM_FORMAT_RGB565_A8:
+       case DRM_FORMAT_BGR565_A8:
+       case DRM_FORMAT_RGB888_A8:
+       case DRM_FORMAT_BGR888_A8:
+       case DRM_FORMAT_RGBX8888_A8:
+       case DRM_FORMAT_BGRX8888_A8:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static void ipu_plane_enable(struct ipu_plane *ipu_plane)
 {
        if (ipu_plane->dp)
                ipu_dp_enable(ipu_plane->ipu);
        ipu_dmfc_enable_channel(ipu_plane->dmfc);
        ipu_idmac_enable_channel(ipu_plane->ipu_ch);
+       if (ipu_plane_separate_alpha(ipu_plane))
+               ipu_idmac_enable_channel(ipu_plane->alpha_ch);
        if (ipu_plane->dp)
                ipu_dp_enable_channel(ipu_plane->dp);
 }
@@ -181,6 +218,8 @@ void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel)
        if (ipu_plane->dp && disable_dp_channel)
                ipu_dp_disable_channel(ipu_plane->dp, false);
        ipu_idmac_disable_channel(ipu_plane->ipu_ch);
+       if (ipu_plane->alpha_ch)
+               ipu_idmac_disable_channel(ipu_plane->alpha_ch);
        ipu_dmfc_disable_channel(ipu_plane->dmfc);
        if (ipu_plane->dp)
                ipu_dp_disable(ipu_plane->ipu);
@@ -224,7 +263,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
        struct device *dev = plane->dev->dev;
        struct drm_framebuffer *fb = state->fb;
        struct drm_framebuffer *old_fb = old_state->fb;
-       unsigned long eba, ubo, vbo, old_ubo, old_vbo;
+       unsigned long eba, ubo, vbo, old_ubo, old_vbo, alpha_eba;
        bool can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY);
        struct drm_rect clip;
        int hsub, vsub;
@@ -355,6 +394,23 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
                if (((state->src.x1 >> 16) & (hsub - 1)) ||
                    ((state->src.y1 >> 16) & (vsub - 1)))
                        return -EINVAL;
+               break;
+       case DRM_FORMAT_RGB565_A8:
+       case DRM_FORMAT_BGR565_A8:
+       case DRM_FORMAT_RGB888_A8:
+       case DRM_FORMAT_BGR888_A8:
+       case DRM_FORMAT_RGBX8888_A8:
+       case DRM_FORMAT_BGRX8888_A8:
+               alpha_eba = drm_plane_state_to_eba(state, 1);
+               if (alpha_eba & 0x7)
+                       return -EINVAL;
+
+               if (fb->pitches[1] < 1 || fb->pitches[1] > 16384)
+                       return -EINVAL;
+
+               if (old_fb && old_fb->pitches[1] != fb->pitches[1])
+                       crtc_state->mode_changed = true;
+               break;
        }
 
        return 0;
@@ -379,6 +435,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
        struct drm_framebuffer *fb = state->fb;
        struct drm_rect *dst = &state->dst;
        unsigned long eba, ubo, vbo;
+       unsigned long alpha_eba = 0;
        enum ipu_color_space ics;
        int active;
 
@@ -391,6 +448,12 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
                active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
                ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
+               if (ipu_plane_separate_alpha(ipu_plane)) {
+                       active = ipu_idmac_get_current_buffer(ipu_plane->alpha_ch);
+                       ipu_cpmem_set_buffer(ipu_plane->alpha_ch, !active,
+                                            alpha_eba);
+                       ipu_idmac_select_buffer(ipu_plane->alpha_ch, !active);
+               }
                return;
        }
 
@@ -416,6 +479,12 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
                case DRM_FORMAT_ABGR8888:
                case DRM_FORMAT_RGBA8888:
                case DRM_FORMAT_BGRA8888:
+               case DRM_FORMAT_RGB565_A8:
+               case DRM_FORMAT_BGR565_A8:
+               case DRM_FORMAT_RGB888_A8:
+               case DRM_FORMAT_BGR888_A8:
+               case DRM_FORMAT_RGBX8888_A8:
+               case DRM_FORMAT_BGRX8888_A8:
                        ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
                        break;
                default:
@@ -466,6 +535,32 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
                        "phy = %lu %lu, x = %d, y = %d", eba, ubo,
                        state->src.x1 >> 16, state->src.y1 >> 16);
                break;
+       case DRM_FORMAT_RGB565_A8:
+       case DRM_FORMAT_BGR565_A8:
+       case DRM_FORMAT_RGB888_A8:
+       case DRM_FORMAT_BGR888_A8:
+       case DRM_FORMAT_RGBX8888_A8:
+       case DRM_FORMAT_BGRX8888_A8:
+               alpha_eba = drm_plane_state_to_eba(state, 1);
+
+               dev_dbg(ipu_plane->base.dev->dev, "phys = %lu %lu, x = %d, y = %d",
+                       eba, alpha_eba, state->src.x1 >> 16, state->src.y1 >> 16);
+
+               ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16);
+
+               ipu_cpmem_zero(ipu_plane->alpha_ch);
+               ipu_cpmem_set_resolution(ipu_plane->alpha_ch,
+                                        drm_rect_width(&state->src) >> 16,
+                                        drm_rect_height(&state->src) >> 16);
+               ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8);
+               ipu_cpmem_set_high_priority(ipu_plane->alpha_ch);
+               ipu_idmac_set_double_buffer(ipu_plane->alpha_ch, 1);
+               ipu_cpmem_set_stride(ipu_plane->alpha_ch,
+                                    state->fb->pitches[1]);
+               ipu_cpmem_set_burstsize(ipu_plane->alpha_ch, 16);
+               ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 0, alpha_eba);
+               ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 1, alpha_eba);
+               break;
        default:
                dev_dbg(ipu_plane->base.dev->dev, "phys = %lu, x = %d, y = %d",
                        eba, state->src.x1 >> 16, state->src.y1 >> 16);
index 0e2a723ff9816d32bb8d1ccf6b2c4e9d591d9b9d..596b24ddbf657001472eae416abba3b38f064307 100644 (file)
@@ -18,6 +18,7 @@ struct ipu_plane {
 
        struct ipu_soc          *ipu;
        struct ipuv3_channel    *ipu_ch;
+       struct ipuv3_channel    *alpha_ch;
        struct dmfc_channel     *dmfc;
        struct ipu_dp           *dp;