18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2016-2017 Lucas Stach, Pengutronix
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
168c2ecf20Sopenharmony_ci#include <linux/regmap.h>
178c2ecf20Sopenharmony_ci#include <video/imx-ipu-v3.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "ipu-prv.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define IPU_PRG_CTL				0x00
228c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_BYPASS(i)			(1 << (0 + i))
238c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_SOFT_ARID_MASK		0x3
248c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_SOFT_ARID_SHIFT(i)		(8 + i * 2)
258c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_SOFT_ARID(i, v)		((v & 0x3) << (8 + 2 * i))
268c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_SO(i)			(1 << (16 + i))
278c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_VFLIP(i)			(1 << (19 + i))
288c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_BLOCK_MODE(i)		(1 << (22 + i))
298c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_CNT_LOAD_EN(i)		(1 << (25 + i))
308c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_SOFTRST			(1 << 30)
318c2ecf20Sopenharmony_ci#define  IPU_PRG_CTL_SHADOW_EN			(1 << 31)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define IPU_PRG_STATUS				0x04
348c2ecf20Sopenharmony_ci#define  IPU_PRG_STATUS_BUFFER0_READY(i)	(1 << (0 + i * 2))
358c2ecf20Sopenharmony_ci#define  IPU_PRG_STATUS_BUFFER1_READY(i)	(1 << (1 + i * 2))
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define IPU_PRG_QOS				0x08
388c2ecf20Sopenharmony_ci#define  IPU_PRG_QOS_ARID_MASK			0xf
398c2ecf20Sopenharmony_ci#define  IPU_PRG_QOS_ARID_SHIFT(i)		(0 + i * 4)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define IPU_PRG_REG_UPDATE			0x0c
428c2ecf20Sopenharmony_ci#define  IPU_PRG_REG_UPDATE_REG_UPDATE		(1 << 0)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define IPU_PRG_STRIDE(i)			(0x10 + i * 0x4)
458c2ecf20Sopenharmony_ci#define  IPU_PRG_STRIDE_STRIDE_MASK		0x3fff
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define IPU_PRG_CROP_LINE			0x1c
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define IPU_PRG_THD				0x20
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define IPU_PRG_BADDR(i)			(0x24 + i * 0x4)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define IPU_PRG_OFFSET(i)			(0x30 + i * 0x4)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define IPU_PRG_ILO(i)				(0x3c + i * 0x4)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define IPU_PRG_HEIGHT(i)			(0x48 + i * 0x4)
588c2ecf20Sopenharmony_ci#define  IPU_PRG_HEIGHT_PRE_HEIGHT_MASK		0xfff
598c2ecf20Sopenharmony_ci#define  IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT	0
608c2ecf20Sopenharmony_ci#define  IPU_PRG_HEIGHT_IPU_HEIGHT_MASK		0xfff
618c2ecf20Sopenharmony_ci#define  IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT	16
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct ipu_prg_channel {
648c2ecf20Sopenharmony_ci	bool			enabled;
658c2ecf20Sopenharmony_ci	int			used_pre;
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistruct ipu_prg {
698c2ecf20Sopenharmony_ci	struct list_head	list;
708c2ecf20Sopenharmony_ci	struct device		*dev;
718c2ecf20Sopenharmony_ci	int			id;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	void __iomem		*regs;
748c2ecf20Sopenharmony_ci	struct clk		*clk_ipg, *clk_axi;
758c2ecf20Sopenharmony_ci	struct regmap		*iomuxc_gpr;
768c2ecf20Sopenharmony_ci	struct ipu_pre		*pres[3];
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	struct ipu_prg_channel	chan[3];
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipu_prg_list_mutex);
828c2ecf20Sopenharmony_cistatic LIST_HEAD(ipu_prg_list);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct ipu_prg *
858c2ecf20Sopenharmony_ciipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct device_node *prg_node = of_parse_phandle(dev->of_node,
888c2ecf20Sopenharmony_ci							name, 0);
898c2ecf20Sopenharmony_ci	struct ipu_prg *prg;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	mutex_lock(&ipu_prg_list_mutex);
928c2ecf20Sopenharmony_ci	list_for_each_entry(prg, &ipu_prg_list, list) {
938c2ecf20Sopenharmony_ci		if (prg_node == prg->dev->of_node) {
948c2ecf20Sopenharmony_ci			mutex_unlock(&ipu_prg_list_mutex);
958c2ecf20Sopenharmony_ci			device_link_add(dev, prg->dev,
968c2ecf20Sopenharmony_ci					DL_FLAG_AUTOREMOVE_CONSUMER);
978c2ecf20Sopenharmony_ci			prg->id = ipu_id;
988c2ecf20Sopenharmony_ci			of_node_put(prg_node);
998c2ecf20Sopenharmony_ci			return prg;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci	mutex_unlock(&ipu_prg_list_mutex);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	of_node_put(prg_node);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return NULL;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciint ipu_prg_max_active_channels(void)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	return ipu_pre_get_available_count();
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_max_active_channels);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cibool ipu_prg_present(struct ipu_soc *ipu)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	if (ipu->prg_priv)
1188c2ecf20Sopenharmony_ci		return true;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return false;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_present);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cibool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format,
1258c2ecf20Sopenharmony_ci			      uint64_t modifier)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	const struct drm_format_info *info = drm_format_info(format);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (info->num_planes != 1)
1308c2ecf20Sopenharmony_ci		return false;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	switch (modifier) {
1338c2ecf20Sopenharmony_ci	case DRM_FORMAT_MOD_LINEAR:
1348c2ecf20Sopenharmony_ci	case DRM_FORMAT_MOD_VIVANTE_TILED:
1358c2ecf20Sopenharmony_ci	case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
1368c2ecf20Sopenharmony_ci		return true;
1378c2ecf20Sopenharmony_ci	default:
1388c2ecf20Sopenharmony_ci		return false;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_format_supported);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciint ipu_prg_enable(struct ipu_soc *ipu)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct ipu_prg *prg = ipu->prg_priv;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!prg)
1488c2ecf20Sopenharmony_ci		return 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return pm_runtime_get_sync(prg->dev);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_enable);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_civoid ipu_prg_disable(struct ipu_soc *ipu)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct ipu_prg *prg = ipu->prg_priv;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (!prg)
1598c2ecf20Sopenharmony_ci		return;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	pm_runtime_put(prg->dev);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_disable);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * The channel configuartion functions below are not thread safe, as they
1678c2ecf20Sopenharmony_ci * must be only called from the atomic commit path in the DRM driver, which
1688c2ecf20Sopenharmony_ci * is properly serialized.
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_cistatic int ipu_prg_ipu_to_prg_chan(int ipu_chan)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	/*
1738c2ecf20Sopenharmony_ci	 * This isn't clearly documented in the RM, but IPU to PRG channel
1748c2ecf20Sopenharmony_ci	 * assignment is fixed, as only with this mapping the control signals
1758c2ecf20Sopenharmony_ci	 * match up.
1768c2ecf20Sopenharmony_ci	 */
1778c2ecf20Sopenharmony_ci	switch (ipu_chan) {
1788c2ecf20Sopenharmony_ci	case IPUV3_CHANNEL_MEM_BG_SYNC:
1798c2ecf20Sopenharmony_ci		return 0;
1808c2ecf20Sopenharmony_ci	case IPUV3_CHANNEL_MEM_FG_SYNC:
1818c2ecf20Sopenharmony_ci		return 1;
1828c2ecf20Sopenharmony_ci	case IPUV3_CHANNEL_MEM_DC_SYNC:
1838c2ecf20Sopenharmony_ci		return 2;
1848c2ecf20Sopenharmony_ci	default:
1858c2ecf20Sopenharmony_ci		return -EINVAL;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	int i, ret;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* channel 0 is special as it is hardwired to one of the PREs */
1948c2ecf20Sopenharmony_ci	if (prg_chan == 0) {
1958c2ecf20Sopenharmony_ci		ret = ipu_pre_get(prg->pres[0]);
1968c2ecf20Sopenharmony_ci		if (ret)
1978c2ecf20Sopenharmony_ci			goto fail;
1988c2ecf20Sopenharmony_ci		prg->chan[prg_chan].used_pre = 0;
1998c2ecf20Sopenharmony_ci		return 0;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	for (i = 1; i < 3; i++) {
2038c2ecf20Sopenharmony_ci		ret = ipu_pre_get(prg->pres[i]);
2048c2ecf20Sopenharmony_ci		if (!ret) {
2058c2ecf20Sopenharmony_ci			u32 val, mux;
2068c2ecf20Sopenharmony_ci			int shift;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci			prg->chan[prg_chan].used_pre = i;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci			/* configure the PRE to PRG channel mux */
2118c2ecf20Sopenharmony_ci			shift = (i == 1) ? 12 : 14;
2128c2ecf20Sopenharmony_ci			mux = (prg->id << 1) | (prg_chan - 1);
2138c2ecf20Sopenharmony_ci			regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
2148c2ecf20Sopenharmony_ci					   0x3 << shift, mux << shift);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci			/* check other mux, must not point to same channel */
2178c2ecf20Sopenharmony_ci			shift = (i == 1) ? 14 : 12;
2188c2ecf20Sopenharmony_ci			regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val);
2198c2ecf20Sopenharmony_ci			if (((val >> shift) & 0x3) == mux) {
2208c2ecf20Sopenharmony_ci				regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
2218c2ecf20Sopenharmony_ci						   0x3 << shift,
2228c2ecf20Sopenharmony_ci						   (mux ^ 0x1) << shift);
2238c2ecf20Sopenharmony_ci			}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci			return 0;
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cifail:
2308c2ecf20Sopenharmony_ci	dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan);
2318c2ecf20Sopenharmony_ci	return ret;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct ipu_prg_channel *chan = &prg->chan[prg_chan];
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	ipu_pre_put(prg->pres[chan->used_pre]);
2398c2ecf20Sopenharmony_ci	chan->used_pre = -1;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_civoid ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
2458c2ecf20Sopenharmony_ci	struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
2468c2ecf20Sopenharmony_ci	struct ipu_prg_channel *chan;
2478c2ecf20Sopenharmony_ci	u32 val;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (prg_chan < 0)
2508c2ecf20Sopenharmony_ci		return;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	chan = &prg->chan[prg_chan];
2538c2ecf20Sopenharmony_ci	if (!chan->enabled)
2548c2ecf20Sopenharmony_ci		return;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	pm_runtime_get_sync(prg->dev);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	val = readl(prg->regs + IPU_PRG_CTL);
2598c2ecf20Sopenharmony_ci	val |= IPU_PRG_CTL_BYPASS(prg_chan);
2608c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_CTL);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	val = IPU_PRG_REG_UPDATE_REG_UPDATE;
2638c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_REG_UPDATE);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	pm_runtime_put(prg->dev);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ipu_prg_put_pre(prg, prg_chan);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	chan->enabled = false;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_channel_disable);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ciint ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
2748c2ecf20Sopenharmony_ci			      unsigned int axi_id, unsigned int width,
2758c2ecf20Sopenharmony_ci			      unsigned int height, unsigned int stride,
2768c2ecf20Sopenharmony_ci			      u32 format, uint64_t modifier, unsigned long *eba)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
2798c2ecf20Sopenharmony_ci	struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
2808c2ecf20Sopenharmony_ci	struct ipu_prg_channel *chan;
2818c2ecf20Sopenharmony_ci	u32 val;
2828c2ecf20Sopenharmony_ci	int ret;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (prg_chan < 0)
2858c2ecf20Sopenharmony_ci		return prg_chan;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	chan = &prg->chan[prg_chan];
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (chan->enabled) {
2908c2ecf20Sopenharmony_ci		ipu_pre_update(prg->pres[chan->used_pre], *eba);
2918c2ecf20Sopenharmony_ci		return 0;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	ret = ipu_prg_get_pre(prg, prg_chan);
2958c2ecf20Sopenharmony_ci	if (ret)
2968c2ecf20Sopenharmony_ci		return ret;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	ipu_pre_configure(prg->pres[chan->used_pre],
2998c2ecf20Sopenharmony_ci			  width, height, stride, format, modifier, *eba);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	pm_runtime_get_sync(prg->dev);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK;
3058c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan));
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) <<
3088c2ecf20Sopenharmony_ci	       IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) |
3098c2ecf20Sopenharmony_ci	      ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) <<
3108c2ecf20Sopenharmony_ci	       IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT);
3118c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan));
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	val = ipu_pre_get_baddr(prg->pres[chan->used_pre]);
3148c2ecf20Sopenharmony_ci	*eba = val;
3158c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_BADDR(prg_chan));
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	val = readl(prg->regs + IPU_PRG_CTL);
3188c2ecf20Sopenharmony_ci	/* config AXI ID */
3198c2ecf20Sopenharmony_ci	val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK <<
3208c2ecf20Sopenharmony_ci		 IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan));
3218c2ecf20Sopenharmony_ci	val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id);
3228c2ecf20Sopenharmony_ci	/* enable channel */
3238c2ecf20Sopenharmony_ci	val &= ~IPU_PRG_CTL_BYPASS(prg_chan);
3248c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_CTL);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	val = IPU_PRG_REG_UPDATE_REG_UPDATE;
3278c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_REG_UPDATE);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* wait for both double buffers to be filled */
3308c2ecf20Sopenharmony_ci	readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val,
3318c2ecf20Sopenharmony_ci			   (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) &&
3328c2ecf20Sopenharmony_ci			   (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)),
3338c2ecf20Sopenharmony_ci			   5, 1000);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	pm_runtime_put(prg->dev);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	chan->enabled = true;
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_channel_configure);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cibool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
3458c2ecf20Sopenharmony_ci	struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
3468c2ecf20Sopenharmony_ci	struct ipu_prg_channel *chan;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (prg_chan < 0)
3498c2ecf20Sopenharmony_ci		return false;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	chan = &prg->chan[prg_chan];
3528c2ecf20Sopenharmony_ci	WARN_ON(!chan->enabled);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return ipu_pre_update_pending(prg->pres[chan->used_pre]);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int ipu_prg_probe(struct platform_device *pdev)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3618c2ecf20Sopenharmony_ci	struct resource *res;
3628c2ecf20Sopenharmony_ci	struct ipu_prg *prg;
3638c2ecf20Sopenharmony_ci	u32 val;
3648c2ecf20Sopenharmony_ci	int i, ret;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL);
3678c2ecf20Sopenharmony_ci	if (!prg)
3688c2ecf20Sopenharmony_ci		return -ENOMEM;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3718c2ecf20Sopenharmony_ci	prg->regs = devm_ioremap_resource(&pdev->dev, res);
3728c2ecf20Sopenharmony_ci	if (IS_ERR(prg->regs))
3738c2ecf20Sopenharmony_ci		return PTR_ERR(prg->regs);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	prg->clk_ipg = devm_clk_get(dev, "ipg");
3778c2ecf20Sopenharmony_ci	if (IS_ERR(prg->clk_ipg))
3788c2ecf20Sopenharmony_ci		return PTR_ERR(prg->clk_ipg);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	prg->clk_axi = devm_clk_get(dev, "axi");
3818c2ecf20Sopenharmony_ci	if (IS_ERR(prg->clk_axi))
3828c2ecf20Sopenharmony_ci		return PTR_ERR(prg->clk_axi);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	prg->iomuxc_gpr =
3858c2ecf20Sopenharmony_ci		syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
3868c2ecf20Sopenharmony_ci	if (IS_ERR(prg->iomuxc_gpr))
3878c2ecf20Sopenharmony_ci		return PTR_ERR(prg->iomuxc_gpr);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
3908c2ecf20Sopenharmony_ci		prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i);
3918c2ecf20Sopenharmony_ci		if (!prg->pres[i])
3928c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(prg->clk_ipg);
3968c2ecf20Sopenharmony_ci	if (ret)
3978c2ecf20Sopenharmony_ci		return ret;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(prg->clk_axi);
4008c2ecf20Sopenharmony_ci	if (ret) {
4018c2ecf20Sopenharmony_ci		clk_disable_unprepare(prg->clk_ipg);
4028c2ecf20Sopenharmony_ci		return ret;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* init to free running mode */
4068c2ecf20Sopenharmony_ci	val = readl(prg->regs + IPU_PRG_CTL);
4078c2ecf20Sopenharmony_ci	val |= IPU_PRG_CTL_SHADOW_EN;
4088c2ecf20Sopenharmony_ci	writel(val, prg->regs + IPU_PRG_CTL);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* disable address threshold */
4118c2ecf20Sopenharmony_ci	writel(0xffffffff, prg->regs + IPU_PRG_THD);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	pm_runtime_set_active(dev);
4148c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	prg->dev = dev;
4178c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, prg);
4188c2ecf20Sopenharmony_ci	mutex_lock(&ipu_prg_list_mutex);
4198c2ecf20Sopenharmony_ci	list_add(&prg->list, &ipu_prg_list);
4208c2ecf20Sopenharmony_ci	mutex_unlock(&ipu_prg_list_mutex);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int ipu_prg_remove(struct platform_device *pdev)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct ipu_prg *prg = platform_get_drvdata(pdev);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	mutex_lock(&ipu_prg_list_mutex);
4308c2ecf20Sopenharmony_ci	list_del(&prg->list);
4318c2ecf20Sopenharmony_ci	mutex_unlock(&ipu_prg_list_mutex);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	return 0;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4378c2ecf20Sopenharmony_cistatic int prg_suspend(struct device *dev)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct ipu_prg *prg = dev_get_drvdata(dev);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	clk_disable_unprepare(prg->clk_axi);
4428c2ecf20Sopenharmony_ci	clk_disable_unprepare(prg->clk_ipg);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	return 0;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int prg_resume(struct device *dev)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct ipu_prg *prg = dev_get_drvdata(dev);
4508c2ecf20Sopenharmony_ci	int ret;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(prg->clk_ipg);
4538c2ecf20Sopenharmony_ci	if (ret)
4548c2ecf20Sopenharmony_ci		return ret;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(prg->clk_axi);
4578c2ecf20Sopenharmony_ci	if (ret) {
4588c2ecf20Sopenharmony_ci		clk_disable_unprepare(prg->clk_ipg);
4598c2ecf20Sopenharmony_ci		return ret;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci#endif
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic const struct dev_pm_ops prg_pm_ops = {
4678c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL)
4688c2ecf20Sopenharmony_ci};
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic const struct of_device_id ipu_prg_dt_ids[] = {
4718c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6qp-prg", },
4728c2ecf20Sopenharmony_ci	{ /* sentinel */ },
4738c2ecf20Sopenharmony_ci};
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistruct platform_driver ipu_prg_drv = {
4768c2ecf20Sopenharmony_ci	.probe		= ipu_prg_probe,
4778c2ecf20Sopenharmony_ci	.remove		= ipu_prg_remove,
4788c2ecf20Sopenharmony_ci	.driver		= {
4798c2ecf20Sopenharmony_ci		.name	= "imx-ipu-prg",
4808c2ecf20Sopenharmony_ci		.pm	= &prg_pm_ops,
4818c2ecf20Sopenharmony_ci		.of_match_table = ipu_prg_dt_ids,
4828c2ecf20Sopenharmony_ci	},
4838c2ecf20Sopenharmony_ci};
484