Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rockchip/drm: vop2: add support for gamma LUT

Add support for gamma LUT in VOP2 driver. The implementation was inspired
by one found in VOP1 driver. Blue and red channels in gamma LUT register
write were swapped with respect to how gamma LUT values are written in
VOP1. Gamma LUT port selection was added before the write of new gamma LUT
table.

If the current SoC is rk356x, check if no other CRTC has gamma LUT enabled
in atomic_check (only one video port can use gamma LUT at a time) and
disable gamma LUT before the LUT table write.

If the current SoC isn't rk356x, "seamless" gamma lut update is performed
similarly to how it was done in the case of RK3399 in VOP1[1]. In seamless
update gamma LUT disable before the write isn't necessary, check if no
other CRTC has gamma LUT enabled is also not necessary, different register
is being used to select gamma LUT port[2] and after setting DSP_LUT_EN bit,
GAMMA_UPDATE_EN bit is set[3].

Gamma size is set and drm color management is enabled for each video port's
CRTC except ones which have no associated device.

Patch was tested on RK3566 (Pinetab2). When using userspace tools
which set eg. constant color temperature no issues were noticed. When
using userspace tools which adjust eg. color temperature the slight screen
flicker is visible probably because of gamma LUT disable needed in the
case of RK356x before gamma LUT write.

Compare behaviour of eg.:
```
gammastep -O 3000
```

To eg.:
```
gammastep -l 53:23 -t 6000:3000
```

In latter case color temperature is slowly adjusted at the beginning which
causes screen to slighly flicker. Then it adjusts every few seconds which
also causes slight screen flicker.

[1] https://lists.infradead.org/pipermail/linux-rockchip/2021-October/028132.html
[2] https://lore.kernel.org/linux-rockchip/48249708-8c05-40d2-a5d8-23de960c5a77@rock-chips.com/
[3] https://github.com/radxa/kernel/blob/linux-6.1-stan-rkr1/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c#L3437

Helped-by: Daniel Stone <daniel@fooishbar.org>
Helped-by: Dragan Simic <dsimic@manjaro.org>
Helped-by: Diederik de Haas <didi.debian@cknow.org>
Helped-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Piotr Zalewski <pZ010001011111@proton.me>
Reviewed-by: Andy Yan <andyshrk@163.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20241101185545.559090-3-pZ010001011111@proton.me

authored by

Piotr Zalewski and committed by
Heiko Stuebner
4f537776 712ec5de

+191
+186
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
··· 278 278 return val; 279 279 } 280 280 281 + static u32 vop2_vp_read(struct vop2_video_port *vp, u32 offset) 282 + { 283 + u32 val; 284 + 285 + regmap_read(vp->vop2->map, vp->data->offset + offset, &val); 286 + 287 + return val; 288 + } 289 + 281 290 static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v) 282 291 { 283 292 regmap_field_write(win->reg[reg], v); ··· 1007 998 clk_disable_unprepare(vop2->hclk); 1008 999 } 1009 1000 1001 + static bool vop2_vp_dsp_lut_is_enabled(struct vop2_video_port *vp) 1002 + { 1003 + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); 1004 + 1005 + return dsp_ctrl & RK3568_VP_DSP_CTRL__DSP_LUT_EN; 1006 + } 1007 + 1008 + static void vop2_vp_dsp_lut_disable(struct vop2_video_port *vp) 1009 + { 1010 + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); 1011 + 1012 + dsp_ctrl &= ~RK3568_VP_DSP_CTRL__DSP_LUT_EN; 1013 + vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); 1014 + } 1015 + 1016 + static bool vop2_vp_dsp_lut_poll_disabled(struct vop2_video_port *vp) 1017 + { 1018 + u32 dsp_ctrl; 1019 + int ret = readx_poll_timeout(vop2_vp_dsp_lut_is_enabled, vp, dsp_ctrl, 1020 + !dsp_ctrl, 5, 30 * 1000); 1021 + if (ret) { 1022 + drm_err(vp->vop2->drm, "display LUT RAM enable timeout!\n"); 1023 + return false; 1024 + } 1025 + 1026 + return true; 1027 + } 1028 + 1029 + static void vop2_vp_dsp_lut_enable(struct vop2_video_port *vp) 1030 + { 1031 + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); 1032 + 1033 + dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_LUT_EN; 1034 + vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); 1035 + } 1036 + 1037 + static void vop2_vp_dsp_lut_update_enable(struct vop2_video_port *vp) 1038 + { 1039 + u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL); 1040 + 1041 + dsp_ctrl |= RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN; 1042 + vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); 1043 + } 1044 + 1045 + static inline bool vop2_supports_seamless_gamma_lut_update(struct vop2 *vop2) 1046 + { 1047 + return (vop2->data->soc_id != 3566 && vop2->data->soc_id != 3568); 1048 + } 1049 + 1050 + static bool vop2_gamma_lut_in_use(struct vop2 *vop2, struct vop2_video_port *vp) 1051 + { 1052 + const int nr_vps = vop2->data->nr_vps; 1053 + int gamma_en_vp_id; 1054 + 1055 + for (gamma_en_vp_id = 0; gamma_en_vp_id < nr_vps; gamma_en_vp_id++) 1056 + if (vop2_vp_dsp_lut_is_enabled(&vop2->vps[gamma_en_vp_id])) 1057 + break; 1058 + 1059 + return gamma_en_vp_id != nr_vps && gamma_en_vp_id != vp->id; 1060 + } 1061 + 1010 1062 static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, 1011 1063 struct drm_atomic_state *state) 1012 1064 { ··· 1552 1482 CRTC_STEREO_DOUBLE); 1553 1483 1554 1484 return true; 1485 + } 1486 + 1487 + static void vop2_crtc_write_gamma_lut(struct vop2 *vop2, struct drm_crtc *crtc) 1488 + { 1489 + const struct vop2_video_port *vp = to_vop2_video_port(crtc); 1490 + const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id]; 1491 + struct drm_color_lut *lut = crtc->state->gamma_lut->data; 1492 + unsigned int i, bpc = ilog2(vp_data->gamma_lut_len); 1493 + u32 word; 1494 + 1495 + for (i = 0; i < crtc->gamma_size; i++) { 1496 + word = (drm_color_lut_extract(lut[i].blue, bpc) << (2 * bpc)) | 1497 + (drm_color_lut_extract(lut[i].green, bpc) << bpc) | 1498 + drm_color_lut_extract(lut[i].red, bpc); 1499 + 1500 + writel(word, vop2->lut_regs + i * 4); 1501 + } 1502 + } 1503 + 1504 + static void vop2_crtc_atomic_set_gamma_seamless(struct vop2 *vop2, 1505 + struct vop2_video_port *vp, 1506 + struct drm_crtc *crtc) 1507 + { 1508 + vop2_writel(vop2, RK3568_LUT_PORT_SEL, 1509 + FIELD_PREP(RK3588_LUT_PORT_SEL__GAMMA_AHB_WRITE_SEL, vp->id)); 1510 + vop2_vp_dsp_lut_enable(vp); 1511 + vop2_crtc_write_gamma_lut(vop2, crtc); 1512 + vop2_vp_dsp_lut_update_enable(vp); 1513 + } 1514 + 1515 + static void vop2_crtc_atomic_set_gamma_rk356x(struct vop2 *vop2, 1516 + struct vop2_video_port *vp, 1517 + struct drm_crtc *crtc) 1518 + { 1519 + vop2_vp_dsp_lut_disable(vp); 1520 + vop2_cfg_done(vp); 1521 + if (!vop2_vp_dsp_lut_poll_disabled(vp)) 1522 + return; 1523 + 1524 + vop2_writel(vop2, RK3568_LUT_PORT_SEL, vp->id); 1525 + vop2_crtc_write_gamma_lut(vop2, crtc); 1526 + vop2_vp_dsp_lut_enable(vp); 1527 + } 1528 + 1529 + static void vop2_crtc_atomic_try_set_gamma(struct vop2 *vop2, 1530 + struct vop2_video_port *vp, 1531 + struct drm_crtc *crtc, 1532 + struct drm_crtc_state *crtc_state) 1533 + { 1534 + if (!vop2->lut_regs || !crtc_state->color_mgmt_changed) 1535 + return; 1536 + 1537 + if (!crtc_state->gamma_lut) { 1538 + vop2_vp_dsp_lut_disable(vp); 1539 + return; 1540 + } 1541 + 1542 + if (vop2_supports_seamless_gamma_lut_update(vop2)) 1543 + vop2_crtc_atomic_set_gamma_seamless(vop2, vp, crtc); 1544 + else 1545 + vop2_crtc_atomic_set_gamma_rk356x(vop2, vp, crtc); 1546 + } 1547 + 1548 + static inline void vop2_crtc_atomic_try_set_gamma_locked(struct vop2 *vop2, 1549 + struct vop2_video_port *vp, 1550 + struct drm_crtc *crtc, 1551 + struct drm_crtc_state *crtc_state) 1552 + { 1553 + vop2_lock(vop2); 1554 + vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state); 1555 + vop2_unlock(vop2); 1555 1556 } 1556 1557 1557 1558 static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl) ··· 2200 2059 2201 2060 vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); 2202 2061 2062 + vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state); 2063 + 2203 2064 drm_crtc_vblank_on(crtc); 2204 2065 2205 2066 vop2_unlock(vop2); 2067 + } 2068 + 2069 + static int vop2_crtc_atomic_check_gamma(struct vop2_video_port *vp, 2070 + struct drm_crtc *crtc, 2071 + struct drm_atomic_state *state, 2072 + struct drm_crtc_state *crtc_state) 2073 + { 2074 + struct vop2 *vop2 = vp->vop2; 2075 + unsigned int len; 2076 + 2077 + if (!vp->vop2->lut_regs || !crtc_state->color_mgmt_changed || 2078 + !crtc_state->gamma_lut) 2079 + return 0; 2080 + 2081 + len = drm_color_lut_size(crtc_state->gamma_lut); 2082 + if (len != crtc->gamma_size) { 2083 + drm_dbg(vop2->drm, "Invalid LUT size; got %d, expected %d\n", 2084 + len, crtc->gamma_size); 2085 + return -EINVAL; 2086 + } 2087 + 2088 + if (!vop2_supports_seamless_gamma_lut_update(vop2) && vop2_gamma_lut_in_use(vop2, vp)) { 2089 + drm_info(vop2->drm, "Gamma LUT can be enabled for only one CRTC at a time\n"); 2090 + return -EINVAL; 2091 + } 2092 + 2093 + return 0; 2206 2094 } 2207 2095 2208 2096 static int vop2_crtc_atomic_check(struct drm_crtc *crtc, ··· 2241 2071 struct drm_plane *plane; 2242 2072 int nplanes = 0; 2243 2073 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 2074 + int ret; 2075 + 2076 + ret = vop2_crtc_atomic_check_gamma(vp, crtc, state, crtc_state); 2077 + if (ret) 2078 + return ret; 2244 2079 2245 2080 drm_atomic_crtc_state_for_each_plane(plane, crtc_state) 2246 2081 nplanes++; ··· 2664 2489 static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, 2665 2490 struct drm_atomic_state *state) 2666 2491 { 2492 + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 2667 2493 struct vop2_video_port *vp = to_vop2_video_port(crtc); 2494 + struct vop2 *vop2 = vp->vop2; 2495 + 2496 + /* In case of modeset, gamma lut update already happened in atomic enable */ 2497 + if (!drm_atomic_crtc_needs_modeset(crtc_state)) 2498 + vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state); 2668 2499 2669 2500 vop2_post_config(crtc); 2670 2501 ··· 2973 2792 } 2974 2793 2975 2794 drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs); 2795 + if (vop2->lut_regs) { 2796 + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; 2976 2797 2798 + drm_mode_crtc_set_gamma_size(&vp->crtc, vp_data->gamma_lut_len); 2799 + drm_crtc_enable_color_mgmt(&vp->crtc, 0, false, vp_data->gamma_lut_len); 2800 + } 2977 2801 init_completion(&vp->dsp_hold_completion); 2978 2802 } 2979 2803
+5
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
··· 394 394 #define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN BIT(15) 395 395 396 396 #define RK3568_VP_DSP_CTRL__STANDBY BIT(31) 397 + #define RK3568_VP_DSP_CTRL__DSP_LUT_EN BIT(28) 397 398 #define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE BIT(20) 398 399 #define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL GENMASK(19, 18) 399 400 #define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN BIT(17) ··· 408 407 #define RK3568_VP_DSP_CTRL__P2I_EN BIT(5) 409 408 #define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV BIT(4) 410 409 #define RK3568_VP_DSP_CTRL__OUT_MODE GENMASK(3, 0) 410 + 411 + #define RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN BIT(22) 411 412 412 413 #define RK3588_VP_CLK_CTRL__DCLK_OUT_DIV GENMASK(3, 2) 413 414 #define RK3588_VP_CLK_CTRL__DCLK_CORE_DIV GENMASK(1, 0) ··· 462 459 463 460 #define RK3588_DSP_IF_POL__DP1_PIN_POL GENMASK(14, 12) 464 461 #define RK3588_DSP_IF_POL__DP0_PIN_POL GENMASK(10, 8) 462 + 463 + #define RK3588_LUT_PORT_SEL__GAMMA_AHB_WRITE_SEL GENMASK(13, 12) 465 464 466 465 #define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK BIT(5) 467 466 #define RK3568_VP0_MIPI_CTRL__DCLK_DIV2 BIT(4)