int omap_mcbsp_dai_resume(struct snd_soc_dai *cpu_dai) { struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); if (cpu_dai->active) { printk("mcbsp_dai_resume() %d\n", mcbsp_data->bus_id); omap_mcbsp_config(mcbsp_data->bus_id, &mcbsp_data->regs); } return 0; }
int omap_mcbsp_dai_resume(struct snd_soc_dai *cpu_dai) { struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); if (cpu_dai->active) { omap_mcbsp_enable_fclk(mcbsp_data->bus_id); omap_mcbsp_config(mcbsp_data->bus_id, &mcbsp_data->regs); omap_mcbsp_dai_set_clks_src(mcbsp_data, mcbsp_data->clk_id); } return 0; }
/* * configure the McBSP registers * id : McBSP interface ID * interface_mode : Master/Slave * rp : McBSP recv parameters * tp : McBSP transmit parameters * param : McBSP SRG and FSG configuration */ void omap2_mcbsp_params_cfg(unsigned int id, int interface_mode, struct omap_mcbsp_cfg_param *rp, struct omap_mcbsp_cfg_param *tp, struct omap_mcbsp_srg_fsg_cfg *param) { if (rp) omap2_mcbsp_set_recv_param(&mcbsp_cfg, rp); if (tp) omap2_mcbsp_set_trans_param(&mcbsp_cfg, tp); if (param) omap2_mcbsp_set_srg_cfg_param(id, interface_mode, &mcbsp_cfg, param); omap_mcbsp_config(id, &mcbsp_cfg); return; }
int omap_mcbsp_dai_resume(struct snd_soc_dai *cpu_dai) { struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); if (cpu_dai->active) { printk("mcbsp_dai_resume() %d\n", mcbsp_data->bus_id); omap_mcbsp_enable_fclk(mcbsp_data->bus_id); omap_mcbsp_config(mcbsp_data->bus_id, &mcbsp_data->regs); omap_mcbsp_dai_set_clks_src(mcbsp_data, OMAP_MCBSP_SYSCLK_CLKS_FCLK); } return 0; }
static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: omap_mcbsp_config(mcbsp_data->bus_id, &mcbsp_data->regs); /* fall through */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (cpu_dai->active) omap_mcbsp_dai_set_clks_src(mcbsp_data, mcbsp_data->clk_id); omap_mcbsp_start(mcbsp_data->bus_id, play, !play); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) mcbsp_data->tx_active = 1; else mcbsp_data->rx_active = 1; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: if (cpu_dai->active) omap_mcbsp_dai_set_clks_src(mcbsp_data, OMAP_MCBSP_SYSCLK_CLKS_FCLK); omap_mcbsp_stop(mcbsp_data->bus_id, play, !play); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) mcbsp_data->tx_active = 0; else mcbsp_data->rx_active = 0; break; default: err = -EINVAL; } return err; }
static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); int bus_id = mcbsp_data->bus_id, id = cpu_dai->id; int xfer_size; xfer_size = omap_mcbsp_dai_dma_params[id][substream->stream].xfer_size; if (!(mcbsp_data->tx_active || mcbsp_data->rx_active)) { omap_mcbsp_config(bus_id, &mcbsp_data->regs); if ((bus_id == 1) && (xfer_size > 0)) omap_mcbsp_set_tx_threshold(bus_id, xfer_size); } return 0; }
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; struct omap_pcm_dma_data *dma_data; int dma, bus_id = mcbsp_data->bus_id; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; int pkt_size = 0; unsigned long port; unsigned int format, div, framesize, master; dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; dma = omap_mcbsp_dma_ch_params(bus_id, substream->stream); port = omap_mcbsp_dma_reg_params(bus_id, substream->stream); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; wlen = 16; break; case SNDRV_PCM_FORMAT_S32_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; wlen = 32; break; default: return -EINVAL; } if (cpu_is_omap34xx() || cpu_is_omap44xx()) { dma_data->set_threshold = omap_mcbsp_set_threshold; /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ if (omap_mcbsp_get_dma_op_mode(bus_id) == MCBSP_DMA_MODE_THRESHOLD) { int period_words, max_thrsh; period_words = params_period_bytes(params) / (wlen / 8); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) max_thrsh = omap_mcbsp_get_max_tx_threshold( mcbsp_data->bus_id); else max_thrsh = omap_mcbsp_get_max_rx_threshold( mcbsp_data->bus_id); /* * If the period contains less or equal number of words, * we are using the original threshold mode setup: * McBSP threshold = sDMA frame size = period_size * Otherwise we switch to sDMA packet mode: * McBSP threshold = sDMA packet size * sDMA frame size = period size */ if (period_words > max_thrsh) { int divider = 0; /* * Look for the biggest threshold value, which * divides the period size evenly. */ divider = period_words / max_thrsh; if (period_words % max_thrsh) divider++; while (period_words % divider && divider < period_words) divider++; if (divider == period_words) return -EINVAL; pkt_size = period_words / divider; sync_mode = OMAP_DMA_SYNC_PACKET; } else { sync_mode = OMAP_DMA_SYNC_FRAME; } } } dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; dma_data->dma_req = dma; dma_data->port_addr = port; dma_data->sync_mode = sync_mode; dma_data->packet_size = pkt_size; snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); if (mcbsp_data->configured) { /* McBSP already configured by another stream */ return 0; } regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7)); regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7)); regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; wpf = channels = params_channels(params); if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || format == SND_SOC_DAIFMT_LEFT_J)) { /* Use dual-phase frames */ regs->rcr2 |= RPHASE; regs->xcr2 |= XPHASE; /* Set 1 word per (McBSP) frame for phase1 and phase2 */ wpf--; regs->rcr2 |= RFRLEN2(wpf - 1); regs->xcr2 |= XFRLEN2(wpf - 1); } regs->rcr1 |= RFRLEN1(wpf - 1); regs->xcr1 |= XFRLEN1(wpf - 1); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); break; case SNDRV_PCM_FORMAT_S32_LE: /* Set word lengths */ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); break; default: /* Unsupported PCM format */ return -EINVAL; } /* In McBSP master modes, FRAME (i.e. sample rate) is generated * by _counting_ BCLKs. Calculate frame size in BCLKs */ master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK; if (master == SND_SOC_DAIFMT_CBS_CFS) { div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1; framesize = (mcbsp_data->in_freq / div) / params_rate(params); if (framesize < wlen * channels) { printk(KERN_ERR "%s: not enough bandwidth for desired rate and " "channels\n", __func__); return -EINVAL; } } else framesize = wlen * channels; /* Set FS period and length in terms of bit clock periods */ regs->srgr2 &= ~FPER(0xfff); regs->srgr1 &= ~FWID(0xff); switch (format) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_LEFT_J: regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID((framesize >> 1) - 1); break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID(0); break; } omap_mcbsp_config(bus_id, &mcbsp_data->regs); mcbsp_data->wlen = wlen; mcbsp_data->configured = 1; return 0; }
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; struct omap_pcm_dma_data *dma_data; int wlen, channels, wpf; int pkt_size = 0; unsigned int format, div, framesize, master; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); channels = params_channels(params); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: wlen = 16; break; case SNDRV_PCM_FORMAT_S32_LE: wlen = 32; break; default: return -EINVAL; } if (mcbsp->pdata->buffer_size) { dma_data->set_threshold = omap_mcbsp_set_threshold; if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { int period_words, max_thrsh; int divider = 0; period_words = params_period_bytes(params) / (wlen / 8); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) max_thrsh = mcbsp->max_tx_thres; else max_thrsh = mcbsp->max_rx_thres; /* * Use sDMA packet mode if McBSP is in threshold mode: * If period words less than the FIFO size the packet * size is set to the number of period words, otherwise * Look for the biggest threshold value which divides * the period size evenly. */ divider = period_words / max_thrsh; if (period_words % max_thrsh) divider++; while (period_words % divider && divider < period_words) divider++; if (divider == period_words) return -EINVAL; pkt_size = period_words / divider; } else if (channels > 1) { /* Use packet mode for non mono streams */ pkt_size = channels; } } dma_data->packet_size = pkt_size; if (mcbsp->configured) { /* McBSP already configured by another stream */ return 0; } regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7)); regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7)); regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK; wpf = channels; if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || format == SND_SOC_DAIFMT_LEFT_J)) { /* Use dual-phase frames */ regs->rcr2 |= RPHASE; regs->xcr2 |= XPHASE; /* Set 1 word per (McBSP) frame for phase1 and phase2 */ wpf--; regs->rcr2 |= RFRLEN2(wpf - 1); regs->xcr2 |= XFRLEN2(wpf - 1); } regs->rcr1 |= RFRLEN1(wpf - 1); regs->xcr1 |= XFRLEN1(wpf - 1); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); break; case SNDRV_PCM_FORMAT_S32_LE: /* Set word lengths */ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); break; default: /* Unsupported PCM format */ return -EINVAL; } /* In McBSP master modes, FRAME (i.e. sample rate) is generated * by _counting_ BCLKs. Calculate frame size in BCLKs */ master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK; if (master == SND_SOC_DAIFMT_CBS_CFS) { div = mcbsp->clk_div ? mcbsp->clk_div : 1; framesize = (mcbsp->in_freq / div) / params_rate(params); if (framesize < wlen * channels) { printk(KERN_ERR "%s: not enough bandwidth for desired rate and " "channels\n", __func__); return -EINVAL; } } else framesize = wlen * channels; /* Set FS period and length in terms of bit clock periods */ regs->srgr2 &= ~FPER(0xfff); regs->srgr1 &= ~FWID(0xff); switch (format) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_LEFT_J: regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID((framesize >> 1) - 1); break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID(0); break; } omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs); mcbsp->wlen = wlen; mcbsp->configured = 1; return 0; }
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; int uninitialized_var(wlen); int uninitialized_var(channels); int uninitialized_var(wpf); unsigned long uninitialized_var(port); unsigned int uninitialized_var(format); int xfer_size = 0; if (cpu_class_is_omap1()) { dma = omap1_dma_reqs[bus_id][substream->stream]; port = omap1_mcbsp_port[bus_id][substream->stream]; } else if (cpu_is_omap2420()) { dma = omap24xx_dma_reqs[bus_id][substream->stream]; port = omap2420_mcbsp_port[bus_id][substream->stream]; } else if (cpu_is_omap2430()) { dma = omap24xx_dma_reqs[bus_id][substream->stream]; port = omap2430_mcbsp_port[bus_id][substream->stream]; } else if (cpu_is_omap343x()) { dma = omap24xx_dma_reqs[bus_id][substream->stream]; port = omap34xx_mcbsp_port[bus_id][substream->stream]; xfer_size = omap34xx_mcbsp_thresholds[bus_id] [substream->stream]; /* reset the xfer_size to the integral multiple of the buffer size. This is for DMA packet mode transfer */ if (xfer_size) { int buffer_size = params_buffer_size(params); if (xfer_size > buffer_size) { printk(KERN_DEBUG "buffer_size is %d \n", buffer_size); xfer_size = 0; } else if (params_channels(params) == 1) { /* Mono needs 16 bits DMA, no FIFO */ xfer_size = 1; } else { int temp = buffer_size / xfer_size; while (buffer_size % xfer_size) { temp++; xfer_size = buffer_size / (temp); } } } } else { return -ENODEV; } omap_mcbsp_dai_dma_params[id][substream->stream].name = substream->stream ? "Audio Capture" : "Audio Playback"; omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; omap_mcbsp_dai_dma_params[id][substream->stream].xfer_size = xfer_size; cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; if (mcbsp_data->configured) { /* McBSP already configured by another stream */ return 0; } format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; wpf = channels = params_channels(params); if (format == SND_SOC_DAIFMT_SPDIF) { regs->xcr2 &= ~(XPHASE); regs->rcr2 &= ~(RPHASE); /* Don't care about channels number and frame length 2. Set 4 frames (frame length 1) */ regs->xcr1 |= XFRLEN1(4 - 1); regs->rcr1 |= RFRLEN1(1 - 1); } else { switch (channels) { case 2: if (format == SND_SOC_DAIFMT_I2S) { /* Use dual-phase frames */ regs->rcr2 |= RPHASE; regs->xcr2 |= XPHASE; /* Set 1 word per (McBSP) frame for phase1 and phase2 */ wpf--; regs->rcr2 |= RFRLEN2(wpf - 1); regs->xcr2 |= XFRLEN2(wpf - 1); } else if (format == SND_SOC_DAIFMT_I2S_1PHASE || format == SND_SOC_DAIFMT_DSP_A_1PHASE) { printk(KERN_DEBUG "Configure McBSP for 1 phase\n"); regs->xcr2 &= ~(XPHASE); regs->rcr2 &= ~(RPHASE); wpf--; } case 1: case 4: /* Set word per (McBSP) frame for phase1 */ regs->rcr1 |= RFRLEN1(wpf - 1); regs->xcr1 |= XFRLEN1(wpf - 1); break; default: /* Unsupported number of channels */ return -EINVAL; } } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */ wlen = 16; if (format == SND_SOC_DAIFMT_SPDIF || (channels != 1 && (format == SND_SOC_DAIFMT_I2S_1PHASE || format == SND_SOC_DAIFMT_DSP_A_1PHASE))) { regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); omap_mcbsp_dai_dma_params[id] [SNDRV_PCM_STREAM_PLAYBACK].dma_word_size = 32; omap_mcbsp_dai_dma_params[id] [SNDRV_PCM_STREAM_CAPTURE].dma_word_size = 32; } else { regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); omap_mcbsp_dai_dma_params[id] [substream->stream].dma_word_size = 16; } break; case SNDRV_PCM_FORMAT_S8: /* Set word lengths */ wlen = 8; if (format == SND_SOC_DAIFMT_SPDIF || (channels != 1 && (format == SND_SOC_DAIFMT_I2S_1PHASE || format == SND_SOC_DAIFMT_DSP_A_1PHASE))) { regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); omap_mcbsp_dai_dma_params[id] [SNDRV_PCM_STREAM_PLAYBACK].dma_word_size = 16; omap_mcbsp_dai_dma_params[id] [SNDRV_PCM_STREAM_CAPTURE].dma_word_size = 16; } else { regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_8); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_8); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_8); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_8); omap_mcbsp_dai_dma_params[id] [substream->stream].dma_word_size = 8; } break; default: /* Unsupported PCM format */ return -EINVAL; } /* Set FS period and length in terms of bit clock periods */ switch (format) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S_1PHASE: regs->srgr2 |= FPER(wlen * channels - 1); regs->srgr1 |= FWID(wlen - 1); break; case SND_SOC_DAIFMT_DSP_A_1PHASE: case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: regs->srgr2 |= FPER(wlen * channels - 1); regs->srgr1 |= FWID(0); break; case SND_SOC_DAIFMT_SPDIF: regs->srgr2 |= FPER(4 * 32 - 1); regs->srgr1 |= FWID(0); break; } regs->xccr |= XDMAEN; regs->wken = XRDYEN; regs->rccr |= RDMAEN; omap_mcbsp_config(bus_id, &mcbsp_data->regs); if ((bus_id == 1) && (xfer_size != 0)) { printk(KERN_DEBUG "Configure McBSP TX FIFO threshold to %d\n", xfer_size); omap_mcbsp_set_tx_threshold(bus_id, xfer_size); } /* We want to be able to change stuff (like channels number) dynamically */ //mcbsp_data->configured = 1; return 0; }