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