1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
*/
#include <drm/drm_managed.h>
#include "dpu_kms.h"
#include "dpu_hw_catalog.h"
#include "dpu_hwio.h"
#include "dpu_hw_lm.h"
#include "dpu_hw_mdss.h"
#define LM_OP_MODE 0x00
#define LM_OUT_SIZE 0x04
#define LM_BORDER_COLOR_0 0x08
#define LM_BORDER_COLOR_1 0x010
/* These register are offset to mixer base + stage base */
#define LM_BLEND0_OP 0x00
/* <v12 DPU with offset to mixer base + stage base */
#define LM_BLEND0_CONST_ALPHA 0x04
#define LM_FG_COLOR_FILL_COLOR_0 0x08
#define LM_FG_COLOR_FILL_COLOR_1 0x0C
#define LM_FG_COLOR_FILL_SIZE 0x10
#define LM_FG_COLOR_FILL_XY 0x14
/* >= v12 DPU */
#define LM_BG_SRC_SEL_V12 0x14
#define LM_BG_SRC_SEL_V12_RESET_VALUE 0x0000c0c0
#define LM_BORDER_COLOR_0_V12 0x1c
#define LM_BORDER_COLOR_1_V12 0x20
/* >= v12 DPU with offset to mixer base + stage base */
#define LM_BLEND0_FG_SRC_SEL_V12 0x04
#define LM_BLEND0_CONST_ALPHA_V12 0x08
#define LM_FG_COLOR_FILL_COLOR_0_V12 0x0c
#define LM_FG_COLOR_FILL_COLOR_1_V12 0x10
#define LM_FG_COLOR_FILL_SIZE_V12 0x14
#define LM_FG_COLOR_FILL_XY_V12 0x18
#define LM_BLEND0_FG_ALPHA 0x04
#define LM_BLEND0_BG_ALPHA 0x08
#define LM_MISR_CTRL 0x310
#define LM_MISR_SIGNATURE 0x314
/**
* _stage_offset(): returns the relative offset of the blend registers
* for the stage to be setup
* @ctx: mixer ctx contains the mixer to be programmed
* @stage: stage index to setup
*/
static inline int _stage_offset(struct dpu_hw_mixer *ctx, enum dpu_stage stage)
{
const struct dpu_lm_sub_blks *sblk = ctx->cap->sblk;
if (stage != DPU_STAGE_BASE && stage <= sblk->maxblendstages)
return sblk->blendstage_base[stage - DPU_STAGE_0];
return -EINVAL;
}
static void dpu_hw_lm_setup_out(struct dpu_hw_mixer *ctx,
struct dpu_hw_mixer_cfg *mixer)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
u32 outsize;
u32 op_mode;
op_mode = DPU_REG_READ(c, LM_OP_MODE);
outsize = mixer->out_height << 16 | mixer->out_width;
DPU_REG_WRITE(c, LM_OUT_SIZE, outsize);
/* SPLIT_LEFT_RIGHT */
if (mixer->right_mixer)
op_mode |= BIT(31);
else
op_mode &= ~BIT(31);
DPU_REG_WRITE(c, LM_OP_MODE, op_mode);
}
static void dpu_hw_lm_setup_border_color(struct dpu_hw_mixer *ctx,
struct dpu_mdss_color *color,
u8 border_en)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
if (border_en) {
DPU_REG_WRITE(c, LM_BORDER_COLOR_0,
(color->color_0 & 0xFFF) |
((color->color_1 & 0xFFF) << 0x10));
DPU_REG_WRITE(c, LM_BORDER_COLOR_1,
(color->color_2 & 0xFFF) |
((color->color_3 & 0xFFF) << 0x10));
}
}
static void dpu_hw_lm_setup_border_color_v12(struct dpu_hw_mixer *ctx,
struct dpu_mdss_color *color,
u8 border_en)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
if (border_en) {
DPU_REG_WRITE(c, LM_BORDER_COLOR_0_V12,
(color->color_0 & 0x3ff) |
((color->color_1 & 0x3ff) << 16));
DPU_REG_WRITE(c, LM_BORDER_COLOR_1_V12,
(color->color_2 & 0x3ff) |
((color->color_3 & 0x3ff) << 16));
}
}
static void dpu_hw_lm_setup_misr(struct dpu_hw_mixer *ctx)
{
dpu_hw_setup_misr(&ctx->hw, LM_MISR_CTRL, 0x0);
}
static int dpu_hw_lm_collect_misr(struct dpu_hw_mixer *ctx, u32 *misr_value)
{
return dpu_hw_collect_misr(&ctx->hw, LM_MISR_CTRL, LM_MISR_SIGNATURE, misr_value);
}
static void dpu_hw_lm_setup_blend_config_combined_alpha(struct dpu_hw_mixer *ctx,
u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
int stage_off;
u32 const_alpha;
if (stage == DPU_STAGE_BASE)
return;
stage_off = _stage_offset(ctx, stage);
if (WARN_ON(stage_off < 0))
return;
const_alpha = (bg_alpha & 0xFF) | ((fg_alpha & 0xFF) << 16);
DPU_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, const_alpha);
DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op);
}
static void
dpu_hw_lm_setup_blend_config_combined_alpha_v12(struct dpu_hw_mixer *ctx,
u32 stage, u32 fg_alpha,
u32 bg_alpha, u32 blend_op)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
int stage_off;
u32 const_alpha;
if (stage == DPU_STAGE_BASE)
return;
stage_off = _stage_offset(ctx, stage);
if (WARN_ON(stage_off < 0))
return;
const_alpha = (bg_alpha & 0x3ff) | ((fg_alpha & 0x3ff) << 16);
DPU_REG_WRITE(c, LM_BLEND0_CONST_ALPHA_V12 + stage_off, const_alpha);
DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op);
}
static void dpu_hw_lm_setup_blend_config(struct dpu_hw_mixer *ctx,
u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
int stage_off;
if (stage == DPU_STAGE_BASE)
return;
stage_off = _stage_offset(ctx, stage);
if (WARN_ON(stage_off < 0))
return;
DPU_REG_WRITE(c, LM_BLEND0_FG_ALPHA + stage_off, fg_alpha);
DPU_REG_WRITE(c, LM_BLEND0_BG_ALPHA + stage_off, bg_alpha);
DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op);
}
static void dpu_hw_lm_setup_color3(struct dpu_hw_mixer *ctx,
uint32_t mixer_op_mode)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
int op_mode;
/* read the existing op_mode configuration */
op_mode = DPU_REG_READ(c, LM_OP_MODE);
op_mode = (op_mode & (BIT(31) | BIT(30))) | mixer_op_mode;
DPU_REG_WRITE(c, LM_OP_MODE, op_mode);
}
static void dpu_hw_lm_setup_color3_v12(struct dpu_hw_mixer *ctx,
uint32_t mixer_op_mode)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
int op_mode, stages, stage_off, i;
stages = ctx->cap->sblk->maxblendstages;
if (stages <= 0)
return;
for (i = DPU_STAGE_0; i <= stages; i++) {
stage_off = _stage_offset(ctx, i);
if (WARN_ON(stage_off < 0))
return;
/* set color_out3 bit in blend0_op when enabled in mixer_op_mode */
op_mode = DPU_REG_READ(c, LM_BLEND0_OP + stage_off);
if (mixer_op_mode & BIT(i))
op_mode |= BIT(30);
else
op_mode &= ~BIT(30);
DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, op_mode);
}
}
static int _set_staged_sspp(u32 stage, struct dpu_hw_stage_cfg *stage_cfg,
int pipes_per_stage, u32 *value)
{
int i;
u32 pipe_type = 0, pipe_id = 0, rec_id = 0;
u32 src_sel[PIPES_PER_STAGE];
*value = LM_BG_SRC_SEL_V12_RESET_VALUE;
if (!stage_cfg || !pipes_per_stage)
return 0;
for (i = 0; i < pipes_per_stage; i++) {
enum dpu_sspp pipe = stage_cfg->stage[stage][i];
enum dpu_sspp_multirect_index rect_index = stage_cfg->multirect_index[stage][i];
src_sel[i] = LM_BG_SRC_SEL_V12_RESET_VALUE;
if (!pipe)
continue;
/* translate pipe data to SWI pipe_type, pipe_id */
if (pipe >= SSPP_DMA0 && pipe <= SSPP_DMA5) {
pipe_type = 0;
pipe_id = pipe - SSPP_DMA0;
} else if (pipe >= SSPP_VIG0 && pipe <= SSPP_VIG3) {
pipe_type = 1;
pipe_id = pipe - SSPP_VIG0;
} else {
DPU_ERROR("invalid rec-%d pipe:%d\n", i, pipe);
return -EINVAL;
}
/* translate rec data to SWI rec_id */
if (rect_index == DPU_SSPP_RECT_SOLO || rect_index == DPU_SSPP_RECT_0) {
rec_id = 0;
} else if (rect_index == DPU_SSPP_RECT_1) {
rec_id = 1;
} else {
DPU_ERROR("invalid rec-%d rect_index:%d\n", i, rect_index);
rec_id = 0;
}
/* calculate SWI value for rec-0 and rec-1 and store it temporary buffer */
src_sel[i] = (((pipe_type & 0x3) << 6) | ((rec_id & 0x3) << 4) | (pipe_id & 0xf));
}
/* calculate final SWI register value for rec-0 and rec-1 */
*value = 0;
for (i = 0; i < pipes_per_stage; i++)
*value |= src_sel[i] << (i * 8);
return 0;
}
static int dpu_hw_lm_setup_blendstage(struct dpu_hw_mixer *ctx, enum dpu_lm lm,
struct dpu_hw_stage_cfg *stage_cfg)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
int i, ret, stages, stage_off, pipes_per_stage;
u32 value;
stages = ctx->cap->sblk->maxblendstages;
if (stages <= 0)
return -EINVAL;
if (test_bit(DPU_MIXER_SOURCESPLIT, &ctx->cap->features))
pipes_per_stage = PIPES_PER_STAGE;
else
pipes_per_stage = 1;
/*
* When stage configuration is empty, we can enable the
* border color by setting the corresponding LAYER_ACTIVE bit
* and un-staging all the pipes from the layer mixer.
*/
if (!stage_cfg)
DPU_REG_WRITE(c, LM_BG_SRC_SEL_V12, LM_BG_SRC_SEL_V12_RESET_VALUE);
for (i = DPU_STAGE_0; i <= stages; i++) {
stage_off = _stage_offset(ctx, i);
if (stage_off < 0)
return stage_off;
ret = _set_staged_sspp(i, stage_cfg, pipes_per_stage, &value);
if (ret)
return ret;
DPU_REG_WRITE(c, LM_BLEND0_FG_SRC_SEL_V12 + stage_off, value);
}
return 0;
}
static int dpu_hw_lm_clear_all_blendstages(struct dpu_hw_mixer *ctx)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
int i, stages, stage_off;
stages = ctx->cap->sblk->maxblendstages;
if (stages <= 0)
return -EINVAL;
DPU_REG_WRITE(c, LM_BG_SRC_SEL_V12, LM_BG_SRC_SEL_V12_RESET_VALUE);
for (i = DPU_STAGE_0; i <= stages; i++) {
stage_off = _stage_offset(ctx, i);
if (stage_off < 0)
return stage_off;
DPU_REG_WRITE(c, LM_BLEND0_FG_SRC_SEL_V12 + stage_off,
LM_BG_SRC_SEL_V12_RESET_VALUE);
}
return 0;
}
/**
* dpu_hw_lm_init() - Initializes the mixer hw driver object.
* should be called once before accessing every mixer.
* @dev: Corresponding device for devres management
* @cfg: mixer catalog entry for which driver object is required
* @addr: mapped register io address of MDP
* @mdss_ver: DPU core's major and minor versions
*/
struct dpu_hw_mixer *dpu_hw_lm_init(struct drm_device *dev,
const struct dpu_lm_cfg *cfg,
void __iomem *addr,
const struct dpu_mdss_version *mdss_ver)
{
struct dpu_hw_mixer *c;
if (cfg->pingpong == PINGPONG_NONE) {
DPU_DEBUG("skip mixer %d without pingpong\n", cfg->id);
return NULL;
}
c = drmm_kzalloc(dev, sizeof(*c), GFP_KERNEL);
if (!c)
return ERR_PTR(-ENOMEM);
c->hw.blk_addr = addr + cfg->base;
c->hw.log_mask = DPU_DBG_MASK_LM;
/* Assign ops */
c->idx = cfg->id;
c->cap = cfg;
c->ops.setup_mixer_out = dpu_hw_lm_setup_out;
if (mdss_ver->core_major_ver >= 12)
c->ops.setup_blend_config = dpu_hw_lm_setup_blend_config_combined_alpha_v12;
else if (mdss_ver->core_major_ver >= 4)
c->ops.setup_blend_config = dpu_hw_lm_setup_blend_config_combined_alpha;
else
c->ops.setup_blend_config = dpu_hw_lm_setup_blend_config;
if (mdss_ver->core_major_ver < 12) {
c->ops.setup_alpha_out = dpu_hw_lm_setup_color3;
c->ops.setup_border_color = dpu_hw_lm_setup_border_color;
} else {
c->ops.setup_alpha_out = dpu_hw_lm_setup_color3_v12;
c->ops.setup_blendstage = dpu_hw_lm_setup_blendstage;
c->ops.clear_all_blendstages = dpu_hw_lm_clear_all_blendstages;
c->ops.setup_border_color = dpu_hw_lm_setup_border_color_v12;
}
c->ops.setup_misr = dpu_hw_lm_setup_misr;
c->ops.collect_misr = dpu_hw_lm_collect_misr;
return c;
}
|