Blame view
sound/soc/au1x/i2sc.c
7.22 KB
b3c70c9ea ASoC: Alchemy AC9... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
/* * Au1000/Au1500/Au1100 I2S controller driver for ASoC * * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> * * Note: clock supplied to the I2S controller must be 256x samplerate. */ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/suspend.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> #include <sound/soc.h> #include <asm/mach-au1x00/au1000.h> #include "psc.h" #define I2S_RXTX 0x00 #define I2S_CFG 0x04 #define I2S_ENABLE 0x08 #define CFG_XU (1 << 25) /* tx underflow */ #define CFG_XO (1 << 24) #define CFG_RU (1 << 23) #define CFG_RO (1 << 22) #define CFG_TR (1 << 21) #define CFG_TE (1 << 20) #define CFG_TF (1 << 19) #define CFG_RR (1 << 18) #define CFG_RF (1 << 17) #define CFG_ICK (1 << 12) /* clock invert */ #define CFG_PD (1 << 11) /* set to make I2SDIO INPUT */ #define CFG_LB (1 << 10) /* loopback */ #define CFG_IC (1 << 9) /* word select invert */ #define CFG_FM_I2S (0 << 7) /* I2S format */ #define CFG_FM_LJ (1 << 7) /* left-justified */ #define CFG_FM_RJ (2 << 7) /* right-justified */ #define CFG_FM_MASK (3 << 7) #define CFG_TN (1 << 6) /* tx fifo en */ #define CFG_RN (1 << 5) /* rx fifo en */ #define CFG_SZ_8 (0x08) #define CFG_SZ_16 (0x10) #define CFG_SZ_18 (0x12) #define CFG_SZ_20 (0x14) #define CFG_SZ_24 (0x18) #define CFG_SZ_MASK (0x1f) #define EN_D (1 << 1) /* DISable */ #define EN_CE (1 << 0) /* clock enable */ /* only limited by clock generator and board design */ #define AU1XI2SC_RATES \ SNDRV_PCM_RATE_CONTINUOUS #define AU1XI2SC_FMTS \ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \ SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE | \ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE | \ 0) static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg) { return __raw_readl(ctx->mmio + reg); } static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v) { __raw_writel(v, ctx->mmio + reg); wmb(); } static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai); unsigned long c; int ret; ret = -EINVAL; c = ctx->cfg; c &= ~CFG_FM_MASK; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: c |= CFG_FM_I2S; break; case SND_SOC_DAIFMT_MSB: c |= CFG_FM_RJ; break; case SND_SOC_DAIFMT_LSB: c |= CFG_FM_LJ; break; default: goto out; } c &= ~(CFG_IC | CFG_ICK); /* IB-IF */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: c |= CFG_IC | CFG_ICK; break; case SND_SOC_DAIFMT_NB_IF: c |= CFG_IC; break; case SND_SOC_DAIFMT_IB_NF: c |= CFG_ICK; break; case SND_SOC_DAIFMT_IB_IF: break; default: goto out; } /* I2S controller only supports master */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */ break; default: goto out; } ret = 0; ctx->cfg = c; out: return ret; } static int au1xi2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); int stype = SUBSTREAM_TYPE(substream); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: /* power up */ WR(ctx, I2S_ENABLE, EN_D | EN_CE); WR(ctx, I2S_ENABLE, EN_CE); ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN; WR(ctx, I2S_CFG, ctx->cfg); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN); WR(ctx, I2S_CFG, ctx->cfg); WR(ctx, I2S_ENABLE, EN_D); /* power off */ break; default: return -EINVAL; } return 0; } static unsigned long msbits_to_reg(int msbits) { switch (msbits) { case 8: return CFG_SZ_8; case 16: return CFG_SZ_16; case 18: return CFG_SZ_18; case 20: return CFG_SZ_20; case 24: return CFG_SZ_24; } return 0; } static int au1xi2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); unsigned long v; v = msbits_to_reg(params->msbits); if (!v) return -EINVAL; ctx->cfg &= ~CFG_SZ_MASK; ctx->cfg |= v; return 0; } static int au1xi2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]); return 0; } |
890754a87 ASoC: Cleanup dup... |
203 |
static const struct snd_soc_dai_ops au1xi2s_dai_ops = { |
b3c70c9ea ASoC: Alchemy AC9... |
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
.startup = au1xi2s_startup, .trigger = au1xi2s_trigger, .hw_params = au1xi2s_hw_params, .set_fmt = au1xi2s_set_fmt, }; static struct snd_soc_dai_driver au1xi2s_dai_driver = { .symmetric_rates = 1, .playback = { .rates = AU1XI2SC_RATES, .formats = AU1XI2SC_FMTS, .channels_min = 2, .channels_max = 2, }, .capture = { .rates = AU1XI2SC_RATES, .formats = AU1XI2SC_FMTS, .channels_min = 2, .channels_max = 2, }, .ops = &au1xi2s_dai_ops, }; static int __devinit au1xi2s_drvprobe(struct platform_device *pdev) { |
226d0f22d ASoC: keep pointe... |
229 |
struct resource *iores, *dmares; |
b3c70c9ea ASoC: Alchemy AC9... |
230 |
struct au1xpsc_audio_data *ctx; |
6d8955262 ASoC: i2sc.c: use... |
231 |
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
b3c70c9ea ASoC: Alchemy AC9... |
232 233 |
if (!ctx) return -ENOMEM; |
226d0f22d ASoC: keep pointe... |
234 |
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
6d8955262 ASoC: i2sc.c: use... |
235 236 |
if (!iores) return -ENODEV; |
b3c70c9ea ASoC: Alchemy AC9... |
237 |
|
6d8955262 ASoC: i2sc.c: use... |
238 239 240 241 |
if (!devm_request_mem_region(&pdev->dev, iores->start, resource_size(iores), pdev->name)) return -EBUSY; |
b3c70c9ea ASoC: Alchemy AC9... |
242 |
|
6d8955262 ASoC: i2sc.c: use... |
243 244 |
ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start, resource_size(iores)); |
b3c70c9ea ASoC: Alchemy AC9... |
245 |
if (!ctx->mmio) |
6d8955262 ASoC: i2sc.c: use... |
246 |
return -EBUSY; |
b3c70c9ea ASoC: Alchemy AC9... |
247 |
|
226d0f22d ASoC: keep pointe... |
248 249 |
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!dmares) |
6d8955262 ASoC: i2sc.c: use... |
250 |
return -EBUSY; |
226d0f22d ASoC: keep pointe... |
251 |
ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start; |
b3c70c9ea ASoC: Alchemy AC9... |
252 |
|
226d0f22d ASoC: keep pointe... |
253 254 |
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!dmares) |
6d8955262 ASoC: i2sc.c: use... |
255 |
return -EBUSY; |
226d0f22d ASoC: keep pointe... |
256 |
ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start; |
b3c70c9ea ASoC: Alchemy AC9... |
257 258 |
platform_set_drvdata(pdev, ctx); |
6d8955262 ASoC: i2sc.c: use... |
259 |
return snd_soc_register_dai(&pdev->dev, &au1xi2s_dai_driver); |
b3c70c9ea ASoC: Alchemy AC9... |
260 261 262 263 264 |
} static int __devexit au1xi2s_drvremove(struct platform_device *pdev) { struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev); |
b3c70c9ea ASoC: Alchemy AC9... |
265 266 267 268 |
snd_soc_unregister_dai(&pdev->dev); WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */ |
b3c70c9ea ASoC: Alchemy AC9... |
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
return 0; } #ifdef CONFIG_PM static int au1xi2s_drvsuspend(struct device *dev) { struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */ return 0; } static int au1xi2s_drvresume(struct device *dev) { return 0; } static const struct dev_pm_ops au1xi2sc_pmops = { .suspend = au1xi2s_drvsuspend, .resume = au1xi2s_drvresume, }; #define AU1XI2SC_PMOPS (&au1xi2sc_pmops) #else #define AU1XI2SC_PMOPS NULL #endif static struct platform_driver au1xi2s_driver = { .driver = { .name = "alchemy-i2sc", .owner = THIS_MODULE, .pm = AU1XI2SC_PMOPS, }, .probe = au1xi2s_drvprobe, .remove = __devexit_p(au1xi2s_drvremove), }; |
8a124f9cc ASoC: Convert au1... |
309 |
module_platform_driver(au1xi2s_driver); |
b3c70c9ea ASoC: Alchemy AC9... |
310 311 312 313 |
MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver"); MODULE_AUTHOR("Manuel Lauss"); |