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