0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/init.h>
0014 #include <linux/module.h>
0015 #include <linux/slab.h>
0016 #include <linux/suspend.h>
0017 #include <sound/core.h>
0018 #include <sound/pcm.h>
0019 #include <sound/initval.h>
0020 #include <sound/soc.h>
0021 #include <asm/mach-au1x00/au1000.h>
0022 #include <asm/mach-au1x00/au1xxx_psc.h>
0023
0024 #include "psc.h"
0025
0026
0027 #define AU1XPSC_I2S_DAIFMT \
0028 (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | \
0029 SND_SOC_DAIFMT_NB_NF)
0030
0031
0032 #define AU1XPSC_I2S_DIR \
0033 (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
0034
0035 #define AU1XPSC_I2S_RATES \
0036 SNDRV_PCM_RATE_8000_192000
0037
0038 #define AU1XPSC_I2S_FMTS \
0039 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
0040
0041 #define I2SSTAT_BUSY(stype) \
0042 ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
0043 #define I2SPCR_START(stype) \
0044 ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
0045 #define I2SPCR_STOP(stype) \
0046 ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
0047 #define I2SPCR_CLRFIFO(stype) \
0048 ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
0049
0050
0051 static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
0052 unsigned int fmt)
0053 {
0054 struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(cpu_dai);
0055 unsigned long ct;
0056 int ret;
0057
0058 ret = -EINVAL;
0059
0060 ct = pscdata->cfg;
0061
0062 ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ);
0063 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0064 case SND_SOC_DAIFMT_I2S:
0065 ct |= PSC_I2SCFG_XM;
0066 break;
0067 case SND_SOC_DAIFMT_MSB:
0068 break;
0069 case SND_SOC_DAIFMT_LSB:
0070 ct |= PSC_I2SCFG_MLJ;
0071 break;
0072 default:
0073 goto out;
0074 }
0075
0076 ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI);
0077 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0078 case SND_SOC_DAIFMT_NB_NF:
0079 ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI;
0080 break;
0081 case SND_SOC_DAIFMT_NB_IF:
0082 ct |= PSC_I2SCFG_BI;
0083 break;
0084 case SND_SOC_DAIFMT_IB_NF:
0085 ct |= PSC_I2SCFG_WI;
0086 break;
0087 case SND_SOC_DAIFMT_IB_IF:
0088 break;
0089 default:
0090 goto out;
0091 }
0092
0093 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
0094 case SND_SOC_DAIFMT_BC_FC:
0095 ct |= PSC_I2SCFG_MS;
0096 break;
0097 case SND_SOC_DAIFMT_BP_FP:
0098 ct &= ~PSC_I2SCFG_MS;
0099 break;
0100 default:
0101 goto out;
0102 }
0103
0104 pscdata->cfg = ct;
0105 ret = 0;
0106 out:
0107 return ret;
0108 }
0109
0110 static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
0111 struct snd_pcm_hw_params *params,
0112 struct snd_soc_dai *dai)
0113 {
0114 struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
0115
0116 int cfgbits;
0117 unsigned long stat;
0118
0119
0120 stat = __raw_readl(I2S_STAT(pscdata));
0121 if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) {
0122
0123 cfgbits = __raw_readl(I2S_CFG(pscdata));
0124 if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
0125 (params_rate(params) != pscdata->rate))
0126 return -EINVAL;
0127 } else {
0128
0129 pscdata->cfg &= ~(0x1f << 4);
0130 pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits);
0131
0132 pscdata->rate = params_rate(params);
0133 }
0134 return 0;
0135 }
0136
0137
0138
0139
0140
0141
0142
0143
0144 static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
0145 {
0146 unsigned long tmo;
0147
0148
0149 __raw_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
0150 wmb();
0151
0152 tmo = 1000000;
0153 while (!(__raw_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
0154 tmo--;
0155
0156 if (!tmo)
0157 goto psc_err;
0158
0159 __raw_writel(0, I2S_CFG(pscdata));
0160 wmb();
0161 __raw_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
0162 wmb();
0163
0164
0165 tmo = 1000000;
0166 while (!(__raw_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
0167 tmo--;
0168
0169 if (tmo)
0170 return 0;
0171
0172 psc_err:
0173 __raw_writel(0, I2S_CFG(pscdata));
0174 __raw_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
0175 wmb();
0176 return -ETIMEDOUT;
0177 }
0178
0179 static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
0180 {
0181 unsigned long tmo, stat;
0182 int ret;
0183
0184 ret = 0;
0185
0186
0187 stat = __raw_readl(I2S_STAT(pscdata));
0188 if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
0189 ret = au1xpsc_i2s_configure(pscdata);
0190 if (ret)
0191 goto out;
0192 }
0193
0194 __raw_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
0195 wmb();
0196 __raw_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
0197 wmb();
0198
0199
0200 tmo = 1000000;
0201 while (!(__raw_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
0202 tmo--;
0203
0204 if (!tmo) {
0205 __raw_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
0206 wmb();
0207 ret = -ETIMEDOUT;
0208 }
0209 out:
0210 return ret;
0211 }
0212
0213 static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
0214 {
0215 unsigned long tmo, stat;
0216
0217 __raw_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
0218 wmb();
0219
0220
0221 tmo = 1000000;
0222 while ((__raw_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
0223 tmo--;
0224
0225
0226 stat = __raw_readl(I2S_STAT(pscdata));
0227 if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
0228 __raw_writel(0, I2S_CFG(pscdata));
0229 wmb();
0230 __raw_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
0231 wmb();
0232 }
0233 return 0;
0234 }
0235
0236 static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
0237 struct snd_soc_dai *dai)
0238 {
0239 struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
0240 int ret, stype = substream->stream;
0241
0242 switch (cmd) {
0243 case SNDRV_PCM_TRIGGER_START:
0244 case SNDRV_PCM_TRIGGER_RESUME:
0245 ret = au1xpsc_i2s_start(pscdata, stype);
0246 break;
0247 case SNDRV_PCM_TRIGGER_STOP:
0248 case SNDRV_PCM_TRIGGER_SUSPEND:
0249 ret = au1xpsc_i2s_stop(pscdata, stype);
0250 break;
0251 default:
0252 ret = -EINVAL;
0253 }
0254 return ret;
0255 }
0256
0257 static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream,
0258 struct snd_soc_dai *dai)
0259 {
0260 struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
0261 snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
0262 return 0;
0263 }
0264
0265 static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
0266 .startup = au1xpsc_i2s_startup,
0267 .trigger = au1xpsc_i2s_trigger,
0268 .hw_params = au1xpsc_i2s_hw_params,
0269 .set_fmt = au1xpsc_i2s_set_fmt,
0270 };
0271
0272 static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = {
0273 .playback = {
0274 .rates = AU1XPSC_I2S_RATES,
0275 .formats = AU1XPSC_I2S_FMTS,
0276 .channels_min = 2,
0277 .channels_max = 8,
0278 },
0279 .capture = {
0280 .rates = AU1XPSC_I2S_RATES,
0281 .formats = AU1XPSC_I2S_FMTS,
0282 .channels_min = 2,
0283 .channels_max = 8,
0284 },
0285 .ops = &au1xpsc_i2s_dai_ops,
0286 };
0287
0288 static const struct snd_soc_component_driver au1xpsc_i2s_component = {
0289 .name = "au1xpsc-i2s",
0290 .legacy_dai_naming = 1,
0291 };
0292
0293 static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
0294 {
0295 struct resource *dmares;
0296 unsigned long sel;
0297 struct au1xpsc_audio_data *wd;
0298
0299 wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data),
0300 GFP_KERNEL);
0301 if (!wd)
0302 return -ENOMEM;
0303
0304 wd->mmio = devm_platform_ioremap_resource(pdev, 0);
0305 if (IS_ERR(wd->mmio))
0306 return PTR_ERR(wd->mmio);
0307
0308 dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
0309 if (!dmares)
0310 return -EBUSY;
0311 wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
0312
0313 dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
0314 if (!dmares)
0315 return -EBUSY;
0316 wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
0317
0318
0319
0320
0321 sel = __raw_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
0322 __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
0323 wmb();
0324 __raw_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
0325 __raw_writel(0, I2S_CFG(wd));
0326 wmb();
0327
0328
0329 wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
0330
0331
0332
0333
0334
0335
0336
0337 memcpy(&wd->dai_drv, &au1xpsc_i2s_dai_template,
0338 sizeof(struct snd_soc_dai_driver));
0339 wd->dai_drv.name = dev_name(&pdev->dev);
0340
0341 platform_set_drvdata(pdev, wd);
0342
0343 return devm_snd_soc_register_component(&pdev->dev,
0344 &au1xpsc_i2s_component, &wd->dai_drv, 1);
0345 }
0346
0347 static int au1xpsc_i2s_drvremove(struct platform_device *pdev)
0348 {
0349 struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
0350
0351 __raw_writel(0, I2S_CFG(wd));
0352 wmb();
0353 __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
0354 wmb();
0355
0356 return 0;
0357 }
0358
0359 #ifdef CONFIG_PM
0360 static int au1xpsc_i2s_drvsuspend(struct device *dev)
0361 {
0362 struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
0363
0364
0365 wd->pm[0] = __raw_readl(PSC_SEL(wd));
0366
0367 __raw_writel(0, I2S_CFG(wd));
0368 wmb();
0369 __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
0370 wmb();
0371
0372 return 0;
0373 }
0374
0375 static int au1xpsc_i2s_drvresume(struct device *dev)
0376 {
0377 struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
0378
0379
0380 __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
0381 wmb();
0382 __raw_writel(0, PSC_SEL(wd));
0383 wmb();
0384 __raw_writel(wd->pm[0], PSC_SEL(wd));
0385 wmb();
0386
0387 return 0;
0388 }
0389
0390 static const struct dev_pm_ops au1xpsci2s_pmops = {
0391 .suspend = au1xpsc_i2s_drvsuspend,
0392 .resume = au1xpsc_i2s_drvresume,
0393 };
0394
0395 #define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
0396
0397 #else
0398
0399 #define AU1XPSCI2S_PMOPS NULL
0400
0401 #endif
0402
0403 static struct platform_driver au1xpsc_i2s_driver = {
0404 .driver = {
0405 .name = "au1xpsc_i2s",
0406 .pm = AU1XPSCI2S_PMOPS,
0407 },
0408 .probe = au1xpsc_i2s_drvprobe,
0409 .remove = au1xpsc_i2s_drvremove,
0410 };
0411
0412 module_platform_driver(au1xpsc_i2s_driver);
0413
0414 MODULE_LICENSE("GPL");
0415 MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
0416 MODULE_AUTHOR("Manuel Lauss");