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