ASoC: qcom: audioreach: add compress offload

Merge series from Srinivas Kandagatla <srinivas.kandagatla@linaro.org>:

This patchset adds compressed offload support to Qualcomm audioreach drivers.
Currently it supports AAC, MP3 and FALC along with gapless.

Tested this on SM8450 and sc7280.
This commit is contained in:
Mark Brown 2023-06-20 15:24:06 +01:00
commit d4b2aee1be
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
6 changed files with 745 additions and 94 deletions

View File

@ -732,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);
@ -766,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;
return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable);
}
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 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,
@ -886,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)
@ -1089,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);
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;
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->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);
@ -1192,6 +1249,8 @@ 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:
@ -1219,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;
}

View File

@ -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,10 @@ 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
@ -143,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
@ -226,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
@ -522,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;
@ -530,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;
@ -608,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 */
@ -716,6 +762,7 @@ struct audioreach_module_config {
u32 channel_allocation;
u32 sd_line_mask;
int fmt;
struct snd_codec codec;
u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
};
@ -752,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__ */

View File

@ -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)

View File

@ -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) {

View File

@ -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_ */

View File

@ -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);