0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/delay.h>
0013 #include <linux/clk.h>
0014 #include <linux/io.h>
0015 #include <linux/module.h>
0016
0017 #include <sound/soc.h>
0018 #include <sound/pcm_params.h>
0019
0020 #include "regs-iis.h"
0021 #include "dma.h"
0022 #include "s3c24xx-i2s.h"
0023
0024 static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
0025 .chan_name = "tx",
0026 .addr_width = 2,
0027 };
0028
0029 static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
0030 .chan_name = "rx",
0031 .addr_width = 2,
0032 };
0033
0034 struct s3c24xx_i2s_info {
0035 void __iomem *regs;
0036 struct clk *iis_clk;
0037 u32 iiscon;
0038 u32 iismod;
0039 u32 iisfcon;
0040 u32 iispsr;
0041 };
0042 static struct s3c24xx_i2s_info s3c24xx_i2s;
0043
0044 static void s3c24xx_snd_txctrl(int on)
0045 {
0046 u32 iisfcon;
0047 u32 iiscon;
0048 u32 iismod;
0049
0050 iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
0051 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
0052 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
0053
0054 pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
0055
0056 if (on) {
0057 iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
0058 iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
0059 iiscon &= ~S3C2410_IISCON_TXIDLE;
0060 iismod |= S3C2410_IISMOD_TXMODE;
0061
0062 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0063 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
0064 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
0065 } else {
0066
0067
0068
0069
0070
0071
0072
0073 iisfcon &= ~S3C2410_IISFCON_TXENABLE;
0074 iisfcon &= ~S3C2410_IISFCON_TXDMA;
0075 iiscon |= S3C2410_IISCON_TXIDLE;
0076 iiscon &= ~S3C2410_IISCON_TXDMAEN;
0077 iismod &= ~S3C2410_IISMOD_TXMODE;
0078
0079 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
0080 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
0081 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0082 }
0083
0084 pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
0085 }
0086
0087 static void s3c24xx_snd_rxctrl(int on)
0088 {
0089 u32 iisfcon;
0090 u32 iiscon;
0091 u32 iismod;
0092
0093 iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
0094 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
0095 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
0096
0097 pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
0098
0099 if (on) {
0100 iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
0101 iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
0102 iiscon &= ~S3C2410_IISCON_RXIDLE;
0103 iismod |= S3C2410_IISMOD_RXMODE;
0104
0105 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0106 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
0107 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
0108 } else {
0109
0110
0111
0112
0113
0114
0115
0116 iisfcon &= ~S3C2410_IISFCON_RXENABLE;
0117 iisfcon &= ~S3C2410_IISFCON_RXDMA;
0118 iiscon |= S3C2410_IISCON_RXIDLE;
0119 iiscon &= ~S3C2410_IISCON_RXDMAEN;
0120 iismod &= ~S3C2410_IISMOD_RXMODE;
0121
0122 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
0123 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
0124 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0125 }
0126
0127 pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
0128 }
0129
0130
0131
0132
0133
0134 static int s3c24xx_snd_lrsync(void)
0135 {
0136 u32 iiscon;
0137 int timeout = 50;
0138
0139 while (1) {
0140 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
0141 if (iiscon & S3C2410_IISCON_LRINDEX)
0142 break;
0143
0144 if (!timeout--)
0145 return -ETIMEDOUT;
0146 udelay(100);
0147 }
0148
0149 return 0;
0150 }
0151
0152
0153
0154
0155 static inline int s3c24xx_snd_is_clkmaster(void)
0156 {
0157 return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
0158 }
0159
0160
0161
0162
0163 static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
0164 unsigned int fmt)
0165 {
0166 u32 iismod;
0167
0168 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
0169 pr_debug("hw_params r: IISMOD: %x \n", iismod);
0170
0171 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
0172 case SND_SOC_DAIFMT_BC_FC:
0173 iismod |= S3C2410_IISMOD_SLAVE;
0174 break;
0175 case SND_SOC_DAIFMT_BP_FP:
0176 iismod &= ~S3C2410_IISMOD_SLAVE;
0177 break;
0178 default:
0179 return -EINVAL;
0180 }
0181
0182 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0183 case SND_SOC_DAIFMT_LEFT_J:
0184 iismod |= S3C2410_IISMOD_MSB;
0185 break;
0186 case SND_SOC_DAIFMT_I2S:
0187 iismod &= ~S3C2410_IISMOD_MSB;
0188 break;
0189 default:
0190 return -EINVAL;
0191 }
0192
0193 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0194 pr_debug("hw_params w: IISMOD: %x \n", iismod);
0195
0196 return 0;
0197 }
0198
0199 static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
0200 struct snd_pcm_hw_params *params,
0201 struct snd_soc_dai *dai)
0202 {
0203 struct snd_dmaengine_dai_dma_data *dma_data;
0204 u32 iismod;
0205
0206 dma_data = snd_soc_dai_get_dma_data(dai, substream);
0207
0208
0209 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
0210 pr_debug("hw_params r: IISMOD: %x\n", iismod);
0211
0212 switch (params_width(params)) {
0213 case 8:
0214 iismod &= ~S3C2410_IISMOD_16BIT;
0215 dma_data->addr_width = 1;
0216 break;
0217 case 16:
0218 iismod |= S3C2410_IISMOD_16BIT;
0219 dma_data->addr_width = 2;
0220 break;
0221 default:
0222 return -EINVAL;
0223 }
0224
0225 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0226 pr_debug("hw_params w: IISMOD: %x\n", iismod);
0227
0228 return 0;
0229 }
0230
0231 static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
0232 struct snd_soc_dai *dai)
0233 {
0234 int ret = 0;
0235
0236 switch (cmd) {
0237 case SNDRV_PCM_TRIGGER_START:
0238 case SNDRV_PCM_TRIGGER_RESUME:
0239 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0240 if (!s3c24xx_snd_is_clkmaster()) {
0241 ret = s3c24xx_snd_lrsync();
0242 if (ret)
0243 goto exit_err;
0244 }
0245
0246 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
0247 s3c24xx_snd_rxctrl(1);
0248 else
0249 s3c24xx_snd_txctrl(1);
0250
0251 break;
0252 case SNDRV_PCM_TRIGGER_STOP:
0253 case SNDRV_PCM_TRIGGER_SUSPEND:
0254 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0255 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
0256 s3c24xx_snd_rxctrl(0);
0257 else
0258 s3c24xx_snd_txctrl(0);
0259 break;
0260 default:
0261 ret = -EINVAL;
0262 break;
0263 }
0264
0265 exit_err:
0266 return ret;
0267 }
0268
0269
0270
0271
0272 static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
0273 int clk_id, unsigned int freq, int dir)
0274 {
0275 u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
0276
0277 iismod &= ~S3C2440_IISMOD_MPLL;
0278
0279 switch (clk_id) {
0280 case S3C24XX_CLKSRC_PCLK:
0281 break;
0282 case S3C24XX_CLKSRC_MPLL:
0283 iismod |= S3C2440_IISMOD_MPLL;
0284 break;
0285 default:
0286 return -EINVAL;
0287 }
0288
0289 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0290 return 0;
0291 }
0292
0293
0294
0295
0296 static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
0297 int div_id, int div)
0298 {
0299 u32 reg;
0300
0301 switch (div_id) {
0302 case S3C24XX_DIV_BCLK:
0303 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
0304 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
0305 break;
0306 case S3C24XX_DIV_MCLK:
0307 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
0308 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
0309 break;
0310 case S3C24XX_DIV_PRESCALER:
0311 writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
0312 reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
0313 writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
0314 break;
0315 default:
0316 return -EINVAL;
0317 }
0318
0319 return 0;
0320 }
0321
0322
0323
0324
0325
0326 u32 s3c24xx_i2s_get_clockrate(void)
0327 {
0328 return clk_get_rate(s3c24xx_i2s.iis_clk);
0329 }
0330 EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
0331
0332 static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
0333 {
0334 int ret;
0335 snd_soc_dai_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out,
0336 &s3c24xx_i2s_pcm_stereo_in);
0337
0338 s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis");
0339 if (IS_ERR(s3c24xx_i2s.iis_clk)) {
0340 pr_err("failed to get iis_clock\n");
0341 return PTR_ERR(s3c24xx_i2s.iis_clk);
0342 }
0343 ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
0344 if (ret)
0345 return ret;
0346
0347 writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
0348
0349 s3c24xx_snd_txctrl(0);
0350 s3c24xx_snd_rxctrl(0);
0351
0352 return 0;
0353 }
0354
0355 #ifdef CONFIG_PM
0356 static int s3c24xx_i2s_suspend(struct snd_soc_component *component)
0357 {
0358 s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
0359 s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
0360 s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
0361 s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
0362
0363 clk_disable_unprepare(s3c24xx_i2s.iis_clk);
0364
0365 return 0;
0366 }
0367
0368 static int s3c24xx_i2s_resume(struct snd_soc_component *component)
0369 {
0370 int ret;
0371
0372 ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
0373 if (ret)
0374 return ret;
0375
0376 writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
0377 writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
0378 writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
0379 writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
0380
0381 return 0;
0382 }
0383 #else
0384 #define s3c24xx_i2s_suspend NULL
0385 #define s3c24xx_i2s_resume NULL
0386 #endif
0387
0388 #define S3C24XX_I2S_RATES \
0389 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
0390 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
0391 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
0392
0393 static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
0394 .trigger = s3c24xx_i2s_trigger,
0395 .hw_params = s3c24xx_i2s_hw_params,
0396 .set_fmt = s3c24xx_i2s_set_fmt,
0397 .set_clkdiv = s3c24xx_i2s_set_clkdiv,
0398 .set_sysclk = s3c24xx_i2s_set_sysclk,
0399 };
0400
0401 static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
0402 .probe = s3c24xx_i2s_probe,
0403 .playback = {
0404 .channels_min = 2,
0405 .channels_max = 2,
0406 .rates = S3C24XX_I2S_RATES,
0407 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
0408 .capture = {
0409 .channels_min = 2,
0410 .channels_max = 2,
0411 .rates = S3C24XX_I2S_RATES,
0412 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
0413 .ops = &s3c24xx_i2s_dai_ops,
0414 };
0415
0416 static const struct snd_soc_component_driver s3c24xx_i2s_component = {
0417 .name = "s3c24xx-i2s",
0418 .suspend = s3c24xx_i2s_suspend,
0419 .resume = s3c24xx_i2s_resume,
0420 .legacy_dai_naming = 1,
0421 };
0422
0423 static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
0424 {
0425 struct resource *res;
0426 int ret;
0427
0428 s3c24xx_i2s.regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0429 if (IS_ERR(s3c24xx_i2s.regs))
0430 return PTR_ERR(s3c24xx_i2s.regs);
0431
0432 s3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO;
0433 s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
0434
0435 ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL,
0436 "tx", "rx", NULL);
0437 if (ret) {
0438 dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret);
0439 return ret;
0440 }
0441
0442 ret = devm_snd_soc_register_component(&pdev->dev,
0443 &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
0444 if (ret)
0445 dev_err(&pdev->dev, "Failed to register the DAI\n");
0446
0447 return ret;
0448 }
0449
0450 static struct platform_driver s3c24xx_iis_driver = {
0451 .probe = s3c24xx_iis_dev_probe,
0452 .driver = {
0453 .name = "s3c24xx-iis",
0454 },
0455 };
0456
0457 module_platform_driver(s3c24xx_iis_driver);
0458
0459
0460 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
0461 MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
0462 MODULE_LICENSE("GPL");
0463 MODULE_ALIAS("platform:s3c24xx-iis");