18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/export.h> 68c2ecf20Sopenharmony_ci#include <linux/types.h> 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <video/imx-ipu-v3.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "ipu-prv.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct ipu_smfc { 188c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv; 198c2ecf20Sopenharmony_ci int chno; 208c2ecf20Sopenharmony_ci bool inuse; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct ipu_smfc_priv { 248c2ecf20Sopenharmony_ci void __iomem *base; 258c2ecf20Sopenharmony_ci spinlock_t lock; 268c2ecf20Sopenharmony_ci struct ipu_soc *ipu; 278c2ecf20Sopenharmony_ci struct ipu_smfc channel[4]; 288c2ecf20Sopenharmony_ci int use_count; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/*SMFC Registers */ 328c2ecf20Sopenharmony_ci#define SMFC_MAP 0x0000 338c2ecf20Sopenharmony_ci#define SMFC_WMC 0x0004 348c2ecf20Sopenharmony_ci#define SMFC_BS 0x0008 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ciint ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv = smfc->priv; 398c2ecf20Sopenharmony_ci unsigned long flags; 408c2ecf20Sopenharmony_ci u32 val, shift; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci shift = smfc->chno * 4; 458c2ecf20Sopenharmony_ci val = readl(priv->base + SMFC_BS); 468c2ecf20Sopenharmony_ci val &= ~(0xf << shift); 478c2ecf20Sopenharmony_ci val |= burstsize << shift; 488c2ecf20Sopenharmony_ci writel(val, priv->base + SMFC_BS); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ciint ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv = smfc->priv; 598c2ecf20Sopenharmony_ci unsigned long flags; 608c2ecf20Sopenharmony_ci u32 val, shift; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci shift = smfc->chno * 3; 658c2ecf20Sopenharmony_ci val = readl(priv->base + SMFC_MAP); 668c2ecf20Sopenharmony_ci val &= ~(0x7 << shift); 678c2ecf20Sopenharmony_ci val |= ((csi_id << 2) | mipi_id) << shift; 688c2ecf20Sopenharmony_ci writel(val, priv->base + SMFC_MAP); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_smfc_map_channel); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciint ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv = smfc->priv; 798c2ecf20Sopenharmony_ci unsigned long flags; 808c2ecf20Sopenharmony_ci u32 val, shift; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); 858c2ecf20Sopenharmony_ci val = readl(priv->base + SMFC_WMC); 868c2ecf20Sopenharmony_ci val &= ~(0x3f << shift); 878c2ecf20Sopenharmony_ci val |= ((clr_level << 3) | set_level) << shift; 888c2ecf20Sopenharmony_ci writel(val, priv->base + SMFC_WMC); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciint ipu_smfc_enable(struct ipu_smfc *smfc) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv = smfc->priv; 998c2ecf20Sopenharmony_ci unsigned long flags; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!priv->use_count) 1048c2ecf20Sopenharmony_ci ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci priv->use_count++; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_smfc_enable); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciint ipu_smfc_disable(struct ipu_smfc *smfc) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv = smfc->priv; 1178c2ecf20Sopenharmony_ci unsigned long flags; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci priv->use_count--; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!priv->use_count) 1248c2ecf20Sopenharmony_ci ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (priv->use_count < 0) 1278c2ecf20Sopenharmony_ci priv->use_count = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_smfc_disable); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistruct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv = ipu->smfc_priv; 1388c2ecf20Sopenharmony_ci struct ipu_smfc *smfc, *ret; 1398c2ecf20Sopenharmony_ci unsigned long flags; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (chno >= 4) 1428c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci smfc = &priv->channel[chno]; 1458c2ecf20Sopenharmony_ci ret = smfc; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (smfc->inuse) { 1508c2ecf20Sopenharmony_ci ret = ERR_PTR(-EBUSY); 1518c2ecf20Sopenharmony_ci goto unlock; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci smfc->inuse = true; 1558c2ecf20Sopenharmony_ciunlock: 1568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_smfc_get); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_civoid ipu_smfc_put(struct ipu_smfc *smfc) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv = smfc->priv; 1648c2ecf20Sopenharmony_ci unsigned long flags; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 1678c2ecf20Sopenharmony_ci smfc->inuse = false; 1688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_smfc_put); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, 1738c2ecf20Sopenharmony_ci unsigned long base) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct ipu_smfc_priv *priv; 1768c2ecf20Sopenharmony_ci int i; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1798c2ecf20Sopenharmony_ci if (!priv) 1808c2ecf20Sopenharmony_ci return -ENOMEM; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ipu->smfc_priv = priv; 1838c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 1848c2ecf20Sopenharmony_ci priv->ipu = ipu; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci priv->base = devm_ioremap(dev, base, PAGE_SIZE); 1878c2ecf20Sopenharmony_ci if (!priv->base) 1888c2ecf20Sopenharmony_ci return -ENOMEM; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1918c2ecf20Sopenharmony_ci priv->channel[i].priv = priv; 1928c2ecf20Sopenharmony_ci priv->channel[i].chno = i; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_civoid ipu_smfc_exit(struct ipu_soc *ipu) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci} 203