/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sde_hw_mdss.h" #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_pingpong.h" #include "sde_dbg.h" #define PP_TEAR_CHECK_EN 0x000 #define PP_SYNC_CONFIG_VSYNC 0x004 #define PP_SYNC_CONFIG_HEIGHT 0x008 #define PP_SYNC_WRCOUNT 0x00C #define PP_VSYNC_INIT_VAL 0x010 #define PP_INT_COUNT_VAL 0x014 #define PP_SYNC_THRESH 0x018 #define PP_START_POS 0x01C #define PP_RD_PTR_IRQ 0x020 #define PP_WR_PTR_IRQ 0x024 #define PP_OUT_LINE_COUNT 0x028 #define PP_LINE_COUNT 0x02C #define PP_AUTOREFRESH_CONFIG 0x030 #define PP_FBC_MODE 0x034 #define PP_FBC_BUDGET_CTL 0x038 #define PP_FBC_LOSSY_MODE 0x03C #define PP_DSC_MODE 0x0a0 #define PP_DCE_DATA_IN_SWAP 0x0ac #define PP_DCE_DATA_OUT_SWAP 0x0c8 static struct sde_pingpong_cfg *_pingpong_offset(enum sde_pingpong pp, struct sde_mdss_cfg *m, void __iomem *addr, struct sde_hw_blk_reg_map *b) { int i; for (i = 0; i < m->pingpong_count; i++) { if (pp == m->pingpong[i].id) { b->base_off = addr; b->blk_off = m->pingpong[i].base; b->length = m->pingpong[i].len; b->hwversion = m->hwversion; b->log_mask = SDE_DBG_MASK_PINGPONG; return &m->pingpong[i]; } } return ERR_PTR(-EINVAL); } static int sde_hw_pp_setup_te_config(struct sde_hw_pingpong *pp, struct sde_hw_tear_check *te) { struct sde_hw_blk_reg_map *c = &pp->hw; int cfg; cfg = BIT(19); /*VSYNC_COUNTER_EN */ if (te->hw_vsync_mode) cfg |= BIT(20); cfg |= te->vsync_count; SDE_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); SDE_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height); SDE_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val); SDE_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq); SDE_REG_WRITE(c, PP_START_POS, te->start_pos); SDE_REG_WRITE(c, PP_SYNC_THRESH, ((te->sync_threshold_continue << 16) | te->sync_threshold_start)); SDE_REG_WRITE(c, PP_SYNC_WRCOUNT, (te->start_pos + te->sync_threshold_start + 1)); return 0; } int sde_hw_pp_setup_autorefresh_config(struct sde_hw_pingpong *pp, struct sde_hw_autorefresh *cfg) { struct sde_hw_blk_reg_map *c = &pp->hw; u32 refresh_cfg; if (cfg->enable) refresh_cfg = BIT(31) | cfg->frame_count; else refresh_cfg = 0; SDE_REG_WRITE(c, PP_AUTOREFRESH_CONFIG, refresh_cfg); return 0; } int sde_hw_pp_setup_dsc_compression(struct sde_hw_pingpong *pp, struct sde_hw_dsc_cfg *cfg) { return 0; } int sde_hw_pp_enable_te(struct sde_hw_pingpong *pp, bool enable) { struct sde_hw_blk_reg_map *c = &pp->hw; SDE_REG_WRITE(c, PP_TEAR_CHECK_EN, enable); return 0; } int sde_hw_pp_get_vsync_info(struct sde_hw_pingpong *pp, struct sde_hw_pp_vsync_info *info) { struct sde_hw_blk_reg_map *c = &pp->hw; u32 val; val = SDE_REG_READ(c, PP_VSYNC_INIT_VAL); info->init_val = val & 0xffff; val = SDE_REG_READ(c, PP_INT_COUNT_VAL); info->vsync_count = (val & 0xffff0000) >> 16; info->line_count = val & 0xffff; return 0; } static void _setup_pingpong_ops(struct sde_hw_pingpong_ops *ops, unsigned long cap) { ops->setup_tearcheck = sde_hw_pp_setup_te_config; ops->enable_tearcheck = sde_hw_pp_enable_te; ops->get_vsync_info = sde_hw_pp_get_vsync_info; ops->setup_autorefresh = sde_hw_pp_setup_autorefresh_config; ops->setup_dsc = sde_hw_pp_setup_dsc_compression; }; struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx, void __iomem *addr, struct sde_mdss_cfg *m) { struct sde_hw_pingpong *c; struct sde_pingpong_cfg *cfg; c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) return ERR_PTR(-ENOMEM); cfg = _pingpong_offset(idx, m, addr, &c->hw); if (IS_ERR_OR_NULL(cfg)) { kfree(c); return ERR_PTR(-EINVAL); } c->idx = idx; c->pingpong_hw_cap = cfg; _setup_pingpong_ops(&c->ops, c->pingpong_hw_cap->features); sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, c->hw.blk_off + c->hw.length, c->hw.xin_id); return c; } void sde_hw_pingpong_destroy(struct sde_hw_pingpong *pp) { kfree(pp); }