197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 225fdd593SJeykumar Sankaran /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. 325fdd593SJeykumar Sankaran */ 425fdd593SJeykumar Sankaran 525fdd593SJeykumar Sankaran #include <linux/iopoll.h> 625fdd593SJeykumar Sankaran 725fdd593SJeykumar Sankaran #include "dpu_hw_mdss.h" 825fdd593SJeykumar Sankaran #include "dpu_hwio.h" 925fdd593SJeykumar Sankaran #include "dpu_hw_catalog.h" 1025fdd593SJeykumar Sankaran #include "dpu_hw_pingpong.h" 1125fdd593SJeykumar Sankaran #include "dpu_kms.h" 1225fdd593SJeykumar Sankaran #include "dpu_trace.h" 1325fdd593SJeykumar Sankaran 1425fdd593SJeykumar Sankaran #define PP_TEAR_CHECK_EN 0x000 1525fdd593SJeykumar Sankaran #define PP_SYNC_CONFIG_VSYNC 0x004 1625fdd593SJeykumar Sankaran #define PP_SYNC_CONFIG_HEIGHT 0x008 1725fdd593SJeykumar Sankaran #define PP_SYNC_WRCOUNT 0x00C 1825fdd593SJeykumar Sankaran #define PP_VSYNC_INIT_VAL 0x010 1925fdd593SJeykumar Sankaran #define PP_INT_COUNT_VAL 0x014 2025fdd593SJeykumar Sankaran #define PP_SYNC_THRESH 0x018 2125fdd593SJeykumar Sankaran #define PP_START_POS 0x01C 2225fdd593SJeykumar Sankaran #define PP_RD_PTR_IRQ 0x020 2325fdd593SJeykumar Sankaran #define PP_WR_PTR_IRQ 0x024 2425fdd593SJeykumar Sankaran #define PP_OUT_LINE_COUNT 0x028 2525fdd593SJeykumar Sankaran #define PP_LINE_COUNT 0x02C 26aa9223a6SAngeloGioacchino Del Regno #define PP_AUTOREFRESH_CONFIG 0x030 2725fdd593SJeykumar Sankaran 2825fdd593SJeykumar Sankaran #define PP_FBC_MODE 0x034 2925fdd593SJeykumar Sankaran #define PP_FBC_BUDGET_CTL 0x038 3025fdd593SJeykumar Sankaran #define PP_FBC_LOSSY_MODE 0x03C 31*893d6982SVinod Koul #define PP_DSC_MODE 0x0a0 32*893d6982SVinod Koul #define PP_DCE_DATA_IN_SWAP 0x0ac 33*893d6982SVinod Koul #define PP_DCE_DATA_OUT_SWAP 0x0c8 3425fdd593SJeykumar Sankaran 353c128638SKalyan Thota #define PP_DITHER_EN 0x000 363c128638SKalyan Thota #define PP_DITHER_BITDEPTH 0x004 373c128638SKalyan Thota #define PP_DITHER_MATRIX 0x008 383c128638SKalyan Thota 393c128638SKalyan Thota #define DITHER_DEPTH_MAP_INDEX 9 403c128638SKalyan Thota 413c128638SKalyan Thota static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = { 423c128638SKalyan Thota 0, 0, 0, 0, 0, 0, 0, 1, 2 433c128638SKalyan Thota }; 443c128638SKalyan Thota 45abda0d92SStephen Boyd static const struct dpu_pingpong_cfg *_pingpong_offset(enum dpu_pingpong pp, 46abda0d92SStephen Boyd const struct dpu_mdss_cfg *m, 4725fdd593SJeykumar Sankaran void __iomem *addr, 4825fdd593SJeykumar Sankaran struct dpu_hw_blk_reg_map *b) 4925fdd593SJeykumar Sankaran { 5025fdd593SJeykumar Sankaran int i; 5125fdd593SJeykumar Sankaran 5225fdd593SJeykumar Sankaran for (i = 0; i < m->pingpong_count; i++) { 5325fdd593SJeykumar Sankaran if (pp == m->pingpong[i].id) { 5425fdd593SJeykumar Sankaran b->base_off = addr; 5525fdd593SJeykumar Sankaran b->blk_off = m->pingpong[i].base; 5625fdd593SJeykumar Sankaran b->length = m->pingpong[i].len; 5725fdd593SJeykumar Sankaran b->hwversion = m->hwversion; 5825fdd593SJeykumar Sankaran b->log_mask = DPU_DBG_MASK_PINGPONG; 5925fdd593SJeykumar Sankaran return &m->pingpong[i]; 6025fdd593SJeykumar Sankaran } 6125fdd593SJeykumar Sankaran } 6225fdd593SJeykumar Sankaran 6325fdd593SJeykumar Sankaran return ERR_PTR(-EINVAL); 6425fdd593SJeykumar Sankaran } 6525fdd593SJeykumar Sankaran 663c128638SKalyan Thota static void dpu_hw_pp_setup_dither(struct dpu_hw_pingpong *pp, 673c128638SKalyan Thota struct dpu_hw_dither_cfg *cfg) 683c128638SKalyan Thota { 693c128638SKalyan Thota struct dpu_hw_blk_reg_map *c; 703c128638SKalyan Thota u32 i, base, data = 0; 713c128638SKalyan Thota 723c128638SKalyan Thota c = &pp->hw; 733c128638SKalyan Thota base = pp->caps->sblk->dither.base; 743c128638SKalyan Thota if (!cfg) { 753c128638SKalyan Thota DPU_REG_WRITE(c, base + PP_DITHER_EN, 0); 763c128638SKalyan Thota return; 773c128638SKalyan Thota } 783c128638SKalyan Thota 793c128638SKalyan Thota data = dither_depth_map[cfg->c0_bitdepth] & REG_MASK(2); 803c128638SKalyan Thota data |= (dither_depth_map[cfg->c1_bitdepth] & REG_MASK(2)) << 2; 813c128638SKalyan Thota data |= (dither_depth_map[cfg->c2_bitdepth] & REG_MASK(2)) << 4; 823c128638SKalyan Thota data |= (dither_depth_map[cfg->c3_bitdepth] & REG_MASK(2)) << 6; 833c128638SKalyan Thota data |= (cfg->temporal_en) ? (1 << 8) : 0; 843c128638SKalyan Thota 853c128638SKalyan Thota DPU_REG_WRITE(c, base + PP_DITHER_BITDEPTH, data); 863c128638SKalyan Thota 873c128638SKalyan Thota for (i = 0; i < DITHER_MATRIX_SZ - 3; i += 4) { 883c128638SKalyan Thota data = (cfg->matrix[i] & REG_MASK(4)) | 893c128638SKalyan Thota ((cfg->matrix[i + 1] & REG_MASK(4)) << 4) | 903c128638SKalyan Thota ((cfg->matrix[i + 2] & REG_MASK(4)) << 8) | 913c128638SKalyan Thota ((cfg->matrix[i + 3] & REG_MASK(4)) << 12); 923c128638SKalyan Thota DPU_REG_WRITE(c, base + PP_DITHER_MATRIX + i, data); 933c128638SKalyan Thota } 943c128638SKalyan Thota DPU_REG_WRITE(c, base + PP_DITHER_EN, 1); 953c128638SKalyan Thota } 963c128638SKalyan Thota 9725fdd593SJeykumar Sankaran static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong *pp, 9825fdd593SJeykumar Sankaran struct dpu_hw_tear_check *te) 9925fdd593SJeykumar Sankaran { 10025fdd593SJeykumar Sankaran struct dpu_hw_blk_reg_map *c; 10125fdd593SJeykumar Sankaran int cfg; 10225fdd593SJeykumar Sankaran 10325fdd593SJeykumar Sankaran if (!pp || !te) 10425fdd593SJeykumar Sankaran return -EINVAL; 10525fdd593SJeykumar Sankaran c = &pp->hw; 10625fdd593SJeykumar Sankaran 10725fdd593SJeykumar Sankaran cfg = BIT(19); /*VSYNC_COUNTER_EN */ 10825fdd593SJeykumar Sankaran if (te->hw_vsync_mode) 10925fdd593SJeykumar Sankaran cfg |= BIT(20); 11025fdd593SJeykumar Sankaran 11125fdd593SJeykumar Sankaran cfg |= te->vsync_count; 11225fdd593SJeykumar Sankaran 11325fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 11425fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height); 11525fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val); 11625fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq); 11725fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_START_POS, te->start_pos); 11825fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_SYNC_THRESH, 11925fdd593SJeykumar Sankaran ((te->sync_threshold_continue << 16) | 12025fdd593SJeykumar Sankaran te->sync_threshold_start)); 12125fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_SYNC_WRCOUNT, 12225fdd593SJeykumar Sankaran (te->start_pos + te->sync_threshold_start + 1)); 12325fdd593SJeykumar Sankaran 12425fdd593SJeykumar Sankaran return 0; 12525fdd593SJeykumar Sankaran } 12625fdd593SJeykumar Sankaran 127aa9223a6SAngeloGioacchino Del Regno static void dpu_hw_pp_setup_autorefresh_config(struct dpu_hw_pingpong *pp, 128aa9223a6SAngeloGioacchino Del Regno u32 frame_count, bool enable) 129aa9223a6SAngeloGioacchino Del Regno { 130aa9223a6SAngeloGioacchino Del Regno DPU_REG_WRITE(&pp->hw, PP_AUTOREFRESH_CONFIG, 131aa9223a6SAngeloGioacchino Del Regno enable ? (BIT(31) | frame_count) : 0); 132aa9223a6SAngeloGioacchino Del Regno } 133aa9223a6SAngeloGioacchino Del Regno 134aa9223a6SAngeloGioacchino Del Regno /* 135aa9223a6SAngeloGioacchino Del Regno * dpu_hw_pp_get_autorefresh_config - Get autorefresh config from HW 136aa9223a6SAngeloGioacchino Del Regno * @pp: DPU pingpong structure 137aa9223a6SAngeloGioacchino Del Regno * @frame_count: Used to return the current frame count from hw 138aa9223a6SAngeloGioacchino Del Regno * 139aa9223a6SAngeloGioacchino Del Regno * Returns: True if autorefresh enabled, false if disabled. 140aa9223a6SAngeloGioacchino Del Regno */ 141aa9223a6SAngeloGioacchino Del Regno static bool dpu_hw_pp_get_autorefresh_config(struct dpu_hw_pingpong *pp, 142aa9223a6SAngeloGioacchino Del Regno u32 *frame_count) 143aa9223a6SAngeloGioacchino Del Regno { 144aa9223a6SAngeloGioacchino Del Regno u32 val = DPU_REG_READ(&pp->hw, PP_AUTOREFRESH_CONFIG); 145aa9223a6SAngeloGioacchino Del Regno if (frame_count != NULL) 146aa9223a6SAngeloGioacchino Del Regno *frame_count = val & 0xffff; 147aa9223a6SAngeloGioacchino Del Regno return !!((val & BIT(31)) >> 31); 148aa9223a6SAngeloGioacchino Del Regno } 149aa9223a6SAngeloGioacchino Del Regno 15025fdd593SJeykumar Sankaran static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp, 15125fdd593SJeykumar Sankaran u32 timeout_us) 15225fdd593SJeykumar Sankaran { 15325fdd593SJeykumar Sankaran struct dpu_hw_blk_reg_map *c; 15425fdd593SJeykumar Sankaran u32 val; 15525fdd593SJeykumar Sankaran int rc; 15625fdd593SJeykumar Sankaran 15725fdd593SJeykumar Sankaran if (!pp) 15825fdd593SJeykumar Sankaran return -EINVAL; 15925fdd593SJeykumar Sankaran 16025fdd593SJeykumar Sankaran c = &pp->hw; 16125fdd593SJeykumar Sankaran rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT, 16225fdd593SJeykumar Sankaran val, (val & 0xffff) >= 1, 10, timeout_us); 16325fdd593SJeykumar Sankaran 16425fdd593SJeykumar Sankaran return rc; 16525fdd593SJeykumar Sankaran } 16625fdd593SJeykumar Sankaran 16725fdd593SJeykumar Sankaran static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable) 16825fdd593SJeykumar Sankaran { 16925fdd593SJeykumar Sankaran struct dpu_hw_blk_reg_map *c; 17025fdd593SJeykumar Sankaran 17125fdd593SJeykumar Sankaran if (!pp) 17225fdd593SJeykumar Sankaran return -EINVAL; 17325fdd593SJeykumar Sankaran c = &pp->hw; 17425fdd593SJeykumar Sankaran 17525fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable); 17625fdd593SJeykumar Sankaran return 0; 17725fdd593SJeykumar Sankaran } 17825fdd593SJeykumar Sankaran 17925fdd593SJeykumar Sankaran static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp, 18025fdd593SJeykumar Sankaran bool enable_external_te) 18125fdd593SJeykumar Sankaran { 18225fdd593SJeykumar Sankaran struct dpu_hw_blk_reg_map *c = &pp->hw; 18325fdd593SJeykumar Sankaran u32 cfg; 18425fdd593SJeykumar Sankaran int orig; 18525fdd593SJeykumar Sankaran 18625fdd593SJeykumar Sankaran if (!pp) 18725fdd593SJeykumar Sankaran return -EINVAL; 18825fdd593SJeykumar Sankaran 18925fdd593SJeykumar Sankaran c = &pp->hw; 19025fdd593SJeykumar Sankaran cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC); 19125fdd593SJeykumar Sankaran orig = (bool)(cfg & BIT(20)); 19225fdd593SJeykumar Sankaran if (enable_external_te) 19325fdd593SJeykumar Sankaran cfg |= BIT(20); 19425fdd593SJeykumar Sankaran else 19525fdd593SJeykumar Sankaran cfg &= ~BIT(20); 19625fdd593SJeykumar Sankaran DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 19725fdd593SJeykumar Sankaran trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg); 19825fdd593SJeykumar Sankaran 19925fdd593SJeykumar Sankaran return orig; 20025fdd593SJeykumar Sankaran } 20125fdd593SJeykumar Sankaran 20225fdd593SJeykumar Sankaran static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp, 20325fdd593SJeykumar Sankaran struct dpu_hw_pp_vsync_info *info) 20425fdd593SJeykumar Sankaran { 20525fdd593SJeykumar Sankaran struct dpu_hw_blk_reg_map *c; 20625fdd593SJeykumar Sankaran u32 val; 20725fdd593SJeykumar Sankaran 20825fdd593SJeykumar Sankaran if (!pp || !info) 20925fdd593SJeykumar Sankaran return -EINVAL; 21025fdd593SJeykumar Sankaran c = &pp->hw; 21125fdd593SJeykumar Sankaran 21225fdd593SJeykumar Sankaran val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL); 21325fdd593SJeykumar Sankaran info->rd_ptr_init_val = val & 0xffff; 21425fdd593SJeykumar Sankaran 21525fdd593SJeykumar Sankaran val = DPU_REG_READ(c, PP_INT_COUNT_VAL); 21625fdd593SJeykumar Sankaran info->rd_ptr_frame_count = (val & 0xffff0000) >> 16; 21725fdd593SJeykumar Sankaran info->rd_ptr_line_count = val & 0xffff; 21825fdd593SJeykumar Sankaran 21925fdd593SJeykumar Sankaran val = DPU_REG_READ(c, PP_LINE_COUNT); 22025fdd593SJeykumar Sankaran info->wr_ptr_line_count = val & 0xffff; 22125fdd593SJeykumar Sankaran 22225fdd593SJeykumar Sankaran return 0; 22325fdd593SJeykumar Sankaran } 22425fdd593SJeykumar Sankaran 22525fdd593SJeykumar Sankaran static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp) 22625fdd593SJeykumar Sankaran { 22725fdd593SJeykumar Sankaran struct dpu_hw_blk_reg_map *c = &pp->hw; 22825fdd593SJeykumar Sankaran u32 height, init; 22925fdd593SJeykumar Sankaran u32 line = 0xFFFF; 23025fdd593SJeykumar Sankaran 23125fdd593SJeykumar Sankaran if (!pp) 23225fdd593SJeykumar Sankaran return 0; 23325fdd593SJeykumar Sankaran c = &pp->hw; 23425fdd593SJeykumar Sankaran 23525fdd593SJeykumar Sankaran init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF; 23625fdd593SJeykumar Sankaran height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF; 23725fdd593SJeykumar Sankaran 23825fdd593SJeykumar Sankaran if (height < init) 2390841851fSSean Paul return line; 24025fdd593SJeykumar Sankaran 24125fdd593SJeykumar Sankaran line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF; 24225fdd593SJeykumar Sankaran 24325fdd593SJeykumar Sankaran if (line < init) 24425fdd593SJeykumar Sankaran line += (0xFFFF - init); 24525fdd593SJeykumar Sankaran else 24625fdd593SJeykumar Sankaran line -= init; 24725fdd593SJeykumar Sankaran 24825fdd593SJeykumar Sankaran return line; 24925fdd593SJeykumar Sankaran } 25025fdd593SJeykumar Sankaran 251*893d6982SVinod Koul static int dpu_hw_pp_dsc_enable(struct dpu_hw_pingpong *pp) 252*893d6982SVinod Koul { 253*893d6982SVinod Koul struct dpu_hw_blk_reg_map *c = &pp->hw; 254*893d6982SVinod Koul 255*893d6982SVinod Koul DPU_REG_WRITE(c, PP_DSC_MODE, 1); 256*893d6982SVinod Koul return 0; 257*893d6982SVinod Koul } 258*893d6982SVinod Koul 259*893d6982SVinod Koul static void dpu_hw_pp_dsc_disable(struct dpu_hw_pingpong *pp) 260*893d6982SVinod Koul { 261*893d6982SVinod Koul struct dpu_hw_blk_reg_map *c = &pp->hw; 262*893d6982SVinod Koul 263*893d6982SVinod Koul DPU_REG_WRITE(c, PP_DSC_MODE, 0); 264*893d6982SVinod Koul } 265*893d6982SVinod Koul 266*893d6982SVinod Koul static int dpu_hw_pp_setup_dsc(struct dpu_hw_pingpong *pp) 267*893d6982SVinod Koul { 268*893d6982SVinod Koul struct dpu_hw_blk_reg_map *pp_c = &pp->hw; 269*893d6982SVinod Koul int data; 270*893d6982SVinod Koul 271*893d6982SVinod Koul data = DPU_REG_READ(pp_c, PP_DCE_DATA_OUT_SWAP); 272*893d6982SVinod Koul data |= BIT(18); /* endian flip */ 273*893d6982SVinod Koul DPU_REG_WRITE(pp_c, PP_DCE_DATA_OUT_SWAP, data); 274*893d6982SVinod Koul return 0; 275*893d6982SVinod Koul } 276*893d6982SVinod Koul 2773c128638SKalyan Thota static void _setup_pingpong_ops(struct dpu_hw_pingpong *c, 2783c128638SKalyan Thota unsigned long features) 27925fdd593SJeykumar Sankaran { 2803c128638SKalyan Thota c->ops.setup_tearcheck = dpu_hw_pp_setup_te_config; 2813c128638SKalyan Thota c->ops.enable_tearcheck = dpu_hw_pp_enable_te; 2823c128638SKalyan Thota c->ops.connect_external_te = dpu_hw_pp_connect_external_te; 2833c128638SKalyan Thota c->ops.get_vsync_info = dpu_hw_pp_get_vsync_info; 284aa9223a6SAngeloGioacchino Del Regno c->ops.setup_autorefresh = dpu_hw_pp_setup_autorefresh_config; 285aa9223a6SAngeloGioacchino Del Regno c->ops.get_autorefresh = dpu_hw_pp_get_autorefresh_config; 2863c128638SKalyan Thota c->ops.poll_timeout_wr_ptr = dpu_hw_pp_poll_timeout_wr_ptr; 2873c128638SKalyan Thota c->ops.get_line_count = dpu_hw_pp_get_line_count; 288*893d6982SVinod Koul c->ops.setup_dsc = dpu_hw_pp_setup_dsc; 289*893d6982SVinod Koul c->ops.enable_dsc = dpu_hw_pp_dsc_enable; 290*893d6982SVinod Koul c->ops.disable_dsc = dpu_hw_pp_dsc_disable; 2913c128638SKalyan Thota 292b8afe9f8SRob Clark if (test_bit(DPU_PINGPONG_DITHER, &features)) 2933c128638SKalyan Thota c->ops.setup_dither = dpu_hw_pp_setup_dither; 29425fdd593SJeykumar Sankaran }; 29525fdd593SJeykumar Sankaran 29625fdd593SJeykumar Sankaran struct dpu_hw_pingpong *dpu_hw_pingpong_init(enum dpu_pingpong idx, 29725fdd593SJeykumar Sankaran void __iomem *addr, 298abda0d92SStephen Boyd const struct dpu_mdss_cfg *m) 29925fdd593SJeykumar Sankaran { 30025fdd593SJeykumar Sankaran struct dpu_hw_pingpong *c; 301abda0d92SStephen Boyd const struct dpu_pingpong_cfg *cfg; 30225fdd593SJeykumar Sankaran 30325fdd593SJeykumar Sankaran c = kzalloc(sizeof(*c), GFP_KERNEL); 30425fdd593SJeykumar Sankaran if (!c) 30525fdd593SJeykumar Sankaran return ERR_PTR(-ENOMEM); 30625fdd593SJeykumar Sankaran 30725fdd593SJeykumar Sankaran cfg = _pingpong_offset(idx, m, addr, &c->hw); 30825fdd593SJeykumar Sankaran if (IS_ERR_OR_NULL(cfg)) { 30925fdd593SJeykumar Sankaran kfree(c); 31025fdd593SJeykumar Sankaran return ERR_PTR(-EINVAL); 31125fdd593SJeykumar Sankaran } 31225fdd593SJeykumar Sankaran 31325fdd593SJeykumar Sankaran c->idx = idx; 31425fdd593SJeykumar Sankaran c->caps = cfg; 3153c128638SKalyan Thota _setup_pingpong_ops(c, c->caps->features); 31625fdd593SJeykumar Sankaran 31725fdd593SJeykumar Sankaran return c; 31825fdd593SJeykumar Sankaran } 31925fdd593SJeykumar Sankaran 32025fdd593SJeykumar Sankaran void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp) 32125fdd593SJeykumar Sankaran { 32225fdd593SJeykumar Sankaran kfree(pp); 32325fdd593SJeykumar Sankaran } 324