📄 at91-ssc.c
字号:
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. */ 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 & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: /* * 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. */ rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD) | (( 1 << 16) & AT91_SSC_STTDLY) | (( AT91_SSC_START_FALLING_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_NEGATIVE ) & AT91_SSC_FSOS) | (((bits - 1) << 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_FALLING_RF ) & AT91_SSC_START) | (( AT91_SSC_CKI_FALLING ) & 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_NEGATIVE ) & AT91_SSC_FSOS) | (((bits - 1) << 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_I2S | SND_SOC_DAIFMT_CBM_CFM: /* * 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 * transmit clock. * * For single channel data, one sample is transferred on the falling * edge of the LRC clock. For two channel data, one sample is * transferred on both edges of the LRC clock. */ start_event = channels == 1 ? AT91_SSC_START_FALLING_RF : AT91_SSC_START_EDGE_RF; rcmr = (( 0 << 24) & AT91_SSC_PERIOD) | (( 1 << 16) & AT91_SSC_STTDLY) | (( start_event ) & AT91_SSC_START) | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) | (( 0 << 16) & AT91_SSC_FSLEN) | (( 0 << 8) & AT91_SSC_DATNB) | (( 1 << 7) & AT91_SSC_MSBF) | (( 0 << 5) & AT91_SSC_LOOP) | (((bits - 1) << 0) & AT91_SSC_DATALEN); tcmr = (( 0 << 24) & AT91_SSC_PERIOD) | (( 1 << 16) & AT91_SSC_STTDLY) | (( start_event ) & AT91_SSC_START) | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) | (( AT91_SSC_CKS_PIN ) & AT91_SSC_CKS); tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | (( 0 << 23) & AT91_SSC_FSDEN) | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) | (( 0 << 16) & AT91_SSC_FSLEN) | (( 0 << 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_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-ssc: unsupported DAI format 0x%x.\n", ssc_p->daifmt); return -EINVAL; break; } DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr); if (!ssc_p->initialized) { /* Enable PMC peripheral clock for this SSC */ DBG("Starting pid %d clock\n", ssc_p->ssc.pid); at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid); /* Reset the SSC and its PDC registers */ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST); at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RPR, 0); at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RCR, 0); at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNPR, 0); at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNCR, 0); at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TPR, 0); at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TCR, 0); 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_ssc_interrupt, 0, ssc_p->name, ssc_p)) < 0) { 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_p->ssc.pid); return ret; } ssc_p->initialized = 1; } /* set SSC clock mode register */ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div); /* set receive clock mode and format */ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr); /* set transmit clock mode and format */ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr); DBG("hw_params: SSC initialized\n"); return 0;}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]; struct at91_pcm_dma_params *dma_params; int dir; dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; dma_params = ssc_p->dma_params[dir]; at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR, dma_params->mask->ssc_enable); DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit", at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR)); return 0;}#ifdef CONFIG_PMstatic int at91_ssc_suspend(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai){ struct at91_ssc_info *ssc_p; if(!cpu_dai->active) return 0; ssc_p = &ssc_info[cpu_dai->id]; /* Save the status register before disabling transmit and receive. */ ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS); /* Save the current interrupt mask, then disable unmasked interrupts. */ ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr); ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR); ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR); ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR); ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR); return 0;}static int at91_ssc_resume(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai){ struct at91_ssc_info *ssc_p; if(!cpu_dai->active) return 0; ssc_p = &ssc_info[cpu_dai->id]; at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); return 0;}#else#define at91_ssc_suspend NULL#define at91_ssc_resume NULL#endif#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)#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_PCM, .suspend = at91_ssc_suspend, .resume = at91_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, .rates = AT91_SSC_RATES, .formats = AT91_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = AT91_SSC_RATES, .formats = AT91_SSC_FORMATS,}, .ops = { .startup = at91_ssc_startup, .shutdown = at91_ssc_shutdown, .prepare = at91_ssc_prepare, .hw_params = at91_ssc_hw_params,}, .dai_ops = { .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", .id = 1, .type = SND_SOC_DAI_PCM, .suspend = at91_ssc_suspend, .resume = at91_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, .rates = AT91_SSC_RATES, .formats = AT91_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = AT91_SSC_RATES, .formats = AT91_SSC_FORMATS,}, .ops = { .startup = at91_ssc_startup, .shutdown = at91_ssc_shutdown, .prepare = at91_ssc_prepare, .hw_params = at91_ssc_hw_params,}, .dai_ops = { .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", .id = 2, .type = SND_SOC_DAI_PCM, .suspend = at91_ssc_suspend, .resume = at91_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, .rates = AT91_SSC_RATES, .formats = AT91_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = AT91_SSC_RATES, .formats = AT91_SSC_FORMATS,}, .ops = { .startup = at91_ssc_startup, .shutdown = at91_ssc_shutdown, .prepare = at91_ssc_prepare, .hw_params = at91_ssc_hw_params,}, .dai_ops = { .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_ssc_dai);/* Module information */MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");MODULE_DESCRIPTION("AT91 SSC ASoC Interface");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -