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

dcss-dtg.c (10457B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2019 NXP.
      4 */
      5
      6#include <linux/clk.h>
      7#include <linux/delay.h>
      8#include <linux/interrupt.h>
      9#include <linux/of.h>
     10#include <linux/platform_device.h>
     11#include <linux/slab.h>
     12
     13#include "dcss-dev.h"
     14
     15#define DCSS_DTG_TC_CONTROL_STATUS			0x00
     16#define   CH3_EN					BIT(0)
     17#define   CH2_EN					BIT(1)
     18#define   CH1_EN					BIT(2)
     19#define   OVL_DATA_MODE					BIT(3)
     20#define   BLENDER_VIDEO_ALPHA_SEL			BIT(7)
     21#define   DTG_START					BIT(8)
     22#define   DBY_MODE_EN					BIT(9)
     23#define   CH1_ALPHA_SEL					BIT(10)
     24#define   CSS_PIX_COMP_SWAP_POS				12
     25#define   CSS_PIX_COMP_SWAP_MASK			GENMASK(14, 12)
     26#define   DEFAULT_FG_ALPHA_POS				24
     27#define   DEFAULT_FG_ALPHA_MASK				GENMASK(31, 24)
     28#define DCSS_DTG_TC_DTG					0x04
     29#define DCSS_DTG_TC_DISP_TOP				0x08
     30#define DCSS_DTG_TC_DISP_BOT				0x0C
     31#define DCSS_DTG_TC_CH1_TOP				0x10
     32#define DCSS_DTG_TC_CH1_BOT				0x14
     33#define DCSS_DTG_TC_CH2_TOP				0x18
     34#define DCSS_DTG_TC_CH2_BOT				0x1C
     35#define DCSS_DTG_TC_CH3_TOP				0x20
     36#define DCSS_DTG_TC_CH3_BOT				0x24
     37#define   TC_X_POS					0
     38#define   TC_X_MASK					GENMASK(12, 0)
     39#define   TC_Y_POS					16
     40#define   TC_Y_MASK					GENMASK(28, 16)
     41#define DCSS_DTG_TC_CTXLD				0x28
     42#define   TC_CTXLD_DB_Y_POS				0
     43#define   TC_CTXLD_DB_Y_MASK				GENMASK(12, 0)
     44#define   TC_CTXLD_SB_Y_POS				16
     45#define   TC_CTXLD_SB_Y_MASK				GENMASK(28, 16)
     46#define DCSS_DTG_TC_CH1_BKRND				0x2C
     47#define DCSS_DTG_TC_CH2_BKRND				0x30
     48#define   BKRND_R_Y_COMP_POS				20
     49#define   BKRND_R_Y_COMP_MASK				GENMASK(29, 20)
     50#define   BKRND_G_U_COMP_POS				10
     51#define   BKRND_G_U_COMP_MASK				GENMASK(19, 10)
     52#define   BKRND_B_V_COMP_POS				0
     53#define   BKRND_B_V_COMP_MASK				GENMASK(9, 0)
     54#define DCSS_DTG_BLENDER_DBY_RANGEINV			0x38
     55#define DCSS_DTG_BLENDER_DBY_RANGEMIN			0x3C
     56#define DCSS_DTG_BLENDER_DBY_BDP			0x40
     57#define DCSS_DTG_BLENDER_BKRND_I			0x44
     58#define DCSS_DTG_BLENDER_BKRND_P			0x48
     59#define DCSS_DTG_BLENDER_BKRND_T			0x4C
     60#define DCSS_DTG_LINE0_INT				0x50
     61#define DCSS_DTG_LINE1_INT				0x54
     62#define DCSS_DTG_BG_ALPHA_DEFAULT			0x58
     63#define DCSS_DTG_INT_STATUS				0x5C
     64#define DCSS_DTG_INT_CONTROL				0x60
     65#define DCSS_DTG_TC_CH3_BKRND				0x64
     66#define DCSS_DTG_INT_MASK				0x68
     67#define   LINE0_IRQ					BIT(0)
     68#define   LINE1_IRQ					BIT(1)
     69#define   LINE2_IRQ					BIT(2)
     70#define   LINE3_IRQ					BIT(3)
     71#define DCSS_DTG_LINE2_INT				0x6C
     72#define DCSS_DTG_LINE3_INT				0x70
     73#define DCSS_DTG_DBY_OL					0x74
     74#define DCSS_DTG_DBY_BL					0x78
     75#define DCSS_DTG_DBY_EL					0x7C
     76
     77struct dcss_dtg {
     78	struct device *dev;
     79	struct dcss_ctxld *ctxld;
     80	void __iomem *base_reg;
     81	u32 base_ofs;
     82
     83	u32 ctx_id;
     84
     85	bool in_use;
     86
     87	u32 dis_ulc_x;
     88	u32 dis_ulc_y;
     89
     90	u32 control_status;
     91	u32 alpha;
     92	u32 alpha_cfg;
     93
     94	int ctxld_kick_irq;
     95	bool ctxld_kick_irq_en;
     96};
     97
     98static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
     99{
    100	if (!dtg->in_use)
    101		dcss_writel(val, dtg->base_reg + ofs);
    102
    103	dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
    104			 val, dtg->base_ofs + ofs);
    105}
    106
    107static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
    108{
    109	struct dcss_dtg *dtg = data;
    110	u32 status;
    111
    112	status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
    113
    114	if (!(status & LINE0_IRQ))
    115		return IRQ_NONE;
    116
    117	dcss_ctxld_kick(dtg->ctxld);
    118
    119	dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
    120
    121	return IRQ_HANDLED;
    122}
    123
    124static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
    125			       struct platform_device *pdev)
    126{
    127	int ret;
    128
    129	dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
    130	if (dtg->ctxld_kick_irq < 0)
    131		return dtg->ctxld_kick_irq;
    132
    133	dcss_update(0, LINE0_IRQ | LINE1_IRQ,
    134		    dtg->base_reg + DCSS_DTG_INT_MASK);
    135
    136	ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
    137			  0, "dcss_ctxld_kick", dtg);
    138	if (ret) {
    139		dev_err(dtg->dev, "dtg: irq request failed.\n");
    140		return ret;
    141	}
    142
    143	disable_irq(dtg->ctxld_kick_irq);
    144
    145	dtg->ctxld_kick_irq_en = false;
    146
    147	return 0;
    148}
    149
    150int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
    151{
    152	int ret = 0;
    153	struct dcss_dtg *dtg;
    154
    155	dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
    156	if (!dtg)
    157		return -ENOMEM;
    158
    159	dcss->dtg = dtg;
    160	dtg->dev = dcss->dev;
    161	dtg->ctxld = dcss->ctxld;
    162
    163	dtg->base_reg = ioremap(dtg_base, SZ_4K);
    164	if (!dtg->base_reg) {
    165		dev_err(dcss->dev, "dtg: unable to remap dtg base\n");
    166		ret = -ENOMEM;
    167		goto err_ioremap;
    168	}
    169
    170	dtg->base_ofs = dtg_base;
    171	dtg->ctx_id = CTX_DB;
    172
    173	dtg->alpha = 255;
    174
    175	dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
    176		((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
    177
    178	ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
    179	if (ret)
    180		goto err_irq;
    181
    182	return 0;
    183
    184err_irq:
    185	iounmap(dtg->base_reg);
    186
    187err_ioremap:
    188	kfree(dtg);
    189
    190	return ret;
    191}
    192
    193void dcss_dtg_exit(struct dcss_dtg *dtg)
    194{
    195	free_irq(dtg->ctxld_kick_irq, dtg);
    196
    197	if (dtg->base_reg)
    198		iounmap(dtg->base_reg);
    199
    200	kfree(dtg);
    201}
    202
    203void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
    204{
    205	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
    206	u16 dtg_lrc_x, dtg_lrc_y;
    207	u16 dis_ulc_x, dis_ulc_y;
    208	u16 dis_lrc_x, dis_lrc_y;
    209	u32 sb_ctxld_trig, db_ctxld_trig;
    210	u32 pixclock = vm->pixelclock;
    211	u32 actual_clk;
    212
    213	dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
    214		    vm->hactive - 1;
    215	dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
    216		    vm->vactive - 1;
    217	dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
    218	dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
    219	dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
    220	dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
    221		    vm->vactive - 1;
    222
    223	clk_disable_unprepare(dcss->pix_clk);
    224	clk_set_rate(dcss->pix_clk, vm->pixelclock);
    225	clk_prepare_enable(dcss->pix_clk);
    226
    227	actual_clk = clk_get_rate(dcss->pix_clk);
    228	if (pixclock != actual_clk) {
    229		dev_info(dtg->dev,
    230			 "Pixel clock set to %u kHz instead of %u kHz.\n",
    231			 (actual_clk / 1000), (pixclock / 1000));
    232	}
    233
    234	dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
    235		       DCSS_DTG_TC_DTG);
    236	dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
    237		       DCSS_DTG_TC_DISP_TOP);
    238	dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
    239		       DCSS_DTG_TC_DISP_BOT);
    240
    241	dtg->dis_ulc_x = dis_ulc_x;
    242	dtg->dis_ulc_y = dis_ulc_y;
    243
    244	sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
    245							TC_CTXLD_SB_Y_MASK;
    246	db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
    247							TC_CTXLD_DB_Y_MASK;
    248
    249	dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
    250
    251	/* vblank trigger */
    252	dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
    253
    254	/* CTXLD trigger */
    255	dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
    256}
    257
    258void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
    259			    int px, int py, int pw, int ph)
    260{
    261	u16 p_ulc_x, p_ulc_y;
    262	u16 p_lrc_x, p_lrc_y;
    263
    264	p_ulc_x = dtg->dis_ulc_x + px;
    265	p_ulc_y = dtg->dis_ulc_y + py;
    266	p_lrc_x = p_ulc_x + pw;
    267	p_lrc_y = p_ulc_y + ph;
    268
    269	if (!px && !py && !pw && !ph) {
    270		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
    271		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
    272	} else {
    273		dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
    274			       DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
    275		dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
    276			       DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
    277	}
    278}
    279
    280bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
    281{
    282	if (ch_num)
    283		return false;
    284
    285	return alpha != dtg->alpha;
    286}
    287
    288void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
    289			      const struct drm_format_info *format, int alpha)
    290{
    291	/* we care about alpha only when channel 0 is concerned */
    292	if (ch_num)
    293		return;
    294
    295	/*
    296	 * Use global alpha if pixel format does not have alpha channel or the
    297	 * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
    298	 */
    299	if (!format->has_alpha || alpha != 255)
    300		dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
    301	else /* use per-pixel alpha otherwise */
    302		dtg->alpha_cfg = CH1_ALPHA_SEL;
    303
    304	dtg->alpha = alpha;
    305}
    306
    307void dcss_dtg_css_set(struct dcss_dtg *dtg)
    308{
    309	dtg->control_status |=
    310			(0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
    311}
    312
    313void dcss_dtg_enable(struct dcss_dtg *dtg)
    314{
    315	dtg->control_status |= DTG_START;
    316
    317	dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
    318	dtg->control_status |= dtg->alpha_cfg;
    319
    320	dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
    321
    322	dtg->in_use = true;
    323}
    324
    325void dcss_dtg_shutoff(struct dcss_dtg *dtg)
    326{
    327	dtg->control_status &= ~DTG_START;
    328
    329	dcss_writel(dtg->control_status,
    330		    dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
    331
    332	dtg->in_use = false;
    333}
    334
    335bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
    336{
    337	return dtg->in_use;
    338}
    339
    340void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
    341{
    342	u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
    343	u32 control_status;
    344
    345	control_status = dtg->control_status & ~ch_en_map[ch_num];
    346	control_status |= en ? ch_en_map[ch_num] : 0;
    347
    348	control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
    349	control_status |= dtg->alpha_cfg;
    350
    351	if (dtg->control_status != control_status)
    352		dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
    353
    354	dtg->control_status = control_status;
    355}
    356
    357void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
    358{
    359	u32 status;
    360	u32 mask = en ? LINE1_IRQ : 0;
    361
    362	if (en) {
    363		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
    364		dcss_writel(status & LINE1_IRQ,
    365			    dtg->base_reg + DCSS_DTG_INT_CONTROL);
    366	}
    367
    368	dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
    369}
    370
    371void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
    372{
    373	u32 status;
    374	u32 mask = en ? LINE0_IRQ : 0;
    375
    376	if (en) {
    377		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
    378
    379		if (!dtg->ctxld_kick_irq_en) {
    380			dcss_writel(status & LINE0_IRQ,
    381				    dtg->base_reg + DCSS_DTG_INT_CONTROL);
    382			enable_irq(dtg->ctxld_kick_irq);
    383			dtg->ctxld_kick_irq_en = true;
    384			dcss_update(mask, LINE0_IRQ,
    385				    dtg->base_reg + DCSS_DTG_INT_MASK);
    386		}
    387
    388		return;
    389	}
    390
    391	if (!dtg->ctxld_kick_irq_en)
    392		return;
    393
    394	disable_irq_nosync(dtg->ctxld_kick_irq);
    395	dtg->ctxld_kick_irq_en = false;
    396
    397	dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
    398}
    399
    400void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
    401{
    402	dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
    403}
    404
    405bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
    406{
    407	return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
    408}
    409