diff options
Diffstat (limited to 'sound/soc/tegra/tegra_offload.c')
-rw-r--r-- | sound/soc/tegra/tegra_offload.c | 212 |
1 files changed, 143 insertions, 69 deletions
diff --git a/sound/soc/tegra/tegra_offload.c b/sound/soc/tegra/tegra_offload.c index bdfe94e30add..660bb4739554 100644 --- a/sound/soc/tegra/tegra_offload.c +++ b/sound/soc/tegra/tegra_offload.c @@ -38,6 +38,7 @@ enum { PCM_OFFLOAD_DAI, COMPR_OFFLOAD_DAI, + PCM_CAPTURE_OFFLOAD_DAI, MAX_OFFLOAD_DAI }; @@ -61,7 +62,7 @@ static DEFINE_MUTEX(tegra_offload_lock); static int codec, spk; -static const struct snd_pcm_hardware tegra_offload_pcm_hardware = { +static const struct snd_pcm_hardware tegra_offload_pcm_hardware_playback = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | @@ -78,6 +79,23 @@ static const struct snd_pcm_hardware tegra_offload_pcm_hardware = { .fifo_size = 4, }; +static const struct snd_pcm_hardware tegra_offload_pcm_hardware_capture = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 128, + .period_bytes_max = PAGE_SIZE * 2, + .periods_min = 1, + .periods_max = 8, + .buffer_bytes_max = PAGE_SIZE * 8, + .fifo_size = 4, +}; + int tegra_register_offload_ops(struct tegra_offload_ops *ops) { mutex_lock(&tegra_offload_lock); @@ -320,7 +338,7 @@ static int tegra_offload_compr_set_params(struct snd_compr_stream *stream, } static int tegra_offload_compr_get_params(struct snd_compr_stream *stream, - struct snd_codec *codec) + struct snd_codec *codec) { struct device *dev = stream->device->dev; struct tegra_offload_compr_data *data = stream->runtime->private_data; @@ -343,7 +361,7 @@ static int tegra_offload_compr_trigger(struct snd_compr_stream *stream, int cmd) } static int tegra_offload_compr_pointer(struct snd_compr_stream *stream, - struct snd_compr_tstamp *tstamp) + struct snd_compr_tstamp *tstamp) { struct device *dev = stream->device->dev; struct tegra_offload_compr_data *data = stream->runtime->private_data; @@ -354,7 +372,7 @@ static int tegra_offload_compr_pointer(struct snd_compr_stream *stream, } static int tegra_offload_compr_copy(struct snd_compr_stream *stream, - char __user *buf, size_t count) + char __user *buf, size_t count) { struct device *dev = stream->device->dev; struct tegra_offload_compr_data *data = stream->runtime->private_data; @@ -365,7 +383,7 @@ static int tegra_offload_compr_copy(struct snd_compr_stream *stream, } static int tegra_offload_compr_get_caps(struct snd_compr_stream *stream, - struct snd_compr_caps *caps) + struct snd_compr_caps *caps) { struct device *dev = stream->device->dev; struct tegra_offload_compr_data *data = stream->runtime->private_data; @@ -383,7 +401,7 @@ static int tegra_offload_compr_get_caps(struct snd_compr_stream *stream, } static int tegra_offload_compr_codec_caps(struct snd_compr_stream *stream, - struct snd_compr_codec_caps *codec_caps) + struct snd_compr_codec_caps *codec_caps) { struct device *dev = stream->device->dev; struct tegra_offload_compr_data *data = stream->runtime->private_data; @@ -403,7 +421,6 @@ static int tegra_offload_compr_codec_caps(struct snd_compr_stream *stream, } static struct snd_compr_ops tegra_compr_ops = { - .open = tegra_offload_compr_open, .free = tegra_offload_compr_free, .set_params = tegra_offload_compr_set_params, @@ -440,28 +457,45 @@ static int tegra_offload_pcm_open(struct snd_pcm_substream *substream) data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) { - dev_err(dev, "Failed to allocate tegra_offload_pcm_data."); + dev_vdbg(dev, + "Failed to allocate tegra_offload_pcm_data\n"); return -ENOMEM; } - - /* Set HW params now that initialization is complete */ - snd_soc_set_runtime_hwparams(substream, &tegra_offload_pcm_hardware); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + /* Set HW params now that initialization is complete */ + snd_soc_set_runtime_hwparams(substream, + &tegra_offload_pcm_hardware_playback); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + snd_soc_set_runtime_hwparams(substream, + &tegra_offload_pcm_hardware_capture); /* Ensure period size is multiple of 4 */ ret = snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x4); + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x4); if (ret) { dev_err(dev, "failed to set constraint %d\n", ret); return ret; } - data->ops = &offload_ops.pcm_ops; - - ret = data->ops->stream_open(&data->stream_id); - if (ret < 0) { - dev_err(dev, "Failed to open offload stream. err %d", ret); - return ret; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + data->ops = &offload_ops.pcm_ops; + + ret = data->ops->stream_open(&data->stream_id, "pcm"); + if (ret < 0) { + dev_err(dev, + "Failed to open offload stream err %d", ret); + return ret; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + data->ops = &offload_ops.loopback_ops; + + ret = data->ops->stream_open(&data->stream_id, "loopback"); + if (ret < 0) { + dev_err(dev, + "Failed to open offload stream err %d", ret); + return ret; + } } - offload_ops.device_ops.set_hw_rate(48000); + offload_ops.device_ops.set_hw_rate(48000); substream->runtime->private_data = data; return 0; } @@ -480,57 +514,69 @@ static int tegra_offload_pcm_close(struct snd_pcm_substream *substream) } static int tegra_offload_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct device *dev = rtd->platform->dev; - struct tegra_offload_pcm_data *data = substream->runtime->private_data; - struct snd_dma_buffer *buf = &substream->dma_buffer; - struct tegra_pcm_dma_params *dmap; + struct tegra_offload_pcm_data *data = + substream->runtime->private_data; struct tegra_offload_pcm_params offl_params; + struct snd_dma_buffer *buf = &substream->dma_buffer; int ret = 0; dev_vdbg(dev, "%s", __func__); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct tegra_pcm_dma_params *dmap; + + dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dmap) { + struct snd_soc_dpcm *dpcm; + + if + (list_empty(&rtd->dpcm[substream->stream].be_clients)) { + dev_err(dev, "No backend DAIs enabled for %s\n", + rtd->dai_link->name); + return -EINVAL; + } - dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - if (!dmap) { - struct snd_soc_dpcm *dpcm; - - if (list_empty(&rtd->dpcm[substream->stream].be_clients)) { - dev_err(dev, "No backend DAIs enabled for %s\n", - rtd->dai_link->name); - return -EINVAL; - } - - list_for_each_entry(dpcm, - &rtd->dpcm[substream->stream].be_clients, list_be) { - struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_pcm_substream *be_substream = - snd_soc_dpcm_get_substream(be, + list_for_each_entry(dpcm, + &rtd->dpcm[substream->stream].be_clients, + list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, substream->stream); - struct snd_soc_dai_link *dai_link = be->dai_link; + struct snd_soc_dai_link *dai_link = + be->dai_link; - dmap = snd_soc_dai_get_dma_data(be->cpu_dai, - be_substream); - - if (spk && strstr(dai_link->name, "speaker")) { - dmap = snd_soc_dai_get_dma_data(be->cpu_dai, - be_substream); - break; - } - if (codec && strstr(dai_link->name, "codec")) { dmap = snd_soc_dai_get_dma_data(be->cpu_dai, - be_substream); - break; + be_substream); + + if (spk && strstr(dai_link->name, "speaker")) { + dmap = snd_soc_dai_get_dma_data( + be->cpu_dai, + be_substream); + break; + } + if (codec && strstr(dai_link->name, "codec")) { + dmap = snd_soc_dai_get_dma_data( + be->cpu_dai, + be_substream); + break; + } + /* TODO : Multiple BE to + * single FE not yet supported */ } - /* TODO : Multiple BE to single FE not yet supported */ } + if (!dmap) { + dev_err(dev, "Failed to get DMA params."); + return -ENODEV; + } + offl_params.dma_params.addr = dmap->addr; + offl_params.dma_params.width = DMA_SLAVE_BUSWIDTH_4_BYTES; + offl_params.dma_params.req_sel = dmap->req_sel; + offl_params.dma_params.max_burst = 4; } - if (!dmap) { - dev_err(dev, "Failed to get DMA params."); - return -ENODEV; - } - offl_params.bits_per_sample = snd_pcm_format_width(params_format(params)); offl_params.rate = params_rate(params); @@ -539,19 +585,17 @@ static int tegra_offload_pcm_hw_params(struct snd_pcm_substream *substream, offl_params.period_size = params_period_size(params) * ((offl_params.bits_per_sample >> 3) * offl_params.channels); - offl_params.dma_params.addr = dmap->addr; - offl_params.dma_params.width = DMA_SLAVE_BUSWIDTH_4_BYTES; - offl_params.dma_params.req_sel = dmap->req_sel; - offl_params.dma_params.max_burst = 4; offl_params.source_buf.virt_addr = buf->area; offl_params.source_buf.phys_addr = buf->addr; offl_params.source_buf.bytes = buf->bytes; - offl_params.period_elapsed_cb = tegra_offload_pcm_period_elapsed; + offl_params.period_elapsed_cb = + tegra_offload_pcm_period_elapsed; offl_params.period_elapsed_args = (void *)substream; - ret = data->ops->set_stream_params(data->stream_id, &offl_params); + ret = data->ops->set_stream_params(data->stream_id, + &offl_params); if (ret < 0) { dev_err(dev, "Failed to set avp params. ret %d", ret); return ret; @@ -582,10 +626,9 @@ static int tegra_offload_pcm_trigger(struct snd_pcm_substream *substream, data->ops->set_stream_state(data->stream_id, cmd); if ((cmd == SNDRV_PCM_TRIGGER_STOP) || - (cmd == SNDRV_PCM_TRIGGER_SUSPEND) || - (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)) + (cmd == SNDRV_PCM_TRIGGER_SUSPEND) || + (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)) data->appl_ptr = 0; - return 0; } @@ -607,6 +650,7 @@ static int tegra_offload_pcm_ack(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct tegra_offload_pcm_data *data = runtime->private_data; + int data_size = runtime->control->appl_ptr - data->appl_ptr; if (data_size < 0) @@ -768,17 +812,40 @@ static int tegra_offload_dma_allocate(struct snd_soc_pcm_runtime *rtd, static int tegra_offload_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct device *dev = rtd->platform->dev; + struct snd_pcm *pcm = rtd->pcm; + int ret = 0; - dev_vdbg(dev, "%s", __func__); - return tegra_offload_dma_allocate(rtd , SNDRV_PCM_STREAM_PLAYBACK, - tegra_offload_pcm_hardware.buffer_bytes_max); + dev_vdbg(dev, "%s", __func__); + dev_err(pcm->card->dev, "Allocating for stream playback\n"); + ret = tegra_offload_dma_allocate(rtd , SNDRV_PCM_STREAM_PLAYBACK, + tegra_offload_pcm_hardware_playback.buffer_bytes_max); + if (ret < 0) { + dev_err(pcm->card->dev, "failing in pcm_new:1 goto err"); + goto err; + } + dev_err(pcm->card->dev, "Allocating for stream capture\n"); + ret = tegra_offload_dma_allocate(rtd , SNDRV_PCM_STREAM_CAPTURE, + tegra_offload_pcm_hardware_capture.buffer_bytes_max); + if (ret < 0) { + dev_err(pcm->card->dev, "failing in pcm_new:1 goto err"); + goto err; + } +err: + return ret; } static void tegra_offload_pcm_free(struct snd_pcm *pcm) { - tegra_offload_dma_free(pcm, SNDRV_PCM_STREAM_PLAYBACK); pr_debug("%s", __func__); + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + dev_err(pcm->card->dev, "PCM free for stream playback\n"); + tegra_offload_dma_free(pcm, SNDRV_PCM_STREAM_PLAYBACK); + } + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + dev_err(pcm->card->dev, "PCM free for stream capture\n"); + tegra_offload_dma_free(pcm, SNDRV_PCM_STREAM_CAPTURE); + } } static int tegra_offload_pcm_probe(struct snd_soc_platform *platform) @@ -827,6 +894,13 @@ static struct snd_soc_dai_driver tegra_offload_dai[] = { .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + .capture = { + .stream_name = "offload-pcm-capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, }, [COMPR_OFFLOAD_DAI] = { .name = "tegra-offload-compr", |