brintos

brintos / linux-shallow public Read only

0
0
Text · 10.7 KiB · 41bd5db Raw
383 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2017 Lucas Stach, Pengutronix4 */5 6#include <drm/drm_fourcc.h>7#include <linux/clk.h>8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/genalloc.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <video/imx-ipu-v3.h>15 16#include "ipu-prv.h"17 18#define IPU_PRE_MAX_WIDTH	204819#define IPU_PRE_NUM_SCANLINES	820 21#define IPU_PRE_CTRL					0x00022#define IPU_PRE_CTRL_SET				0x00423#define  IPU_PRE_CTRL_ENABLE				(1 << 0)24#define  IPU_PRE_CTRL_BLOCK_EN				(1 << 1)25#define  IPU_PRE_CTRL_BLOCK_16				(1 << 2)26#define  IPU_PRE_CTRL_SDW_UPDATE			(1 << 4)27#define  IPU_PRE_CTRL_VFLIP				(1 << 5)28#define  IPU_PRE_CTRL_SO				(1 << 6)29#define  IPU_PRE_CTRL_INTERLACED_FIELD			(1 << 7)30#define  IPU_PRE_CTRL_HANDSHAKE_EN			(1 << 8)31#define  IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v)		((v & 0x3) << 9)32#define  IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN		(1 << 11)33#define  IPU_PRE_CTRL_EN_REPEAT				(1 << 28)34#define  IPU_PRE_CTRL_TPR_REST_SEL			(1 << 29)35#define  IPU_PRE_CTRL_CLKGATE				(1 << 30)36#define  IPU_PRE_CTRL_SFTRST				(1 << 31)37 38#define IPU_PRE_CUR_BUF					0x03039 40#define IPU_PRE_NEXT_BUF				0x04041 42#define IPU_PRE_TPR_CTRL				0x07043#define  IPU_PRE_TPR_CTRL_TILE_FORMAT(v)		((v & 0xff) << 0)44#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK		0xff45#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT		(1 << 0)46#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF		(1 << 4)47#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF	(1 << 5)48#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED	(1 << 6)49 50#define IPU_PRE_PREFETCH_ENG_CTRL			0x08051#define  IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN		(1 << 0)52#define  IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v)		((v & 0x7) << 1)53#define  IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v)	((v & 0x3) << 4)54#define  IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v)	((v & 0x7) << 8)55#define  IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS		(1 << 11)56#define  IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE		(1 << 12)57#define  IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP		(1 << 14)58#define  IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN	(1 << 15)59 60#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE			0x0a061#define  IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v)	((v & 0xffff) << 0)62#define  IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v)	((v & 0xffff) << 16)63 64#define IPU_PRE_PREFETCH_ENG_PITCH			0x0d065#define  IPU_PRE_PREFETCH_ENG_PITCH_Y(v)		((v & 0xffff) << 0)66#define  IPU_PRE_PREFETCH_ENG_PITCH_UV(v)		((v & 0xffff) << 16)67 68#define IPU_PRE_STORE_ENG_CTRL				0x11069#define  IPU_PRE_STORE_ENG_CTRL_STORE_EN		(1 << 0)70#define  IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v)		((v & 0x7) << 1)71#define  IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v)	((v & 0x3) << 4)72 73#define IPU_PRE_STORE_ENG_STATUS			0x12074#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK	0xffff75#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT	076#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK	0x3fff77#define  IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT	1678#define  IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL	(1 << 30)79#define  IPU_PRE_STORE_ENG_STATUS_STORE_FIELD		(1 << 31)80 81#define IPU_PRE_STORE_ENG_SIZE				0x13082#define  IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v)		((v & 0xffff) << 0)83#define  IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v)		((v & 0xffff) << 16)84 85#define IPU_PRE_STORE_ENG_PITCH				0x14086#define  IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v)		((v & 0xffff) << 0)87 88#define IPU_PRE_STORE_ENG_ADDR				0x15089 90struct ipu_pre {91	struct list_head	list;92	struct device		*dev;93 94	void __iomem		*regs;95	struct clk		*clk_axi;96	struct gen_pool		*iram;97 98	dma_addr_t		buffer_paddr;99	void			*buffer_virt;100 101	struct {102		bool		in_use;103		uint64_t	modifier;104		unsigned int	height;105		unsigned int	safe_window_end;106		unsigned int	bufaddr;107		u32		ctrl;108		u8		cpp;109	} cur;110};111 112static DEFINE_MUTEX(ipu_pre_list_mutex);113static LIST_HEAD(ipu_pre_list);114static int available_pres;115 116int ipu_pre_get_available_count(void)117{118	return available_pres;119}120 121struct ipu_pre *122ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index)123{124	struct device_node *pre_node __free(device_node) =125		of_parse_phandle(dev->of_node, name, index);126	struct ipu_pre *pre;127 128	mutex_lock(&ipu_pre_list_mutex);129	list_for_each_entry(pre, &ipu_pre_list, list) {130		if (pre_node == pre->dev->of_node) {131			mutex_unlock(&ipu_pre_list_mutex);132			device_link_add(dev, pre->dev,133					DL_FLAG_AUTOREMOVE_CONSUMER);134			return pre;135		}136	}137	mutex_unlock(&ipu_pre_list_mutex);138 139	return NULL;140}141 142int ipu_pre_get(struct ipu_pre *pre)143{144	u32 val;145 146	if (pre->cur.in_use)147		return -EBUSY;148 149	/* first get the engine out of reset and remove clock gating */150	writel(0, pre->regs + IPU_PRE_CTRL);151 152	/* init defaults that should be applied to all streams */153	val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN |154	      IPU_PRE_CTRL_HANDSHAKE_EN |155	      IPU_PRE_CTRL_TPR_REST_SEL |156	      IPU_PRE_CTRL_SDW_UPDATE;157	writel(val, pre->regs + IPU_PRE_CTRL);158 159	pre->cur.in_use = true;160	return 0;161}162 163void ipu_pre_put(struct ipu_pre *pre)164{165	writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL);166 167	pre->cur.in_use = false;168}169 170static inline void171ipu_pre_update_safe_window(struct ipu_pre *pre)172{173	if (pre->cur.modifier == DRM_FORMAT_MOD_LINEAR)174		pre->cur.safe_window_end = pre->cur.height - 2;175	else176		pre->cur.safe_window_end = DIV_ROUND_UP(pre->cur.height, 4) - 1;177}178 179static void180ipu_pre_configure_modifier(struct ipu_pre *pre, uint64_t modifier)181{182	u32 val;183 184	val = readl(pre->regs + IPU_PRE_TPR_CTRL);185	val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK;186	if (modifier != DRM_FORMAT_MOD_LINEAR) {187		/* only support single buffer formats for now */188		val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF;189		if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED)190			val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED;191		if (pre->cur.cpp == 2)192			val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT;193	}194	writel(val, pre->regs + IPU_PRE_TPR_CTRL);195 196	if (modifier == DRM_FORMAT_MOD_LINEAR)197		pre->cur.ctrl &= ~IPU_PRE_CTRL_BLOCK_EN;198	else199		pre->cur.ctrl |= IPU_PRE_CTRL_BLOCK_EN;200 201	pre->cur.modifier = modifier;202}203 204void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,205		       unsigned int height, unsigned int stride, u32 format,206		       uint64_t modifier, unsigned int bufaddr)207{208	const struct drm_format_info *info = drm_format_info(format);209	u32 active_bpp = info->cpp[0] >> 1;210	u32 val;211 212	pre->cur.bufaddr = bufaddr;213	pre->cur.height = height;214	pre->cur.cpp = info->cpp[0];215	pre->cur.ctrl = readl(pre->regs + IPU_PRE_CTRL);216 217	/* calculate safe window for ctrl register updates */218	ipu_pre_update_safe_window(pre);219 220	writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);221	writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);222 223	val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |224	      IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |225	      IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) |226	      IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS |227	      IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN;228	writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL);229 230	val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) |231	      IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height);232	writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE);233 234	val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride);235	writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH);236 237	val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) |238	      IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) |239	      IPU_PRE_STORE_ENG_CTRL_STORE_EN;240	writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL);241 242	val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) |243	      IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height);244	writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE);245 246	val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride);247	writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH);248 249	writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR);250 251	ipu_pre_configure_modifier(pre, modifier);252 253	pre->cur.ctrl |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE;254	writel(pre->cur.ctrl | IPU_PRE_CTRL_SDW_UPDATE,255	       pre->regs + IPU_PRE_CTRL);256}257 258void ipu_pre_update(struct ipu_pre *pre, uint64_t modifier, unsigned int bufaddr)259{260	if (bufaddr == pre->cur.bufaddr &&261	    modifier == pre->cur.modifier)262		return;263 264	writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);265	pre->cur.bufaddr = bufaddr;266 267	if (modifier != pre->cur.modifier)268		ipu_pre_configure_modifier(pre, modifier);269 270	for (int i = 0;; i++) {271		unsigned short current_yblock;272		u32 val;273 274		if (i > 500) {275			dev_warn(pre->dev, "timeout waiting for PRE safe window\n");276			return;277		}278 279		val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS);280		current_yblock =281			(val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) &282			IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK;283 284		if (current_yblock != 0 &&285		    current_yblock < pre->cur.safe_window_end)286			break;287 288		udelay(10);289		cpu_relax();290	}291 292	writel(pre->cur.ctrl | IPU_PRE_CTRL_SDW_UPDATE,293	       pre->regs + IPU_PRE_CTRL);294 295	/* calculate safe window for the next update with the new modifier */296	ipu_pre_update_safe_window(pre);297}298 299bool ipu_pre_update_pending(struct ipu_pre *pre)300{301	return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) &302		  IPU_PRE_CTRL_SDW_UPDATE);303}304 305u32 ipu_pre_get_baddr(struct ipu_pre *pre)306{307	return (u32)pre->buffer_paddr;308}309 310static int ipu_pre_probe(struct platform_device *pdev)311{312	struct device *dev = &pdev->dev;313	struct ipu_pre *pre;314 315	pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL);316	if (!pre)317		return -ENOMEM;318 319	pre->regs = devm_platform_ioremap_resource(pdev, 0);320	if (IS_ERR(pre->regs))321		return PTR_ERR(pre->regs);322 323	pre->clk_axi = devm_clk_get(dev, "axi");324	if (IS_ERR(pre->clk_axi))325		return PTR_ERR(pre->clk_axi);326 327	pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0);328	if (!pre->iram)329		return -EPROBE_DEFER;330 331	/*332	 * Allocate IRAM buffer with maximum size. This could be made dynamic,333	 * but as there is no other user of this IRAM region and we can fit all334	 * max sized buffers into it, there is no need yet.335	 */336	pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH *337					      IPU_PRE_NUM_SCANLINES * 4,338					      &pre->buffer_paddr);339	if (!pre->buffer_virt)340		return -ENOMEM;341 342	clk_prepare_enable(pre->clk_axi);343 344	pre->dev = dev;345	platform_set_drvdata(pdev, pre);346	mutex_lock(&ipu_pre_list_mutex);347	list_add(&pre->list, &ipu_pre_list);348	available_pres++;349	mutex_unlock(&ipu_pre_list_mutex);350 351	return 0;352}353 354static void ipu_pre_remove(struct platform_device *pdev)355{356	struct ipu_pre *pre = platform_get_drvdata(pdev);357 358	mutex_lock(&ipu_pre_list_mutex);359	list_del(&pre->list);360	available_pres--;361	mutex_unlock(&ipu_pre_list_mutex);362 363	clk_disable_unprepare(pre->clk_axi);364 365	if (pre->buffer_virt)366		gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt,367			      IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4);368}369 370static const struct of_device_id ipu_pre_dt_ids[] = {371	{ .compatible = "fsl,imx6qp-pre", },372	{ /* sentinel */ },373};374 375struct platform_driver ipu_pre_drv = {376	.probe		= ipu_pre_probe,377	.remove_new	= ipu_pre_remove,378	.driver		= {379		.name	= "imx-ipu-pre",380		.of_match_table = ipu_pre_dt_ids,381	},382};383