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

ipu-dp.c (8881B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
      4 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
      5 */
      6#include <linux/export.h>
      7#include <linux/kernel.h>
      8#include <linux/types.h>
      9#include <linux/errno.h>
     10#include <linux/io.h>
     11#include <linux/err.h>
     12
     13#include <drm/drm_color_mgmt.h>
     14#include <video/imx-ipu-v3.h>
     15#include "ipu-prv.h"
     16
     17#define DP_SYNC 0
     18#define DP_ASYNC0 0x60
     19#define DP_ASYNC1 0xBC
     20
     21#define DP_COM_CONF		0x0
     22#define DP_GRAPH_WIND_CTRL	0x0004
     23#define DP_FG_POS		0x0008
     24#define DP_CSC_A_0		0x0044
     25#define DP_CSC_A_1		0x0048
     26#define DP_CSC_A_2		0x004C
     27#define DP_CSC_A_3		0x0050
     28#define DP_CSC_0		0x0054
     29#define DP_CSC_1		0x0058
     30
     31#define DP_COM_CONF_FG_EN		(1 << 0)
     32#define DP_COM_CONF_GWSEL		(1 << 1)
     33#define DP_COM_CONF_GWAM		(1 << 2)
     34#define DP_COM_CONF_GWCKE		(1 << 3)
     35#define DP_COM_CONF_CSC_DEF_MASK	(3 << 8)
     36#define DP_COM_CONF_CSC_DEF_OFFSET	8
     37#define DP_COM_CONF_CSC_DEF_FG		(3 << 8)
     38#define DP_COM_CONF_CSC_DEF_BG		(2 << 8)
     39#define DP_COM_CONF_CSC_DEF_BOTH	(1 << 8)
     40
     41#define IPUV3_NUM_FLOWS		3
     42
     43struct ipu_dp_priv;
     44
     45struct ipu_dp {
     46	u32 flow;
     47	bool in_use;
     48	bool foreground;
     49	enum ipu_color_space in_cs;
     50};
     51
     52struct ipu_flow {
     53	struct ipu_dp foreground;
     54	struct ipu_dp background;
     55	enum ipu_color_space out_cs;
     56	void __iomem *base;
     57	struct ipu_dp_priv *priv;
     58};
     59
     60struct ipu_dp_priv {
     61	struct ipu_soc *ipu;
     62	struct device *dev;
     63	void __iomem *base;
     64	struct ipu_flow flow[IPUV3_NUM_FLOWS];
     65	struct mutex mutex;
     66	int use_count;
     67};
     68
     69static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
     70
     71static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
     72{
     73	if (dp->foreground)
     74		return container_of(dp, struct ipu_flow, foreground);
     75	else
     76		return container_of(dp, struct ipu_flow, background);
     77}
     78
     79int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
     80		u8 alpha, bool bg_chan)
     81{
     82	struct ipu_flow *flow = to_flow(dp);
     83	struct ipu_dp_priv *priv = flow->priv;
     84	u32 reg;
     85
     86	mutex_lock(&priv->mutex);
     87
     88	reg = readl(flow->base + DP_COM_CONF);
     89	if (bg_chan)
     90		reg &= ~DP_COM_CONF_GWSEL;
     91	else
     92		reg |= DP_COM_CONF_GWSEL;
     93	writel(reg, flow->base + DP_COM_CONF);
     94
     95	if (enable) {
     96		reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
     97		writel(reg | ((u32) alpha << 24),
     98			     flow->base + DP_GRAPH_WIND_CTRL);
     99
    100		reg = readl(flow->base + DP_COM_CONF);
    101		writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
    102	} else {
    103		reg = readl(flow->base + DP_COM_CONF);
    104		writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
    105	}
    106
    107	ipu_srm_dp_update(priv->ipu, true);
    108
    109	mutex_unlock(&priv->mutex);
    110
    111	return 0;
    112}
    113EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
    114
    115int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
    116{
    117	struct ipu_flow *flow = to_flow(dp);
    118	struct ipu_dp_priv *priv = flow->priv;
    119
    120	writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
    121
    122	ipu_srm_dp_update(priv->ipu, true);
    123
    124	return 0;
    125}
    126EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
    127
    128static void ipu_dp_csc_init(struct ipu_flow *flow,
    129		enum drm_color_encoding ycbcr_enc,
    130		enum drm_color_range range,
    131		enum ipu_color_space in,
    132		enum ipu_color_space out,
    133		u32 place)
    134{
    135	u32 reg;
    136
    137	reg = readl(flow->base + DP_COM_CONF);
    138	reg &= ~DP_COM_CONF_CSC_DEF_MASK;
    139
    140	if (in == out) {
    141		writel(reg, flow->base + DP_COM_CONF);
    142		return;
    143	}
    144
    145	if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
    146		writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
    147		writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
    148		writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
    149		writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
    150		writel(0x3d6 | (0x0000 << 16) | (2 << 30),
    151				flow->base + DP_CSC_0);
    152		writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
    153				flow->base + DP_CSC_1);
    154	} else if (ycbcr_enc == DRM_COLOR_YCBCR_BT709) {
    155		/* Rec.709 limited range */
    156		writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
    157		writel(0x0e5 | (0x095 << 16), flow->base + DP_CSC_A_1);
    158		writel(0x3e5 | (0x3bc << 16), flow->base + DP_CSC_A_2);
    159		writel(0x095 | (0x10e << 16), flow->base + DP_CSC_A_3);
    160		writel(0x000 | (0x3e10 << 16) | (1 << 30),
    161				flow->base + DP_CSC_0);
    162		writel(0x09a | (1 << 14) | (0x3dbe << 16) | (1 << 30),
    163				flow->base + DP_CSC_1);
    164	} else {
    165		/* BT.601 limited range */
    166		writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
    167		writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
    168		writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
    169		writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
    170		writel(0x000 | (0x3e42 << 16) | (1 << 30),
    171				flow->base + DP_CSC_0);
    172		writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
    173				flow->base + DP_CSC_1);
    174	}
    175
    176	reg |= place;
    177
    178	writel(reg, flow->base + DP_COM_CONF);
    179}
    180
    181int ipu_dp_setup_channel(struct ipu_dp *dp,
    182		enum drm_color_encoding ycbcr_enc,
    183		enum drm_color_range range,
    184		enum ipu_color_space in,
    185		enum ipu_color_space out)
    186{
    187	struct ipu_flow *flow = to_flow(dp);
    188	struct ipu_dp_priv *priv = flow->priv;
    189
    190	mutex_lock(&priv->mutex);
    191
    192	dp->in_cs = in;
    193
    194	if (!dp->foreground)
    195		flow->out_cs = out;
    196
    197	if (flow->foreground.in_cs == flow->background.in_cs) {
    198		/*
    199		 * foreground and background are of same colorspace, put
    200		 * colorspace converter after combining unit.
    201		 */
    202		ipu_dp_csc_init(flow, ycbcr_enc, range,
    203				flow->foreground.in_cs, flow->out_cs,
    204				DP_COM_CONF_CSC_DEF_BOTH);
    205	} else {
    206		if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN ||
    207		    flow->foreground.in_cs == flow->out_cs)
    208			/*
    209			 * foreground identical to output, apply color
    210			 * conversion on background
    211			 */
    212			ipu_dp_csc_init(flow, ycbcr_enc, range,
    213					flow->background.in_cs,
    214					flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
    215		else
    216			ipu_dp_csc_init(flow, ycbcr_enc, range,
    217					flow->foreground.in_cs,
    218					flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
    219	}
    220
    221	ipu_srm_dp_update(priv->ipu, true);
    222
    223	mutex_unlock(&priv->mutex);
    224
    225	return 0;
    226}
    227EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
    228
    229int ipu_dp_enable(struct ipu_soc *ipu)
    230{
    231	struct ipu_dp_priv *priv = ipu->dp_priv;
    232
    233	mutex_lock(&priv->mutex);
    234
    235	if (!priv->use_count)
    236		ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
    237
    238	priv->use_count++;
    239
    240	mutex_unlock(&priv->mutex);
    241
    242	return 0;
    243}
    244EXPORT_SYMBOL_GPL(ipu_dp_enable);
    245
    246int ipu_dp_enable_channel(struct ipu_dp *dp)
    247{
    248	struct ipu_flow *flow = to_flow(dp);
    249	struct ipu_dp_priv *priv = flow->priv;
    250	u32 reg;
    251
    252	if (!dp->foreground)
    253		return 0;
    254
    255	mutex_lock(&priv->mutex);
    256
    257	reg = readl(flow->base + DP_COM_CONF);
    258	reg |= DP_COM_CONF_FG_EN;
    259	writel(reg, flow->base + DP_COM_CONF);
    260
    261	ipu_srm_dp_update(priv->ipu, true);
    262
    263	mutex_unlock(&priv->mutex);
    264
    265	return 0;
    266}
    267EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
    268
    269void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync)
    270{
    271	struct ipu_flow *flow = to_flow(dp);
    272	struct ipu_dp_priv *priv = flow->priv;
    273	u32 reg, csc;
    274
    275	dp->in_cs = IPUV3_COLORSPACE_UNKNOWN;
    276
    277	if (!dp->foreground)
    278		return;
    279
    280	mutex_lock(&priv->mutex);
    281
    282	reg = readl(flow->base + DP_COM_CONF);
    283	csc = reg & DP_COM_CONF_CSC_DEF_MASK;
    284	reg &= ~DP_COM_CONF_CSC_DEF_MASK;
    285	if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG)
    286		reg |= DP_COM_CONF_CSC_DEF_BG;
    287
    288	reg &= ~DP_COM_CONF_FG_EN;
    289	writel(reg, flow->base + DP_COM_CONF);
    290
    291	writel(0, flow->base + DP_FG_POS);
    292	ipu_srm_dp_update(priv->ipu, sync);
    293
    294	mutex_unlock(&priv->mutex);
    295}
    296EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
    297
    298void ipu_dp_disable(struct ipu_soc *ipu)
    299{
    300	struct ipu_dp_priv *priv = ipu->dp_priv;
    301
    302	mutex_lock(&priv->mutex);
    303
    304	priv->use_count--;
    305
    306	if (!priv->use_count)
    307		ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
    308
    309	if (priv->use_count < 0)
    310		priv->use_count = 0;
    311
    312	mutex_unlock(&priv->mutex);
    313}
    314EXPORT_SYMBOL_GPL(ipu_dp_disable);
    315
    316struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
    317{
    318	struct ipu_dp_priv *priv = ipu->dp_priv;
    319	struct ipu_dp *dp;
    320
    321	if ((flow >> 1) >= IPUV3_NUM_FLOWS)
    322		return ERR_PTR(-EINVAL);
    323
    324	if (flow & 1)
    325		dp = &priv->flow[flow >> 1].foreground;
    326	else
    327		dp = &priv->flow[flow >> 1].background;
    328
    329	if (dp->in_use)
    330		return ERR_PTR(-EBUSY);
    331
    332	dp->in_use = true;
    333
    334	return dp;
    335}
    336EXPORT_SYMBOL_GPL(ipu_dp_get);
    337
    338void ipu_dp_put(struct ipu_dp *dp)
    339{
    340	dp->in_use = false;
    341}
    342EXPORT_SYMBOL_GPL(ipu_dp_put);
    343
    344int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
    345{
    346	struct ipu_dp_priv *priv;
    347	int i;
    348
    349	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    350	if (!priv)
    351		return -ENOMEM;
    352	priv->dev = dev;
    353	priv->ipu = ipu;
    354
    355	ipu->dp_priv = priv;
    356
    357	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
    358	if (!priv->base)
    359		return -ENOMEM;
    360
    361	mutex_init(&priv->mutex);
    362
    363	for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
    364		priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN;
    365		priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN;
    366		priv->flow[i].foreground.foreground = true;
    367		priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
    368		priv->flow[i].priv = priv;
    369	}
    370
    371	return 0;
    372}
    373
    374void ipu_dp_exit(struct ipu_soc *ipu)
    375{
    376}