linux/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.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_hwio.h"
   6#include "dpu_hw_catalog.h"
   7#include "dpu_hw_top.h"
   8#include "dpu_kms.h"
   9
  10#define SSPP_SPARE                        0x28
  11#define UBWC_STATIC                       0x144
  12
  13#define FLD_SPLIT_DISPLAY_CMD             BIT(1)
  14#define FLD_SMART_PANEL_FREE_RUN          BIT(2)
  15#define FLD_INTF_1_SW_TRG_MUX             BIT(4)
  16#define FLD_INTF_2_SW_TRG_MUX             BIT(8)
  17#define FLD_TE_LINE_INTER_WATERLEVEL_MASK 0xFFFF
  18
  19#define DANGER_STATUS                     0x360
  20#define SAFE_STATUS                       0x364
  21
  22#define TE_LINE_INTERVAL                  0x3F4
  23
  24#define TRAFFIC_SHAPER_EN                 BIT(31)
  25#define TRAFFIC_SHAPER_RD_CLIENT(num)     (0x030 + (num * 4))
  26#define TRAFFIC_SHAPER_WR_CLIENT(num)     (0x060 + (num * 4))
  27#define TRAFFIC_SHAPER_FIXPOINT_FACTOR    4
  28
  29#define MDP_WD_TIMER_0_CTL                0x380
  30#define MDP_WD_TIMER_0_CTL2               0x384
  31#define MDP_WD_TIMER_0_LOAD_VALUE         0x388
  32#define MDP_WD_TIMER_1_CTL                0x390
  33#define MDP_WD_TIMER_1_CTL2               0x394
  34#define MDP_WD_TIMER_1_LOAD_VALUE         0x398
  35#define MDP_WD_TIMER_2_CTL                0x420
  36#define MDP_WD_TIMER_2_CTL2               0x424
  37#define MDP_WD_TIMER_2_LOAD_VALUE         0x428
  38#define MDP_WD_TIMER_3_CTL                0x430
  39#define MDP_WD_TIMER_3_CTL2               0x434
  40#define MDP_WD_TIMER_3_LOAD_VALUE         0x438
  41#define MDP_WD_TIMER_4_CTL                0x440
  42#define MDP_WD_TIMER_4_CTL2               0x444
  43#define MDP_WD_TIMER_4_LOAD_VALUE         0x448
  44
  45#define MDP_TICK_COUNT                    16
  46#define XO_CLK_RATE                       19200
  47#define MS_TICKS_IN_SEC                   1000
  48
  49#define CALCULATE_WD_LOAD_VALUE(fps) \
  50        ((uint32_t)((MS_TICKS_IN_SEC * XO_CLK_RATE)/(MDP_TICK_COUNT * fps)))
  51
  52#define DCE_SEL                           0x450
  53
  54static void dpu_hw_setup_split_pipe(struct dpu_hw_mdp *mdp,
  55                struct split_pipe_cfg *cfg)
  56{
  57        struct dpu_hw_blk_reg_map *c;
  58        u32 upper_pipe = 0;
  59        u32 lower_pipe = 0;
  60
  61        if (!mdp || !cfg)
  62                return;
  63
  64        c = &mdp->hw;
  65
  66        if (cfg->en) {
  67                if (cfg->mode == INTF_MODE_CMD) {
  68                        lower_pipe = FLD_SPLIT_DISPLAY_CMD;
  69                        /* interface controlling sw trigger */
  70                        if (cfg->intf == INTF_2)
  71                                lower_pipe |= FLD_INTF_1_SW_TRG_MUX;
  72                        else
  73                                lower_pipe |= FLD_INTF_2_SW_TRG_MUX;
  74                        upper_pipe = lower_pipe;
  75                } else {
  76                        if (cfg->intf == INTF_2) {
  77                                lower_pipe = FLD_INTF_1_SW_TRG_MUX;
  78                                upper_pipe = FLD_INTF_2_SW_TRG_MUX;
  79                        } else {
  80                                lower_pipe = FLD_INTF_2_SW_TRG_MUX;
  81                                upper_pipe = FLD_INTF_1_SW_TRG_MUX;
  82                        }
  83                }
  84        }
  85
  86        DPU_REG_WRITE(c, SSPP_SPARE, cfg->split_flush_en ? 0x1 : 0x0);
  87        DPU_REG_WRITE(c, SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower_pipe);
  88        DPU_REG_WRITE(c, SPLIT_DISPLAY_UPPER_PIPE_CTRL, upper_pipe);
  89        DPU_REG_WRITE(c, SPLIT_DISPLAY_EN, cfg->en & 0x1);
  90}
  91
  92static bool dpu_hw_setup_clk_force_ctrl(struct dpu_hw_mdp *mdp,
  93                enum dpu_clk_ctrl_type clk_ctrl, bool enable)
  94{
  95        struct dpu_hw_blk_reg_map *c;
  96        u32 reg_off, bit_off;
  97        u32 reg_val, new_val;
  98        bool clk_forced_on;
  99
 100        if (!mdp)
 101                return false;
 102
 103        c = &mdp->hw;
 104
 105        if (clk_ctrl <= DPU_CLK_CTRL_NONE || clk_ctrl >= DPU_CLK_CTRL_MAX)
 106                return false;
 107
 108        reg_off = mdp->caps->clk_ctrls[clk_ctrl].reg_off;
 109        bit_off = mdp->caps->clk_ctrls[clk_ctrl].bit_off;
 110
 111        reg_val = DPU_REG_READ(c, reg_off);
 112
 113        if (enable)
 114                new_val = reg_val | BIT(bit_off);
 115        else
 116                new_val = reg_val & ~BIT(bit_off);
 117
 118        DPU_REG_WRITE(c, reg_off, new_val);
 119
 120        clk_forced_on = !(reg_val & BIT(bit_off));
 121
 122        return clk_forced_on;
 123}
 124
 125
 126static void dpu_hw_get_danger_status(struct dpu_hw_mdp *mdp,
 127                struct dpu_danger_safe_status *status)
 128{
 129        struct dpu_hw_blk_reg_map *c;
 130        u32 value;
 131
 132        if (!mdp || !status)
 133                return;
 134
 135        c = &mdp->hw;
 136
 137        value = DPU_REG_READ(c, DANGER_STATUS);
 138        status->mdp = (value >> 0) & 0x3;
 139        status->sspp[SSPP_VIG0] = (value >> 4) & 0x3;
 140        status->sspp[SSPP_VIG1] = (value >> 6) & 0x3;
 141        status->sspp[SSPP_VIG2] = (value >> 8) & 0x3;
 142        status->sspp[SSPP_VIG3] = (value >> 10) & 0x3;
 143        status->sspp[SSPP_RGB0] = (value >> 12) & 0x3;
 144        status->sspp[SSPP_RGB1] = (value >> 14) & 0x3;
 145        status->sspp[SSPP_RGB2] = (value >> 16) & 0x3;
 146        status->sspp[SSPP_RGB3] = (value >> 18) & 0x3;
 147        status->sspp[SSPP_DMA0] = (value >> 20) & 0x3;
 148        status->sspp[SSPP_DMA1] = (value >> 22) & 0x3;
 149        status->sspp[SSPP_DMA2] = (value >> 28) & 0x3;
 150        status->sspp[SSPP_DMA3] = (value >> 30) & 0x3;
 151        status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x3;
 152        status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x3;
 153}
 154
 155static void dpu_hw_setup_vsync_source(struct dpu_hw_mdp *mdp,
 156                struct dpu_vsync_source_cfg *cfg)
 157{
 158        struct dpu_hw_blk_reg_map *c;
 159        u32 reg, wd_load_value, wd_ctl, wd_ctl2, i;
 160        static const u32 pp_offset[PINGPONG_MAX] = {0xC, 0x8, 0x4, 0x13, 0x18};
 161
 162        if (!mdp || !cfg || (cfg->pp_count > ARRAY_SIZE(cfg->ppnumber)))
 163                return;
 164
 165        c = &mdp->hw;
 166        reg = DPU_REG_READ(c, MDP_VSYNC_SEL);
 167        for (i = 0; i < cfg->pp_count; i++) {
 168                int pp_idx = cfg->ppnumber[i] - PINGPONG_0;
 169
 170                if (pp_idx >= ARRAY_SIZE(pp_offset))
 171                        continue;
 172
 173                reg &= ~(0xf << pp_offset[pp_idx]);
 174                reg |= (cfg->vsync_source & 0xf) << pp_offset[pp_idx];
 175        }
 176        DPU_REG_WRITE(c, MDP_VSYNC_SEL, reg);
 177
 178        if (cfg->vsync_source >= DPU_VSYNC_SOURCE_WD_TIMER_4 &&
 179                        cfg->vsync_source <= DPU_VSYNC_SOURCE_WD_TIMER_0) {
 180                switch (cfg->vsync_source) {
 181                case DPU_VSYNC_SOURCE_WD_TIMER_4:
 182                        wd_load_value = MDP_WD_TIMER_4_LOAD_VALUE;
 183                        wd_ctl = MDP_WD_TIMER_4_CTL;
 184                        wd_ctl2 = MDP_WD_TIMER_4_CTL2;
 185                        break;
 186                case DPU_VSYNC_SOURCE_WD_TIMER_3:
 187                        wd_load_value = MDP_WD_TIMER_3_LOAD_VALUE;
 188                        wd_ctl = MDP_WD_TIMER_3_CTL;
 189                        wd_ctl2 = MDP_WD_TIMER_3_CTL2;
 190                        break;
 191                case DPU_VSYNC_SOURCE_WD_TIMER_2:
 192                        wd_load_value = MDP_WD_TIMER_2_LOAD_VALUE;
 193                        wd_ctl = MDP_WD_TIMER_2_CTL;
 194                        wd_ctl2 = MDP_WD_TIMER_2_CTL2;
 195                        break;
 196                case DPU_VSYNC_SOURCE_WD_TIMER_1:
 197                        wd_load_value = MDP_WD_TIMER_1_LOAD_VALUE;
 198                        wd_ctl = MDP_WD_TIMER_1_CTL;
 199                        wd_ctl2 = MDP_WD_TIMER_1_CTL2;
 200                        break;
 201                case DPU_VSYNC_SOURCE_WD_TIMER_0:
 202                default:
 203                        wd_load_value = MDP_WD_TIMER_0_LOAD_VALUE;
 204                        wd_ctl = MDP_WD_TIMER_0_CTL;
 205                        wd_ctl2 = MDP_WD_TIMER_0_CTL2;
 206                        break;
 207                }
 208
 209                DPU_REG_WRITE(c, wd_load_value,
 210                        CALCULATE_WD_LOAD_VALUE(cfg->frame_rate));
 211
 212                DPU_REG_WRITE(c, wd_ctl, BIT(0)); /* clear timer */
 213                reg = DPU_REG_READ(c, wd_ctl2);
 214                reg |= BIT(8);          /* enable heartbeat timer */
 215                reg |= BIT(0);          /* enable WD timer */
 216                DPU_REG_WRITE(c, wd_ctl2, reg);
 217
 218                /* make sure that timers are enabled/disabled for vsync state */
 219                wmb();
 220        }
 221}
 222
 223static void dpu_hw_get_safe_status(struct dpu_hw_mdp *mdp,
 224                struct dpu_danger_safe_status *status)
 225{
 226        struct dpu_hw_blk_reg_map *c;
 227        u32 value;
 228
 229        if (!mdp || !status)
 230                return;
 231
 232        c = &mdp->hw;
 233
 234        value = DPU_REG_READ(c, SAFE_STATUS);
 235        status->mdp = (value >> 0) & 0x1;
 236        status->sspp[SSPP_VIG0] = (value >> 4) & 0x1;
 237        status->sspp[SSPP_VIG1] = (value >> 6) & 0x1;
 238        status->sspp[SSPP_VIG2] = (value >> 8) & 0x1;
 239        status->sspp[SSPP_VIG3] = (value >> 10) & 0x1;
 240        status->sspp[SSPP_RGB0] = (value >> 12) & 0x1;
 241        status->sspp[SSPP_RGB1] = (value >> 14) & 0x1;
 242        status->sspp[SSPP_RGB2] = (value >> 16) & 0x1;
 243        status->sspp[SSPP_RGB3] = (value >> 18) & 0x1;
 244        status->sspp[SSPP_DMA0] = (value >> 20) & 0x1;
 245        status->sspp[SSPP_DMA1] = (value >> 22) & 0x1;
 246        status->sspp[SSPP_DMA2] = (value >> 28) & 0x1;
 247        status->sspp[SSPP_DMA3] = (value >> 30) & 0x1;
 248        status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x1;
 249        status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x1;
 250}
 251
 252static void dpu_hw_reset_ubwc(struct dpu_hw_mdp *mdp, struct dpu_mdss_cfg *m)
 253{
 254        struct dpu_hw_blk_reg_map c;
 255
 256        if (!mdp || !m)
 257                return;
 258
 259        if (!IS_UBWC_20_SUPPORTED(m->caps->ubwc_version))
 260                return;
 261
 262        /* force blk offset to zero to access beginning of register region */
 263        c = mdp->hw;
 264        c.blk_off = 0x0;
 265        DPU_REG_WRITE(&c, UBWC_STATIC, m->mdp[0].ubwc_static);
 266}
 267
 268static void dpu_hw_intf_audio_select(struct dpu_hw_mdp *mdp)
 269{
 270        struct dpu_hw_blk_reg_map *c;
 271
 272        if (!mdp)
 273                return;
 274
 275        c = &mdp->hw;
 276
 277        DPU_REG_WRITE(c, HDMI_DP_CORE_SELECT, 0x1);
 278}
 279
 280static void _setup_mdp_ops(struct dpu_hw_mdp_ops *ops,
 281                unsigned long cap)
 282{
 283        ops->setup_split_pipe = dpu_hw_setup_split_pipe;
 284        ops->setup_clk_force_ctrl = dpu_hw_setup_clk_force_ctrl;
 285        ops->get_danger_status = dpu_hw_get_danger_status;
 286        ops->setup_vsync_source = dpu_hw_setup_vsync_source;
 287        ops->get_safe_status = dpu_hw_get_safe_status;
 288        ops->reset_ubwc = dpu_hw_reset_ubwc;
 289        ops->intf_audio_select = dpu_hw_intf_audio_select;
 290}
 291
 292static const struct dpu_mdp_cfg *_top_offset(enum dpu_mdp mdp,
 293                const struct dpu_mdss_cfg *m,
 294                void __iomem *addr,
 295                struct dpu_hw_blk_reg_map *b)
 296{
 297        int i;
 298
 299        if (!m || !addr || !b)
 300                return ERR_PTR(-EINVAL);
 301
 302        for (i = 0; i < m->mdp_count; i++) {
 303                if (mdp == m->mdp[i].id) {
 304                        b->base_off = addr;
 305                        b->blk_off = m->mdp[i].base;
 306                        b->length = m->mdp[i].len;
 307                        b->hwversion = m->hwversion;
 308                        b->log_mask = DPU_DBG_MASK_TOP;
 309                        return &m->mdp[i];
 310                }
 311        }
 312
 313        return ERR_PTR(-EINVAL);
 314}
 315
 316static struct dpu_hw_blk_ops dpu_hw_ops;
 317
 318struct dpu_hw_mdp *dpu_hw_mdptop_init(enum dpu_mdp idx,
 319                void __iomem *addr,
 320                const struct dpu_mdss_cfg *m)
 321{
 322        struct dpu_hw_mdp *mdp;
 323        const struct dpu_mdp_cfg *cfg;
 324
 325        if (!addr || !m)
 326                return ERR_PTR(-EINVAL);
 327
 328        mdp = kzalloc(sizeof(*mdp), GFP_KERNEL);
 329        if (!mdp)
 330                return ERR_PTR(-ENOMEM);
 331
 332        cfg = _top_offset(idx, m, addr, &mdp->hw);
 333        if (IS_ERR_OR_NULL(cfg)) {
 334                kfree(mdp);
 335                return ERR_PTR(-EINVAL);
 336        }
 337
 338        /*
 339         * Assign ops
 340         */
 341        mdp->idx = idx;
 342        mdp->caps = cfg;
 343        _setup_mdp_ops(&mdp->ops, mdp->caps->features);
 344
 345        dpu_hw_blk_init(&mdp->base, DPU_HW_BLK_TOP, idx, &dpu_hw_ops);
 346
 347        return mdp;
 348}
 349
 350void dpu_hw_mdp_destroy(struct dpu_hw_mdp *mdp)
 351{
 352        if (mdp)
 353                dpu_hw_blk_destroy(&mdp->base);
 354        kfree(mdp);
 355}
 356
 357