From 36b8a8bbb402911e59acf13b5074eb8915e47a6a Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Mon, 16 Apr 2007 17:18:52 +0200 Subject: [PATCH] [ALSA] ASoC AT91xxxx - SSC port DSP support This patch series by Frank Madarino updates the AT91xxxx core to add DSP/PCM audio hardware formats. Changes:- o Rename at19-i2s.c -> at91-ssc.c o Rename at91-i2s.h -> at91-ssc.h o Add DSP hardware formats. o Rename various I2S labels to SSC Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/{at91-i2s.c => at91-ssc.c} | 253 ++++++++++++++-------- sound/soc/at91/{at91-i2s.h => at91-ssc.h} | 14 +- 2 files changed, 169 insertions(+), 98 deletions(-) rename sound/soc/at91/{at91-i2s.c => at91-ssc.c} (73%) rename sound/soc/at91/{at91-i2s.h => at91-ssc.h} (72%) diff --git a/sound/soc/at91/at91-i2s.c b/sound/soc/at91/at91-ssc.c similarity index 73% rename from sound/soc/at91/at91-i2s.c rename to sound/soc/at91/at91-ssc.c index 9fc0c0388881..db1635a09556 100644 --- a/sound/soc/at91/at91-i2s.c +++ b/sound/soc/at91/at91-ssc.c @@ -1,5 +1,5 @@ /* - * at91-i2s.c -- ALSA SoC I2S Audio Layer Platform driver + * at91-ssc.c -- ALSA SoC AT91 SSC Audio Layer Platform driver * * Author: Frank Mandarino * Endrelia Technologies Inc. @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -33,10 +34,10 @@ #include #include "at91-pcm.h" -#include "at91-i2s.h" +#include "at91-ssc.h" #if 0 -#define DBG(x...) printk(KERN_DEBUG "at91-i2s:" x) +#define DBG(x...) printk(KERN_DEBUG "at91-ssc:" x) #else #define DBG(x...) #endif @@ -92,33 +93,33 @@ static struct at91_ssc_mask ssc_rx_mask = { */ static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { {{ - .name = "SSC0/I2S PCM Stereo out", + .name = "SSC0 PCM out", .pdc = &pdc_tx_reg, .mask = &ssc_tx_mask, }, { - .name = "SSC0/I2S PCM Stereo in", + .name = "SSC0 PCM in", .pdc = &pdc_rx_reg, .mask = &ssc_rx_mask, }}, #if NUM_SSC_DEVICES == 3 {{ - .name = "SSC1/I2S PCM Stereo out", + .name = "SSC1 PCM out", .pdc = &pdc_tx_reg, .mask = &ssc_tx_mask, }, { - .name = "SSC1/I2S PCM Stereo in", + .name = "SSC1 PCM in", .pdc = &pdc_rx_reg, .mask = &ssc_rx_mask, }}, {{ - .name = "SSC2/I2S PCM Stereo out", + .name = "SSC2 PCM out", .pdc = &pdc_tx_reg, .mask = &ssc_tx_mask, }, { - .name = "SSC1/I2S PCM Stereo in", + .name = "SSC2 PCM in", .pdc = &pdc_rx_reg, .mask = &ssc_rx_mask, }}, @@ -171,13 +172,13 @@ static struct at91_ssc_info { #endif }; -static unsigned int at91_i2s_sysclk; +static unsigned int at91_ssc_sysclk; /* * SSC interrupt handler. Passes PDC interrupts to the DMA * interrupt handler in the PCM driver. */ -static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id) +static irqreturn_t at91_ssc_interrupt(int irq, void *dev_id) { struct at91_ssc_info *ssc_p = dev_id; struct at91_pcm_dma_params *dma_params; @@ -209,13 +210,13 @@ static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id) /* * Startup. Only that one substream allowed in each direction. */ -static int at91_i2s_startup(struct snd_pcm_substream *substream) +static int at91_ssc_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; int dir_mask; - DBG("i2s_startup: SSC_SR=0x%08lx\n", + DBG("ssc_startup: SSC_SR=0x%08lx\n", at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)); dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2; @@ -234,7 +235,7 @@ static int at91_i2s_startup(struct snd_pcm_substream *substream) * Shutdown. Clear DMA parameters and shutdown the SSC if there * are no other substreams open. */ -static void at91_i2s_shutdown(struct snd_pcm_substream *substream) +static void at91_ssc_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; @@ -281,7 +282,7 @@ static void at91_i2s_shutdown(struct snd_pcm_substream *substream) /* * Record the SSC system clock rate. */ -static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, +static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { /* @@ -291,7 +292,7 @@ static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, */ switch (clk_id) { case AT91_SYSCLK_MCK: - at91_i2s_sysclk = freq; + at91_ssc_sysclk = freq; break; default: return -EINVAL; @@ -303,14 +304,11 @@ static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, /* * Record the DAI format for use in hw_params(). */ -static int at91_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, +static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int fmt) { struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; - if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) - return -EINVAL; - ssc_p->daifmt = fmt; return 0; } @@ -318,7 +316,7 @@ static int at91_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, /* * Record SSC clock dividers for use in hw_params(). */ -static int at91_i2s_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai, +static int at91_ssc_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai, int div_id, int div) { struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; @@ -355,7 +353,7 @@ static int at91_i2s_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai, /* * Configure the SSC. */ -static int at91_i2s_hw_params(struct snd_pcm_substream *substream, +static int at91_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -390,21 +388,51 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, channels = params_channels(params); + /* + * Determine sample size in bits and the PDC increment. + */ + switch(params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + bits = 8; + dma_params->pdc_xfer_size = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + bits = 16; + dma_params->pdc_xfer_size = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bits = 24; + dma_params->pdc_xfer_size = 4; + break; + case SNDRV_PCM_FORMAT_S32_LE: + bits = 32; + dma_params->pdc_xfer_size = 4; + break; + default: + printk(KERN_WARNING "at91-ssc: unsupported PCM format"); + return -EINVAL; + } + /* * The SSC only supports up to 16-bit samples in I2S format, due - * to the size of the Frame Mode Register FSLEN field. Also, I2S - * implies signed data. + * to the size of the Frame Mode Register FSLEN field. */ - bits = 16; - dma_params->pdc_xfer_size = 2; + if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S + && bits > 16) { + printk(KERN_WARNING + "at91-ssc: sample size %d is too large for I2S\n", bits); + return -EINVAL; + } /* * Compute SSC register settings. */ - switch (ssc_p->daifmt) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (ssc_p->daifmt + & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { + + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: /* - * SSC provides BCLK and LRC clocks. + * I2S format, SSC provides BCLK and LRC clocks. * * The SSC transmit and receive clocks are generated from the * MCK divider, and the BCLK signal is output on the SSC TK line. @@ -441,10 +469,9 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, | (((bits - 1) << 0) & AT91_SSC_DATALEN); break; - case SND_SOC_DAIFMT_CBM_CFM: - + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: /* - * CODEC supplies BCLK and LRC clocks. + * I2S format, CODEC supplies BCLK and LRC clocks. * * The SSC transmit clock is obtained from the BCLK signal on * on the TK line, and the SSC receive clock is generated from the @@ -490,10 +517,51 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, | (((bits - 1) << 0) & AT91_SSC_DATALEN); break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: + /* + * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks. + * + * The SSC transmit and receive clocks are generated from the + * MCK divider, and the BCLK signal is output on the SSC TK line. + */ + rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START) + | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); + + rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS) + | (( 0 << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_LOOP) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + + tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START) + | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); + + tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( 0 << 23) & AT91_SSC_FSDEN) + | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS) + | (( 0 << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_DATDEF) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + + + + break; + + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: default: - printk(KERN_WARNING "at91-i2s: unsupported DAI format 0x%x.\n", + printk(KERN_WARNING "at91-ssc: unsupported DAI format 0x%x.\n", ssc_p->daifmt); return -EINVAL; break; @@ -518,9 +586,9 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0); at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0); - if ((ret = request_irq(ssc_p->ssc.pid, at91_i2s_interrupt, + if ((ret = request_irq(ssc_p->ssc.pid, at91_ssc_interrupt, 0, ssc_p->name, ssc_p)) < 0) { - printk(KERN_WARNING "at91-i2s: request_irq failure\n"); + printk(KERN_WARNING "at91-ssc: request_irq failure\n"); DBG("Stopping pid %d clock\n", ssc_p->ssc.pid); at91_sys_write(AT91_PMC_PCER, 1<ssc.pid); @@ -546,7 +614,7 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, } -static int at91_i2s_prepare(struct snd_pcm_substream *substream) +static int at91_ssc_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; @@ -566,7 +634,7 @@ static int at91_i2s_prepare(struct snd_pcm_substream *substream) #ifdef CONFIG_PM -static int at91_i2s_suspend(struct platform_device *pdev, +static int at91_ssc_suspend(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai) { struct at91_ssc_info *ssc_p; @@ -594,7 +662,7 @@ static int at91_i2s_suspend(struct platform_device *pdev, return 0; } -static int at91_i2s_resume(struct platform_device *pdev, +static int at91_ssc_resume(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai) { struct at91_ssc_info *ssc_p; @@ -620,102 +688,105 @@ static int at91_i2s_resume(struct platform_device *pdev, } #else -#define at91_i2s_suspend NULL -#define at91_i2s_resume NULL +#define at91_ssc_suspend NULL +#define at91_ssc_resume NULL #endif -#define AT91_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ +#define AT91_SSC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000) -struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { - { .name = "at91_ssc0/i2s", +#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_cpu_dai at91_ssc_dai[NUM_SSC_DEVICES] = { + { .name = "at91-ssc0", .id = 0, - .type = SND_SOC_DAI_I2S, - .suspend = at91_i2s_suspend, - .resume = at91_i2s_resume, + .type = SND_SOC_DAI_PCM, + .suspend = at91_ssc_suspend, + .resume = at91_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, - .rates = AT91_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .rates = AT91_SSC_RATES, + .formats = AT91_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, - .rates = AT91_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .rates = AT91_SSC_RATES, + .formats = AT91_SSC_FORMATS,}, .ops = { - .startup = at91_i2s_startup, - .shutdown = at91_i2s_shutdown, - .prepare = at91_i2s_prepare, - .hw_params = at91_i2s_hw_params,}, + .startup = at91_ssc_startup, + .shutdown = at91_ssc_shutdown, + .prepare = at91_ssc_prepare, + .hw_params = at91_ssc_hw_params,}, .dai_ops = { - .set_sysclk = at91_i2s_set_dai_sysclk, - .set_fmt = at91_i2s_set_dai_fmt, - .set_clkdiv = at91_i2s_set_dai_clkdiv,}, + .set_sysclk = at91_ssc_set_dai_sysclk, + .set_fmt = at91_ssc_set_dai_fmt, + .set_clkdiv = at91_ssc_set_dai_clkdiv,}, .private_data = &ssc_info[0].ssc, }, #if NUM_SSC_DEVICES == 3 - { .name = "at91_ssc1/i2s", + { .name = "at91-ssc1", .id = 1, - .type = SND_SOC_DAI_I2S, - .suspend = at91_i2s_suspend, - .resume = at91_i2s_resume, + .type = SND_SOC_DAI_PCM, + .suspend = at91_ssc_suspend, + .resume = at91_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, - .rates = AT91_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .rates = AT91_SSC_RATES, + .formats = AT91_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, - .rates = AT91_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .rates = AT91_SSC_RATES, + .formats = AT91_SSC_FORMATS,}, .ops = { - .startup = at91_i2s_startup, - .shutdown = at91_i2s_shutdown, - .prepare = at91_i2s_prepare, - .hw_params = at91_i2s_hw_params,}, + .startup = at91_ssc_startup, + .shutdown = at91_ssc_shutdown, + .prepare = at91_ssc_prepare, + .hw_params = at91_ssc_hw_params,}, .dai_ops = { - .set_sysclk = at91_i2s_set_dai_sysclk, - .set_fmt = at91_i2s_set_dai_fmt, - .set_clkdiv = at91_i2s_set_dai_clkdiv,}, + .set_sysclk = at91_ssc_set_dai_sysclk, + .set_fmt = at91_ssc_set_dai_fmt, + .set_clkdiv = at91_ssc_set_dai_clkdiv,}, .private_data = &ssc_info[1].ssc, }, - { .name = "at91_ssc2/i2s", + { .name = "at91-ssc2", .id = 2, - .type = SND_SOC_DAI_I2S, - .suspend = at91_i2s_suspend, - .resume = at91_i2s_resume, + .type = SND_SOC_DAI_PCM, + .suspend = at91_ssc_suspend, + .resume = at91_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, - .rates = AT91_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .rates = AT91_SSC_RATES, + .formats = AT91_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, - .rates = AT91_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .rates = AT91_SSC_RATES, + .formats = AT91_SSC_FORMATS,}, .ops = { - .startup = at91_i2s_startup, - .shutdown = at91_i2s_shutdown, - .prepare = at91_i2s_prepare, - .hw_params = at91_i2s_hw_params,}, + .startup = at91_ssc_startup, + .shutdown = at91_ssc_shutdown, + .prepare = at91_ssc_prepare, + .hw_params = at91_ssc_hw_params,}, .dai_ops = { - .set_sysclk = at91_i2s_set_dai_sysclk, - .set_fmt = at91_i2s_set_dai_fmt, - .set_clkdiv = at91_i2s_set_dai_clkdiv,}, + .set_sysclk = at91_ssc_set_dai_sysclk, + .set_fmt = at91_ssc_set_dai_fmt, + .set_clkdiv = at91_ssc_set_dai_clkdiv,}, .private_data = &ssc_info[2].ssc, }, #endif }; -EXPORT_SYMBOL_GPL(at91_i2s_dai); +EXPORT_SYMBOL_GPL(at91_ssc_dai); /* Module information */ MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com"); -MODULE_DESCRIPTION("AT91 I2S ASoC Interface"); +MODULE_DESCRIPTION("AT91 SSC ASoC Interface"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/at91/at91-i2s.h b/sound/soc/at91/at91-ssc.h similarity index 72% rename from sound/soc/at91/at91-i2s.h rename to sound/soc/at91/at91-ssc.h index f8a875ba0ccc..b188f973df9f 100644 --- a/sound/soc/at91/at91-i2s.h +++ b/sound/soc/at91/at91-ssc.h @@ -1,5 +1,5 @@ /* - * at91-i2s.h - ALSA I2S interface for the Atmel AT91 SoC + * at91-ssc.h - ALSA SSC interface for the Atmel AT91 SoC * * Author: Frank Mandarino * Endrelia Technologies Inc. @@ -10,18 +10,18 @@ * published by the Free Software Foundation. */ -#ifndef _AT91_I2S_H -#define _AT91_I2S_H +#ifndef _AT91_SSC_H +#define _AT91_SSC_H -/* I2S system clock ids */ +/* SSC system clock ids */ #define AT91_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */ -/* I2S divider ids */ +/* SSC divider ids */ #define AT91SSC_CMR_DIV 0 /* MCK divider for BCLK */ #define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */ #define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */ -extern struct snd_soc_cpu_dai at91_i2s_dai[]; +extern struct snd_soc_cpu_dai at91_ssc_dai[]; -#endif /* _AT91_I2S_H */ +#endif /* _AT91_SSC_H */ -- 2.20.1