Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
0003  */
0004 
0005 #include <linux/iopoll.h>
0006 
0007 #include "dpu_hw_mdss.h"
0008 #include "dpu_hwio.h"
0009 #include "dpu_hw_catalog.h"
0010 #include "dpu_hw_pingpong.h"
0011 #include "dpu_kms.h"
0012 #include "dpu_trace.h"
0013 
0014 #define PP_TEAR_CHECK_EN                0x000
0015 #define PP_SYNC_CONFIG_VSYNC            0x004
0016 #define PP_SYNC_CONFIG_HEIGHT           0x008
0017 #define PP_SYNC_WRCOUNT                 0x00C
0018 #define PP_VSYNC_INIT_VAL               0x010
0019 #define PP_INT_COUNT_VAL                0x014
0020 #define PP_SYNC_THRESH                  0x018
0021 #define PP_START_POS                    0x01C
0022 #define PP_RD_PTR_IRQ                   0x020
0023 #define PP_WR_PTR_IRQ                   0x024
0024 #define PP_OUT_LINE_COUNT               0x028
0025 #define PP_LINE_COUNT                   0x02C
0026 #define PP_AUTOREFRESH_CONFIG           0x030
0027 
0028 #define PP_FBC_MODE                     0x034
0029 #define PP_FBC_BUDGET_CTL               0x038
0030 #define PP_FBC_LOSSY_MODE               0x03C
0031 #define PP_DSC_MODE                     0x0a0
0032 #define PP_DCE_DATA_IN_SWAP             0x0ac
0033 #define PP_DCE_DATA_OUT_SWAP            0x0c8
0034 
0035 #define PP_DITHER_EN            0x000
0036 #define PP_DITHER_BITDEPTH      0x004
0037 #define PP_DITHER_MATRIX        0x008
0038 
0039 #define DITHER_DEPTH_MAP_INDEX 9
0040 
0041 static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = {
0042     0, 0, 0, 0, 0, 0, 0, 1, 2
0043 };
0044 
0045 static const struct dpu_pingpong_cfg *_pingpong_offset(enum dpu_pingpong pp,
0046         const struct dpu_mdss_cfg *m,
0047         void __iomem *addr,
0048         struct dpu_hw_blk_reg_map *b)
0049 {
0050     int i;
0051 
0052     for (i = 0; i < m->pingpong_count; i++) {
0053         if (pp == m->pingpong[i].id) {
0054             b->blk_addr = addr + m->pingpong[i].base;
0055             b->log_mask = DPU_DBG_MASK_PINGPONG;
0056             return &m->pingpong[i];
0057         }
0058     }
0059 
0060     return ERR_PTR(-EINVAL);
0061 }
0062 
0063 static void dpu_hw_pp_setup_dither(struct dpu_hw_pingpong *pp,
0064                     struct dpu_hw_dither_cfg *cfg)
0065 {
0066     struct dpu_hw_blk_reg_map *c;
0067     u32 i, base, data = 0;
0068 
0069     c = &pp->hw;
0070     base = pp->caps->sblk->dither.base;
0071     if (!cfg) {
0072         DPU_REG_WRITE(c, base + PP_DITHER_EN, 0);
0073         return;
0074     }
0075 
0076     data = dither_depth_map[cfg->c0_bitdepth] & REG_MASK(2);
0077     data |= (dither_depth_map[cfg->c1_bitdepth] & REG_MASK(2)) << 2;
0078     data |= (dither_depth_map[cfg->c2_bitdepth] & REG_MASK(2)) << 4;
0079     data |= (dither_depth_map[cfg->c3_bitdepth] & REG_MASK(2)) << 6;
0080     data |= (cfg->temporal_en) ? (1 << 8) : 0;
0081 
0082     DPU_REG_WRITE(c, base + PP_DITHER_BITDEPTH, data);
0083 
0084     for (i = 0; i < DITHER_MATRIX_SZ - 3; i += 4) {
0085         data = (cfg->matrix[i] & REG_MASK(4)) |
0086             ((cfg->matrix[i + 1] & REG_MASK(4)) << 4) |
0087             ((cfg->matrix[i + 2] & REG_MASK(4)) << 8) |
0088             ((cfg->matrix[i + 3] & REG_MASK(4)) << 12);
0089         DPU_REG_WRITE(c, base + PP_DITHER_MATRIX + i, data);
0090     }
0091     DPU_REG_WRITE(c, base + PP_DITHER_EN, 1);
0092 }
0093 
0094 static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong *pp,
0095         struct dpu_hw_tear_check *te)
0096 {
0097     struct dpu_hw_blk_reg_map *c;
0098     int cfg;
0099 
0100     if (!pp || !te)
0101         return -EINVAL;
0102     c = &pp->hw;
0103 
0104     cfg = BIT(19); /*VSYNC_COUNTER_EN */
0105     if (te->hw_vsync_mode)
0106         cfg |= BIT(20);
0107 
0108     cfg |= te->vsync_count;
0109 
0110     DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
0111     DPU_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height);
0112     DPU_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val);
0113     DPU_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq);
0114     DPU_REG_WRITE(c, PP_START_POS, te->start_pos);
0115     DPU_REG_WRITE(c, PP_SYNC_THRESH,
0116             ((te->sync_threshold_continue << 16) |
0117              te->sync_threshold_start));
0118     DPU_REG_WRITE(c, PP_SYNC_WRCOUNT,
0119             (te->start_pos + te->sync_threshold_start + 1));
0120 
0121     return 0;
0122 }
0123 
0124 static void dpu_hw_pp_setup_autorefresh_config(struct dpu_hw_pingpong *pp,
0125                            u32 frame_count, bool enable)
0126 {
0127     DPU_REG_WRITE(&pp->hw, PP_AUTOREFRESH_CONFIG,
0128               enable ? (BIT(31) | frame_count) : 0);
0129 }
0130 
0131 /*
0132  * dpu_hw_pp_get_autorefresh_config - Get autorefresh config from HW
0133  * @pp:          DPU pingpong structure
0134  * @frame_count: Used to return the current frame count from hw
0135  *
0136  * Returns: True if autorefresh enabled, false if disabled.
0137  */
0138 static bool dpu_hw_pp_get_autorefresh_config(struct dpu_hw_pingpong *pp,
0139                          u32 *frame_count)
0140 {
0141     u32 val = DPU_REG_READ(&pp->hw, PP_AUTOREFRESH_CONFIG);
0142     if (frame_count != NULL)
0143         *frame_count = val & 0xffff;
0144     return !!((val & BIT(31)) >> 31);
0145 }
0146 
0147 static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp,
0148         u32 timeout_us)
0149 {
0150     struct dpu_hw_blk_reg_map *c;
0151     u32 val;
0152     int rc;
0153 
0154     if (!pp)
0155         return -EINVAL;
0156 
0157     c = &pp->hw;
0158     rc = readl_poll_timeout(c->blk_addr + PP_LINE_COUNT,
0159             val, (val & 0xffff) >= 1, 10, timeout_us);
0160 
0161     return rc;
0162 }
0163 
0164 static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable)
0165 {
0166     struct dpu_hw_blk_reg_map *c;
0167 
0168     if (!pp)
0169         return -EINVAL;
0170     c = &pp->hw;
0171 
0172     DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable);
0173     return 0;
0174 }
0175 
0176 static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp,
0177         bool enable_external_te)
0178 {
0179     struct dpu_hw_blk_reg_map *c = &pp->hw;
0180     u32 cfg;
0181     int orig;
0182 
0183     if (!pp)
0184         return -EINVAL;
0185 
0186     c = &pp->hw;
0187     cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC);
0188     orig = (bool)(cfg & BIT(20));
0189     if (enable_external_te)
0190         cfg |= BIT(20);
0191     else
0192         cfg &= ~BIT(20);
0193     DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
0194     trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg);
0195 
0196     return orig;
0197 }
0198 
0199 static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp,
0200         struct dpu_hw_pp_vsync_info *info)
0201 {
0202     struct dpu_hw_blk_reg_map *c;
0203     u32 val;
0204 
0205     if (!pp || !info)
0206         return -EINVAL;
0207     c = &pp->hw;
0208 
0209     val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL);
0210     info->rd_ptr_init_val = val & 0xffff;
0211 
0212     val = DPU_REG_READ(c, PP_INT_COUNT_VAL);
0213     info->rd_ptr_frame_count = (val & 0xffff0000) >> 16;
0214     info->rd_ptr_line_count = val & 0xffff;
0215 
0216     val = DPU_REG_READ(c, PP_LINE_COUNT);
0217     info->wr_ptr_line_count = val & 0xffff;
0218 
0219     return 0;
0220 }
0221 
0222 static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp)
0223 {
0224     struct dpu_hw_blk_reg_map *c = &pp->hw;
0225     u32 height, init;
0226     u32 line = 0xFFFF;
0227 
0228     if (!pp)
0229         return 0;
0230     c = &pp->hw;
0231 
0232     init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF;
0233     height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF;
0234 
0235     if (height < init)
0236         return line;
0237 
0238     line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF;
0239 
0240     if (line < init)
0241         line += (0xFFFF - init);
0242     else
0243         line -= init;
0244 
0245     return line;
0246 }
0247 
0248 static int dpu_hw_pp_dsc_enable(struct dpu_hw_pingpong *pp)
0249 {
0250     struct dpu_hw_blk_reg_map *c = &pp->hw;
0251 
0252     DPU_REG_WRITE(c, PP_DSC_MODE, 1);
0253     return 0;
0254 }
0255 
0256 static void dpu_hw_pp_dsc_disable(struct dpu_hw_pingpong *pp)
0257 {
0258     struct dpu_hw_blk_reg_map *c = &pp->hw;
0259 
0260     DPU_REG_WRITE(c, PP_DSC_MODE, 0);
0261 }
0262 
0263 static int dpu_hw_pp_setup_dsc(struct dpu_hw_pingpong *pp)
0264 {
0265     struct dpu_hw_blk_reg_map *pp_c = &pp->hw;
0266     int data;
0267 
0268     data = DPU_REG_READ(pp_c, PP_DCE_DATA_OUT_SWAP);
0269     data |= BIT(18); /* endian flip */
0270     DPU_REG_WRITE(pp_c, PP_DCE_DATA_OUT_SWAP, data);
0271     return 0;
0272 }
0273 
0274 static void _setup_pingpong_ops(struct dpu_hw_pingpong *c,
0275                 unsigned long features)
0276 {
0277     c->ops.setup_tearcheck = dpu_hw_pp_setup_te_config;
0278     c->ops.enable_tearcheck = dpu_hw_pp_enable_te;
0279     c->ops.connect_external_te = dpu_hw_pp_connect_external_te;
0280     c->ops.get_vsync_info = dpu_hw_pp_get_vsync_info;
0281     c->ops.setup_autorefresh = dpu_hw_pp_setup_autorefresh_config;
0282     c->ops.get_autorefresh = dpu_hw_pp_get_autorefresh_config;
0283     c->ops.poll_timeout_wr_ptr = dpu_hw_pp_poll_timeout_wr_ptr;
0284     c->ops.get_line_count = dpu_hw_pp_get_line_count;
0285     c->ops.setup_dsc = dpu_hw_pp_setup_dsc;
0286     c->ops.enable_dsc = dpu_hw_pp_dsc_enable;
0287     c->ops.disable_dsc = dpu_hw_pp_dsc_disable;
0288 
0289     if (test_bit(DPU_PINGPONG_DITHER, &features))
0290         c->ops.setup_dither = dpu_hw_pp_setup_dither;
0291 };
0292 
0293 struct dpu_hw_pingpong *dpu_hw_pingpong_init(enum dpu_pingpong idx,
0294         void __iomem *addr,
0295         const struct dpu_mdss_cfg *m)
0296 {
0297     struct dpu_hw_pingpong *c;
0298     const struct dpu_pingpong_cfg *cfg;
0299 
0300     c = kzalloc(sizeof(*c), GFP_KERNEL);
0301     if (!c)
0302         return ERR_PTR(-ENOMEM);
0303 
0304     cfg = _pingpong_offset(idx, m, addr, &c->hw);
0305     if (IS_ERR_OR_NULL(cfg)) {
0306         kfree(c);
0307         return ERR_PTR(-EINVAL);
0308     }
0309 
0310     c->idx = idx;
0311     c->caps = cfg;
0312     _setup_pingpong_ops(c, c->caps->features);
0313 
0314     return c;
0315 }
0316 
0317 void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp)
0318 {
0319     kfree(pp);
0320 }