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