0001
0002
0003
0004
0005 #include <linux/export.h>
0006 #include <linux/types.h>
0007 #include <linux/init.h>
0008 #include <linux/io.h>
0009 #include <linux/errno.h>
0010 #include <linux/spinlock.h>
0011 #include <linux/delay.h>
0012 #include <linux/clk.h>
0013 #include <video/imx-ipu-v3.h>
0014
0015 #include "ipu-prv.h"
0016
0017 struct ipu_smfc {
0018 struct ipu_smfc_priv *priv;
0019 int chno;
0020 bool inuse;
0021 };
0022
0023 struct ipu_smfc_priv {
0024 void __iomem *base;
0025 spinlock_t lock;
0026 struct ipu_soc *ipu;
0027 struct ipu_smfc channel[4];
0028 int use_count;
0029 };
0030
0031
0032 #define SMFC_MAP 0x0000
0033 #define SMFC_WMC 0x0004
0034 #define SMFC_BS 0x0008
0035
0036 int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize)
0037 {
0038 struct ipu_smfc_priv *priv = smfc->priv;
0039 unsigned long flags;
0040 u32 val, shift;
0041
0042 spin_lock_irqsave(&priv->lock, flags);
0043
0044 shift = smfc->chno * 4;
0045 val = readl(priv->base + SMFC_BS);
0046 val &= ~(0xf << shift);
0047 val |= burstsize << shift;
0048 writel(val, priv->base + SMFC_BS);
0049
0050 spin_unlock_irqrestore(&priv->lock, flags);
0051
0052 return 0;
0053 }
0054 EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
0055
0056 int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id)
0057 {
0058 struct ipu_smfc_priv *priv = smfc->priv;
0059 unsigned long flags;
0060 u32 val, shift;
0061
0062 spin_lock_irqsave(&priv->lock, flags);
0063
0064 shift = smfc->chno * 3;
0065 val = readl(priv->base + SMFC_MAP);
0066 val &= ~(0x7 << shift);
0067 val |= ((csi_id << 2) | mipi_id) << shift;
0068 writel(val, priv->base + SMFC_MAP);
0069
0070 spin_unlock_irqrestore(&priv->lock, flags);
0071
0072 return 0;
0073 }
0074 EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
0075
0076 int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level)
0077 {
0078 struct ipu_smfc_priv *priv = smfc->priv;
0079 unsigned long flags;
0080 u32 val, shift;
0081
0082 spin_lock_irqsave(&priv->lock, flags);
0083
0084 shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0);
0085 val = readl(priv->base + SMFC_WMC);
0086 val &= ~(0x3f << shift);
0087 val |= ((clr_level << 3) | set_level) << shift;
0088 writel(val, priv->base + SMFC_WMC);
0089
0090 spin_unlock_irqrestore(&priv->lock, flags);
0091
0092 return 0;
0093 }
0094 EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark);
0095
0096 int ipu_smfc_enable(struct ipu_smfc *smfc)
0097 {
0098 struct ipu_smfc_priv *priv = smfc->priv;
0099 unsigned long flags;
0100
0101 spin_lock_irqsave(&priv->lock, flags);
0102
0103 if (!priv->use_count)
0104 ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN);
0105
0106 priv->use_count++;
0107
0108 spin_unlock_irqrestore(&priv->lock, flags);
0109
0110 return 0;
0111 }
0112 EXPORT_SYMBOL_GPL(ipu_smfc_enable);
0113
0114 int ipu_smfc_disable(struct ipu_smfc *smfc)
0115 {
0116 struct ipu_smfc_priv *priv = smfc->priv;
0117 unsigned long flags;
0118
0119 spin_lock_irqsave(&priv->lock, flags);
0120
0121 priv->use_count--;
0122
0123 if (!priv->use_count)
0124 ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN);
0125
0126 if (priv->use_count < 0)
0127 priv->use_count = 0;
0128
0129 spin_unlock_irqrestore(&priv->lock, flags);
0130
0131 return 0;
0132 }
0133 EXPORT_SYMBOL_GPL(ipu_smfc_disable);
0134
0135 struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno)
0136 {
0137 struct ipu_smfc_priv *priv = ipu->smfc_priv;
0138 struct ipu_smfc *smfc, *ret;
0139 unsigned long flags;
0140
0141 if (chno >= 4)
0142 return ERR_PTR(-EINVAL);
0143
0144 smfc = &priv->channel[chno];
0145 ret = smfc;
0146
0147 spin_lock_irqsave(&priv->lock, flags);
0148
0149 if (smfc->inuse) {
0150 ret = ERR_PTR(-EBUSY);
0151 goto unlock;
0152 }
0153
0154 smfc->inuse = true;
0155 unlock:
0156 spin_unlock_irqrestore(&priv->lock, flags);
0157 return ret;
0158 }
0159 EXPORT_SYMBOL_GPL(ipu_smfc_get);
0160
0161 void ipu_smfc_put(struct ipu_smfc *smfc)
0162 {
0163 struct ipu_smfc_priv *priv = smfc->priv;
0164 unsigned long flags;
0165
0166 spin_lock_irqsave(&priv->lock, flags);
0167 smfc->inuse = false;
0168 spin_unlock_irqrestore(&priv->lock, flags);
0169 }
0170 EXPORT_SYMBOL_GPL(ipu_smfc_put);
0171
0172 int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev,
0173 unsigned long base)
0174 {
0175 struct ipu_smfc_priv *priv;
0176 int i;
0177
0178 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0179 if (!priv)
0180 return -ENOMEM;
0181
0182 ipu->smfc_priv = priv;
0183 spin_lock_init(&priv->lock);
0184 priv->ipu = ipu;
0185
0186 priv->base = devm_ioremap(dev, base, PAGE_SIZE);
0187 if (!priv->base)
0188 return -ENOMEM;
0189
0190 for (i = 0; i < 4; i++) {
0191 priv->channel[i].priv = priv;
0192 priv->channel[i].chno = i;
0193 }
0194
0195 pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base);
0196
0197 return 0;
0198 }
0199
0200 void ipu_smfc_exit(struct ipu_soc *ipu)
0201 {
0202 }