mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-05 02:26:24 -04:00
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>
This commit is contained in:
committed by
Dmitry Baryshkov
parent
f185076da4
commit
39a750ff5f
@@ -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);
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user