1 /* 2 * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. 3 * 4 * The code contained herein is licensed under the GNU General Public 5 * License. You may obtain a copy of the GNU General Public License 6 * Version 2 or later at the following locations: 7 * 8 * http://www.opensource.org/licenses/gpl-license.html 9 * http://www.gnu.org/copyleft/gpl.html 10 */ 11 #include <linux/export.h> 12 #include <linux/types.h> 13 #include <linux/init.h> 14 #include <linux/io.h> 15 #include <linux/errno.h> 16 #include <linux/spinlock.h> 17 #include <linux/delay.h> 18 #include <linux/clk.h> 19 #include <video/imx-ipu-v3.h> 20 21 #include "ipu-prv.h" 22 23 struct ipu_smfc { 24 struct ipu_smfc_priv *priv; 25 int chno; 26 bool inuse; 27 }; 28 29 struct ipu_smfc_priv { 30 void __iomem *base; 31 spinlock_t lock; 32 struct ipu_soc *ipu; 33 struct ipu_smfc channel[4]; 34 int use_count; 35 }; 36 37 /*SMFC Registers */ 38 #define SMFC_MAP 0x0000 39 #define SMFC_WMC 0x0004 40 #define SMFC_BS 0x0008 41 42 int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) 43 { 44 struct ipu_smfc_priv *priv = smfc->priv; 45 unsigned long flags; 46 u32 val, shift; 47 48 spin_lock_irqsave(&priv->lock, flags); 49 50 shift = smfc->chno * 4; 51 val = readl(priv->base + SMFC_BS); 52 val &= ~(0xf << shift); 53 val |= burstsize << shift; 54 writel(val, priv->base + SMFC_BS); 55 56 spin_unlock_irqrestore(&priv->lock, flags); 57 58 return 0; 59 } 60 EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); 61 62 int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) 63 { 64 struct ipu_smfc_priv *priv = smfc->priv; 65 unsigned long flags; 66 u32 val, shift; 67 68 spin_lock_irqsave(&priv->lock, flags); 69 70 shift = smfc->chno * 3; 71 val = readl(priv->base + SMFC_MAP); 72 val &= ~(0x7 << shift); 73 val |= ((csi_id << 2) | mipi_id) << shift; 74 writel(val, priv->base + SMFC_MAP); 75 76 spin_unlock_irqrestore(&priv->lock, flags); 77 78 return 0; 79 } 80 EXPORT_SYMBOL_GPL(ipu_smfc_map_channel); 81 82 int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) 83 { 84 struct ipu_smfc_priv *priv = smfc->priv; 85 unsigned long flags; 86 u32 val, shift; 87 88 spin_lock_irqsave(&priv->lock, flags); 89 90 shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); 91 val = readl(priv->base + SMFC_WMC); 92 val &= ~(0x3f << shift); 93 val |= ((clr_level << 3) | set_level) << shift; 94 writel(val, priv->base + SMFC_WMC); 95 96 spin_unlock_irqrestore(&priv->lock, flags); 97 98 return 0; 99 } 100 EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); 101 102 int ipu_smfc_enable(struct ipu_smfc *smfc) 103 { 104 struct ipu_smfc_priv *priv = smfc->priv; 105 unsigned long flags; 106 107 spin_lock_irqsave(&priv->lock, flags); 108 109 if (!priv->use_count) 110 ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); 111 112 priv->use_count++; 113 114 spin_unlock_irqrestore(&priv->lock, flags); 115 116 return 0; 117 } 118 EXPORT_SYMBOL_GPL(ipu_smfc_enable); 119 120 int ipu_smfc_disable(struct ipu_smfc *smfc) 121 { 122 struct ipu_smfc_priv *priv = smfc->priv; 123 unsigned long flags; 124 125 spin_lock_irqsave(&priv->lock, flags); 126 127 priv->use_count--; 128 129 if (!priv->use_count) 130 ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); 131 132 if (priv->use_count < 0) 133 priv->use_count = 0; 134 135 spin_unlock_irqrestore(&priv->lock, flags); 136 137 return 0; 138 } 139 EXPORT_SYMBOL_GPL(ipu_smfc_disable); 140 141 struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) 142 { 143 struct ipu_smfc_priv *priv = ipu->smfc_priv; 144 struct ipu_smfc *smfc, *ret; 145 unsigned long flags; 146 147 if (chno >= 4) 148 return ERR_PTR(-EINVAL); 149 150 smfc = &priv->channel[chno]; 151 ret = smfc; 152 153 spin_lock_irqsave(&priv->lock, flags); 154 155 if (smfc->inuse) { 156 ret = ERR_PTR(-EBUSY); 157 goto unlock; 158 } 159 160 smfc->inuse = true; 161 unlock: 162 spin_unlock_irqrestore(&priv->lock, flags); 163 return ret; 164 } 165 EXPORT_SYMBOL_GPL(ipu_smfc_get); 166 167 void ipu_smfc_put(struct ipu_smfc *smfc) 168 { 169 struct ipu_smfc_priv *priv = smfc->priv; 170 unsigned long flags; 171 172 spin_lock_irqsave(&priv->lock, flags); 173 smfc->inuse = false; 174 spin_unlock_irqrestore(&priv->lock, flags); 175 } 176 EXPORT_SYMBOL_GPL(ipu_smfc_put); 177 178 int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, 179 unsigned long base) 180 { 181 struct ipu_smfc_priv *priv; 182 int i; 183 184 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 185 if (!priv) 186 return -ENOMEM; 187 188 ipu->smfc_priv = priv; 189 spin_lock_init(&priv->lock); 190 priv->ipu = ipu; 191 192 priv->base = devm_ioremap(dev, base, PAGE_SIZE); 193 if (!priv->base) 194 return -ENOMEM; 195 196 for (i = 0; i < 4; i++) { 197 priv->channel[i].priv = priv; 198 priv->channel[i].chno = i; 199 } 200 201 pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); 202 203 return 0; 204 } 205 206 void ipu_smfc_exit(struct ipu_soc *ipu) 207 { 208 } 209