summaryrefslogtreecommitdiffstats
path: root/sound/soc/qcom
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/qcom')
-rw-r--r--sound/soc/qcom/common.c34
-rw-r--r--sound/soc/qcom/lpass-sc7180.c2
-rw-r--r--sound/soc/qcom/lpass-sc7280.c2
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c325
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h63
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c34
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c445
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c39
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c68
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h6
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.c35
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.h1
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c43
-rw-r--r--sound/soc/qcom/sc7280.c23
14 files changed, 959 insertions, 161 deletions
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index c1f24af17506..e2d8c41945fa 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -8,6 +8,11 @@
#include "qdsp6/q6afe.h"
#include "common.h"
+static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
int qcom_snd_parse_of(struct snd_soc_card *card)
{
struct device_node *np;
@@ -96,22 +101,15 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
goto err;
}
- ret = of_parse_phandle_with_args(cpu, "sound-dai",
- "#sound-dai-cells", 0, &args);
- if (ret) {
- dev_err(card->dev, "%s: error getting cpu phandle\n", link->name);
- goto err;
- }
- link->cpus->of_node = args.np;
- link->id = args.args[0];
-
- ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name);
+ ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0);
if (ret) {
dev_err_probe(card->dev, ret,
"%s: error getting cpu dai name\n", link->name);
goto err;
}
+ link->id = args.args[0];
+
if (platform) {
link->platforms->of_node = of_parse_phandle(platform,
"sound-dai",
@@ -140,17 +138,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
}
} else {
/* DPCM frontend */
- dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
- if (!dlc) {
- ret = -ENOMEM;
- goto err;
- }
-
- link->codecs = dlc;
+ link->codecs = &asoc_dummy_dlc;
link->num_codecs = 1;
-
- link->codecs->dai_name = "snd-soc-dummy-dai";
- link->codecs->name = "snd-soc-dummy";
link->dynamic = 1;
}
@@ -169,6 +158,11 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
of_node_put(platform);
}
+ if (!card->dapm_widgets) {
+ card->dapm_widgets = qcom_jack_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(qcom_jack_snd_widgets);
+ }
+
return 0;
err:
of_node_put(cpu);
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
index 41db6617e2ed..56db852f4eab 100644
--- a/sound/soc/qcom/lpass-sc7180.c
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm.h>
#include <dt-bindings/sound/sc7180-lpass.h>
#include <sound/pcm.h>
#include <sound/soc.h>
diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c
index d43f480cbae3..bcf18fe8e14d 100644
--- a/sound/soc/qcom/lpass-sc7280.c
+++ b/sound/soc/qcom/lpass-sc7280.c
@@ -8,7 +8,7 @@
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/soc.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm.h>
#include <dt-bindings/sound/sc7180-lpass.h>
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index 1e0c918eb576..5974c7929dd3 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -196,6 +196,12 @@ struct apm_codec_dma_module_intf_cfg {
#define APM_CDMA_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_codec_dma_module_intf_cfg), 8)
+struct apm_display_port_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_display_port_intf_cfg cfg;
+} __packed;
+#define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8)
+
static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr)
{
@@ -582,6 +588,72 @@ int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pk
}
EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync);
+static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_display_port_module_intf_cfg *intf_cfg;
+ struct apm_module_frame_size_factor_cfg *fs_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz, ep_sz, fs_sz, dl_sz;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ ic_sz = APM_DP_INTF_CFG_PSIZE;
+ ep_sz = APM_HW_EP_CFG_PSIZE;
+ fs_sz = APM_FS_CFG_PSIZE;
+ dl_sz = 0;
+
+ payload_size = ic_sz + ep_sz + fs_sz + dl_sz;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+ p += ep_sz;
+
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+ p += fs_sz;
+
+ intf_cfg = p;
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_DISPLAY_PORT_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.channel_allocation = cfg->channel_allocation;
+ intf_cfg->cfg.mst_idx = 0;
+ intf_cfg->cfg.dptx_idx = cfg->dp_idx;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
/* LPASS Codec DMA port Module Media Format Setup */
static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
struct audioreach_module *module,
@@ -660,33 +732,32 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
return rc;
}
-static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
- struct audioreach_module *module, bool enable)
+int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ uint32_t param_id, uint32_t param_val)
{
struct apm_module_param_data *param_data;
- struct param_id_sal_limiter_enable *limiter_enable;
- int payload_size;
struct gpr_pkt *pkt;
- int rc;
+ uint32_t *param;
+ int rc, payload_size;
void *p;
- payload_size = sizeof(*limiter_enable) + APM_MODULE_PARAM_DATA_SIZE;
+ payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE;
+ p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(pkt))
- return PTR_ERR(pkt);
-
- p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = p;
param_data->module_instance_id = module->instance_id;
param_data->error_code = 0;
- param_data->param_id = PARAM_ID_SAL_LIMITER_ENABLE;
- param_data->param_size = sizeof(*limiter_enable);
- p = p + APM_MODULE_PARAM_DATA_SIZE;
- limiter_enable = p;
+ param_data->param_id = param_id;
+ param_data->param_size = sizeof(uint32_t);
- limiter_enable->enable_lim = enable;
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ param = p;
+ *param = param_val;
rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
@@ -694,77 +765,34 @@ static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
return rc;
}
+EXPORT_SYMBOL_GPL(audioreach_send_u32_param);
+
+static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
+ struct audioreach_module *module, bool enable)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_LIMITER_ENABLE, enable);
+}
static int audioreach_sal_set_media_format(struct q6apm_graph *graph,
struct audioreach_module *module,
struct audioreach_module_config *cfg)
{
- struct apm_module_param_data *param_data;
- struct param_id_sal_output_config *media_format;
- int payload_size;
- struct gpr_pkt *pkt;
- int rc;
- void *p;
-
- payload_size = sizeof(*media_format) + APM_MODULE_PARAM_DATA_SIZE;
-
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(pkt))
- return PTR_ERR(pkt);
-
- p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
-
- param_data = p;
- param_data->module_instance_id = module->instance_id;
- param_data->error_code = 0;
- param_data->param_id = PARAM_ID_SAL_OUTPUT_CFG;
- param_data->param_size = sizeof(*media_format);
- p = p + APM_MODULE_PARAM_DATA_SIZE;
- media_format = p;
-
- media_format->bits_per_sample = cfg->bit_width;
-
- rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
-
- kfree(pkt);
-
- return rc;
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_OUTPUT_CFG, cfg->bit_width);
}
static int audioreach_module_enable(struct q6apm_graph *graph,
struct audioreach_module *module,
bool enable)
{
- struct apm_module_param_data *param_data;
- struct param_id_module_enable *param;
- int payload_size;
- struct gpr_pkt *pkt;
- int rc;
- void *p;
-
- payload_size = sizeof(*param) + APM_MODULE_PARAM_DATA_SIZE;
-
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(pkt))
- return PTR_ERR(pkt);
-
- p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
-
- param_data = p;
- param_data->module_instance_id = module->instance_id;
- param_data->error_code = 0;
- param_data->param_id = PARAM_ID_MODULE_ENABLE;
- param_data->param_size = sizeof(*param);
- p = p + APM_MODULE_PARAM_DATA_SIZE;
- param = p;
-
- param->enable = enable;
-
- rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
-
- kfree(pkt);
+ return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable);
+}
- return rc;
+static int audioreach_gapless_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_EARLY_EOS_DELAY,
+ EARLY_EOS_DELAY_MS);
}
static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
@@ -814,6 +842,99 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
return rc;
}
+static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
+ void *p, struct audioreach_module_config *mcfg)
+{
+ struct payload_media_fmt_aac_t *aac_cfg;
+ struct payload_media_fmt_pcm *mp3_cfg;
+ struct payload_media_fmt_flac_t *flac_cfg;
+
+ switch (mcfg->fmt) {
+ case SND_AUDIOCODEC_MP3:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_MP3;
+ media_fmt_hdr->payload_size = 0;
+ p = p + sizeof(*media_fmt_hdr);
+ mp3_cfg = p;
+ mp3_cfg->sample_rate = mcfg->sample_rate;
+ mp3_cfg->bit_width = mcfg->bit_width;
+ mp3_cfg->alignment = PCM_LSB_ALIGNED;
+ mp3_cfg->bits_per_sample = mcfg->bit_width;
+ mp3_cfg->q_factor = mcfg->bit_width - 1;
+ mp3_cfg->endianness = PCM_LITTLE_ENDIAN;
+ mp3_cfg->num_channels = mcfg->num_channels;
+
+ if (mcfg->num_channels == 1) {
+ mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ } else if (mcfg->num_channels == 2) {
+ mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ mp3_cfg->channel_mapping[1] = PCM_CHANNEL_R;
+ }
+ break;
+ case SND_AUDIOCODEC_AAC:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_AAC;
+ media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_aac_t);
+ p = p + sizeof(*media_fmt_hdr);
+ aac_cfg = p;
+ aac_cfg->aac_fmt_flag = 0;
+ aac_cfg->audio_obj_type = 5;
+ aac_cfg->num_channels = mcfg->num_channels;
+ aac_cfg->total_size_of_PCE_bits = 0;
+ aac_cfg->sample_rate = mcfg->sample_rate;
+ break;
+ case SND_AUDIOCODEC_FLAC:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_FLAC;
+ media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_flac_t);
+ p = p + sizeof(*media_fmt_hdr);
+ flac_cfg = p;
+ flac_cfg->sample_size = mcfg->codec.options.flac_d.sample_size;
+ flac_cfg->num_channels = mcfg->num_channels;
+ flac_cfg->min_blk_size = mcfg->codec.options.flac_d.min_blk_size;
+ flac_cfg->max_blk_size = mcfg->codec.options.flac_d.max_blk_size;
+ flac_cfg->sample_rate = mcfg->sample_rate;
+ flac_cfg->min_frame_size = mcfg->codec.options.flac_d.min_frame_size;
+ flac_cfg->max_frame_size = mcfg->codec.options.flac_d.max_frame_size;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg)
+{
+ struct media_format *header;
+ struct gpr_pkt *pkt;
+ int iid, payload_size, rc;
+ void *p;
+
+ payload_size = sizeof(struct apm_sh_module_media_fmt_cmd);
+
+ iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ pkt = audioreach_alloc_cmd_pkt(payload_size, DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT,
+ 0, graph->port->id, iid);
+
+ if (IS_ERR(pkt))
+ return -ENOMEM;
+
+ p = (void *)pkt + GPR_HDR_SIZE;
+ header = p;
+ rc = audioreach_set_compr_media_format(header, p, mcfg);
+ if (rc) {
+ kfree(pkt);
+ return rc;
+ }
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_compr_set_param);
+
static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
struct audioreach_module *module,
struct audioreach_module_config *cfg)
@@ -1017,25 +1138,33 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
p = p + APM_MODULE_PARAM_DATA_SIZE;
header = p;
- header->data_format = DATA_FORMAT_FIXED_POINT;
- header->fmt_id = MEDIA_FMT_ID_PCM;
- header->payload_size = payload_size - sizeof(*header);
-
- p = p + sizeof(*header);
- cfg = p;
- cfg->sample_rate = mcfg->sample_rate;
- cfg->bit_width = mcfg->bit_width;
- cfg->alignment = PCM_LSB_ALIGNED;
- cfg->bits_per_sample = mcfg->bit_width;
- cfg->q_factor = mcfg->bit_width - 1;
- cfg->endianness = PCM_LITTLE_ENDIAN;
- cfg->num_channels = mcfg->num_channels;
-
- if (mcfg->num_channels == 1) {
- cfg->channel_mapping[0] = PCM_CHANNEL_L;
- } else if (num_channels == 2) {
- cfg->channel_mapping[0] = PCM_CHANNEL_L;
- cfg->channel_mapping[1] = PCM_CHANNEL_R;
+ if (mcfg->fmt == SND_AUDIOCODEC_PCM) {
+ header->data_format = DATA_FORMAT_FIXED_POINT;
+ header->fmt_id = MEDIA_FMT_ID_PCM;
+ header->payload_size = payload_size - sizeof(*header);
+
+ p = p + sizeof(*header);
+ cfg = p;
+ cfg->sample_rate = mcfg->sample_rate;
+ cfg->bit_width = mcfg->bit_width;
+ cfg->alignment = PCM_LSB_ALIGNED;
+ cfg->bits_per_sample = mcfg->bit_width;
+ cfg->q_factor = mcfg->bit_width - 1;
+ cfg->endianness = PCM_LITTLE_ENDIAN;
+ cfg->num_channels = mcfg->num_channels;
+
+ if (mcfg->num_channels == 1)
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ else if (num_channels == 2) {
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ cfg->channel_mapping[1] = PCM_CHANNEL_R;
+ }
+ } else {
+ rc = audioreach_set_compr_media_format(header, p, mcfg);
+ if (rc) {
+ kfree(pkt);
+ return rc;
+ }
}
rc = audioreach_graph_send_cmd_sync(graph, pkt, 0);
@@ -1120,8 +1249,13 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
case MODULE_ID_PCM_DEC:
case MODULE_ID_PCM_ENC:
case MODULE_ID_PCM_CNV:
+ case MODULE_ID_PLACEHOLDER_DECODER:
+ case MODULE_ID_PLACEHOLDER_ENCODER:
rc = audioreach_pcm_set_media_format(graph, module, cfg);
break;
+ case MODULE_ID_DISPLAY_PORT_SINK:
+ rc = audioreach_display_port_set_media_format(graph, module, cfg);
+ break;
case MODULE_ID_I2S_SOURCE:
case MODULE_ID_I2S_SINK:
rc = audioreach_i2s_set_media_format(graph, module, cfg);
@@ -1144,6 +1278,9 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
case MODULE_ID_MFC:
rc = audioreach_mfc_set_media_format(graph, module, cfg);
break;
+ case MODULE_ID_GAPLESS:
+ rc = audioreach_gapless_set_media_format(graph, module, cfg);
+ break;
default:
rc = 0;
}
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index 1d1d47d47d40..e38111ffd7b9 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -15,6 +15,8 @@ struct q6apm_graph;
#define MODULE_ID_PCM_CNV 0x07001003
#define MODULE_ID_PCM_ENC 0x07001004
#define MODULE_ID_PCM_DEC 0x07001005
+#define MODULE_ID_PLACEHOLDER_ENCODER 0x07001008
+#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009
#define MODULE_ID_SAL 0x07001010
#define MODULE_ID_MFC 0x07001015
#define MODULE_ID_CODEC_DMA_SINK 0x07001023
@@ -22,6 +24,11 @@ struct q6apm_graph;
#define MODULE_ID_I2S_SINK 0x0700100A
#define MODULE_ID_I2S_SOURCE 0x0700100B
#define MODULE_ID_DATA_LOGGING 0x0700101A
+#define MODULE_ID_AAC_DEC 0x0700101F
+#define MODULE_ID_FLAC_DEC 0x0700102F
+#define MODULE_ID_MP3_DECODE 0x0700103B
+#define MODULE_ID_GAPLESS 0x0700104D
+#define MODULE_ID_DISPLAY_PORT_SINK 0x07001069
#define APM_CMD_GET_SPF_STATE 0x01001021
#define APM_CMD_RSP_GET_SPF_STATE 0x02001007
@@ -142,12 +149,15 @@ struct param_id_enc_bitrate_param {
} __packed;
#define DATA_FORMAT_FIXED_POINT 1
+#define DATA_FORMAT_GENERIC_COMPRESSED 5
+#define DATA_FORMAT_RAW_COMPRESSED 6
#define PCM_LSB_ALIGNED 1
#define PCM_MSB_ALIGNED 2
#define PCM_LITTLE_ENDIAN 1
#define PCM_BIT_ENDIAN 2
#define MEDIA_FMT_ID_PCM 0x09001000
+#define MEDIA_FMT_ID_MP3 0x09001009
#define PCM_CHANNEL_L 1
#define PCM_CHANNEL_R 2
#define SAMPLE_RATE_48K 48000
@@ -225,6 +235,28 @@ struct apm_media_format {
uint32_t payload_size;
} __packed;
+#define MEDIA_FMT_ID_FLAC 0x09001004
+
+struct payload_media_fmt_flac_t {
+ uint16_t num_channels;
+ uint16_t sample_size;
+ uint16_t min_blk_size;
+ uint16_t max_blk_size;
+ uint32_t sample_rate;
+ uint32_t min_frame_size;
+ uint32_t max_frame_size;
+} __packed;
+
+#define MEDIA_FMT_ID_AAC 0x09001001
+
+struct payload_media_fmt_aac_t {
+ uint16_t aac_fmt_flag;
+ uint16_t audio_obj_type;
+ uint16_t num_channels;
+ uint16_t total_size_of_PCE_bits;
+ uint32_t sample_rate;
+} __packed;
+
#define DATA_CMD_WR_SH_MEM_EP_EOS 0x04001002
#define WR_SH_MEM_EP_EOS_POLICY_LAST 1
#define WR_SH_MEM_EP_EOS_POLICY_EACH 2
@@ -444,6 +476,15 @@ struct param_id_i2s_intf_cfg {
#define PORT_ID_I2S_OUPUT 1
#define I2S_STACK_SIZE 2048
+#define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154
+
+struct param_id_display_port_intf_cfg {
+ uint32_t channel_allocation;
+ /* Multi-Steam Transport index */
+ uint32_t mst_idx;
+ uint32_t dptx_idx;
+} __packed;
+
#define PARAM_ID_HW_EP_MF_CFG 0x08001017
struct param_id_hw_ep_mf {
uint32_t sample_rate;
@@ -512,6 +553,8 @@ struct param_id_sal_limiter_enable {
} __packed;
#define PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x08001024
+#define PARAM_ID_EARLY_EOS_DELAY 0x0800114C
+#define EARLY_EOS_DELAY_MS 150
struct param_id_mfc_media_format {
uint32_t sample_rate;
@@ -520,6 +563,10 @@ struct param_id_mfc_media_format {
uint16_t channel_mapping[];
} __packed;
+struct param_id_gapless_early_eos_delay_t {
+ uint32_t early_eos_delay_ms;
+} __packed;
+
struct media_format {
uint32_t data_format;
uint32_t fmt_id;
@@ -598,6 +645,15 @@ struct param_id_vol_ctrl_master_gain {
} __packed;
+#define PARAM_ID_REMOVE_INITIAL_SILENCE 0x0800114B
+#define PARAM_ID_REMOVE_TRAILING_SILENCE 0x0800115D
+
+#define PARAM_ID_REAL_MODULE_ID 0x0800100B
+
+struct param_id_placeholder_real_module_id {
+ uint32_t real_module_id;
+} __packed;
+
/* Graph */
struct audioreach_connection {
/* Connections */
@@ -702,8 +758,11 @@ struct audioreach_module_config {
u16 data_format;
u16 num_channels;
u16 active_channels_mask;
+ u16 dp_idx;
+ u32 channel_allocation;
u32 sd_line_mask;
int fmt;
+ struct snd_codec codec;
u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
};
@@ -740,4 +799,8 @@ int audioreach_set_media_format(struct q6apm_graph *graph,
int audioreach_shared_memory_send_eos(struct q6apm_graph *graph);
int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
struct audioreach_module *module, int vol);
+int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ uint32_t param_id, uint32_t param_val);
+int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg);
+
#endif /* __AUDIOREACH_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
index 8bb7452b8f18..31e0bad71e95 100644
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -12,6 +12,7 @@
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "q6dsp-lpass-ports.h"
+#include "q6dsp-common.h"
#include "q6afe.h"
@@ -69,6 +70,7 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
int channels = params_channels(params);
struct q6afe_hdmi_cfg *hdmi = &dai_data->port_config[dai->id].hdmi;
+ int ret;
hdmi->sample_rate = params_rate(params);
switch (params_format(params)) {
@@ -80,33 +82,11 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
break;
}
- /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */
- switch (channels) {
- case 2:
- hdmi->channel_allocation = 0;
- break;
- case 3:
- hdmi->channel_allocation = 0x02;
- break;
- case 4:
- hdmi->channel_allocation = 0x06;
- break;
- case 5:
- hdmi->channel_allocation = 0x0A;
- break;
- case 6:
- hdmi->channel_allocation = 0x0B;
- break;
- case 7:
- hdmi->channel_allocation = 0x12;
- break;
- case 8:
- hdmi->channel_allocation = 0x13;
- break;
- default:
- dev_err(dai->dev, "invalid Channels = %u\n", channels);
- return -EINVAL;
- }
+ ret = q6dsp_get_channel_allocation(channels);
+ if (ret < 0)
+ return ret;
+
+ hdmi->channel_allocation = (u16) ret;
return 0;
}
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c
index 7f02f5b2c33f..5eb0b864c740 100644
--- a/sound/soc/qcom/qdsp6/q6apm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6apm-dai.c
@@ -28,8 +28,27 @@
#define CAPTURE_MIN_PERIOD_SIZE 320
#define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)
#define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)
+#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
+#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)
+#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
+#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
#define SID_MASK_DEFAULT 0xF
+static const struct snd_compr_codec_caps q6apm_compr_caps = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000 },
+ .descriptor[0].num_sample_rates = 13,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 128,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
enum stream_state {
Q6APM_STREAM_IDLE = 0,
Q6APM_STREAM_STOPPED,
@@ -39,6 +58,7 @@ enum stream_state {
struct q6apm_dai_rtd {
struct snd_pcm_substream *substream;
struct snd_compr_stream *cstream;
+ struct snd_codec codec;
struct snd_compr_params codec_param;
struct snd_dma_buffer dma_buffer;
phys_addr_t phys;
@@ -52,9 +72,13 @@ struct q6apm_dai_rtd {
uint16_t bits_per_sample;
uint16_t source; /* Encoding source bit mask */
uint16_t session_id;
+ bool next_track;
enum stream_state state;
struct q6apm_graph *graph;
spinlock_t lock;
+ uint32_t initial_samples_drop;
+ uint32_t trailing_samples_drop;
+ bool notify_on_drain;
};
struct q6apm_dai_data {
@@ -132,6 +156,69 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo
}
}
+static void event_handler_compr(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6apm_dai_rtd *prtd = priv;
+ struct snd_compr_stream *substream = prtd->cstream;
+ unsigned long flags;
+ uint32_t wflags = 0;
+ uint64_t avail;
+ uint32_t bytes_written, bytes_to_write;
+ bool is_last_buffer = false;
+
+ switch (opcode) {
+ case APM_CLIENT_EVENT_CMD_EOS_DONE:
+ spin_lock_irqsave(&prtd->lock, flags);
+ if (prtd->notify_on_drain) {
+ snd_compr_drain_notify(prtd->cstream);
+ prtd->notify_on_drain = false;
+ } else {
+ prtd->state = Q6APM_STREAM_STOPPED;
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ case APM_CLIENT_EVENT_DATA_WRITE_DONE:
+ spin_lock_irqsave(&prtd->lock, flags);
+ bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT;
+ prtd->copied_total += bytes_written;
+ snd_compr_fragment_elapsed(substream);
+
+ if (prtd->state != Q6APM_STREAM_RUNNING) {
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+
+ avail = prtd->bytes_received - prtd->bytes_sent;
+
+ if (avail > prtd->pcm_count) {
+ bytes_to_write = prtd->pcm_count;
+ } else {
+ if (substream->partial_drain || prtd->notify_on_drain)
+ is_last_buffer = true;
+ bytes_to_write = avail;
+ }
+
+ if (bytes_to_write) {
+ if (substream->partial_drain && is_last_buffer)
+ wflags |= APM_LAST_BUFFER_FLAG;
+
+ q6apm_write_async(prtd->graph,
+ bytes_to_write, 0, 0, wflags);
+
+ prtd->bytes_sent += bytes_to_write;
+
+ if (prtd->notify_on_drain && is_last_buffer)
+ audioreach_shared_memory_send_eos(prtd->graph);
+ }
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ default:
+ break;
+ }
+}
+
static int q6apm_dai_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -155,6 +242,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component,
cfg.sample_rate = runtime->rate;
cfg.num_channels = runtime->channels;
cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = SND_AUDIOCODEC_PCM;
if (prtd->state) {
/* clear the previous setup if any */
@@ -386,6 +474,362 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc
return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);
}
+static int q6apm_dai_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd;
+ struct q6apm_dai_data *pdata;
+ struct device *dev = component->dev;
+ int ret, size;
+ int graph_id;
+
+ graph_id = cpu_dai->driver->id;
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ prtd->cstream = stream;
+ prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler_compr, prtd, graph_id);
+ if (IS_ERR(prtd->graph)) {
+ ret = PTR_ERR(prtd->graph);
+ kfree(prtd);
+ return ret;
+ }
+
+ runtime->private_data = prtd;
+ runtime->dma_bytes = BUFFER_BYTES_MAX;
+ size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer);
+ if (ret)
+ return ret;
+
+ if (pdata->sid < 0)
+ prtd->phys = prtd->dma_buffer.addr;
+ else
+ prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32);
+
+ snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer);
+ spin_lock_init(&prtd->lock);
+
+ q6apm_enable_compress_module(dev, prtd->graph, true);
+ return 0;
+}
+
+static int q6apm_dai_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ q6apm_graph_stop(prtd->graph);
+ q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK);
+ q6apm_graph_close(prtd->graph);
+ snd_dma_free_pages(&prtd->dma_buffer);
+ prtd->graph = NULL;
+ kfree(prtd);
+ runtime->private_data = NULL;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps)
+{
+ caps->direction = SND_COMPRESS_PLAYBACK;
+ caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;
+ caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;
+ caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
+ caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ caps->num_codecs = 3;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+ caps->codecs[2] = SND_AUDIOCODEC_FLAC;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_codec_caps *codec)
+{
+ switch (codec->codec) {
+ case SND_AUDIOCODEC_MP3:
+ *codec = q6apm_compr_caps;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int q6apm_dai_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ tstamp->copied_total = prtd->copied_total;
+ tstamp->byte_offset = prtd->copied_total % prtd->pcm_size;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return 0;
+}
+
+static int q6apm_dai_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, int cmd)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ case SND_COMPR_TRIGGER_NEXT_TRACK:
+ prtd->next_track = true;
+ break;
+ case SND_COMPR_TRIGGER_DRAIN:
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ prtd->notify_on_drain = true;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ prtd->bytes_received += count;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return count;
+}
+
+static int q6apm_dai_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct q6apm_dai_data *pdata;
+ struct audioreach_module_config cfg;
+ struct snd_codec *codec = &params->codec;
+ int dir = stream->direction;
+ int ret;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ prtd->periods = runtime->fragments;
+ prtd->pcm_count = runtime->fragment_size;
+ prtd->pcm_size = runtime->fragments * runtime->fragment_size;
+ prtd->bits_per_sample = 16;
+
+ prtd->pos = 0;
+
+ if (prtd->next_track != true) {
+ memcpy(&prtd->codec, codec, sizeof(*codec));
+
+ ret = q6apm_set_real_module_id(component->dev, prtd->graph, codec->id);
+ if (ret)
+ return ret;
+
+ cfg.direction = dir;
+ cfg.sample_rate = codec->sample_rate;
+ cfg.num_channels = 2;
+ cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = codec->id;
+ memcpy(&cfg.codec, codec, sizeof(*codec));
+
+ ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
+ if (ret < 0)
+ return ret;
+
+ ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
+ if (ret)
+ return ret;
+
+ ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK,
+ prtd->phys, (prtd->pcm_size / prtd->periods),
+ prtd->periods);
+ if (ret < 0)
+ return -ENOMEM;
+
+ ret = q6apm_graph_prepare(prtd->graph);
+ if (ret)
+ return ret;
+
+ ret = q6apm_graph_start(prtd->graph);
+ if (ret)
+ return ret;
+
+ } else {
+ cfg.direction = dir;
+ cfg.sample_rate = codec->sample_rate;
+ cfg.num_channels = 2;
+ cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = codec->id;
+ memcpy(&cfg.codec, codec, sizeof(*codec));
+
+ ret = audioreach_compr_set_param(prtd->graph, &cfg);
+ if (ret < 0)
+ return ret;
+ }
+ prtd->state = Q6APM_STREAM_RUNNING;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_set_metadata(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (metadata->key) {
+ case SNDRV_COMPRESS_ENCODER_PADDING:
+ prtd->trailing_samples_drop = metadata->value[0];
+ q6apm_remove_trailing_silence(component->dev, prtd->graph,
+ prtd->trailing_samples_drop);
+ break;
+ case SNDRV_COMPRESS_ENCODER_DELAY:
+ prtd->initial_samples_drop = metadata->value[0];
+ q6apm_remove_initial_silence(component->dev, prtd->graph,
+ prtd->initial_samples_drop);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_compr_mmap(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct vm_area_struct *vma)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct device *dev = component->dev;
+
+ return dma_mmap_coherent(dev, vma, prtd->dma_buffer.area, prtd->dma_buffer.addr,
+ prtd->dma_buffer.bytes);
+}
+
+static int q6apm_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, char __user *buf,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ void *dstn;
+ unsigned long flags;
+ size_t copy;
+ u32 wflags = 0;
+ u32 app_pointer;
+ u32 bytes_received;
+ uint32_t bytes_to_write;
+ int avail, bytes_in_flight = 0;
+
+ bytes_received = prtd->bytes_received;
+
+ /**
+ * Make sure that next track data pointer is aligned at 32 bit boundary
+ * This is a Mandatory requirement from DSP data buffers alignment
+ */
+ if (prtd->next_track)
+ bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count);
+
+ app_pointer = bytes_received/prtd->pcm_size;
+ app_pointer = bytes_received - (app_pointer * prtd->pcm_size);
+ dstn = prtd->dma_buffer.area + app_pointer;
+
+ if (count < prtd->pcm_size - app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ } else {
+ copy = prtd->pcm_size - app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(prtd->dma_buffer.area, buf + copy, count - copy))
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ bytes_in_flight = prtd->bytes_received - prtd->copied_total;
+
+ if (prtd->next_track) {
+ prtd->next_track = false;
+ prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count);
+ prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count);
+ }
+
+ prtd->bytes_received = bytes_received + count;
+
+ /* Kick off the data to dsp if its starving!! */
+ if (prtd->state == Q6APM_STREAM_RUNNING && (bytes_in_flight == 0)) {
+ bytes_to_write = prtd->pcm_count;
+ avail = prtd->bytes_received - prtd->bytes_sent;
+
+ if (avail < prtd->pcm_count)
+ bytes_to_write = avail;
+
+ q6apm_write_async(prtd->graph, bytes_to_write, 0, 0, wflags);
+ prtd->bytes_sent += bytes_to_write;
+ }
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return count;
+}
+
+static const struct snd_compress_ops q6apm_dai_compress_ops = {
+ .open = q6apm_dai_compr_open,
+ .free = q6apm_dai_compr_free,
+ .get_caps = q6apm_dai_compr_get_caps,
+ .get_codec_caps = q6apm_dai_compr_get_codec_caps,
+ .pointer = q6apm_dai_compr_pointer,
+ .trigger = q6apm_dai_compr_trigger,
+ .ack = q6apm_dai_compr_ack,
+ .set_params = q6apm_dai_compr_set_params,
+ .set_metadata = q6apm_dai_compr_set_metadata,
+ .mmap = q6apm_dai_compr_mmap,
+ .copy = q6apm_compr_copy,
+};
+
static const struct snd_soc_component_driver q6apm_fe_dai_component = {
.name = DRV_NAME,
.open = q6apm_dai_open,
@@ -395,6 +839,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = {
.hw_params = q6apm_dai_hw_params,
.pointer = q6apm_dai_pointer,
.trigger = q6apm_dai_trigger,
+ .compress_ops = &q6apm_dai_compress_ops,
};
static int q6apm_dai_probe(struct platform_device *pdev)
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
index 420e8aa11f42..7ad604b80e25 100644
--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -11,6 +11,7 @@
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "q6dsp-lpass-ports.h"
+#include "q6dsp-common.h"
#include "audioreach.h"
#include "q6apm.h"
@@ -91,6 +92,36 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai,
return 0;
}
+static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ int channels = params_channels(params);
+ int ret;
+
+ cfg->bit_width = params_width(params);
+ cfg->sample_rate = params_rate(params);
+ cfg->num_channels = channels;
+
+ switch (dai->id) {
+ case DISPLAY_PORT_RX_0:
+ cfg->dp_idx = 0;
+ break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ cfg->dp_idx = dai->id - DISPLAY_PORT_RX_1 + 1;
+ break;
+ }
+
+ ret = q6dsp_get_channel_allocation(channels);
+ if (ret < 0)
+ return ret;
+
+ cfg->channel_allocation = ret;
+
+ return 0;
+}
+
static int q6dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
@@ -215,6 +246,13 @@ static const struct snd_soc_dai_ops q6i2s_ops = {
.shutdown = q6apm_lpass_dai_shutdown,
.set_channel_map = q6dma_set_channel_map,
.hw_params = q6dma_hw_params,
+};
+
+static const struct snd_soc_dai_ops q6hdmi_ops = {
+ .prepare = q6apm_lpass_dai_prepare,
+ .startup = q6apm_lpass_dai_startup,
+ .shutdown = q6apm_lpass_dai_shutdown,
+ .hw_params = q6hdmi_hw_params,
.set_fmt = q6i2s_set_fmt,
};
@@ -242,6 +280,7 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev)
memset(&cfg, 0, sizeof(cfg));
cfg.q6i2s_ops = &q6i2s_ops;
cfg.q6dma_ops = &q6dma_ops;
+ cfg.q6hdmi_ops = &q6hdmi_ops;
dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais);
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index a7a3f973eb6d..7bfac9492ab5 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -298,6 +298,71 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
}
EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
+int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_INITIAL_SILENCE, samples);
+}
+EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence);
+
+int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_TRAILING_SILENCE, samples);
+}
+EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence);
+
+int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en);
+}
+EXPORT_SYMBOL_GPL(q6apm_enable_compress_module);
+
+int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph,
+ uint32_t codec_id)
+{
+ struct audioreach_module *module;
+ uint32_t module_id;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ switch (codec_id) {
+ case SND_AUDIOCODEC_MP3:
+ module_id = MODULE_ID_MP3_DECODE;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ module_id = MODULE_ID_AAC_DEC;
+ break;
+ case SND_AUDIOCODEC_FLAC:
+ module_id = MODULE_ID_FLAC_DEC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID,
+ module_id);
+}
+EXPORT_SYMBOL_GPL(q6apm_set_real_module_id);
+
int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
{
struct audioreach_graph_info *info = graph->info;
@@ -497,6 +562,9 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
}
break;
case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
+ client_event = APM_CLIENT_EVENT_CMD_EOS_DONE;
+ if (graph->cb)
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
break;
case GPR_BASIC_RSP_RESULT:
switch (result->opcode) {
diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h
index 7005be9b63e3..8ee40732ce9e 100644
--- a/sound/soc/qcom/qdsp6/q6apm.h
+++ b/sound/soc/qcom/qdsp6/q6apm.h
@@ -45,6 +45,8 @@
#define APM_WRITE_TOKEN_LEN_SHIFT 16
#define APM_MAX_SESSIONS 8
+#define APM_LAST_BUFFER_FLAG BIT(30)
+#define NO_TIMESTAMP 0xFF00
struct q6apm {
struct device *dev;
@@ -147,4 +149,8 @@ int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);
bool q6apm_is_adsp_ready(void);
+int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en);
+int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
+int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
+int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id);
#endif /* __APM_GRAPH_ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.c b/sound/soc/qcom/qdsp6/q6dsp-common.c
index d393003492c7..95585dea2b36 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-common.c
+++ b/sound/soc/qcom/qdsp6/q6dsp-common.c
@@ -63,4 +63,39 @@ int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch)
return 0;
}
EXPORT_SYMBOL_GPL(q6dsp_map_channels);
+
+int q6dsp_get_channel_allocation(int channels)
+{
+ int channel_allocation;
+
+ /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */
+ switch (channels) {
+ case 2:
+ channel_allocation = 0;
+ break;
+ case 3:
+ channel_allocation = 0x02;
+ break;
+ case 4:
+ channel_allocation = 0x06;
+ break;
+ case 5:
+ channel_allocation = 0x0A;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ break;
+ case 7:
+ channel_allocation = 0x12;
+ break;
+ case 8:
+ channel_allocation = 0x13;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return channel_allocation;
+}
+EXPORT_SYMBOL_GPL(q6dsp_get_channel_allocation);
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.h b/sound/soc/qcom/qdsp6/q6dsp-common.h
index 01094d108b8a..9e704db5f604 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-common.h
+++ b/sound/soc/qcom/qdsp6/q6dsp-common.h
@@ -20,5 +20,6 @@
#define PCM_CHANNELS 10 /* Top surround channel. */
int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch);
+int q6dsp_get_channel_allocation(int channels);
#endif /* __Q6DSP_COMMON_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
index f67c16fd90b9..ac937a6bf909 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
@@ -79,6 +79,22 @@
.id = did, \
}
+#define Q6AFE_DP_RX_DAI(did) { \
+ .playback = { \
+ .stream_name = #did" Playback", \
+ .rates = SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE, \
+ .channels_min = 2, \
+ .channels_max = 8, \
+ .rate_min = 48000, \
+ .rate_max = 192000, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
{
@@ -528,22 +544,14 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5),
Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6),
Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7),
- {
- .playback = {
- .stream_name = "Display Port Playback",
- .rates = SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 2,
- .channels_max = 8,
- .rate_max = 192000,
- .rate_min = 48000,
- },
- .id = DISPLAY_PORT_RX,
- .name = "DISPLAY_PORT",
- },
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_0),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_1),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_2),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_3),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_4),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_5),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_6),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_7),
Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0),
Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0),
Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1),
@@ -603,6 +611,9 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
case DISPLAY_PORT_RX:
q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops;
break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops;
+ break;
case SLIMBUS_0_RX ... SLIMBUS_6_TX:
q6dsp_audio_fe_dais[i].ops = cfg->q6slim_ops;
break;
diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c
index da7469a6a267..787dd49e03f6 100644
--- a/sound/soc/qcom/sc7280.c
+++ b/sound/soc/qcom/sc7280.c
@@ -14,6 +14,7 @@
#include <sound/soc.h>
#include <sound/rt5682s.h>
#include <linux/soundwire/sdw.h>
+#include <sound/pcm_params.h>
#include "../codecs/rt5682.h"
#include "../codecs/rt5682s.h"
@@ -196,8 +197,10 @@ static int sc7280_snd_hw_params(struct snd_pcm_substream *substream,
struct sdw_stream_runtime *sruntime;
int i;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000);
+ if (!rtd->dai_link->no_pcm) {
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000);
+ }
switch (cpu_dai->id) {
case LPASS_CDC_DMA_TX3:
@@ -358,6 +361,20 @@ static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = {
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
+static int sc7280_snd_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
static int sc7280_snd_platform_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
@@ -387,6 +404,8 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev)
for_each_card_prelinks(card, i, link) {
link->init = sc7280_init;
link->ops = &sc7280_ops;
+ if (link->no_pcm == 1)
+ link->be_hw_params_fixup = sc7280_snd_be_hw_params_fixup;
}
return devm_snd_soc_register_card(dev, card);