linux/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
   3 */
   4
   5#include "dpu_kms.h"
   6#include "dpu_hw_catalog.h"
   7#include "dpu_hwio.h"
   8#include "dpu_hw_lm.h"
   9#include "dpu_hw_mdss.h"
  10
  11#define LM_OP_MODE                        0x00
  12#define LM_OUT_SIZE                       0x04
  13#define LM_BORDER_COLOR_0                 0x08
  14#define LM_BORDER_COLOR_1                 0x010
  15
  16/* These register are offset to mixer base + stage base */
  17#define LM_BLEND0_OP                     0x00
  18#define LM_BLEND0_CONST_ALPHA            0x04
  19#define LM_FG_COLOR_FILL_COLOR_0         0x08
  20#define LM_FG_COLOR_FILL_COLOR_1         0x0C
  21#define LM_FG_COLOR_FILL_SIZE            0x10
  22#define LM_FG_COLOR_FILL_XY              0x14
  23
  24#define LM_BLEND0_FG_ALPHA               0x04
  25#define LM_BLEND0_BG_ALPHA               0x08
  26
  27static struct dpu_lm_cfg *_lm_offset(enum dpu_lm mixer,
  28                struct dpu_mdss_cfg *m,
  29                void __iomem *addr,
  30                struct dpu_hw_blk_reg_map *b)
  31{
  32        int i;
  33
  34        for (i = 0; i < m->mixer_count; i++) {
  35                if (mixer == m->mixer[i].id) {
  36                        b->base_off = addr;
  37                        b->blk_off = m->mixer[i].base;
  38                        b->length = m->mixer[i].len;
  39                        b->hwversion = m->hwversion;
  40                        b->log_mask = DPU_DBG_MASK_LM;
  41                        return &m->mixer[i];
  42                }
  43        }
  44
  45        return ERR_PTR(-ENOMEM);
  46}
  47
  48/**
  49 * _stage_offset(): returns the relative offset of the blend registers
  50 * for the stage to be setup
  51 * @c:     mixer ctx contains the mixer to be programmed
  52 * @stage: stage index to setup
  53 */
  54static inline int _stage_offset(struct dpu_hw_mixer *ctx, enum dpu_stage stage)
  55{
  56        const struct dpu_lm_sub_blks *sblk = ctx->cap->sblk;
  57        if (stage != DPU_STAGE_BASE && stage <= sblk->maxblendstages)
  58                return sblk->blendstage_base[stage - DPU_STAGE_0];
  59
  60        return -EINVAL;
  61}
  62
  63static void dpu_hw_lm_setup_out(struct dpu_hw_mixer *ctx,
  64                struct dpu_hw_mixer_cfg *mixer)
  65{
  66        struct dpu_hw_blk_reg_map *c = &ctx->hw;
  67        u32 outsize;
  68        u32 op_mode;
  69
  70        op_mode = DPU_REG_READ(c, LM_OP_MODE);
  71
  72        outsize = mixer->out_height << 16 | mixer->out_width;
  73        DPU_REG_WRITE(c, LM_OUT_SIZE, outsize);
  74
  75        /* SPLIT_LEFT_RIGHT */
  76        if (mixer->right_mixer)
  77                op_mode |= BIT(31);
  78        else
  79                op_mode &= ~BIT(31);
  80        DPU_REG_WRITE(c, LM_OP_MODE, op_mode);
  81}
  82
  83static void dpu_hw_lm_setup_border_color(struct dpu_hw_mixer *ctx,
  84                struct dpu_mdss_color *color,
  85                u8 border_en)
  86{
  87        struct dpu_hw_blk_reg_map *c = &ctx->hw;
  88
  89        if (border_en) {
  90                DPU_REG_WRITE(c, LM_BORDER_COLOR_0,
  91                        (color->color_0 & 0xFFF) |
  92                        ((color->color_1 & 0xFFF) << 0x10));
  93                DPU_REG_WRITE(c, LM_BORDER_COLOR_1,
  94                        (color->color_2 & 0xFFF) |
  95                        ((color->color_3 & 0xFFF) << 0x10));
  96        }
  97}
  98
  99static void dpu_hw_lm_setup_blend_config_sdm845(struct dpu_hw_mixer *ctx,
 100        u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op)
 101{
 102        struct dpu_hw_blk_reg_map *c = &ctx->hw;
 103        int stage_off;
 104        u32 const_alpha;
 105
 106        if (stage == DPU_STAGE_BASE)
 107                return;
 108
 109        stage_off = _stage_offset(ctx, stage);
 110        if (WARN_ON(stage_off < 0))
 111                return;
 112
 113        const_alpha = (bg_alpha & 0xFF) | ((fg_alpha & 0xFF) << 16);
 114        DPU_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, const_alpha);
 115        DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op);
 116}
 117
 118static void dpu_hw_lm_setup_blend_config(struct dpu_hw_mixer *ctx,
 119        u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op)
 120{
 121        struct dpu_hw_blk_reg_map *c = &ctx->hw;
 122        int stage_off;
 123
 124        if (stage == DPU_STAGE_BASE)
 125                return;
 126
 127        stage_off = _stage_offset(ctx, stage);
 128        if (WARN_ON(stage_off < 0))
 129                return;
 130
 131        DPU_REG_WRITE(c, LM_BLEND0_FG_ALPHA + stage_off, fg_alpha);
 132        DPU_REG_WRITE(c, LM_BLEND0_BG_ALPHA + stage_off, bg_alpha);
 133        DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op);
 134}
 135
 136static void dpu_hw_lm_setup_color3(struct dpu_hw_mixer *ctx,
 137        uint32_t mixer_op_mode)
 138{
 139        struct dpu_hw_blk_reg_map *c = &ctx->hw;
 140        int op_mode;
 141
 142        /* read the existing op_mode configuration */
 143        op_mode = DPU_REG_READ(c, LM_OP_MODE);
 144
 145        op_mode = (op_mode & (BIT(31) | BIT(30))) | mixer_op_mode;
 146
 147        DPU_REG_WRITE(c, LM_OP_MODE, op_mode);
 148}
 149
 150static void _setup_mixer_ops(struct dpu_mdss_cfg *m,
 151                struct dpu_hw_lm_ops *ops,
 152                unsigned long features)
 153{
 154        ops->setup_mixer_out = dpu_hw_lm_setup_out;
 155        if (IS_SDM845_TARGET(m->hwversion) || IS_SDM670_TARGET(m->hwversion))
 156                ops->setup_blend_config = dpu_hw_lm_setup_blend_config_sdm845;
 157        else
 158                ops->setup_blend_config = dpu_hw_lm_setup_blend_config;
 159        ops->setup_alpha_out = dpu_hw_lm_setup_color3;
 160        ops->setup_border_color = dpu_hw_lm_setup_border_color;
 161};
 162
 163static struct dpu_hw_blk_ops dpu_hw_ops;
 164
 165struct dpu_hw_mixer *dpu_hw_lm_init(enum dpu_lm idx,
 166                void __iomem *addr,
 167                struct dpu_mdss_cfg *m)
 168{
 169        struct dpu_hw_mixer *c;
 170        struct dpu_lm_cfg *cfg;
 171
 172        c = kzalloc(sizeof(*c), GFP_KERNEL);
 173        if (!c)
 174                return ERR_PTR(-ENOMEM);
 175
 176        cfg = _lm_offset(idx, m, addr, &c->hw);
 177        if (IS_ERR_OR_NULL(cfg)) {
 178                kfree(c);
 179                return ERR_PTR(-EINVAL);
 180        }
 181
 182        /* Assign ops */
 183        c->idx = idx;
 184        c->cap = cfg;
 185        _setup_mixer_ops(m, &c->ops, c->cap->features);
 186
 187        dpu_hw_blk_init(&c->base, DPU_HW_BLK_LM, idx, &dpu_hw_ops);
 188
 189        return c;
 190}
 191
 192void dpu_hw_lm_destroy(struct dpu_hw_mixer *lm)
 193{
 194        if (lm)
 195                dpu_hw_blk_destroy(&lm->base);
 196        kfree(lm);
 197}
 198