summaryrefslogtreecommitdiff
path: root/sound/soc/tegra/tegra_offload.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra_offload.c')
-rw-r--r--sound/soc/tegra/tegra_offload.c212
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",