cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dpu_hw_pingpong.c (8177B)


      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#define PP_AUTOREFRESH_CONFIG           0x030
     27
     28#define PP_FBC_MODE                     0x034
     29#define PP_FBC_BUDGET_CTL               0x038
     30#define PP_FBC_LOSSY_MODE               0x03C
     31#define PP_DSC_MODE                     0x0a0
     32#define PP_DCE_DATA_IN_SWAP             0x0ac
     33#define PP_DCE_DATA_OUT_SWAP            0x0c8
     34
     35#define PP_DITHER_EN			0x000
     36#define PP_DITHER_BITDEPTH		0x004
     37#define PP_DITHER_MATRIX		0x008
     38
     39#define DITHER_DEPTH_MAP_INDEX 9
     40
     41static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = {
     42	0, 0, 0, 0, 0, 0, 0, 1, 2
     43};
     44
     45static const struct dpu_pingpong_cfg *_pingpong_offset(enum dpu_pingpong pp,
     46		const struct dpu_mdss_cfg *m,
     47		void __iomem *addr,
     48		struct dpu_hw_blk_reg_map *b)
     49{
     50	int i;
     51
     52	for (i = 0; i < m->pingpong_count; i++) {
     53		if (pp == m->pingpong[i].id) {
     54			b->base_off = addr;
     55			b->blk_off = m->pingpong[i].base;
     56			b->length = m->pingpong[i].len;
     57			b->hwversion = m->hwversion;
     58			b->log_mask = DPU_DBG_MASK_PINGPONG;
     59			return &m->pingpong[i];
     60		}
     61	}
     62
     63	return ERR_PTR(-EINVAL);
     64}
     65
     66static void dpu_hw_pp_setup_dither(struct dpu_hw_pingpong *pp,
     67				    struct dpu_hw_dither_cfg *cfg)
     68{
     69	struct dpu_hw_blk_reg_map *c;
     70	u32 i, base, data = 0;
     71
     72	c = &pp->hw;
     73	base = pp->caps->sblk->dither.base;
     74	if (!cfg) {
     75		DPU_REG_WRITE(c, base + PP_DITHER_EN, 0);
     76		return;
     77	}
     78
     79	data = dither_depth_map[cfg->c0_bitdepth] & REG_MASK(2);
     80	data |= (dither_depth_map[cfg->c1_bitdepth] & REG_MASK(2)) << 2;
     81	data |= (dither_depth_map[cfg->c2_bitdepth] & REG_MASK(2)) << 4;
     82	data |= (dither_depth_map[cfg->c3_bitdepth] & REG_MASK(2)) << 6;
     83	data |= (cfg->temporal_en) ? (1 << 8) : 0;
     84
     85	DPU_REG_WRITE(c, base + PP_DITHER_BITDEPTH, data);
     86
     87	for (i = 0; i < DITHER_MATRIX_SZ - 3; i += 4) {
     88		data = (cfg->matrix[i] & REG_MASK(4)) |
     89			((cfg->matrix[i + 1] & REG_MASK(4)) << 4) |
     90			((cfg->matrix[i + 2] & REG_MASK(4)) << 8) |
     91			((cfg->matrix[i + 3] & REG_MASK(4)) << 12);
     92		DPU_REG_WRITE(c, base + PP_DITHER_MATRIX + i, data);
     93	}
     94	DPU_REG_WRITE(c, base + PP_DITHER_EN, 1);
     95}
     96
     97static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong *pp,
     98		struct dpu_hw_tear_check *te)
     99{
    100	struct dpu_hw_blk_reg_map *c;
    101	int cfg;
    102
    103	if (!pp || !te)
    104		return -EINVAL;
    105	c = &pp->hw;
    106
    107	cfg = BIT(19); /*VSYNC_COUNTER_EN */
    108	if (te->hw_vsync_mode)
    109		cfg |= BIT(20);
    110
    111	cfg |= te->vsync_count;
    112
    113	DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
    114	DPU_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height);
    115	DPU_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val);
    116	DPU_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq);
    117	DPU_REG_WRITE(c, PP_START_POS, te->start_pos);
    118	DPU_REG_WRITE(c, PP_SYNC_THRESH,
    119			((te->sync_threshold_continue << 16) |
    120			 te->sync_threshold_start));
    121	DPU_REG_WRITE(c, PP_SYNC_WRCOUNT,
    122			(te->start_pos + te->sync_threshold_start + 1));
    123
    124	return 0;
    125}
    126
    127static void dpu_hw_pp_setup_autorefresh_config(struct dpu_hw_pingpong *pp,
    128					       u32 frame_count, bool enable)
    129{
    130	DPU_REG_WRITE(&pp->hw, PP_AUTOREFRESH_CONFIG,
    131		      enable ? (BIT(31) | frame_count) : 0);
    132}
    133
    134/*
    135 * dpu_hw_pp_get_autorefresh_config - Get autorefresh config from HW
    136 * @pp:          DPU pingpong structure
    137 * @frame_count: Used to return the current frame count from hw
    138 *
    139 * Returns: True if autorefresh enabled, false if disabled.
    140 */
    141static bool dpu_hw_pp_get_autorefresh_config(struct dpu_hw_pingpong *pp,
    142					     u32 *frame_count)
    143{
    144	u32 val = DPU_REG_READ(&pp->hw, PP_AUTOREFRESH_CONFIG);
    145	if (frame_count != NULL)
    146		*frame_count = val & 0xffff;
    147	return !!((val & BIT(31)) >> 31);
    148}
    149
    150static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp,
    151		u32 timeout_us)
    152{
    153	struct dpu_hw_blk_reg_map *c;
    154	u32 val;
    155	int rc;
    156
    157	if (!pp)
    158		return -EINVAL;
    159
    160	c = &pp->hw;
    161	rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT,
    162			val, (val & 0xffff) >= 1, 10, timeout_us);
    163
    164	return rc;
    165}
    166
    167static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable)
    168{
    169	struct dpu_hw_blk_reg_map *c;
    170
    171	if (!pp)
    172		return -EINVAL;
    173	c = &pp->hw;
    174
    175	DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable);
    176	return 0;
    177}
    178
    179static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp,
    180		bool enable_external_te)
    181{
    182	struct dpu_hw_blk_reg_map *c = &pp->hw;
    183	u32 cfg;
    184	int orig;
    185
    186	if (!pp)
    187		return -EINVAL;
    188
    189	c = &pp->hw;
    190	cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC);
    191	orig = (bool)(cfg & BIT(20));
    192	if (enable_external_te)
    193		cfg |= BIT(20);
    194	else
    195		cfg &= ~BIT(20);
    196	DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
    197	trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg);
    198
    199	return orig;
    200}
    201
    202static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp,
    203		struct dpu_hw_pp_vsync_info *info)
    204{
    205	struct dpu_hw_blk_reg_map *c;
    206	u32 val;
    207
    208	if (!pp || !info)
    209		return -EINVAL;
    210	c = &pp->hw;
    211
    212	val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL);
    213	info->rd_ptr_init_val = val & 0xffff;
    214
    215	val = DPU_REG_READ(c, PP_INT_COUNT_VAL);
    216	info->rd_ptr_frame_count = (val & 0xffff0000) >> 16;
    217	info->rd_ptr_line_count = val & 0xffff;
    218
    219	val = DPU_REG_READ(c, PP_LINE_COUNT);
    220	info->wr_ptr_line_count = val & 0xffff;
    221
    222	return 0;
    223}
    224
    225static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp)
    226{
    227	struct dpu_hw_blk_reg_map *c = &pp->hw;
    228	u32 height, init;
    229	u32 line = 0xFFFF;
    230
    231	if (!pp)
    232		return 0;
    233	c = &pp->hw;
    234
    235	init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF;
    236	height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF;
    237
    238	if (height < init)
    239		return line;
    240
    241	line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF;
    242
    243	if (line < init)
    244		line += (0xFFFF - init);
    245	else
    246		line -= init;
    247
    248	return line;
    249}
    250
    251static int dpu_hw_pp_dsc_enable(struct dpu_hw_pingpong *pp)
    252{
    253	struct dpu_hw_blk_reg_map *c = &pp->hw;
    254
    255	DPU_REG_WRITE(c, PP_DSC_MODE, 1);
    256	return 0;
    257}
    258
    259static void dpu_hw_pp_dsc_disable(struct dpu_hw_pingpong *pp)
    260{
    261	struct dpu_hw_blk_reg_map *c = &pp->hw;
    262
    263	DPU_REG_WRITE(c, PP_DSC_MODE, 0);
    264}
    265
    266static int dpu_hw_pp_setup_dsc(struct dpu_hw_pingpong *pp)
    267{
    268	struct dpu_hw_blk_reg_map *pp_c = &pp->hw;
    269	int data;
    270
    271	data = DPU_REG_READ(pp_c, PP_DCE_DATA_OUT_SWAP);
    272	data |= BIT(18); /* endian flip */
    273	DPU_REG_WRITE(pp_c, PP_DCE_DATA_OUT_SWAP, data);
    274	return 0;
    275}
    276
    277static void _setup_pingpong_ops(struct dpu_hw_pingpong *c,
    278				unsigned long features)
    279{
    280	c->ops.setup_tearcheck = dpu_hw_pp_setup_te_config;
    281	c->ops.enable_tearcheck = dpu_hw_pp_enable_te;
    282	c->ops.connect_external_te = dpu_hw_pp_connect_external_te;
    283	c->ops.get_vsync_info = dpu_hw_pp_get_vsync_info;
    284	c->ops.setup_autorefresh = dpu_hw_pp_setup_autorefresh_config;
    285	c->ops.get_autorefresh = dpu_hw_pp_get_autorefresh_config;
    286	c->ops.poll_timeout_wr_ptr = dpu_hw_pp_poll_timeout_wr_ptr;
    287	c->ops.get_line_count = dpu_hw_pp_get_line_count;
    288	c->ops.setup_dsc = dpu_hw_pp_setup_dsc;
    289	c->ops.enable_dsc = dpu_hw_pp_dsc_enable;
    290	c->ops.disable_dsc = dpu_hw_pp_dsc_disable;
    291
    292	if (test_bit(DPU_PINGPONG_DITHER, &features))
    293		c->ops.setup_dither = dpu_hw_pp_setup_dither;
    294};
    295
    296struct dpu_hw_pingpong *dpu_hw_pingpong_init(enum dpu_pingpong idx,
    297		void __iomem *addr,
    298		const struct dpu_mdss_cfg *m)
    299{
    300	struct dpu_hw_pingpong *c;
    301	const struct dpu_pingpong_cfg *cfg;
    302
    303	c = kzalloc(sizeof(*c), GFP_KERNEL);
    304	if (!c)
    305		return ERR_PTR(-ENOMEM);
    306
    307	cfg = _pingpong_offset(idx, m, addr, &c->hw);
    308	if (IS_ERR_OR_NULL(cfg)) {
    309		kfree(c);
    310		return ERR_PTR(-EINVAL);
    311	}
    312
    313	c->idx = idx;
    314	c->caps = cfg;
    315	_setup_pingpong_ops(c, c->caps->features);
    316
    317	return c;
    318}
    319
    320void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp)
    321{
    322	kfree(pp);
    323}