brintos

brintos / linux-shallow public Read only

0
0
Text · 11.0 KiB · afb2d72 Raw
479 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2016-2017 Lucas Stach, Pengutronix4 */5 6#include <drm/drm_fourcc.h>7#include <linux/clk.h>8#include <linux/err.h>9#include <linux/iopoll.h>10#include <linux/mfd/syscon.h>11#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/pm_runtime.h>16#include <linux/regmap.h>17#include <video/imx-ipu-v3.h>18 19#include "ipu-prv.h"20 21#define IPU_PRG_CTL				0x0022#define  IPU_PRG_CTL_BYPASS(i)			(1 << (0 + i))23#define  IPU_PRG_CTL_SOFT_ARID_MASK		0x324#define  IPU_PRG_CTL_SOFT_ARID_SHIFT(i)		(8 + i * 2)25#define  IPU_PRG_CTL_SOFT_ARID(i, v)		((v & 0x3) << (8 + 2 * i))26#define  IPU_PRG_CTL_SO(i)			(1 << (16 + i))27#define  IPU_PRG_CTL_VFLIP(i)			(1 << (19 + i))28#define  IPU_PRG_CTL_BLOCK_MODE(i)		(1 << (22 + i))29#define  IPU_PRG_CTL_CNT_LOAD_EN(i)		(1 << (25 + i))30#define  IPU_PRG_CTL_SOFTRST			(1 << 30)31#define  IPU_PRG_CTL_SHADOW_EN			(1 << 31)32 33#define IPU_PRG_STATUS				0x0434#define  IPU_PRG_STATUS_BUFFER0_READY(i)	(1 << (0 + i * 2))35#define  IPU_PRG_STATUS_BUFFER1_READY(i)	(1 << (1 + i * 2))36 37#define IPU_PRG_QOS				0x0838#define  IPU_PRG_QOS_ARID_MASK			0xf39#define  IPU_PRG_QOS_ARID_SHIFT(i)		(0 + i * 4)40 41#define IPU_PRG_REG_UPDATE			0x0c42#define  IPU_PRG_REG_UPDATE_REG_UPDATE		(1 << 0)43 44#define IPU_PRG_STRIDE(i)			(0x10 + i * 0x4)45#define  IPU_PRG_STRIDE_STRIDE_MASK		0x3fff46 47#define IPU_PRG_CROP_LINE			0x1c48 49#define IPU_PRG_THD				0x2050 51#define IPU_PRG_BADDR(i)			(0x24 + i * 0x4)52 53#define IPU_PRG_OFFSET(i)			(0x30 + i * 0x4)54 55#define IPU_PRG_ILO(i)				(0x3c + i * 0x4)56 57#define IPU_PRG_HEIGHT(i)			(0x48 + i * 0x4)58#define  IPU_PRG_HEIGHT_PRE_HEIGHT_MASK		0xfff59#define  IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT	060#define  IPU_PRG_HEIGHT_IPU_HEIGHT_MASK		0xfff61#define  IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT	1662 63struct ipu_prg_channel {64	bool			enabled;65	int			used_pre;66};67 68struct ipu_prg {69	struct list_head	list;70	struct device		*dev;71	int			id;72 73	void __iomem		*regs;74	struct clk		*clk_ipg, *clk_axi;75	struct regmap		*iomuxc_gpr;76	struct ipu_pre		*pres[3];77 78	struct ipu_prg_channel	chan[3];79};80 81static DEFINE_MUTEX(ipu_prg_list_mutex);82static LIST_HEAD(ipu_prg_list);83 84struct ipu_prg *85ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id)86{87	struct device_node *prg_node = of_parse_phandle(dev->of_node,88							name, 0);89	struct ipu_prg *prg;90 91	mutex_lock(&ipu_prg_list_mutex);92	list_for_each_entry(prg, &ipu_prg_list, list) {93		if (prg_node == prg->dev->of_node) {94			mutex_unlock(&ipu_prg_list_mutex);95			device_link_add(dev, prg->dev,96					DL_FLAG_AUTOREMOVE_CONSUMER);97			prg->id = ipu_id;98			of_node_put(prg_node);99			return prg;100		}101	}102	mutex_unlock(&ipu_prg_list_mutex);103 104	of_node_put(prg_node);105 106	return NULL;107}108 109int ipu_prg_max_active_channels(void)110{111	return ipu_pre_get_available_count();112}113EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels);114 115bool ipu_prg_present(struct ipu_soc *ipu)116{117	if (ipu->prg_priv)118		return true;119 120	return false;121}122EXPORT_SYMBOL_GPL(ipu_prg_present);123 124bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format,125			      uint64_t modifier)126{127	const struct drm_format_info *info = drm_format_info(format);128 129	if (info->num_planes != 1)130		return false;131 132	switch (modifier) {133	case DRM_FORMAT_MOD_LINEAR:134	case DRM_FORMAT_MOD_VIVANTE_TILED:135	case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:136		return true;137	default:138		return false;139	}140}141EXPORT_SYMBOL_GPL(ipu_prg_format_supported);142 143int ipu_prg_enable(struct ipu_soc *ipu)144{145	struct ipu_prg *prg = ipu->prg_priv;146 147	if (!prg)148		return 0;149 150	return pm_runtime_get_sync(prg->dev);151}152EXPORT_SYMBOL_GPL(ipu_prg_enable);153 154void ipu_prg_disable(struct ipu_soc *ipu)155{156	struct ipu_prg *prg = ipu->prg_priv;157 158	if (!prg)159		return;160 161	pm_runtime_put(prg->dev);162}163EXPORT_SYMBOL_GPL(ipu_prg_disable);164 165/*166 * The channel configuartion functions below are not thread safe, as they167 * must be only called from the atomic commit path in the DRM driver, which168 * is properly serialized.169 */170static int ipu_prg_ipu_to_prg_chan(int ipu_chan)171{172	/*173	 * This isn't clearly documented in the RM, but IPU to PRG channel174	 * assignment is fixed, as only with this mapping the control signals175	 * match up.176	 */177	switch (ipu_chan) {178	case IPUV3_CHANNEL_MEM_BG_SYNC:179		return 0;180	case IPUV3_CHANNEL_MEM_FG_SYNC:181		return 1;182	case IPUV3_CHANNEL_MEM_DC_SYNC:183		return 2;184	default:185		return -EINVAL;186	}187}188 189static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan)190{191	int i, ret;192 193	/* channel 0 is special as it is hardwired to one of the PREs */194	if (prg_chan == 0) {195		ret = ipu_pre_get(prg->pres[0]);196		if (ret)197			goto fail;198		prg->chan[prg_chan].used_pre = 0;199		return 0;200	}201 202	for (i = 1; i < 3; i++) {203		ret = ipu_pre_get(prg->pres[i]);204		if (!ret) {205			u32 val, mux;206			int shift;207 208			prg->chan[prg_chan].used_pre = i;209 210			/* configure the PRE to PRG channel mux */211			shift = (i == 1) ? 12 : 14;212			mux = (prg->id << 1) | (prg_chan - 1);213			regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,214					   0x3 << shift, mux << shift);215 216			/* check other mux, must not point to same channel */217			shift = (i == 1) ? 14 : 12;218			regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val);219			if (((val >> shift) & 0x3) == mux) {220				regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,221						   0x3 << shift,222						   (mux ^ 0x1) << shift);223			}224 225			return 0;226		}227	}228 229fail:230	dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan);231	return ret;232}233 234static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan)235{236	struct ipu_prg_channel *chan = &prg->chan[prg_chan];237 238	ipu_pre_put(prg->pres[chan->used_pre]);239	chan->used_pre = -1;240}241 242void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan)243{244	int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);245	struct ipu_prg *prg = ipu_chan->ipu->prg_priv;246	struct ipu_prg_channel *chan;247	u32 val;248 249	if (prg_chan < 0)250		return;251 252	chan = &prg->chan[prg_chan];253	if (!chan->enabled)254		return;255 256	pm_runtime_get_sync(prg->dev);257 258	val = readl(prg->regs + IPU_PRG_CTL);259	val |= IPU_PRG_CTL_BYPASS(prg_chan);260	writel(val, prg->regs + IPU_PRG_CTL);261 262	val = IPU_PRG_REG_UPDATE_REG_UPDATE;263	writel(val, prg->regs + IPU_PRG_REG_UPDATE);264 265	pm_runtime_put(prg->dev);266 267	ipu_prg_put_pre(prg, prg_chan);268 269	chan->enabled = false;270}271EXPORT_SYMBOL_GPL(ipu_prg_channel_disable);272 273int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,274			      unsigned int axi_id, unsigned int width,275			      unsigned int height, unsigned int stride,276			      u32 format, uint64_t modifier, unsigned long *eba)277{278	int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);279	struct ipu_prg *prg = ipu_chan->ipu->prg_priv;280	struct ipu_prg_channel *chan;281	u32 val;282	int ret;283 284	if (prg_chan < 0)285		return prg_chan;286 287	chan = &prg->chan[prg_chan];288 289	if (chan->enabled) {290		ipu_pre_update(prg->pres[chan->used_pre], modifier, *eba);291		return 0;292	}293 294	ret = ipu_prg_get_pre(prg, prg_chan);295	if (ret)296		return ret;297 298	ipu_pre_configure(prg->pres[chan->used_pre],299			  width, height, stride, format, modifier, *eba);300 301 302	pm_runtime_get_sync(prg->dev);303 304	val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK;305	writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan));306 307	val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) <<308	       IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) |309	      ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) <<310	       IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT);311	writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan));312 313	val = ipu_pre_get_baddr(prg->pres[chan->used_pre]);314	*eba = val;315	writel(val, prg->regs + IPU_PRG_BADDR(prg_chan));316 317	val = readl(prg->regs + IPU_PRG_CTL);318	/* config AXI ID */319	val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK <<320		 IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan));321	val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id);322	/* enable channel */323	val &= ~IPU_PRG_CTL_BYPASS(prg_chan);324	writel(val, prg->regs + IPU_PRG_CTL);325 326	val = IPU_PRG_REG_UPDATE_REG_UPDATE;327	writel(val, prg->regs + IPU_PRG_REG_UPDATE);328 329	/* wait for both double buffers to be filled */330	readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val,331			   (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) &&332			   (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)),333			   5, 1000);334 335	pm_runtime_put(prg->dev);336 337	chan->enabled = true;338	return 0;339}340EXPORT_SYMBOL_GPL(ipu_prg_channel_configure);341 342bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan)343{344	int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);345	struct ipu_prg *prg = ipu_chan->ipu->prg_priv;346	struct ipu_prg_channel *chan;347 348	if (prg_chan < 0)349		return false;350 351	chan = &prg->chan[prg_chan];352	WARN_ON(!chan->enabled);353 354	return ipu_pre_update_pending(prg->pres[chan->used_pre]);355}356EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending);357 358static int ipu_prg_probe(struct platform_device *pdev)359{360	struct device *dev = &pdev->dev;361	struct ipu_prg *prg;362	u32 val;363	int i, ret;364 365	prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL);366	if (!prg)367		return -ENOMEM;368 369	prg->regs = devm_platform_ioremap_resource(pdev, 0);370	if (IS_ERR(prg->regs))371		return PTR_ERR(prg->regs);372 373	prg->clk_ipg = devm_clk_get(dev, "ipg");374	if (IS_ERR(prg->clk_ipg))375		return PTR_ERR(prg->clk_ipg);376 377	prg->clk_axi = devm_clk_get(dev, "axi");378	if (IS_ERR(prg->clk_axi))379		return PTR_ERR(prg->clk_axi);380 381	prg->iomuxc_gpr =382		syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");383	if (IS_ERR(prg->iomuxc_gpr))384		return PTR_ERR(prg->iomuxc_gpr);385 386	for (i = 0; i < 3; i++) {387		prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i);388		if (!prg->pres[i])389			return -EPROBE_DEFER;390	}391 392	ret = clk_prepare_enable(prg->clk_ipg);393	if (ret)394		return ret;395 396	ret = clk_prepare_enable(prg->clk_axi);397	if (ret) {398		clk_disable_unprepare(prg->clk_ipg);399		return ret;400	}401 402	/* init to free running mode */403	val = readl(prg->regs + IPU_PRG_CTL);404	val |= IPU_PRG_CTL_SHADOW_EN;405	writel(val, prg->regs + IPU_PRG_CTL);406 407	/* disable address threshold */408	writel(0xffffffff, prg->regs + IPU_PRG_THD);409 410	pm_runtime_set_active(dev);411	pm_runtime_enable(dev);412 413	prg->dev = dev;414	platform_set_drvdata(pdev, prg);415	mutex_lock(&ipu_prg_list_mutex);416	list_add(&prg->list, &ipu_prg_list);417	mutex_unlock(&ipu_prg_list_mutex);418 419	return 0;420}421 422static void ipu_prg_remove(struct platform_device *pdev)423{424	struct ipu_prg *prg = platform_get_drvdata(pdev);425 426	mutex_lock(&ipu_prg_list_mutex);427	list_del(&prg->list);428	mutex_unlock(&ipu_prg_list_mutex);429}430 431#ifdef CONFIG_PM432static int prg_suspend(struct device *dev)433{434	struct ipu_prg *prg = dev_get_drvdata(dev);435 436	clk_disable_unprepare(prg->clk_axi);437	clk_disable_unprepare(prg->clk_ipg);438 439	return 0;440}441 442static int prg_resume(struct device *dev)443{444	struct ipu_prg *prg = dev_get_drvdata(dev);445	int ret;446 447	ret = clk_prepare_enable(prg->clk_ipg);448	if (ret)449		return ret;450 451	ret = clk_prepare_enable(prg->clk_axi);452	if (ret) {453		clk_disable_unprepare(prg->clk_ipg);454		return ret;455	}456 457	return 0;458}459#endif460 461static const struct dev_pm_ops prg_pm_ops = {462	SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL)463};464 465static const struct of_device_id ipu_prg_dt_ids[] = {466	{ .compatible = "fsl,imx6qp-prg", },467	{ /* sentinel */ },468};469 470struct platform_driver ipu_prg_drv = {471	.probe		= ipu_prg_probe,472	.remove_new	= ipu_prg_remove,473	.driver		= {474		.name	= "imx-ipu-prg",475		.pm	= &prg_pm_ops,476		.of_match_table = ipu_prg_dt_ids,477	},478};479