Following the DRM assumption, VKMS currently assumes that the alpha is
pre-multiplied. Moreover, it doesn't support the alpha property.

So, first, implement the alpha property to VKMS and then, the blend
mode property. In order to support all possible supported modes,
change the pre_mul_blend_channel() function to check the plane blend
mode and apply the correct blend formula, following the DRM
convention, using the proper plane alpha value.

Tested with igt@kms_plane_alpha_blend.

Signed-off-by: Maíra Canal <[email protected]>
---
 drivers/gpu/drm/vkms/vkms_composer.c | 50 ++++++++++++++++++----------
 drivers/gpu/drm/vkms/vkms_drv.h      |  2 ++
 drivers/gpu/drm/vkms/vkms_plane.c    | 10 ++++++
 3 files changed, 44 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index 80164e79af00..bc80ea2a1ab0 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -4,6 +4,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_vblank.h>
@@ -11,33 +12,47 @@
 
 #include "vkms_drv.h"
 
-static u16 pre_mul_blend_channel(u16 src, u16 dst, u16 alpha)
+static u16 blend_channel(struct vkms_frame_info *frame_info, u16 src, u16 dst, 
u16 alpha)
 {
        u32 new_color;
 
-       new_color = (src * 0xffff + dst * (0xffff - alpha));
+       switch (frame_info->pixel_blend_mode) {
+       case DRM_MODE_BLEND_PIXEL_NONE:
+               new_color = 0xffff * frame_info->alpha * src
+                       + (0xfffe0001 - 0xffff * frame_info->alpha) * dst;
+               break;
+       case DRM_MODE_BLEND_COVERAGE:
+               new_color = alpha * frame_info->alpha * src
+                       + (0xfffe0001 - alpha * frame_info->alpha) * dst;
+               break;
+       case DRM_MODE_BLEND_PREMULTI:
+       default:
+               new_color = 0xffff * frame_info->alpha * src
+                       + (0xfffe0001 - alpha * frame_info->alpha) * dst;
+               break;
+       }
 
-       return DIV_ROUND_CLOSEST(new_color, 0xffff);
+       return DIV_ROUND_CLOSEST(new_color, 0xfffe0001);
 }
 
 /**
- * pre_mul_alpha_blend - alpha blending equation
+ * alpha_blend - alpha blending equation
  * @src_frame_info: source framebuffer's metadata
  * @stage_buffer: The line with the pixels from src_plane
  * @output_buffer: A line buffer that receives all the blends output
  *
  * Using the information from the `frame_info`, this blends only the
  * necessary pixels from the `stage_buffer` to the `output_buffer`
- * using premultiplied blend formula.
+ * using the adequate blend formula depending on the plane blend mode
+ * (see blend_channel()).
  *
- * The current DRM assumption is that pixel color values have been already
- * pre-multiplied with the alpha channel values. See more
- * drm_plane_create_blend_mode_property(). Also, this formula assumes a
- * completely opaque background.
+ * By default, the current DRM assumption is that pixel color values have
+ * been already pre-multiplied with the alpha channel values. See more
+ * drm_plane_create_blend_mode_property().
  */
-static void pre_mul_alpha_blend(struct vkms_frame_info *frame_info,
-                               struct line_buffer *stage_buffer,
-                               struct line_buffer *output_buffer)
+static void alpha_blend(struct vkms_frame_info *frame_info,
+                       struct line_buffer *stage_buffer,
+                       struct line_buffer *output_buffer)
 {
        int x_dst = frame_info->dst.x1;
        struct pixel_argb_u16 *out = output_buffer->pixels + x_dst;
@@ -47,9 +62,9 @@ static void pre_mul_alpha_blend(struct vkms_frame_info 
*frame_info,
 
        for (int x = 0; x < x_limit; x++) {
                out[x].a = (u16)0xffff;
-               out[x].r = pre_mul_blend_channel(in[x].r, out[x].r, in[x].a);
-               out[x].g = pre_mul_blend_channel(in[x].g, out[x].g, in[x].a);
-               out[x].b = pre_mul_blend_channel(in[x].b, out[x].b, in[x].a);
+               out[x].r = blend_channel(frame_info, in[x].r, out[x].r, 
in[x].a);
+               out[x].g = blend_channel(frame_info, in[x].g, out[x].g, 
in[x].a);
+               out[x].b = blend_channel(frame_info, in[x].b, out[x].b, 
in[x].a);
        }
 }
 
@@ -75,7 +90,7 @@ static void fill_background(const struct pixel_argb_u16 
*background_color,
  * @output_buffer: A buffer of a row that will receive the result of the 
blend(s)
  * @stage_buffer: The line with the pixels from plane being blend to the output
  *
- * This function blends the pixels (Using the `pre_mul_alpha_blend`)
+ * This function blends the pixels (Using the `alpha_blend()`)
  * from all planes, calculates the crc32 of the output from the former step,
  * and, if necessary, convert and store the output to the writeback buffer.
  */
@@ -100,8 +115,7 @@ static void blend(struct vkms_writeback_job *wb,
                                continue;
 
                        vkms_compose_row(stage_buffer, plane[i], y);
-                       pre_mul_alpha_blend(plane[i]->frame_info, stage_buffer,
-                                           output_buffer);
+                       alpha_blend(plane[i]->frame_info, stage_buffer, 
output_buffer);
                }
 
                *crc32 = crc32_le(*crc32, (void *)output_buffer->pixels, 
row_size);
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index f152d54baf76..e646a153831b 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -27,6 +27,8 @@ struct vkms_frame_info {
        struct drm_framebuffer *fb;
        struct drm_rect src, dst;
        struct iosys_map map[DRM_FORMAT_MAX_PLANES];
+       u16 alpha;
+       u16 pixel_blend_mode;
        unsigned int offset;
        unsigned int pitch;
        unsigned int cpp;
diff --git a/drivers/gpu/drm/vkms/vkms_plane.c 
b/drivers/gpu/drm/vkms/vkms_plane.c
index 87b2bccaf083..fd57ff8cb20f 100644
--- a/drivers/gpu/drm/vkms/vkms_plane.c
+++ b/drivers/gpu/drm/vkms/vkms_plane.c
@@ -4,6 +4,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -111,6 +112,8 @@ static void vkms_plane_atomic_update(struct drm_plane 
*plane,
        frame_info = vkms_plane_state->frame_info;
        memcpy(&frame_info->src, &new_state->src, sizeof(struct drm_rect));
        memcpy(&frame_info->dst, &new_state->dst, sizeof(struct drm_rect));
+       frame_info->alpha = new_state->alpha;
+       frame_info->pixel_blend_mode = new_state->pixel_blend_mode;
        frame_info->fb = fb;
        memcpy(&frame_info->map, &shadow_plane_state->data, 
sizeof(frame_info->map));
        drm_framebuffer_get(frame_info->fb);
@@ -201,5 +204,12 @@ struct vkms_plane *vkms_plane_init(struct vkms_device 
*vkmsdev,
 
        drm_plane_helper_add(&plane->base, &vkms_plane_helper_funcs);
 
+       drm_plane_create_alpha_property(&plane->base);
+
+       drm_plane_create_blend_mode_property(&plane->base,
+                                            BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                                            BIT(DRM_MODE_BLEND_PREMULTI)   |
+                                            BIT(DRM_MODE_BLEND_COVERAGE));
+
        return plane;
 }
-- 
2.40.0

Reply via email to