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

drm/msm/dpu: Implement LM crossbar for v12.0 DPU

v12.0 DPU on SM8750 comes with new LM crossbar that requires each pipe
rectangle to be programmed separately in blend stage. Implement support
for this along with a new CTL_LAYER_ACTIVE register and setting the
blend stage in layer mixer code.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Patchwork: https://patchwork.freedesktop.org/patch/659632/
Link: https://lore.kernel.org/r/20250618-b4-sm8750-display-v7-12-a591c609743d@linaro.org
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

authored by

Krzysztof Kozlowski and committed by
Dmitry Baryshkov
68baf833 b567e928

+201 -3
+16 -2
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
··· 525 525 struct dpu_hw_ctl *ctl; 526 526 struct dpu_hw_mixer *lm; 527 527 struct dpu_hw_stage_cfg stage_cfg; 528 + DECLARE_BITMAP(active_lms, LM_MAX); 528 529 int i; 529 530 530 531 DRM_DEBUG_ATOMIC("%s\n", dpu_crtc->name); ··· 539 538 mixer[i].lm_ctl->ops.set_active_fetch_pipes(mixer[i].lm_ctl, NULL); 540 539 if (mixer[i].lm_ctl->ops.set_active_pipes) 541 540 mixer[i].lm_ctl->ops.set_active_pipes(mixer[i].lm_ctl, NULL); 541 + 542 + if (mixer[i].hw_lm->ops.clear_all_blendstages) 543 + mixer[i].hw_lm->ops.clear_all_blendstages(mixer[i].hw_lm); 542 544 } 543 545 544 546 /* initialize stage cfg */ 545 547 memset(&stage_cfg, 0, sizeof(struct dpu_hw_stage_cfg)); 548 + memset(active_lms, 0, sizeof(active_lms)); 546 549 547 550 _dpu_crtc_blend_setup_mixer(crtc, dpu_crtc, mixer, &stage_cfg); 548 551 ··· 560 555 ctl->ops.update_pending_flush_mixer(ctl, 561 556 mixer[i].hw_lm->idx); 562 557 558 + set_bit(lm->idx, active_lms); 559 + if (ctl->ops.set_active_lms) 560 + ctl->ops.set_active_lms(ctl, active_lms); 561 + 563 562 DRM_DEBUG_ATOMIC("lm %d, op_mode 0x%X, ctl %d\n", 564 563 mixer[i].hw_lm->idx - LM_0, 565 564 mixer[i].mixer_op_mode, 566 565 ctl->idx - CTL_0); 567 566 568 - ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx, 569 - &stage_cfg); 567 + if (ctl->ops.setup_blendstage) 568 + ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx, 569 + &stage_cfg); 570 + 571 + if (lm->ops.setup_blendstage) 572 + lm->ops.setup_blendstage(lm, mixer[i].hw_lm->idx, 573 + &stage_cfg); 570 574 } 571 575 } 572 576
+6
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
··· 2195 2195 if (ctl->ops.setup_blendstage) 2196 2196 ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL); 2197 2197 2198 + if (hw_mixer[i]->ops.clear_all_blendstages) 2199 + hw_mixer[i]->ops.clear_all_blendstages(hw_mixer[i]); 2200 + 2201 + if (ctl->ops.set_active_lms) 2202 + ctl->ops.set_active_lms(ctl, NULL); 2203 + 2198 2204 if (ctl->ops.set_active_fetch_pipes) 2199 2205 ctl->ops.set_active_fetch_pipes(ctl, NULL); 2200 2206
+26 -1
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
··· 43 43 #define CTL_CDM_FLUSH 0x114 44 44 #define CTL_PERIPH_FLUSH 0x128 45 45 #define CTL_PIPE_ACTIVE 0x12c 46 + #define CTL_LAYER_ACTIVE 0x130 46 47 #define CTL_INTF_MASTER 0x134 47 48 #define CTL_DSPP_n_FLUSH(n) ((0x13C) + ((n) * 4)) 48 49 ··· 65 64 static const u32 fetch_tbl[SSPP_MAX] = {CTL_INVALID_BIT, 16, 17, 18, 19, 66 65 CTL_INVALID_BIT, CTL_INVALID_BIT, CTL_INVALID_BIT, CTL_INVALID_BIT, 0, 67 66 1, 2, 3, 4, 5}; 67 + 68 + static const u32 lm_tbl[LM_MAX] = {CTL_INVALID_BIT, 0, 1, 2, 3, 4, 5, 6, 7}; 68 69 69 70 static int _mixer_stages(const struct dpu_lm_cfg *mixer, int count, 70 71 enum dpu_lm lm) ··· 680 677 merge3d_active); 681 678 } 682 679 683 - dpu_hw_ctl_clear_all_blendstages(ctx); 680 + if (ctx->ops.clear_all_blendstages) 681 + ctx->ops.clear_all_blendstages(ctx); 682 + 683 + if (ctx->ops.set_active_lms) 684 + ctx->ops.set_active_lms(ctx, NULL); 684 685 685 686 if (ctx->ops.set_active_fetch_pipes) 686 687 ctx->ops.set_active_fetch_pipes(ctx, NULL); ··· 765 758 DPU_REG_WRITE(&ctx->hw, CTL_PIPE_ACTIVE, val); 766 759 } 767 760 761 + static void dpu_hw_ctl_set_active_lms(struct dpu_hw_ctl *ctx, 762 + unsigned long *active_lms) 763 + { 764 + int i; 765 + u32 val = 0; 766 + 767 + if (active_lms) { 768 + for (i = LM_0; i < LM_MAX; i++) { 769 + if (test_bit(i, active_lms) && 770 + lm_tbl[i] != CTL_INVALID_BIT) 771 + val |= BIT(lm_tbl[i]); 772 + } 773 + } 774 + 775 + DPU_REG_WRITE(&ctx->hw, CTL_LAYER_ACTIVE, val); 776 + } 777 + 768 778 /** 769 779 * dpu_hw_ctl_init() - Initializes the ctl_path hw driver object. 770 780 * Should be called before accessing any ctl_path register. ··· 850 826 c->ops.setup_blendstage = dpu_hw_ctl_setup_blendstage; 851 827 } else { 852 828 c->ops.set_active_pipes = dpu_hw_ctl_set_active_pipes; 829 + c->ops.set_active_lms = dpu_hw_ctl_set_active_lms; 853 830 } 854 831 c->ops.update_pending_flush_sspp = dpu_hw_ctl_update_pending_flush_sspp; 855 832 c->ops.update_pending_flush_mixer = dpu_hw_ctl_update_pending_flush_mixer;
+9
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
··· 266 266 */ 267 267 void (*set_active_pipes)(struct dpu_hw_ctl *ctx, 268 268 unsigned long *active_pipes); 269 + 270 + /** 271 + * Set active layer mixers attached to this CTL 272 + * @ctx: ctl path ctx pointer 273 + * @active_lms: bitmap of enum dpu_lm 274 + */ 275 + void (*set_active_lms)(struct dpu_hw_ctl *ctx, 276 + unsigned long *active_lms); 277 + 269 278 }; 270 279 271 280 /**
+126
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c
··· 28 28 #define LM_FG_COLOR_FILL_XY 0x14 29 29 30 30 /* >= v12 DPU */ 31 + #define LM_BG_SRC_SEL_V12 0x14 32 + #define LM_BG_SRC_SEL_V12_RESET_VALUE 0x0000c0c0 31 33 #define LM_BORDER_COLOR_0_V12 0x1c 32 34 #define LM_BORDER_COLOR_1_V12 0x20 33 35 34 36 /* >= v12 DPU with offset to mixer base + stage base */ 37 + #define LM_BLEND0_FG_SRC_SEL_V12 0x04 35 38 #define LM_BLEND0_CONST_ALPHA_V12 0x08 39 + #define LM_FG_COLOR_FILL_COLOR_0_V12 0x0c 40 + #define LM_FG_COLOR_FILL_COLOR_1_V12 0x10 41 + #define LM_FG_COLOR_FILL_SIZE_V12 0x14 42 + #define LM_FG_COLOR_FILL_XY_V12 0x18 43 + 36 44 #define LM_BLEND0_FG_ALPHA 0x04 37 45 #define LM_BLEND0_BG_ALPHA 0x08 38 46 ··· 223 215 } 224 216 } 225 217 218 + static int _set_staged_sspp(u32 stage, struct dpu_hw_stage_cfg *stage_cfg, 219 + int pipes_per_stage, u32 *value) 220 + { 221 + int i; 222 + u32 pipe_type = 0, pipe_id = 0, rec_id = 0; 223 + u32 src_sel[PIPES_PER_STAGE]; 224 + 225 + *value = LM_BG_SRC_SEL_V12_RESET_VALUE; 226 + if (!stage_cfg || !pipes_per_stage) 227 + return 0; 228 + 229 + for (i = 0; i < pipes_per_stage; i++) { 230 + enum dpu_sspp pipe = stage_cfg->stage[stage][i]; 231 + enum dpu_sspp_multirect_index rect_index = stage_cfg->multirect_index[stage][i]; 232 + 233 + src_sel[i] = LM_BG_SRC_SEL_V12_RESET_VALUE; 234 + 235 + if (!pipe) 236 + continue; 237 + 238 + /* translate pipe data to SWI pipe_type, pipe_id */ 239 + if (pipe >= SSPP_DMA0 && pipe <= SSPP_DMA5) { 240 + pipe_type = 0; 241 + pipe_id = pipe - SSPP_DMA0; 242 + } else if (pipe >= SSPP_VIG0 && pipe <= SSPP_VIG3) { 243 + pipe_type = 1; 244 + pipe_id = pipe - SSPP_VIG0; 245 + } else { 246 + DPU_ERROR("invalid rec-%d pipe:%d\n", i, pipe); 247 + return -EINVAL; 248 + } 249 + 250 + /* translate rec data to SWI rec_id */ 251 + if (rect_index == DPU_SSPP_RECT_SOLO || rect_index == DPU_SSPP_RECT_0) { 252 + rec_id = 0; 253 + } else if (rect_index == DPU_SSPP_RECT_1) { 254 + rec_id = 1; 255 + } else { 256 + DPU_ERROR("invalid rec-%d rect_index:%d\n", i, rect_index); 257 + rec_id = 0; 258 + } 259 + 260 + /* calculate SWI value for rec-0 and rec-1 and store it temporary buffer */ 261 + src_sel[i] = (((pipe_type & 0x3) << 6) | ((rec_id & 0x3) << 4) | (pipe_id & 0xf)); 262 + } 263 + 264 + /* calculate final SWI register value for rec-0 and rec-1 */ 265 + *value = 0; 266 + for (i = 0; i < pipes_per_stage; i++) 267 + *value |= src_sel[i] << (i * 8); 268 + 269 + return 0; 270 + } 271 + 272 + static int dpu_hw_lm_setup_blendstage(struct dpu_hw_mixer *ctx, enum dpu_lm lm, 273 + struct dpu_hw_stage_cfg *stage_cfg) 274 + { 275 + struct dpu_hw_blk_reg_map *c = &ctx->hw; 276 + int i, ret, stages, stage_off, pipes_per_stage; 277 + u32 value; 278 + 279 + stages = ctx->cap->sblk->maxblendstages; 280 + if (stages <= 0) 281 + return -EINVAL; 282 + 283 + if (test_bit(DPU_MIXER_SOURCESPLIT, &ctx->cap->features)) 284 + pipes_per_stage = PIPES_PER_STAGE; 285 + else 286 + pipes_per_stage = 1; 287 + 288 + /* 289 + * When stage configuration is empty, we can enable the 290 + * border color by setting the corresponding LAYER_ACTIVE bit 291 + * and un-staging all the pipes from the layer mixer. 292 + */ 293 + if (!stage_cfg) 294 + DPU_REG_WRITE(c, LM_BG_SRC_SEL_V12, LM_BG_SRC_SEL_V12_RESET_VALUE); 295 + 296 + for (i = DPU_STAGE_0; i <= stages; i++) { 297 + stage_off = _stage_offset(ctx, i); 298 + if (stage_off < 0) 299 + return stage_off; 300 + 301 + ret = _set_staged_sspp(i, stage_cfg, pipes_per_stage, &value); 302 + if (ret) 303 + return ret; 304 + 305 + DPU_REG_WRITE(c, LM_BLEND0_FG_SRC_SEL_V12 + stage_off, value); 306 + } 307 + 308 + return 0; 309 + } 310 + 311 + static int dpu_hw_lm_clear_all_blendstages(struct dpu_hw_mixer *ctx) 312 + { 313 + struct dpu_hw_blk_reg_map *c = &ctx->hw; 314 + int i, stages, stage_off; 315 + 316 + stages = ctx->cap->sblk->maxblendstages; 317 + if (stages <= 0) 318 + return -EINVAL; 319 + 320 + DPU_REG_WRITE(c, LM_BG_SRC_SEL_V12, LM_BG_SRC_SEL_V12_RESET_VALUE); 321 + 322 + for (i = DPU_STAGE_0; i <= stages; i++) { 323 + stage_off = _stage_offset(ctx, i); 324 + if (stage_off < 0) 325 + return stage_off; 326 + 327 + DPU_REG_WRITE(c, LM_BLEND0_FG_SRC_SEL_V12 + stage_off, 328 + LM_BG_SRC_SEL_V12_RESET_VALUE); 329 + } 330 + 331 + return 0; 332 + } 333 + 226 334 /** 227 335 * dpu_hw_lm_init() - Initializes the mixer hw driver object. 228 336 * should be called once before accessing every mixer. ··· 381 257 c->ops.setup_border_color = dpu_hw_lm_setup_border_color; 382 258 } else { 383 259 c->ops.setup_alpha_out = dpu_hw_lm_setup_color3_v12; 260 + c->ops.setup_blendstage = dpu_hw_lm_setup_blendstage; 261 + c->ops.clear_all_blendstages = dpu_hw_lm_clear_all_blendstages; 384 262 c->ops.setup_border_color = dpu_hw_lm_setup_border_color_v12; 385 263 } 386 264 c->ops.setup_misr = dpu_hw_lm_setup_misr;
+18
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.h
··· 11 11 #include "dpu_hw_util.h" 12 12 13 13 struct dpu_hw_mixer; 14 + struct dpu_hw_stage_cfg; 14 15 15 16 struct dpu_hw_mixer_cfg { 16 17 u32 out_width; ··· 48 47 * Alpha color component selection from either fg or bg 49 48 */ 50 49 void (*setup_alpha_out)(struct dpu_hw_mixer *ctx, uint32_t mixer_op); 50 + 51 + /** 52 + * Clear layer mixer to pipe configuration 53 + * @ctx : mixer ctx pointer 54 + * Returns: 0 on success or -error 55 + */ 56 + int (*clear_all_blendstages)(struct dpu_hw_mixer *ctx); 57 + 58 + /** 59 + * Configure layer mixer to pipe configuration 60 + * @ctx : mixer ctx pointer 61 + * @lm : layer mixer enumeration 62 + * @stage_cfg : blend stage configuration 63 + * Returns: 0 on success or -error 64 + */ 65 + int (*setup_blendstage)(struct dpu_hw_mixer *ctx, enum dpu_lm lm, 66 + struct dpu_hw_stage_cfg *stage_cfg); 51 67 52 68 /** 53 69 * setup_border_color : enable/disable border color