diff options
| author | Federico Amedeo Izzo <federico@izzo.pro> | 2025-10-19 12:06:32 +0200 |
|---|---|---|
| committer | Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> | 2026-01-22 01:22:37 +0200 |
| commit | 39a750ff5fc9cab1085dc2d4b6a3f34fe1ad23c3 (patch) | |
| tree | 6989e0e6ff442881d9589144c6d6c0a14087a1c6 | |
| parent | f185076da44c774241a16a82a7773ece3c1c607b (diff) | |
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>
| -rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 86 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.c | 54 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.h | 26 |
6 files changed, 163 insertions, 14 deletions
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 2d06c950e814..6bf7c46379ae 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -819,12 +819,42 @@ static void _dpu_crtc_get_pcc_coeff(struct drm_crtc_state *state, cfg->b.b = CONVERT_S3_15(ctm->matrix[8]); } +static void _dpu_crtc_get_gc_lut(struct drm_crtc_state *state, + struct dpu_hw_gc_lut *gc_lut) +{ + struct drm_color_lut *lut; + int i; + u32 val_even, val_odd; + + lut = (struct drm_color_lut *)state->gamma_lut->data; + + if (!lut) + return; + + /* Pack 1024 10-bit entries in 512 32-bit registers */ + for (i = 0; i < PGC_TBL_LEN; i++) { + val_even = drm_color_lut_extract(lut[i * 2].green, 10); + val_odd = drm_color_lut_extract(lut[i * 2 + 1].green, 10); + gc_lut->c0[i] = val_even | (val_odd << 16); + val_even = drm_color_lut_extract(lut[i * 2].blue, 10); + val_odd = drm_color_lut_extract(lut[i * 2 + 1].blue, 10); + gc_lut->c1[i] = val_even | (val_odd << 16); + val_even = drm_color_lut_extract(lut[i * 2].red, 10); + val_odd = drm_color_lut_extract(lut[i * 2 + 1].red, 10); + gc_lut->c2[i] = val_even | (val_odd << 16); + } + + /* Disable 8-bit rounding mode */ + gc_lut->flags = 0; +} + static void _dpu_crtc_setup_cp_blocks(struct drm_crtc *crtc) { struct drm_crtc_state *state = crtc->state; struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); struct dpu_crtc_mixer *mixer = cstate->mixers; struct dpu_hw_pcc_cfg cfg; + struct dpu_hw_gc_lut *gc_lut; struct dpu_hw_ctl *ctl; struct dpu_hw_dspp *dspp; int i; @@ -837,19 +867,38 @@ static void _dpu_crtc_setup_cp_blocks(struct drm_crtc *crtc) ctl = mixer[i].lm_ctl; dspp = mixer[i].hw_dspp; - if (!dspp || !dspp->ops.setup_pcc) + if (!dspp) continue; - if (!state->ctm) { - dspp->ops.setup_pcc(dspp, NULL); - } else { - _dpu_crtc_get_pcc_coeff(state, &cfg); - dspp->ops.setup_pcc(dspp, &cfg); + if (dspp->ops.setup_pcc) { + if (!state->ctm) { + dspp->ops.setup_pcc(dspp, NULL); + } else { + _dpu_crtc_get_pcc_coeff(state, &cfg); + dspp->ops.setup_pcc(dspp, &cfg); + } + + /* stage config flush mask */ + ctl->ops.update_pending_flush_dspp(ctl, + mixer[i].hw_dspp->idx, DPU_DSPP_PCC); } - /* stage config flush mask */ - ctl->ops.update_pending_flush_dspp(ctl, - mixer[i].hw_dspp->idx, DPU_DSPP_PCC); + if (dspp->ops.setup_gc) { + if (!state->gamma_lut) { + dspp->ops.setup_gc(dspp, NULL); + } else { + gc_lut = kzalloc(sizeof(*gc_lut), GFP_KERNEL); + if (!gc_lut) + continue; + _dpu_crtc_get_gc_lut(state, gc_lut); + dspp->ops.setup_gc(dspp, gc_lut); + kfree(gc_lut); + } + + /* stage config flush mask */ + ctl->ops.update_pending_flush_dspp(ctl, + mixer[i].hw_dspp->idx, DPU_DSPP_GC); + } } } @@ -1347,7 +1396,7 @@ static struct msm_display_topology dpu_crtc_get_topology( * * If DSC is enabled, use 2 LMs for 2:2:1 topology * - * Add dspps to the reservation requirements if ctm is requested + * Add dspps to the reservation requirements if ctm or gamma_lut are requested * * Only hardcode num_lm to 2 for cases where num_intf == 2 and CWB is not * enabled. This is because in cases where CWB is enabled, num_intf will @@ -1366,7 +1415,7 @@ static struct msm_display_topology dpu_crtc_get_topology( else topology.num_lm = 1; - if (crtc_state->ctm) + if (crtc_state->ctm || crtc_state->gamma_lut) topology.num_dspp = topology.num_lm; return topology; @@ -1478,7 +1527,8 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state); /* don't reallocate resources if only ACTIVE has beeen changed */ - if (crtc_state->mode_changed || crtc_state->connectors_changed) { + if (crtc_state->mode_changed || crtc_state->connectors_changed || + crtc_state->color_mgmt_changed) { rc = dpu_crtc_assign_resources(crtc, crtc_state); if (rc < 0) return rc; @@ -1841,8 +1891,16 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane, drm_crtc_helper_add(crtc, &dpu_crtc_helper_funcs); - if (dpu_kms->catalog->dspp_count) - drm_crtc_enable_color_mgmt(crtc, 0, true, 0); + if (dpu_kms->catalog->dspp_count) { + const struct dpu_dspp_cfg *dspp = &dpu_kms->catalog->dspp[0]; + + if (dspp->sblk->gc.base) { + drm_mode_crtc_set_gamma_size(crtc, DPU_GAMMA_LUT_SIZE); + drm_crtc_enable_color_mgmt(crtc, 0, true, DPU_GAMMA_LUT_SIZE); + } else { + drm_crtc_enable_color_mgmt(crtc, 0, true, 0); + } + } /* save user friendly CRTC name for later */ snprintf(dpu_crtc->name, DPU_CRTC_NAME_SIZE, "crtc%u", crtc->base.id); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index be3492df8bde..c4e1f6b7345d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -402,11 +402,15 @@ static const struct dpu_lm_sub_blks qcm2290_lm_sblk = { static const struct dpu_dspp_sub_blks msm8998_dspp_sblk = { .pcc = {.name = "pcc", .base = 0x1700, .len = 0x90, .version = 0x10007}, + .gc = {.name = "gc", .base = 0x17c0, + .len = 0x40, .version = 0x10007}, }; static const struct dpu_dspp_sub_blks sdm845_dspp_sblk = { .pcc = {.name = "pcc", .base = 0x1700, .len = 0x90, .version = 0x40000}, + .gc = {.name = "gc", .base = 0x17c0, + .len = 0x40, .version = 0x10008}, }; static const struct dpu_dspp_sub_blks sm8750_dspp_sblk = { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index 24a14f8f8ad6..70d5ed4732f2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -77,9 +77,11 @@ enum { /** * DSPP sub-blocks * @DPU_DSPP_PCC Panel color correction block + * @DPU_DSPP_GC Gamma correction block */ enum { DPU_DSPP_PCC = 0x1, + DPU_DSPP_GC, DPU_DSPP_MAX }; @@ -328,9 +330,11 @@ struct dpu_lm_sub_blks { /** * struct dpu_dspp_sub_blks: Information of DSPP block * @pcc: pixel color correction block + * @gc: gamma correction block */ struct dpu_dspp_sub_blks { struct dpu_pp_blk pcc; + struct dpu_pp_blk gc; }; struct dpu_pingpong_sub_blks { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index ac834db2e4c1..36a497f1d6c1 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -399,6 +399,9 @@ static void dpu_hw_ctl_update_pending_flush_dspp_sub_blocks( case DPU_DSPP_PCC: ctx->pending_dspp_flush_mask[dspp - DSPP_0] |= BIT(4); break; + case DPU_DSPP_GC: + ctx->pending_dspp_flush_mask[dspp - DSPP_0] |= BIT(5); + break; default: return; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.c index 54b20faa0b69..188ee0af2c90 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.c @@ -24,6 +24,18 @@ #define PCC_BLUE_G_OFF 0x24 #define PCC_BLUE_B_OFF 0x30 +/* DSPP_GC */ +#define GC_EN BIT(0) +#define GC_DIS 0 +#define GC_8B_ROUND_EN BIT(1) +#define GC_LUT_SWAP_OFF 0x1c +#define GC_C0_OFF 0x4 +#define GC_C1_OFF 0xc +#define GC_C2_OFF 0x14 +#define GC_C0_INDEX_OFF 0x8 +#define GC_C1_INDEX_OFF 0x10 +#define GC_C2_INDEX_OFF 0x18 + static void dpu_setup_dspp_pcc(struct dpu_hw_dspp *ctx, struct dpu_hw_pcc_cfg *cfg) { @@ -63,6 +75,46 @@ static void dpu_setup_dspp_pcc(struct dpu_hw_dspp *ctx, DPU_REG_WRITE(&ctx->hw, base, PCC_EN); } +static void dpu_setup_dspp_gc(struct dpu_hw_dspp *ctx, + struct dpu_hw_gc_lut *gc_lut) +{ + int i = 0; + u32 base, reg; + + if (!ctx) { + DRM_ERROR("invalid ctx\n"); + return; + } + + base = ctx->cap->sblk->gc.base; + + if (!base) { + DRM_ERROR("invalid ctx %pK gc base\n", ctx); + return; + } + + if (!gc_lut) { + DRM_DEBUG_DRIVER("disable gc feature\n"); + DPU_REG_WRITE(&ctx->hw, base, GC_DIS); + return; + } + + DPU_REG_WRITE(&ctx->hw, base + GC_C0_INDEX_OFF, 0); + DPU_REG_WRITE(&ctx->hw, base + GC_C1_INDEX_OFF, 0); + DPU_REG_WRITE(&ctx->hw, base + GC_C2_INDEX_OFF, 0); + + for (i = 0; i < PGC_TBL_LEN; i++) { + DPU_REG_WRITE(&ctx->hw, base + GC_C0_OFF, gc_lut->c0[i]); + DPU_REG_WRITE(&ctx->hw, base + GC_C1_OFF, gc_lut->c1[i]); + DPU_REG_WRITE(&ctx->hw, base + GC_C2_OFF, gc_lut->c2[i]); + } + + DPU_REG_WRITE(&ctx->hw, base + GC_LUT_SWAP_OFF, BIT(0)); + + reg = GC_EN | ((gc_lut->flags & PGC_8B_ROUND) ? GC_8B_ROUND_EN : 0); + DPU_REG_WRITE(&ctx->hw, base, reg); +} + /** * dpu_hw_dspp_init() - Initializes the DSPP hw driver object. * should be called once before accessing every DSPP. @@ -92,6 +144,8 @@ struct dpu_hw_dspp *dpu_hw_dspp_init(struct drm_device *dev, c->cap = cfg; if (c->cap->sblk->pcc.base) c->ops.setup_pcc = dpu_setup_dspp_pcc; + if (c->cap->sblk->gc.base) + c->ops.setup_gc = dpu_setup_dspp_gc; return c; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.h index 722b0f482e9b..b47b7788064b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.h @@ -33,6 +33,25 @@ struct dpu_hw_pcc_cfg { struct dpu_hw_pcc_coeff b; }; +#define DPU_GAMMA_LUT_SIZE 1024 +#define PGC_TBL_LEN 512 +#define PGC_8B_ROUND BIT(0) + +/** + * struct dpu_hw_gc_lut - gc lut feature structure + * @flags: flags for the feature values can be: + * - PGC_8B_ROUND + * @c0: color0 component lut + * @c1: color1 component lut + * @c2: color2 component lut + */ +struct dpu_hw_gc_lut { + __u64 flags; + __u32 c0[PGC_TBL_LEN]; + __u32 c1[PGC_TBL_LEN]; + __u32 c2[PGC_TBL_LEN]; +}; + /** * struct dpu_hw_dspp_ops - interface to the dspp hardware driver functions * Caller must call the init function to get the dspp context for each dspp @@ -46,6 +65,13 @@ struct dpu_hw_dspp_ops { */ void (*setup_pcc)(struct dpu_hw_dspp *ctx, struct dpu_hw_pcc_cfg *cfg); + /** + * setup_gc - setup dspp gc + * @ctx: Pointer to dspp context + * @gc_lut: Pointer to lut content + */ + void (*setup_gc)(struct dpu_hw_dspp *ctx, struct dpu_hw_gc_lut *gc_lut); + }; /** |
