ipu-pre.c 10.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
/*
 * Copyright (c) 2017 Lucas Stach, Pengutronix
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope 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 <drm/drm_fourcc.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <video/imx-ipu-v3.h>

#include "ipu-prv.h"

#define IPU_PRE_MAX_WIDTH	2048
#define IPU_PRE_NUM_SCANLINES	8

#define IPU_PRE_CTRL					0x000
#define IPU_PRE_CTRL_SET				0x004
#define  IPU_PRE_CTRL_ENABLE				(1 << 0)
#define  IPU_PRE_CTRL_BLOCK_EN				(1 << 1)
#define  IPU_PRE_CTRL_BLOCK_16				(1 << 2)
#define  IPU_PRE_CTRL_SDW_UPDATE			(1 << 4)
#define  IPU_PRE_CTRL_VFLIP				(1 << 5)
#define  IPU_PRE_CTRL_SO				(1 << 6)
#define  IPU_PRE_CTRL_INTERLACED_FIELD			(1 << 7)
#define  IPU_PRE_CTRL_HANDSHAKE_EN			(1 << 8)
#define  IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v)		((v & 0x3) << 9)
#define  IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN		(1 << 11)
#define  IPU_PRE_CTRL_EN_REPEAT				(1 << 28)
#define  IPU_PRE_CTRL_TPR_REST_SEL			(1 << 29)
#define  IPU_PRE_CTRL_CLKGATE				(1 << 30)
#define  IPU_PRE_CTRL_SFTRST				(1 << 31)

#define IPU_PRE_CUR_BUF					0x030

#define IPU_PRE_NEXT_BUF				0x040

#define IPU_PRE_TPR_CTRL				0x070
#define  IPU_PRE_TPR_CTRL_TILE_FORMAT(v)		((v & 0xff) << 0)
#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK		0xff
52 53 54 55
#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT		(1 << 0)
#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF		(1 << 4)
#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF	(1 << 5)
#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED	(1 << 6)
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

#define IPU_PRE_PREFETCH_ENG_CTRL			0x080
#define  IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN		(1 << 0)
#define  IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v)		((v & 0x7) << 1)
#define  IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v)	((v & 0x3) << 4)
#define  IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v)	((v & 0x7) << 8)
#define  IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS		(1 << 11)
#define  IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE		(1 << 12)
#define  IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP		(1 << 14)
#define  IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN	(1 << 15)

#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE			0x0a0
#define  IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v)	((v & 0xffff) << 0)
#define  IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v)	((v & 0xffff) << 16)

#define IPU_PRE_PREFETCH_ENG_PITCH			0x0d0
#define  IPU_PRE_PREFETCH_ENG_PITCH_Y(v)		((v & 0xffff) << 0)
#define  IPU_PRE_PREFETCH_ENG_PITCH_UV(v)		((v & 0xffff) << 16)

#define IPU_PRE_STORE_ENG_CTRL				0x110
#define  IPU_PRE_STORE_ENG_CTRL_STORE_EN		(1 << 0)
#define  IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v)		((v & 0x7) << 1)
#define  IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v)	((v & 0x3) << 4)

80 81 82 83 84 85 86 87
#define IPU_PRE_STORE_ENG_STATUS			0x120
#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK	0xffff
#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT	0
#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK	0x3fff
#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT	16
#define  IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL	(1 << 30)
#define  IPU_PRE_STORE_ENG_STATUS_STORE_FIELD		(1 << 31)

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
#define IPU_PRE_STORE_ENG_SIZE				0x130
#define  IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v)		((v & 0xffff) << 0)
#define  IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v)		((v & 0xffff) << 16)

#define IPU_PRE_STORE_ENG_PITCH				0x140
#define  IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v)		((v & 0xffff) << 0)

#define IPU_PRE_STORE_ENG_ADDR				0x150

struct ipu_pre {
	struct list_head	list;
	struct device		*dev;

	void __iomem		*regs;
	struct clk		*clk_axi;
	struct gen_pool		*iram;

	dma_addr_t		buffer_paddr;
	void			*buffer_virt;
	bool			in_use;
108
	unsigned int		safe_window_end;
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
};

static DEFINE_MUTEX(ipu_pre_list_mutex);
static LIST_HEAD(ipu_pre_list);
static int available_pres;

int ipu_pre_get_available_count(void)
{
	return available_pres;
}

struct ipu_pre *
ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index)
{
	struct device_node *pre_node = of_parse_phandle(dev->of_node,
							name, index);
	struct ipu_pre *pre;

	mutex_lock(&ipu_pre_list_mutex);
	list_for_each_entry(pre, &ipu_pre_list, list) {
		if (pre_node == pre->dev->of_node) {
			mutex_unlock(&ipu_pre_list_mutex);
			device_link_add(dev, pre->dev, DL_FLAG_AUTOREMOVE);
132
			of_node_put(pre_node);
133 134 135 136 137
			return pre;
		}
	}
	mutex_unlock(&ipu_pre_list_mutex);

138 139
	of_node_put(pre_node);

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
	return NULL;
}

int ipu_pre_get(struct ipu_pre *pre)
{
	u32 val;

	if (pre->in_use)
		return -EBUSY;

	/* first get the engine out of reset and remove clock gating */
	writel(0, pre->regs + IPU_PRE_CTRL);

	/* init defaults that should be applied to all streams */
	val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN |
	      IPU_PRE_CTRL_HANDSHAKE_EN |
	      IPU_PRE_CTRL_TPR_REST_SEL |
157
	      IPU_PRE_CTRL_SDW_UPDATE;
158 159 160 161 162 163 164 165
	writel(val, pre->regs + IPU_PRE_CTRL);

	pre->in_use = true;
	return 0;
}

void ipu_pre_put(struct ipu_pre *pre)
{
166
	writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL);
167 168 169 170 171 172

	pre->in_use = false;
}

void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
		       unsigned int height, unsigned int stride, u32 format,
173
		       uint64_t modifier, unsigned int bufaddr)
174 175 176 177 178
{
	const struct drm_format_info *info = drm_format_info(format);
	u32 active_bpp = info->cpp[0] >> 1;
	u32 val;

179
	/* calculate safe window for ctrl register updates */
180 181 182 183
	if (modifier == DRM_FORMAT_MOD_LINEAR)
		pre->safe_window_end = height - 2;
	else
		pre->safe_window_end = DIV_ROUND_UP(height, 4) - 1;
184

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
	writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);

	val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |
	      IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |
	      IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) |
	      IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS |
	      IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN;
	writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL);

	val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) |
	      IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height);
	writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE);

	val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride);
	writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH);

	val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) |
	      IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) |
	      IPU_PRE_STORE_ENG_CTRL_STORE_EN;
	writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL);

	val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) |
	      IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height);
	writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE);

	val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride);
	writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH);

	writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR);

216 217 218 219 220 221 222 223 224 225 226 227
	val = readl(pre->regs + IPU_PRE_TPR_CTRL);
	val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK;
	if (modifier != DRM_FORMAT_MOD_LINEAR) {
		/* only support single buffer formats for now */
		val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF;
		if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED)
			val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED;
		if (info->cpp[0] == 2)
			val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT;
	}
	writel(val, pre->regs + IPU_PRE_TPR_CTRL);

228 229 230
	val = readl(pre->regs + IPU_PRE_CTRL);
	val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE |
	       IPU_PRE_CTRL_SDW_UPDATE;
231 232 233 234
	if (modifier == DRM_FORMAT_MOD_LINEAR)
		val &= ~IPU_PRE_CTRL_BLOCK_EN;
	else
		val |= IPU_PRE_CTRL_BLOCK_EN;
235 236 237 238 239
	writel(val, pre->regs + IPU_PRE_CTRL);
}

void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr)
{
240 241 242 243
	unsigned long timeout = jiffies + msecs_to_jiffies(5);
	unsigned short current_yblock;
	u32 val;

244
	writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
245 246 247 248 249 250 251 252 253 254 255 256 257

	do {
		if (time_after(jiffies, timeout)) {
			dev_warn(pre->dev, "timeout waiting for PRE safe window\n");
			return;
		}

		val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS);
		current_yblock =
			(val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) &
			IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK;
	} while (current_yblock == 0 || current_yblock >= pre->safe_window_end);

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET);
}

u32 ipu_pre_get_baddr(struct ipu_pre *pre)
{
	return (u32)pre->buffer_paddr;
}

static int ipu_pre_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct resource *res;
	struct ipu_pre *pre;

	pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL);
	if (!pre)
		return -ENOMEM;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	pre->regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(pre->regs))
		return PTR_ERR(pre->regs);

	pre->clk_axi = devm_clk_get(dev, "axi");
	if (IS_ERR(pre->clk_axi))
		return PTR_ERR(pre->clk_axi);

	pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0);
	if (!pre->iram)
		return -EPROBE_DEFER;

	/*
	 * Allocate IRAM buffer with maximum size. This could be made dynamic,
	 * but as there is no other user of this IRAM region and we can fit all
	 * max sized buffers into it, there is no need yet.
	 */
	pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH *
					      IPU_PRE_NUM_SCANLINES * 4,
					      &pre->buffer_paddr);
	if (!pre->buffer_virt)
		return -ENOMEM;

300 301
	clk_prepare_enable(pre->clk_axi);

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
	pre->dev = dev;
	platform_set_drvdata(pdev, pre);
	mutex_lock(&ipu_pre_list_mutex);
	list_add(&pre->list, &ipu_pre_list);
	available_pres++;
	mutex_unlock(&ipu_pre_list_mutex);

	return 0;
}

static int ipu_pre_remove(struct platform_device *pdev)
{
	struct ipu_pre *pre = platform_get_drvdata(pdev);

	mutex_lock(&ipu_pre_list_mutex);
	list_del(&pre->list);
	available_pres--;
	mutex_unlock(&ipu_pre_list_mutex);

321 322
	clk_disable_unprepare(pre->clk_axi);

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
	if (pre->buffer_virt)
		gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt,
			      IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4);
	return 0;
}

static const struct of_device_id ipu_pre_dt_ids[] = {
	{ .compatible = "fsl,imx6qp-pre", },
	{ /* sentinel */ },
};

struct platform_driver ipu_pre_drv = {
	.probe		= ipu_pre_probe,
	.remove		= ipu_pre_remove,
	.driver		= {
		.name	= "imx-ipu-pre",
		.of_match_table = ipu_pre_dt_ids,
	},
};