The VOP2 background color must be programmed with 10-bit precision,
using YUV format when the overlay operates in YUV mode, and RGB
otherwise.

Add the required RGB-to-YCbCr conversion logic, covering all color
spaces supported by the display controller: BT601L, BT601F, BT709L and
BT2020L.

Since the color is currently programmed to hardware on every atomic
commit, minimize the computation cost by splitting the work across the
two paths: in atomic_enable(), perform the conversion unconditionally
(the hardware state is unknown after power-on), while in atomic_flush(),
perform it only when the DRM property has actually changed.

Signed-off-by: Cristian Ciocaltea <[email protected]>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 153 ++++++++++++++++++++++++---
 1 file changed, 138 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c 
b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 64ac07cb1b0d..9b261f0c7eec 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -728,6 +728,89 @@ static void vop2_setup_csc_mode(struct vop2_video_port *vp,
        vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
 }
 
+/*
+ * RGB-to-YCbCr conversion based on color_to_ycbcr() and rgb2ycbcr() from
+ * drivers/media/common/v4l2-tpg/v4l2-tpg-core.c.
+ *
+ * Limited-range Y offset & chroma midpoint are expressed in 16-bit space.
+ */
+#define RGB2YUV_LIMITED_Y_OFFSET       (16 << 8)
+#define RGB2YUV_CHROMA_OFFSET          (128 << 8)
+#define COEFF(v, r)                    ((s32)(0.5 + (v) * (r) * 256.0))
+
+struct rgb2yuv_matrix {
+       s32 y_r,  y_g,  y_b;
+       s32 cb_r, cb_g, cb_b;
+       s32 cr_r, cr_g, cr_b;
+       s32 y_offset;
+};
+
+/* BT.601 Limited range */
+static const struct rgb2yuv_matrix rgb2yuv_bt601l = {
+       .y_r  = COEFF(0.299, 219),   .y_g  = COEFF(0.587, 219),   .y_b  = 
COEFF(0.114, 219),
+       .cb_r = COEFF(-0.1687, 224), .cb_g = COEFF(-0.3313, 224), .cb_b = 
COEFF(0.5, 224),
+       .cr_r = COEFF(0.5, 224),     .cr_g = COEFF(-0.4187, 224), .cr_b = 
COEFF(-0.0813, 224),
+       .y_offset = RGB2YUV_LIMITED_Y_OFFSET,
+};
+
+/* BT.601 Full range */
+static const struct rgb2yuv_matrix rgb2yuv_bt601f = {
+       .y_r  = COEFF(0.299, 255),   .y_g  = COEFF(0.587, 255),   .y_b  = 
COEFF(0.114, 255),
+       .cb_r = COEFF(-0.1687, 255), .cb_g = COEFF(-0.3313, 255), .cb_b = 
COEFF(0.5, 255),
+       .cr_r = COEFF(0.5, 255),     .cr_g = COEFF(-0.4187, 255), .cr_b = 
COEFF(-0.0813, 255),
+       .y_offset = 0,
+};
+
+/* BT.709 Limited range */
+static const struct rgb2yuv_matrix rgb2yuv_bt709l = {
+       .y_r  = COEFF(0.2126, 219),  .y_g  = COEFF(0.7152, 219),  .y_b  = 
COEFF(0.0722, 219),
+       .cb_r = COEFF(-0.1146, 224), .cb_g = COEFF(-0.3854, 224), .cb_b = 
COEFF(0.5, 224),
+       .cr_r = COEFF(0.5, 224),     .cr_g = COEFF(-0.4542, 224), .cr_b = 
COEFF(-0.0458, 224),
+       .y_offset = RGB2YUV_LIMITED_Y_OFFSET,
+};
+
+/* BT.2020 Limited range */
+static const struct rgb2yuv_matrix rgb2yuv_bt2020l = {
+       .y_r  = COEFF(0.2627, 219),  .y_g  = COEFF(0.6780, 219),  .y_b  = 
COEFF(0.0593, 219),
+       .cb_r = COEFF(-0.1396, 224), .cb_g = COEFF(-0.3604, 224), .cb_b = 
COEFF(0.5, 224),
+       .cr_r = COEFF(0.5, 224),     .cr_g = COEFF(-0.4598, 224), .cr_b = 
COEFF(-0.0402, 224),
+       .y_offset = RGB2YUV_LIMITED_Y_OFFSET,
+};
+
+static const struct rgb2yuv_matrix *
+vop2_rgb2yuv_get_matrix(enum vop_csc_format csc)
+{
+       switch (csc) {
+       case CSC_BT601L:
+               return &rgb2yuv_bt601l;
+       case CSC_BT601F:
+               return &rgb2yuv_bt601f;
+       case CSC_BT2020L:
+               return &rgb2yuv_bt2020l;
+       case CSC_BT709L:
+       default:
+               return &rgb2yuv_bt709l;
+       }
+}
+
+/* Convert an RGB (16bpc) to YUV444 (16bpc). */
+static void vop2_rgb16_to_yuv16(int v4l2_cs, u16 r, u16 g, u16 b,
+                               u16 *y, u16 *cb, u16 *cr)
+{
+       enum vop_csc_format csc = vop2_convert_csc_mode(v4l2_cs);
+       const struct rgb2yuv_matrix *m = vop2_rgb2yuv_get_matrix(csc);
+       s64 rs = r, gs = g, bs = b;
+       s64 ys, cbs, crs;
+
+       ys  = m->y_r  * rs + m->y_g  * gs + m->y_b  * bs;
+       cbs = m->cb_r * rs + m->cb_g * gs + m->cb_b * bs;
+       crs = m->cr_r * rs + m->cr_g * gs + m->cr_b * bs;
+
+       *y  = (ys  >> 16) + m->y_offset;
+       *cb = (cbs >> 16) + RGB2YUV_CHROMA_OFFSET;
+       *cr = (crs >> 16) + RGB2YUV_CHROMA_OFFSET;
+}
+
 static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
 {
        struct vop2 *vop2 = vp->vop2;
@@ -1554,12 +1637,58 @@ static void vop2_dither_setup(struct drm_crtc *crtc, 
u32 *dsp_ctrl)
                                DITHER_DOWN_ALLEGRO);
 }
 
-static void vop2_post_config(struct drm_crtc *crtc)
+static void vop2_bgcolor_setup(struct drm_crtc *crtc, bool force,
+                              struct drm_crtc_state *new_crtc_state,
+                              struct drm_crtc_state *old_crtc_state)
+{
+       struct rockchip_crtc_state *new_vcstate = 
to_rockchip_crtc_state(new_crtc_state);
+       struct rockchip_crtc_state *old_vcstate = 
to_rockchip_crtc_state(old_crtc_state);
+       struct vop2_video_port *vp = to_vop2_video_port(crtc);
+       u64 bgcolor = new_crtc_state->background_color;
+       u16 y, cb, cr;
+       u32 val;
+
+       if (!force && old_crtc_state->background_color == bgcolor &&
+           old_vcstate->color_space == new_vcstate->color_space)
+               return;
+
+       /*
+        * Background color is programmed with 10 bits of precision, using YUV
+        * format when operating in YUV overlay mode, and RGB otherwise.
+        */
+       if (new_vcstate->yuv_overlay) {
+               vop2_rgb16_to_yuv16(new_vcstate->color_space,
+                                   DRM_ARGB64_GETR(bgcolor),
+                                   DRM_ARGB64_GETG(bgcolor),
+                                   DRM_ARGB64_GETB(bgcolor),
+                                   &y, &cb, &cr);
+
+               val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, cr >> 6);
+               FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, y >> 6);
+               FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, cb >> 6);
+       } else {
+               /*
+                * Since performance is more important than accuracy here, make
+                * use of the DRM_ARGB64_GET*_BPCS() helpers.
+                */
+               val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED,
+                                DRM_ARGB64_GETR_BPCS(bgcolor, 10));
+               FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val,
+                            DRM_ARGB64_GETG_BPCS(bgcolor, 10));
+               FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val,
+                            DRM_ARGB64_GETB_BPCS(bgcolor, 10));
+       }
+
+       vop2_vp_write(vp, RK3568_VP_DSP_BG, val);
+}
+
+static void vop2_post_config(struct drm_crtc *crtc, bool force,
+                            struct drm_crtc_state *new_crtc_state,
+                            struct drm_crtc_state *old_crtc_state)
 {
        struct vop2_video_port *vp = to_vop2_video_port(crtc);
        struct vop2 *vop2 = vp->vop2;
-       struct drm_display_mode *mode = &crtc->state->adjusted_mode;
-       u64 bgcolor = crtc->state->background_color;
+       struct drm_display_mode *mode = &new_crtc_state->adjusted_mode;
        u16 vtotal = mode->crtc_vtotal;
        u16 hdisplay = mode->crtc_hdisplay;
        u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
@@ -1605,15 +1734,7 @@ static void vop2_post_config(struct drm_crtc *crtc)
                vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
        }
 
-       /*
-        * Background color is programmed with 10 bits of precision.
-        * Since performance is more important than accuracy here,
-        * make use of the DRM_ARGB64_GET*_BPCS() helpers.
-        */
-       val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, 
DRM_ARGB64_GETR_BPCS(bgcolor, 10));
-       FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, 
DRM_ARGB64_GETG_BPCS(bgcolor, 10));
-       FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, 
DRM_ARGB64_GETB_BPCS(bgcolor, 10));
-       vop2_vp_write(vp, RK3568_VP_DSP_BG, val);
+       vop2_bgcolor_setup(crtc, force, new_crtc_state, old_crtc_state);
 }
 
 static int us_to_vertical_line(struct drm_display_mode *mode, int us)
@@ -1628,8 +1749,9 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
        struct vop2 *vop2 = vp->vop2;
        const struct vop2_data *vop2_data = vop2->data;
        const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
+       struct drm_crtc_state *old_crtc_state = 
drm_atomic_get_old_crtc_state(state, crtc);
        struct drm_crtc_state *crtc_state = 
drm_atomic_get_new_crtc_state(state, crtc);
-       struct rockchip_crtc_state *vcstate = 
to_rockchip_crtc_state(crtc->state);
+       struct rockchip_crtc_state *vcstate = 
to_rockchip_crtc_state(crtc_state);
        struct drm_display_mode *mode = &crtc->state->adjusted_mode;
        unsigned long clock = mode->crtc_clock * 1000;
        u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
@@ -1799,7 +1921,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
 
        clk_set_rate(vp->dclk, clock);
 
-       vop2_post_config(crtc);
+       vop2_post_config(crtc, true, crtc_state, old_crtc_state);
 
        vop2_cfg_done(vp);
 
@@ -1874,6 +1996,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
                                   struct drm_atomic_commit *state)
 {
        struct drm_crtc_state *crtc_state = 
drm_atomic_get_new_crtc_state(state, crtc);
+       struct drm_crtc_state *old_crtc_state = 
drm_atomic_get_old_crtc_state(state, crtc);
        struct vop2_video_port *vp = to_vop2_video_port(crtc);
        struct vop2 *vop2 = vp->vop2;
 
@@ -1881,7 +2004,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
        if (!drm_atomic_crtc_needs_modeset(crtc_state) && 
crtc_state->color_mgmt_changed)
                vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, 
crtc_state);
 
-       vop2_post_config(crtc);
+       vop2_post_config(crtc, false, crtc_state, old_crtc_state);
 
        vop2_cfg_done(vp);
 

-- 
2.54.0

Reply via email to