linux/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.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 <linux/iopoll.h>
   6
   7#include "dpu_hw_mdss.h"
   8#include "dpu_hwio.h"
   9#include "dpu_hw_catalog.h"
  10#include "dpu_hw_pingpong.h"
  11#include "dpu_kms.h"
  12#include "dpu_trace.h"
  13
  14#define PP_TEAR_CHECK_EN                0x000
  15#define PP_SYNC_CONFIG_VSYNC            0x004
  16#define PP_SYNC_CONFIG_HEIGHT           0x008
  17#define PP_SYNC_WRCOUNT                 0x00C
  18#define PP_VSYNC_INIT_VAL               0x010
  19#define PP_INT_COUNT_VAL                0x014
  20#define PP_SYNC_THRESH                  0x018
  21#define PP_START_POS                    0x01C
  22#define PP_RD_PTR_IRQ                   0x020
  23#define PP_WR_PTR_IRQ                   0x024
  24#define PP_OUT_LINE_COUNT               0x028
  25#define PP_LINE_COUNT                   0x02C
  26
  27#define PP_FBC_MODE                     0x034
  28#define PP_FBC_BUDGET_CTL               0x038
  29#define PP_FBC_LOSSY_MODE               0x03C
  30
  31static struct dpu_pingpong_cfg *_pingpong_offset(enum dpu_pingpong pp,
  32                struct dpu_mdss_cfg *m,
  33                void __iomem *addr,
  34                struct dpu_hw_blk_reg_map *b)
  35{
  36        int i;
  37
  38        for (i = 0; i < m->pingpong_count; i++) {
  39                if (pp == m->pingpong[i].id) {
  40                        b->base_off = addr;
  41                        b->blk_off = m->pingpong[i].base;
  42                        b->length = m->pingpong[i].len;
  43                        b->hwversion = m->hwversion;
  44                        b->log_mask = DPU_DBG_MASK_PINGPONG;
  45                        return &m->pingpong[i];
  46                }
  47        }
  48
  49        return ERR_PTR(-EINVAL);
  50}
  51
  52static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong *pp,
  53                struct dpu_hw_tear_check *te)
  54{
  55        struct dpu_hw_blk_reg_map *c;
  56        int cfg;
  57
  58        if (!pp || !te)
  59                return -EINVAL;
  60        c = &pp->hw;
  61
  62        cfg = BIT(19); /*VSYNC_COUNTER_EN */
  63        if (te->hw_vsync_mode)
  64                cfg |= BIT(20);
  65
  66        cfg |= te->vsync_count;
  67
  68        DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
  69        DPU_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height);
  70        DPU_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val);
  71        DPU_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq);
  72        DPU_REG_WRITE(c, PP_START_POS, te->start_pos);
  73        DPU_REG_WRITE(c, PP_SYNC_THRESH,
  74                        ((te->sync_threshold_continue << 16) |
  75                         te->sync_threshold_start));
  76        DPU_REG_WRITE(c, PP_SYNC_WRCOUNT,
  77                        (te->start_pos + te->sync_threshold_start + 1));
  78
  79        return 0;
  80}
  81
  82static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp,
  83                u32 timeout_us)
  84{
  85        struct dpu_hw_blk_reg_map *c;
  86        u32 val;
  87        int rc;
  88
  89        if (!pp)
  90                return -EINVAL;
  91
  92        c = &pp->hw;
  93        rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT,
  94                        val, (val & 0xffff) >= 1, 10, timeout_us);
  95
  96        return rc;
  97}
  98
  99static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable)
 100{
 101        struct dpu_hw_blk_reg_map *c;
 102
 103        if (!pp)
 104                return -EINVAL;
 105        c = &pp->hw;
 106
 107        DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable);
 108        return 0;
 109}
 110
 111static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp,
 112                bool enable_external_te)
 113{
 114        struct dpu_hw_blk_reg_map *c = &pp->hw;
 115        u32 cfg;
 116        int orig;
 117
 118        if (!pp)
 119                return -EINVAL;
 120
 121        c = &pp->hw;
 122        cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC);
 123        orig = (bool)(cfg & BIT(20));
 124        if (enable_external_te)
 125                cfg |= BIT(20);
 126        else
 127                cfg &= ~BIT(20);
 128        DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
 129        trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg);
 130
 131        return orig;
 132}
 133
 134static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp,
 135                struct dpu_hw_pp_vsync_info *info)
 136{
 137        struct dpu_hw_blk_reg_map *c;
 138        u32 val;
 139
 140        if (!pp || !info)
 141                return -EINVAL;
 142        c = &pp->hw;
 143
 144        val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL);
 145        info->rd_ptr_init_val = val & 0xffff;
 146
 147        val = DPU_REG_READ(c, PP_INT_COUNT_VAL);
 148        info->rd_ptr_frame_count = (val & 0xffff0000) >> 16;
 149        info->rd_ptr_line_count = val & 0xffff;
 150
 151        val = DPU_REG_READ(c, PP_LINE_COUNT);
 152        info->wr_ptr_line_count = val & 0xffff;
 153
 154        return 0;
 155}
 156
 157static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp)
 158{
 159        struct dpu_hw_blk_reg_map *c = &pp->hw;
 160        u32 height, init;
 161        u32 line = 0xFFFF;
 162
 163        if (!pp)
 164                return 0;
 165        c = &pp->hw;
 166
 167        init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF;
 168        height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF;
 169
 170        if (height < init)
 171                return line;
 172
 173        line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF;
 174
 175        if (line < init)
 176                line += (0xFFFF - init);
 177        else
 178                line -= init;
 179
 180        return line;
 181}
 182
 183static void _setup_pingpong_ops(struct dpu_hw_pingpong_ops *ops,
 184        const struct dpu_pingpong_cfg *hw_cap)
 185{
 186        ops->setup_tearcheck = dpu_hw_pp_setup_te_config;
 187        ops->enable_tearcheck = dpu_hw_pp_enable_te;
 188        ops->connect_external_te = dpu_hw_pp_connect_external_te;
 189        ops->get_vsync_info = dpu_hw_pp_get_vsync_info;
 190        ops->poll_timeout_wr_ptr = dpu_hw_pp_poll_timeout_wr_ptr;
 191        ops->get_line_count = dpu_hw_pp_get_line_count;
 192};
 193
 194static struct dpu_hw_blk_ops dpu_hw_ops;
 195
 196struct dpu_hw_pingpong *dpu_hw_pingpong_init(enum dpu_pingpong idx,
 197                void __iomem *addr,
 198                struct dpu_mdss_cfg *m)
 199{
 200        struct dpu_hw_pingpong *c;
 201        struct dpu_pingpong_cfg *cfg;
 202
 203        c = kzalloc(sizeof(*c), GFP_KERNEL);
 204        if (!c)
 205                return ERR_PTR(-ENOMEM);
 206
 207        cfg = _pingpong_offset(idx, m, addr, &c->hw);
 208        if (IS_ERR_OR_NULL(cfg)) {
 209                kfree(c);
 210                return ERR_PTR(-EINVAL);
 211        }
 212
 213        c->idx = idx;
 214        c->caps = cfg;
 215        _setup_pingpong_ops(&c->ops, c->caps);
 216
 217        dpu_hw_blk_init(&c->base, DPU_HW_BLK_PINGPONG, idx, &dpu_hw_ops);
 218
 219        return c;
 220}
 221
 222void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp)
 223{
 224        if (pp)
 225                dpu_hw_blk_destroy(&pp->base);
 226        kfree(pp);
 227}
 228