summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Baluta <daniel.baluta@nxp.com>2018-10-04 14:46:32 +0300
committerDong Aisheng <aisheng.dong@nxp.com>2019-11-25 15:52:02 +0800
commit09194b889d50146c20eafa3a661c5911d4ee2cbf (patch)
treed8390154a5da5f3fd367bf6d5652e0ec07386e54
parent78520df6826e552bbc2c23e76e0f11c32fa559f1 (diff)
MLK-18497-11: ASoC: fsl: compress: Introduce compress implemenation
Reviewed-by: Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com> Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
-rw-r--r--sound/soc/fsl/Makefile3
-rw-r--r--sound/soc/fsl/fsl_dsp.c13
-rw-r--r--sound/soc/fsl/fsl_dsp.h10
-rw-r--r--sound/soc/fsl/fsl_dsp_platform.h21
-rw-r--r--sound/soc/fsl/fsl_dsp_platform_compress.c462
5 files changed, 508 insertions, 1 deletions
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 58d4c7c78430..36ff6b3a534b 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -16,7 +16,8 @@ snd-soc-fsl-audmix-objs := fsl_audmix.o
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-dsp-objs := fsl_dsp.o fsl_dsp_proxy.o fsl_dsp_pool.o \
- fsl_dsp_library_load.o fsl_dsp_xaf_api.o fsl_dsp_cpu.o
+ fsl_dsp_library_load.o fsl_dsp_xaf_api.o fsl_dsp_cpu.o \
+ fsl_dsp_platform_compress.o
snd-soc-fsl-sai-objs := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
diff --git a/sound/soc/fsl/fsl_dsp.c b/sound/soc/fsl/fsl_dsp.c
index 3dddaf9e6f53..ef30b5a30bd2 100644
--- a/sound/soc/fsl/fsl_dsp.c
+++ b/sound/soc/fsl/fsl_dsp.c
@@ -765,6 +765,13 @@ static const struct file_operations dsp_fops = {
.release = fsl_dsp_close,
};
+extern struct snd_compr_ops dsp_platform_compr_ops;
+
+static const struct snd_soc_component_driver dsp_soc_platform_drv = {
+ .name = FSL_DSP_COMP_NAME,
+ .compr_ops = &dsp_platform_compr_ops,
+};
+
static int fsl_dsp_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -927,6 +934,12 @@ static int fsl_dsp_probe(struct platform_device *pdev)
/* ...initialize mutex */
mutex_init(&dsp_priv->dsp_mutex);
+ ret = devm_snd_soc_register_component(&pdev->dev, &dsp_soc_platform_drv, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "registering soc platform failed\n");
+ return ret;
+ }
+
return 0;
}
diff --git a/sound/soc/fsl/fsl_dsp.h b/sound/soc/fsl/fsl_dsp.h
index eaabe17e0f54..ad6f7a9e8be1 100644
--- a/sound/soc/fsl/fsl_dsp.h
+++ b/sound/soc/fsl/fsl_dsp.h
@@ -10,8 +10,11 @@
#include <uapi/linux/mxc_dsp.h>
#include <linux/firmware/imx/ipc.h>
#include "fsl_dsp_proxy.h"
+#include "fsl_dsp_platform.h"
+#define FSL_DSP_COMP_NAME "fsl-dsp-component"
+
typedef void (*memcpy_func) (void *dest, const void *src, size_t n);
typedef void (*memset_func) (void *s, int c, size_t n);
@@ -41,8 +44,13 @@ struct xf_client {
void *global;
struct xf_message m;
+ struct snd_compr_stream *cstream;
+
struct work_struct work;
struct completion compr_complete;
+
+ int input_bytes;
+ int consume_bytes;
};
union xf_client_link {
@@ -88,6 +96,8 @@ struct fsl_dsp {
/* ...mutex lock */
struct mutex dsp_mutex;
+ struct dsp_data dsp_data;
+
/* ...global clients pool (item[0] serves as list terminator) */
union xf_client_link xf_client_map[XF_CFG_MAX_IPC_CLIENTS];
};
diff --git a/sound/soc/fsl/fsl_dsp_platform.h b/sound/soc/fsl/fsl_dsp_platform.h
new file mode 100644
index 000000000000..15d085c9f23d
--- /dev/null
+++ b/sound/soc/fsl/fsl_dsp_platform.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED.
+ * Copyright 2018 NXP
+ */
+
+#ifndef _FSL_DSP_PLATFORM_H
+#define _FSL_DSP_PLATFORM_H
+
+#include "fsl_dsp_xaf_api.h"
+
+struct dsp_data {
+ struct xf_client *client;
+ struct xaf_pipeline *p_pipe;
+ struct xaf_pipeline pipeline;
+ struct xaf_comp component[2];
+ int codec_type;
+ int status;
+};
+
+#endif /*_FSL_DSP_PLATFORM_H*/
diff --git a/sound/soc/fsl/fsl_dsp_platform_compress.c b/sound/soc/fsl/fsl_dsp_platform_compress.c
new file mode 100644
index 000000000000..004a539d2b8a
--- /dev/null
+++ b/sound/soc/fsl/fsl_dsp_platform_compress.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// DSP driver compress implementation
+//
+// Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED.
+// Copyright 2018 NXP
+
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/core.h>
+#include <sound/compress_driver.h>
+
+#include "fsl_dsp.h"
+#include "fsl_dsp_platform.h"
+#include "fsl_dsp_xaf_api.h"
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 1
+#define MAX_FRAGMENT 1
+#define MIN_FRAGMENT_SIZE (4 * 1024)
+#define MAX_FRAGMENT_SIZE (4 * 1024)
+
+void dsp_platform_process(struct work_struct *w)
+{
+ struct xf_client *client = container_of(w, struct xf_client, work);
+ struct xf_proxy *proxy = client->proxy;
+ struct xf_message *rmsg;
+
+ while (1) {
+ rmsg = xf_cmd_recv(proxy, &client->wait, &client->queue, 1);
+ if (IS_ERR(rmsg)) {
+ return;
+ }
+ if (rmsg->opcode == XF_EMPTY_THIS_BUFFER) {
+ client->consume_bytes += rmsg->length;
+ snd_compr_fragment_elapsed(client->cstream);
+
+ if (rmsg->buffer == NULL && rmsg->length == 0)
+ snd_compr_drain_notify(client->cstream);
+
+ } else {
+ memcpy(&client->m, rmsg, sizeof(struct xf_message));
+ complete(&client->compr_complete);
+ }
+
+ xf_msg_free(proxy, rmsg);
+ xf_unlock(&proxy->lock);
+ }
+}
+
+static int dsp_platform_compr_open(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+
+ drv->client = xf_client_alloc(dsp_priv);
+ if (IS_ERR(drv->client))
+ return PTR_ERR(drv->client);
+
+ fsl_dsp_open_func(dsp_priv, drv->client);
+
+ drv->client->proxy = &dsp_priv->proxy;
+
+ cpu_dai->driver->ops->startup(NULL, cpu_dai);
+
+ drv->client->cstream = cstream;
+
+ INIT_WORK(&drv->client->work, dsp_platform_process);
+
+ return 0;
+}
+
+static int dsp_platform_compr_free(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+ int ret;
+
+ if (cstream->runtime->state != SNDRV_PCM_STATE_PAUSED &&
+ cstream->runtime->state != SNDRV_PCM_STATE_RUNNING &&
+ cstream->runtime->state != SNDRV_PCM_STATE_DRAINING) {
+
+ ret = xaf_comp_delete(drv->client, &drv->component[1]);
+ if (ret) {
+ dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = xaf_comp_delete(drv->client, &drv->component[0]);
+ if (ret) {
+ dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
+ return ret;
+ }
+ }
+
+ cpu_dai->driver->ops->shutdown(NULL, cpu_dai);
+
+ ret = cancel_work(&drv->client->work);
+
+ fsl_dsp_close_func(drv->client);
+
+ return 0;
+}
+
+static int dsp_platform_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ /* accroding to the params, load the library and create component*/
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+ struct xf_proxy *p_proxy = &dsp_priv->proxy;
+ struct xf_set_param_msg s_param;
+ int ret;
+
+ switch (params->codec.id) {
+ case SND_AUDIOCODEC_MP3:
+ drv->codec_type = CODEC_MP3_DEC;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ drv->codec_type = CODEC_AAC_DEC;
+ break;
+ default:
+ dev_err(component->dev, "codec not supported, id =%d\n", params->codec.id);
+ return -EINVAL;
+ }
+
+ /* ...create auxiliary buffers pool for control commands */
+ ret = xf_pool_alloc(drv->client,
+ p_proxy,
+ XA_AUX_POOL_SIZE,
+ XA_AUX_POOL_MSG_LENGTH,
+ XF_POOL_AUX,
+ &p_proxy->aux);
+ if (ret) {
+ dev_err(component->dev, "xf_pool_alloc failed");
+ return ret;
+ }
+
+ /* ...create pipeline */
+ ret = xaf_pipeline_create(&drv->pipeline);
+ if (ret) {
+ dev_err(component->dev, "create pipeline error\n");
+ goto failed;
+ }
+
+ /* ...create component */
+ ret = xaf_comp_create(drv->client, p_proxy, &drv->component[0], drv->codec_type);
+ if (ret) {
+ dev_err(component->dev, "create component failed, type = %d, err = %d\n",
+ drv->codec_type, ret);
+ goto failed;
+ }
+
+ ret = xaf_comp_create(drv->client, p_proxy, &drv->component[1], RENDER_ESAI);
+ if (ret) {
+ dev_err(component->dev, "create component failed, type = %d, err = %d\n",
+ RENDER_ESAI, ret);
+ goto failed;
+ }
+
+ /* ...add component into pipeline */
+ ret = xaf_comp_add(&drv->pipeline, &drv->component[0]);
+ if (ret) {
+ dev_err(component->dev, "add component failed, type = %d, err = %d\n",
+ drv->codec_type, ret);
+ goto failed;
+ }
+
+ ret = xaf_comp_add(&drv->pipeline, &drv->component[1]);
+ if (ret) {
+ dev_err(component->dev, "add component failed, type = %d, err = %d\n",
+ drv->codec_type, ret);
+ goto failed;
+ }
+
+ drv->client->input_bytes = 0;
+ drv->client->consume_bytes = 0;
+
+ s_param.id = XA_RENDERER_CONFIG_PARAM_SAMPLE_RATE;
+ s_param.value = params->codec.sample_rate;
+ ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param);
+ if (ret) {
+ dev_err(component->dev, "set param[cmd:0x%x|val:0x%x] error, err = %d\n",
+ s_param.id, s_param.value, ret);
+ goto failed;
+ }
+
+ s_param.id = XA_RENDERER_CONFIG_PARAM_CHANNELS;
+ s_param.value = params->codec.ch_out;
+ ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param);
+ if (ret) {
+ dev_err(component->dev, "set param[cmd:0x%x|val:0x%x] error, err = %d\n",
+ s_param.id, s_param.value, ret);
+ goto failed;
+ }
+
+ s_param.id = XA_RENDERER_CONFIG_PARAM_PCM_WIDTH;
+ s_param.value = 16;
+ ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param);
+ if (ret) {
+ dev_err(component->dev, "set param[cmd:0x%x|val:0x%x] error, err = %d\n",
+ s_param.id, s_param.value, ret);
+ goto failed;
+ }
+
+failed:
+ return 0;
+}
+
+static int dsp_platform_compr_trigger_start(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+ struct xaf_comp *p_comp = &drv->component[0];
+ int ret;
+
+ ret = xaf_comp_process(drv->client,
+ p_comp,
+ p_comp->inptr,
+ drv->client->input_bytes,
+ XF_EMPTY_THIS_BUFFER);
+
+ ret = xaf_connect(drv->client,
+ &drv->component[0],
+ &drv->component[1],
+ 1,
+ OUTBUF_SIZE);
+ if (ret) {
+ dev_err(component->dev, "Failed to connect component, err = %d\n", ret);
+ return ret;
+ }
+
+ schedule_work(&drv->client->work);
+
+ return 0;
+}
+
+static int dsp_platform_compr_trigger_stop(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+ int ret;
+
+ ret = xaf_comp_flush(drv->client, &drv->component[0]);
+ if (ret) {
+ dev_err(component->dev, "Fail to flush component, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = xaf_comp_flush(drv->client, &drv->component[1]);
+ if (ret) {
+ dev_err(component->dev, "Fail to flush component, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = xaf_comp_delete(drv->client, &drv->component[0]);
+ if (ret) {
+ dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = xaf_comp_delete(drv->client, &drv->component[1]);
+ if (ret) {
+ dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dsp_platform_compr_trigger_drain(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+ struct xaf_comp *p_comp = &drv->component[0];
+ int ret;
+
+ ret = xaf_comp_process(drv->client, p_comp, NULL, 0,
+ XF_EMPTY_THIS_BUFFER);
+
+ schedule_work(&drv->client->work);
+ return 0;
+}
+
+static int dsp_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = dsp_platform_compr_trigger_start(cstream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = dsp_platform_compr_trigger_stop(cstream);
+ break;
+ case SND_COMPR_TRIGGER_DRAIN:
+ ret = dsp_platform_compr_trigger_drain(cstream);
+ break;
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+ }
+
+ /*send command*/
+ return ret;
+}
+
+static int dsp_platform_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+
+ tstamp->copied_total = drv->client->input_bytes;
+ tstamp->byte_offset = drv->client->input_bytes;
+ tstamp->pcm_frames = 0x900;
+ tstamp->pcm_io_frames = 0,
+ tstamp->sampling_rate = 48000;
+
+ return 0;
+}
+
+static int dsp_platform_compr_copy(struct snd_compr_stream *cstream,
+ char __user *buf,
+ size_t count)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
+ struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
+ struct dsp_data *drv = &dsp_priv->dsp_data;
+ struct xaf_comp *p_comp = &drv->component[0];
+ int copied = 0;
+ int ret;
+
+ if (drv->client->input_bytes == drv->client->consume_bytes) {
+ if (count > INBUF_SIZE){
+ ret = copy_from_user(p_comp->inptr, buf, INBUF_SIZE);
+ if (ret) {
+ dev_err(component->dev, "failed to get message from user space\n");
+ return -EFAULT;
+ }
+ copied = INBUF_SIZE;
+ } else {
+ ret = copy_from_user(p_comp->inptr, buf, count);
+ if (ret) {
+ dev_err(component->dev, "failed to get message from user space\n");
+ return -EFAULT;
+ }
+ copied = count;
+ }
+ drv->client->input_bytes += copied;
+
+ if (cstream->runtime->state == SNDRV_PCM_STATE_RUNNING) {
+ ret = xaf_comp_process(drv->client, p_comp,
+ p_comp->inptr, copied,
+ XF_EMPTY_THIS_BUFFER);
+ schedule_work(&drv->client->work);
+ }
+ }
+
+ return copied;
+}
+
+static int dsp_platform_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ caps->num_codecs = NUM_CODEC;
+ caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */
+ caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */
+ caps->min_fragments = MIN_FRAGMENT;
+ caps->max_fragments = MAX_FRAGMENT;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+
+ return 0;
+}
+
+static struct snd_compr_codec_caps caps_mp3 = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 192,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
+static struct snd_compr_codec_caps caps_aac = {
+ .num_descriptors = 2,
+ .descriptor[1].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[1].bit_rate[0] = 320,
+ .descriptor[1].bit_rate[1] = 192,
+ .descriptor[1].num_bitrates = 2,
+ .descriptor[1].profiles = 0,
+ .descriptor[1].modes = 0,
+ .descriptor[1].formats =
+ (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+ SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int dsp_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ if (codec->codec == SND_AUDIOCODEC_MP3)
+ *codec = caps_mp3;
+ else if (codec->codec == SND_AUDIOCODEC_AAC)
+ *codec = caps_aac;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int dsp_platform_compr_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ return 0;
+}
+
+const struct snd_compr_ops dsp_platform_compr_ops = {
+ .open = dsp_platform_compr_open,
+ .free = dsp_platform_compr_free,
+ .set_params = dsp_platform_compr_set_params,
+ .set_metadata = dsp_platform_compr_set_metadata,
+ .trigger = dsp_platform_compr_trigger,
+ .pointer = dsp_platform_compr_pointer,
+ .copy = dsp_platform_compr_copy,
+ .get_caps = dsp_platform_compr_get_caps,
+ .get_codec_caps = dsp_platform_compr_get_codec_caps,
+};