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