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.

drm/msm/dpu: Add DSPP GC driver to provide GAMMA_LUT DRM property

Add support for DSPP GC block in DPU driver for Qualcomm SoCs.
Expose the GAMMA_LUT DRM property, which is needed to enable
night light and basic screen color calibration.

I used LineageOS downstream kernel as a reference and found the LUT
format by trial-and-error on OnePlus 6.

Tested on oneplus-enchilada (sdm845-mainline 6.16-dev) and xiaomi-tissot
(msm8953-mainline 6.12/main).

Tested-by: David Heidelberg <david@ixit.cz> # Pixel 3 (next-20251018)
Tested-by: Guido Günther <agx@sigxcpu.org> # on sdm845-shift-axolotl
Signed-off-by: Federico Amedeo Izzo <federico@izzo.pro>
Tested-by: Steev Klimaszewski <threeway@gmail.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Patchwork: https://patchwork.freedesktop.org/patch/682102/
Link: https://lore.kernel.org/r/20251019-dpu-add-dspp-gc-driver-v3-1-840491934e56@izzo.pro
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

authored by

Federico Amedeo Izzo and committed by
Dmitry Baryshkov
39a750ff f185076d

+163 -14
+72 -14
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
··· 819 819 cfg->b.b = CONVERT_S3_15(ctm->matrix[8]); 820 820 } 821 821 822 + static void _dpu_crtc_get_gc_lut(struct drm_crtc_state *state, 823 + struct dpu_hw_gc_lut *gc_lut) 824 + { 825 + struct drm_color_lut *lut; 826 + int i; 827 + u32 val_even, val_odd; 828 + 829 + lut = (struct drm_color_lut *)state->gamma_lut->data; 830 + 831 + if (!lut) 832 + return; 833 + 834 + /* Pack 1024 10-bit entries in 512 32-bit registers */ 835 + for (i = 0; i < PGC_TBL_LEN; i++) { 836 + val_even = drm_color_lut_extract(lut[i * 2].green, 10); 837 + val_odd = drm_color_lut_extract(lut[i * 2 + 1].green, 10); 838 + gc_lut->c0[i] = val_even | (val_odd << 16); 839 + val_even = drm_color_lut_extract(lut[i * 2].blue, 10); 840 + val_odd = drm_color_lut_extract(lut[i * 2 + 1].blue, 10); 841 + gc_lut->c1[i] = val_even | (val_odd << 16); 842 + val_even = drm_color_lut_extract(lut[i * 2].red, 10); 843 + val_odd = drm_color_lut_extract(lut[i * 2 + 1].red, 10); 844 + gc_lut->c2[i] = val_even | (val_odd << 16); 845 + } 846 + 847 + /* Disable 8-bit rounding mode */ 848 + gc_lut->flags = 0; 849 + } 850 + 822 851 static void _dpu_crtc_setup_cp_blocks(struct drm_crtc *crtc) 823 852 { 824 853 struct drm_crtc_state *state = crtc->state; 825 854 struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); 826 855 struct dpu_crtc_mixer *mixer = cstate->mixers; 827 856 struct dpu_hw_pcc_cfg cfg; 857 + struct dpu_hw_gc_lut *gc_lut; 828 858 struct dpu_hw_ctl *ctl; 829 859 struct dpu_hw_dspp *dspp; 830 860 int i; ··· 867 837 ctl = mixer[i].lm_ctl; 868 838 dspp = mixer[i].hw_dspp; 869 839 870 - if (!dspp || !dspp->ops.setup_pcc) 840 + if (!dspp) 871 841 continue; 872 842 873 - if (!state->ctm) { 874 - dspp->ops.setup_pcc(dspp, NULL); 875 - } else { 876 - _dpu_crtc_get_pcc_coeff(state, &cfg); 877 - dspp->ops.setup_pcc(dspp, &cfg); 843 + if (dspp->ops.setup_pcc) { 844 + if (!state->ctm) { 845 + dspp->ops.setup_pcc(dspp, NULL); 846 + } else { 847 + _dpu_crtc_get_pcc_coeff(state, &cfg); 848 + dspp->ops.setup_pcc(dspp, &cfg); 849 + } 850 + 851 + /* stage config flush mask */ 852 + ctl->ops.update_pending_flush_dspp(ctl, 853 + mixer[i].hw_dspp->idx, DPU_DSPP_PCC); 878 854 } 879 855 880 - /* stage config flush mask */ 881 - ctl->ops.update_pending_flush_dspp(ctl, 882 - mixer[i].hw_dspp->idx, DPU_DSPP_PCC); 856 + if (dspp->ops.setup_gc) { 857 + if (!state->gamma_lut) { 858 + dspp->ops.setup_gc(dspp, NULL); 859 + } else { 860 + gc_lut = kzalloc(sizeof(*gc_lut), GFP_KERNEL); 861 + if (!gc_lut) 862 + continue; 863 + _dpu_crtc_get_gc_lut(state, gc_lut); 864 + dspp->ops.setup_gc(dspp, gc_lut); 865 + kfree(gc_lut); 866 + } 867 + 868 + /* stage config flush mask */ 869 + ctl->ops.update_pending_flush_dspp(ctl, 870 + mixer[i].hw_dspp->idx, DPU_DSPP_GC); 871 + } 883 872 } 884 873 } 885 874 ··· 1396 1347 * 1397 1348 * If DSC is enabled, use 2 LMs for 2:2:1 topology 1398 1349 * 1399 - * Add dspps to the reservation requirements if ctm is requested 1350 + * Add dspps to the reservation requirements if ctm or gamma_lut are requested 1400 1351 * 1401 1352 * Only hardcode num_lm to 2 for cases where num_intf == 2 and CWB is not 1402 1353 * enabled. This is because in cases where CWB is enabled, num_intf will ··· 1415 1366 else 1416 1367 topology.num_lm = 1; 1417 1368 1418 - if (crtc_state->ctm) 1369 + if (crtc_state->ctm || crtc_state->gamma_lut) 1419 1370 topology.num_dspp = topology.num_lm; 1420 1371 1421 1372 return topology; ··· 1527 1478 bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state); 1528 1479 1529 1480 /* don't reallocate resources if only ACTIVE has beeen changed */ 1530 - if (crtc_state->mode_changed || crtc_state->connectors_changed) { 1481 + if (crtc_state->mode_changed || crtc_state->connectors_changed || 1482 + crtc_state->color_mgmt_changed) { 1531 1483 rc = dpu_crtc_assign_resources(crtc, crtc_state); 1532 1484 if (rc < 0) 1533 1485 return rc; ··· 1891 1841 1892 1842 drm_crtc_helper_add(crtc, &dpu_crtc_helper_funcs); 1893 1843 1894 - if (dpu_kms->catalog->dspp_count) 1895 - drm_crtc_enable_color_mgmt(crtc, 0, true, 0); 1844 + if (dpu_kms->catalog->dspp_count) { 1845 + const struct dpu_dspp_cfg *dspp = &dpu_kms->catalog->dspp[0]; 1846 + 1847 + if (dspp->sblk->gc.base) { 1848 + drm_mode_crtc_set_gamma_size(crtc, DPU_GAMMA_LUT_SIZE); 1849 + drm_crtc_enable_color_mgmt(crtc, 0, true, DPU_GAMMA_LUT_SIZE); 1850 + } else { 1851 + drm_crtc_enable_color_mgmt(crtc, 0, true, 0); 1852 + } 1853 + } 1896 1854 1897 1855 /* save user friendly CRTC name for later */ 1898 1856 snprintf(dpu_crtc->name, DPU_CRTC_NAME_SIZE, "crtc%u", crtc->base.id);
+4
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
··· 402 402 static const struct dpu_dspp_sub_blks msm8998_dspp_sblk = { 403 403 .pcc = {.name = "pcc", .base = 0x1700, 404 404 .len = 0x90, .version = 0x10007}, 405 + .gc = {.name = "gc", .base = 0x17c0, 406 + .len = 0x40, .version = 0x10007}, 405 407 }; 406 408 407 409 static const struct dpu_dspp_sub_blks sdm845_dspp_sblk = { 408 410 .pcc = {.name = "pcc", .base = 0x1700, 409 411 .len = 0x90, .version = 0x40000}, 412 + .gc = {.name = "gc", .base = 0x17c0, 413 + .len = 0x40, .version = 0x10008}, 410 414 }; 411 415 412 416 static const struct dpu_dspp_sub_blks sm8750_dspp_sblk = {
+4
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
··· 77 77 /** 78 78 * DSPP sub-blocks 79 79 * @DPU_DSPP_PCC Panel color correction block 80 + * @DPU_DSPP_GC Gamma correction block 80 81 */ 81 82 enum { 82 83 DPU_DSPP_PCC = 0x1, 84 + DPU_DSPP_GC, 83 85 DPU_DSPP_MAX 84 86 }; 85 87 ··· 330 328 /** 331 329 * struct dpu_dspp_sub_blks: Information of DSPP block 332 330 * @pcc: pixel color correction block 331 + * @gc: gamma correction block 333 332 */ 334 333 struct dpu_dspp_sub_blks { 335 334 struct dpu_pp_blk pcc; 335 + struct dpu_pp_blk gc; 336 336 }; 337 337 338 338 struct dpu_pingpong_sub_blks {
+3
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
··· 399 399 case DPU_DSPP_PCC: 400 400 ctx->pending_dspp_flush_mask[dspp - DSPP_0] |= BIT(4); 401 401 break; 402 + case DPU_DSPP_GC: 403 + ctx->pending_dspp_flush_mask[dspp - DSPP_0] |= BIT(5); 404 + break; 402 405 default: 403 406 return; 404 407 }
+54
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.c
··· 24 24 #define PCC_BLUE_G_OFF 0x24 25 25 #define PCC_BLUE_B_OFF 0x30 26 26 27 + /* DSPP_GC */ 28 + #define GC_EN BIT(0) 29 + #define GC_DIS 0 30 + #define GC_8B_ROUND_EN BIT(1) 31 + #define GC_LUT_SWAP_OFF 0x1c 32 + #define GC_C0_OFF 0x4 33 + #define GC_C1_OFF 0xc 34 + #define GC_C2_OFF 0x14 35 + #define GC_C0_INDEX_OFF 0x8 36 + #define GC_C1_INDEX_OFF 0x10 37 + #define GC_C2_INDEX_OFF 0x18 38 + 27 39 static void dpu_setup_dspp_pcc(struct dpu_hw_dspp *ctx, 28 40 struct dpu_hw_pcc_cfg *cfg) 29 41 { ··· 75 63 DPU_REG_WRITE(&ctx->hw, base, PCC_EN); 76 64 } 77 65 66 + static void dpu_setup_dspp_gc(struct dpu_hw_dspp *ctx, 67 + struct dpu_hw_gc_lut *gc_lut) 68 + { 69 + int i = 0; 70 + u32 base, reg; 71 + 72 + if (!ctx) { 73 + DRM_ERROR("invalid ctx\n"); 74 + return; 75 + } 76 + 77 + base = ctx->cap->sblk->gc.base; 78 + 79 + if (!base) { 80 + DRM_ERROR("invalid ctx %pK gc base\n", ctx); 81 + return; 82 + } 83 + 84 + if (!gc_lut) { 85 + DRM_DEBUG_DRIVER("disable gc feature\n"); 86 + DPU_REG_WRITE(&ctx->hw, base, GC_DIS); 87 + return; 88 + } 89 + 90 + DPU_REG_WRITE(&ctx->hw, base + GC_C0_INDEX_OFF, 0); 91 + DPU_REG_WRITE(&ctx->hw, base + GC_C1_INDEX_OFF, 0); 92 + DPU_REG_WRITE(&ctx->hw, base + GC_C2_INDEX_OFF, 0); 93 + 94 + for (i = 0; i < PGC_TBL_LEN; i++) { 95 + DPU_REG_WRITE(&ctx->hw, base + GC_C0_OFF, gc_lut->c0[i]); 96 + DPU_REG_WRITE(&ctx->hw, base + GC_C1_OFF, gc_lut->c1[i]); 97 + DPU_REG_WRITE(&ctx->hw, base + GC_C2_OFF, gc_lut->c2[i]); 98 + } 99 + 100 + DPU_REG_WRITE(&ctx->hw, base + GC_LUT_SWAP_OFF, BIT(0)); 101 + 102 + reg = GC_EN | ((gc_lut->flags & PGC_8B_ROUND) ? GC_8B_ROUND_EN : 0); 103 + DPU_REG_WRITE(&ctx->hw, base, reg); 104 + } 105 + 78 106 /** 79 107 * dpu_hw_dspp_init() - Initializes the DSPP hw driver object. 80 108 * should be called once before accessing every DSPP. ··· 144 92 c->cap = cfg; 145 93 if (c->cap->sblk->pcc.base) 146 94 c->ops.setup_pcc = dpu_setup_dspp_pcc; 95 + if (c->cap->sblk->gc.base) 96 + c->ops.setup_gc = dpu_setup_dspp_gc; 147 97 148 98 return c; 149 99 }
+26
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.h
··· 33 33 struct dpu_hw_pcc_coeff b; 34 34 }; 35 35 36 + #define DPU_GAMMA_LUT_SIZE 1024 37 + #define PGC_TBL_LEN 512 38 + #define PGC_8B_ROUND BIT(0) 39 + 40 + /** 41 + * struct dpu_hw_gc_lut - gc lut feature structure 42 + * @flags: flags for the feature values can be: 43 + * - PGC_8B_ROUND 44 + * @c0: color0 component lut 45 + * @c1: color1 component lut 46 + * @c2: color2 component lut 47 + */ 48 + struct dpu_hw_gc_lut { 49 + __u64 flags; 50 + __u32 c0[PGC_TBL_LEN]; 51 + __u32 c1[PGC_TBL_LEN]; 52 + __u32 c2[PGC_TBL_LEN]; 53 + }; 54 + 36 55 /** 37 56 * struct dpu_hw_dspp_ops - interface to the dspp hardware driver functions 38 57 * Caller must call the init function to get the dspp context for each dspp ··· 64 45 * @cfg: Pointer to configuration 65 46 */ 66 47 void (*setup_pcc)(struct dpu_hw_dspp *ctx, struct dpu_hw_pcc_cfg *cfg); 48 + 49 + /** 50 + * setup_gc - setup dspp gc 51 + * @ctx: Pointer to dspp context 52 + * @gc_lut: Pointer to lut content 53 + */ 54 + void (*setup_gc)(struct dpu_hw_dspp *ctx, struct dpu_hw_gc_lut *gc_lut); 67 55 68 56 }; 69 57