summaryrefslogtreecommitdiff
path: root/sound/soc/qcom/qdsp6/q6asm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/qcom/qdsp6/q6asm.c')
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c588
1 files changed, 437 insertions, 151 deletions
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
index 4f85cb19a309..e7295b7b2461 100644
--- a/sound/soc/qcom/qdsp6/q6asm.c
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -2,9 +2,11 @@
// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
// Copyright (c) 2018, Linaro Limited
+#include <dt-bindings/sound/qcom,q6asm.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/module.h>
+#include <linux/atomic.h>
#include <linux/soc/qcom/apr.h>
#include <linux/device.h>
#include <linux/of_platform.h>
@@ -25,6 +27,7 @@
#define ASM_STREAM_CMD_FLUSH 0x00010BCE
#define ASM_SESSION_CMD_PAUSE 0x00010BD3
#define ASM_DATA_CMD_EOS 0x00010BDB
+#define ASM_DATA_EVENT_RENDERED_EOS 0x00010C1C
#define ASM_NULL_POPP_TOPOLOGY 0x00010C68
#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09
#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10
@@ -38,6 +41,9 @@
#define ASM_SESSION_CMD_RUN_V2 0x00010DAA
#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5
#define ASM_MEDIA_FMT_MP3 0x00010BE9
+#define ASM_MEDIA_FMT_FLAC 0x00010C16
+#define ASM_MEDIA_FMT_WMA_V9 0x00010DA8
+#define ASM_MEDIA_FMT_WMA_V10 0x00010DA7
#define ASM_DATA_CMD_WRITE_V2 0x00010DAB
#define ASM_DATA_CMD_READ_V2 0x00010DAC
#define ASM_SESSION_CMD_SUSPEND 0x00010DEC
@@ -45,6 +51,10 @@
#define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB4
#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A
#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D
+#define ASM_MEDIA_FMT_ALAC 0x00012f31
+#define ASM_MEDIA_FMT_APE 0x00012f32
+#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67
+#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68
#define ASM_LEGACY_STREAM_SESSION 0
@@ -89,6 +99,77 @@ struct asm_multi_channel_pcm_fmt_blk_v2 {
u8 channel_mapping[PCM_MAX_NUM_CHANNEL];
} __packed;
+struct asm_flac_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 is_stream_info_present;
+ u16 num_channels;
+ u16 min_blk_size;
+ u16 max_blk_size;
+ u16 md5_sum[8];
+ u32 sample_rate;
+ u32 min_frame_size;
+ u32 max_frame_size;
+ u16 sample_size;
+ u16 reserved;
+} __packed;
+
+struct asm_wmastdv9_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 fmtag;
+ u16 num_channels;
+ u32 sample_rate;
+ u32 bytes_per_sec;
+ u16 blk_align;
+ u16 bits_per_sample;
+ u32 channel_mask;
+ u16 enc_options;
+ u16 reserved;
+} __packed;
+
+struct asm_wmaprov10_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 fmtag;
+ u16 num_channels;
+ u32 sample_rate;
+ u32 bytes_per_sec;
+ u16 blk_align;
+ u16 bits_per_sample;
+ u32 channel_mask;
+ u16 enc_options;
+ u16 advanced_enc_options1;
+ u32 advanced_enc_options2;
+} __packed;
+
+struct asm_alac_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u32 frame_length;
+ u8 compatible_version;
+ u8 bit_depth;
+ u8 pb;
+ u8 mb;
+ u8 kb;
+ u8 num_channels;
+ u16 max_run;
+ u32 max_frame_bytes;
+ u32 avg_bit_rate;
+ u32 sample_rate;
+ u32 channel_layout_tag;
+} __packed;
+
+struct asm_ape_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 compatible_version;
+ u16 compression_level;
+ u32 format_flags;
+ u32 blocks_per_frame;
+ u32 final_frame_blocks;
+ u32 total_frames;
+ u16 bits_per_sample;
+ u16 num_channels;
+ u32 sample_rate;
+ u32 seek_table_present;
+} __packed;
+
struct asm_stream_cmd_set_encdec_param {
u32 param_id;
u32 param_size;
@@ -168,6 +249,7 @@ struct audio_port_data {
uint32_t num_periods;
uint32_t dsp_buf;
uint32_t mem_map_handle;
+ atomic_t hw_ptr;
};
struct q6asm {
@@ -193,7 +275,6 @@ struct audio_client {
wait_queue_head_t cmd_wait;
struct aprv2_ibasic_rsp_result_t result;
int perf_mode;
- int stream_id;
struct q6asm *q6asm;
struct device *dev;
};
@@ -234,7 +315,7 @@ static int q6asm_apr_send_session_pkt(struct q6asm *a, struct audio_client *ac,
5 * HZ);
if (!rc) {
- dev_err(a->dev, "CMD timeout\n");
+ dev_err(a->dev, "CMD %x timeout\n", hdr->opcode);
rc = -ETIMEDOUT;
} else if (ac->result.status > 0) {
dev_err(a->dev, "DSP returned error[%x]\n",
@@ -254,7 +335,7 @@ static int __q6asm_memory_unmap(struct audio_client *ac,
struct q6asm *a = dev_get_drvdata(ac->dev->parent);
struct apr_pkt *pkt;
int rc, pkt_size;
- void *p;
+ void *p __free(kfree) = NULL;
if (ac->port[dir].mem_map_handle == 0) {
dev_err(ac->dev, "invalid mem handle\n");
@@ -279,14 +360,11 @@ static int __q6asm_memory_unmap(struct audio_client *ac,
mem_unmap->mem_map_handle = ac->port[dir].mem_map_handle;
rc = q6asm_apr_send_session_pkt(a, ac, pkt, 0);
- if (rc < 0) {
- kfree(pkt);
+ if (rc < 0)
return rc;
- }
ac->port[dir].mem_map_handle = 0;
- kfree(pkt);
return 0;
}
@@ -298,9 +376,9 @@ static void q6asm_audio_client_free_buf(struct audio_client *ac,
spin_lock_irqsave(&ac->lock, flags);
port->num_periods = 0;
+ spin_unlock_irqrestore(&ac->lock, flags);
kfree(port->buf);
port->buf = NULL;
- spin_unlock_irqrestore(&ac->lock, flags);
}
/**
@@ -350,10 +428,10 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
struct audio_port_data *port = NULL;
struct audio_buffer *ab = NULL;
struct apr_pkt *pkt;
- void *p;
+ void *p __free(kfree) = NULL;
unsigned long flags;
uint32_t num_regions, buf_sz;
- int rc, i, pkt_size;
+ int i, pkt_size;
if (is_contiguous) {
num_regions = 1;
@@ -400,12 +478,7 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
}
spin_unlock_irqrestore(&ac->lock, flags);
- rc = q6asm_apr_send_session_pkt(a, ac, pkt,
- ASM_CMDRSP_SHARED_MEM_MAP_REGIONS);
-
- kfree(pkt);
-
- return rc;
+ return q6asm_apr_send_session_pkt(a, ac, pkt, ASM_CMDRSP_SHARED_MEM_MAP_REGIONS);
}
/**
@@ -413,7 +486,7 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
*
* @dir: direction of audio stream
* @ac: audio client instanace
- * @phys: physcial address that needs mapping.
+ * @phys: physical address that needs mapping.
* @period_sz: audio period size
* @periods: number of periods
*
@@ -435,7 +508,7 @@ int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac,
return 0;
}
- buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_ATOMIC);
+ buf = kcalloc(periods, sizeof(*buf), GFP_ATOMIC);
if (!buf) {
spin_unlock_irqrestore(&ac->lock, flags);
return -ENOMEM;
@@ -498,9 +571,8 @@ static struct audio_client *q6asm_get_audio_client(struct q6asm *a,
int session_id)
{
struct audio_client *ac = NULL;
- unsigned long flags;
- spin_lock_irqsave(&a->slock, flags);
+ guard(spinlock_irqsave)(&a->slock);
if ((session_id <= 0) || (session_id > MAX_SESSIONS)) {
dev_err(a->dev, "invalid session: %d\n", session_id);
goto err;
@@ -515,10 +587,17 @@ static struct audio_client *q6asm_get_audio_client(struct q6asm *a,
ac = a->session[session_id];
kref_get(&ac->refcount);
err:
- spin_unlock_irqrestore(&a->slock, flags);
return ac;
}
+int q6asm_get_hw_pointer(struct audio_client *ac, unsigned int dir)
+{
+ struct audio_port_data *data = &ac->port[dir];
+
+ return (int)atomic_read(&data->hw_ptr);
+}
+EXPORT_SYMBOL_GPL(q6asm_get_hw_pointer);
+
static int32_t q6asm_stream_callback(struct apr_device *adev,
struct apr_resp_pkt *data,
int session_id)
@@ -546,9 +625,6 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
case ASM_SESSION_CMD_SUSPEND:
client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE;
break;
- case ASM_DATA_CMD_EOS:
- client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;
- break;
case ASM_STREAM_CMD_FLUSH:
client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE;
break;
@@ -562,10 +638,13 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE;
break;
case ASM_STREAM_CMD_OPEN_WRITE_V3:
+ case ASM_DATA_CMD_WRITE_V2:
case ASM_STREAM_CMD_OPEN_READ_V3:
case ASM_STREAM_CMD_OPEN_READWRITE_V2:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+ case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
+ case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
if (result->status != 0) {
dev_err(ac->dev,
"cmd = 0x%x returned error = 0x%x\n",
@@ -576,6 +655,10 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
goto done;
}
break;
+ case ASM_DATA_CMD_EOS:
+ case ASM_DATA_CMD_READ_V2:
+ /* response as result of close stream */
+ break;
default:
dev_err(ac->dev, "command[0x%x] not expecting rsp\n",
result->opcode);
@@ -596,47 +679,44 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE;
if (ac->io_mode & ASM_SYNC_IO_MODE) {
phys_addr_t phys;
- unsigned long flags;
+ int token = hdr->token & ASM_WRITE_TOKEN_MASK;
- spin_lock_irqsave(&ac->lock, flags);
+ guard(spinlock_irqsave)(&ac->lock);
port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
if (!port->buf) {
- spin_unlock_irqrestore(&ac->lock, flags);
ret = 0;
goto done;
}
- phys = port->buf[hdr->token].phys;
+ phys = port->buf[token].phys;
if (lower_32_bits(phys) != result->opcode ||
upper_32_bits(phys) != result->status) {
dev_err(ac->dev, "Expected addr %pa\n",
- &port->buf[hdr->token].phys);
- spin_unlock_irqrestore(&ac->lock, flags);
+ &port->buf[token].phys);
ret = -EINVAL;
goto done;
}
- spin_unlock_irqrestore(&ac->lock, flags);
+ atomic_set(&port->hw_ptr, token + 1);
}
break;
case ASM_DATA_EVENT_READ_DONE_V2:
client_event = ASM_CLIENT_EVENT_DATA_READ_DONE;
if (ac->io_mode & ASM_SYNC_IO_MODE) {
struct asm_data_cmd_read_v2_done *done = data->payload;
- unsigned long flags;
phys_addr_t phys;
- spin_lock_irqsave(&ac->lock, flags);
+ guard(spinlock_irqsave)(&ac->lock);
port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];
if (!port->buf) {
- spin_unlock_irqrestore(&ac->lock, flags);
ret = 0;
goto done;
}
phys = port->buf[hdr->token].phys;
+ atomic_set(&port->hw_ptr, hdr->token + 1);
if (upper_32_bits(phys) != done->buf_addr_msw ||
lower_32_bits(phys) != done->buf_addr_lsw) {
@@ -644,14 +724,15 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
&port->buf[hdr->token].phys,
done->buf_addr_lsw,
done->buf_addr_msw);
- spin_unlock_irqrestore(&ac->lock, flags);
ret = -EINVAL;
goto done;
}
- spin_unlock_irqrestore(&ac->lock, flags);
}
break;
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;
+ break;
}
if (ac->cb)
@@ -751,21 +832,21 @@ EXPORT_SYMBOL_GPL(q6asm_get_session_id);
* @dev: Pointer to asm child device.
* @cb: event callback.
* @priv: private data associated with this client.
- * @stream_id: stream id
+ * @session_id: session id
* @perf_mode: performace mode for this client
*
* Return: Will be an error pointer on error or a valid audio client
* on success.
*/
struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb,
- void *priv, int stream_id,
+ void *priv, int session_id,
int perf_mode)
{
struct q6asm *a = dev_get_drvdata(dev->parent);
struct audio_client *ac;
unsigned long flags;
- ac = q6asm_get_audio_client(a, stream_id + 1);
+ ac = q6asm_get_audio_client(a, session_id + 1);
if (ac) {
dev_err(dev, "Audio Client already active\n");
return ac;
@@ -776,17 +857,15 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb,
return ERR_PTR(-ENOMEM);
spin_lock_irqsave(&a->slock, flags);
- a->session[stream_id + 1] = ac;
+ a->session[session_id + 1] = ac;
spin_unlock_irqrestore(&a->slock, flags);
- ac->session = stream_id + 1;
+ ac->session = session_id + 1;
ac->cb = cb;
ac->dev = dev;
ac->q6asm = a;
ac->priv = priv;
ac->io_mode = ASM_SYNC_IO_MODE;
ac->perf_mode = perf_mode;
- /* DSP expects stream id from 1 */
- ac->stream_id = 1;
ac->adev = a->adev;
kref_init(&ac->refcount);
@@ -814,7 +893,7 @@ static int q6asm_ac_send_cmd_sync(struct audio_client *ac, struct apr_pkt *pkt)
rc = wait_event_timeout(ac->cmd_wait,
(ac->result.opcode == hdr->opcode), 5 * HZ);
if (!rc) {
- dev_err(ac->dev, "CMD timeout\n");
+ dev_err(ac->dev, "CMD %x timeout\n", hdr->opcode);
rc = -ETIMEDOUT;
goto err;
}
@@ -835,34 +914,35 @@ err:
/**
* q6asm_open_write() - Open audio client for writing
- *
* @ac: audio client pointer
+ * @stream_id: stream id of q6asm session
* @format: audio sample format
+ * @codec_profile: compressed format profile
* @bits_per_sample: bits per sample
+ * @is_gapless: flag to indicate if this is a gapless stream
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_open_write(struct audio_client *ac, uint32_t format,
- uint16_t bits_per_sample)
+int q6asm_open_write(struct audio_client *ac, uint32_t stream_id,
+ uint32_t format, u32 codec_profile,
+ uint16_t bits_per_sample, bool is_gapless)
{
struct asm_stream_cmd_open_write_v3 *open;
struct apr_pkt *pkt;
- void *p;
- int rc, pkt_size;
-
- pkt_size = APR_HDR_SIZE + sizeof(*open);
-
- p = kzalloc(pkt_size, GFP_KERNEL);
+ int rc, pkt_size = APR_HDR_SIZE + sizeof(*open);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
pkt = p;
open = p + APR_HDR_SIZE;
- q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id);
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;
open->mode_flags = 0x00;
open->mode_flags |= ASM_LEGACY_STREAM_SESSION;
+ if (is_gapless)
+ open->mode_flags |= BIT(ASM_SHIFT_GAPLESS_MODE_FLAG);
/* source endpoint : matrix */
open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;
@@ -876,6 +956,33 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format,
case FORMAT_LINEAR_PCM:
open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
break;
+ case SND_AUDIOCODEC_FLAC:
+ open->dec_fmt_id = ASM_MEDIA_FMT_FLAC;
+ break;
+ case SND_AUDIOCODEC_WMA:
+ switch (codec_profile) {
+ case SND_AUDIOPROFILE_WMA9:
+ open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V9;
+ break;
+ case SND_AUDIOPROFILE_WMA10:
+ case SND_AUDIOPROFILE_WMA9_PRO:
+ case SND_AUDIOPROFILE_WMA9_LOSSLESS:
+ case SND_AUDIOPROFILE_WMA10_LOSSLESS:
+ open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V10;
+ break;
+ default:
+ dev_err(ac->dev, "Invalid codec profile 0x%x\n",
+ codec_profile);
+ rc = -EINVAL;
+ goto err;
+ }
+ break;
+ case SND_AUDIOCODEC_ALAC:
+ open->dec_fmt_id = ASM_MEDIA_FMT_ALAC;
+ break;
+ case SND_AUDIOCODEC_APE:
+ open->dec_fmt_id = ASM_MEDIA_FMT_APE;
+ break;
default:
dev_err(ac->dev, "Invalid format 0x%x\n", format);
rc = -EINVAL;
@@ -889,28 +996,25 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format,
ac->io_mode |= ASM_TUN_WRITE_IO_MODE;
err:
- kfree(pkt);
return rc;
}
EXPORT_SYMBOL_GPL(q6asm_open_write);
-static int __q6asm_run(struct audio_client *ac, uint32_t flags,
- uint32_t msw_ts, uint32_t lsw_ts, bool wait)
+static int __q6asm_run(struct audio_client *ac, uint32_t stream_id,
+ uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts,
+ bool wait)
{
struct asm_session_cmd_run_v2 *run;
struct apr_pkt *pkt;
- int pkt_size, rc;
- void *p;
-
- pkt_size = APR_HDR_SIZE + sizeof(*run);
- p = kzalloc(pkt_size, GFP_ATOMIC);
+ int rc, pkt_size = APR_HDR_SIZE + sizeof(*run);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
pkt = p;
run = p + APR_HDR_SIZE;
- q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id);
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2;
run->flags = flags;
@@ -924,7 +1028,6 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags,
rc = 0;
}
- kfree(pkt);
return rc;
}
@@ -932,16 +1035,17 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags,
* q6asm_run() - start the audio client
*
* @ac: audio client pointer
+ * @stream_id: stream id of q6asm session
* @flags: flags associated with write
* @msw_ts: timestamp msw
* @lsw_ts: timestamp lsw
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_run(struct audio_client *ac, uint32_t flags,
+int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags,
uint32_t msw_ts, uint32_t lsw_ts)
{
- return __q6asm_run(ac, flags, msw_ts, lsw_ts, true);
+ return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, true);
}
EXPORT_SYMBOL_GPL(q6asm_run);
@@ -949,16 +1053,17 @@ EXPORT_SYMBOL_GPL(q6asm_run);
* q6asm_run_nowait() - start the audio client withou blocking
*
* @ac: audio client pointer
+ * @stream_id: stream id
* @flags: flags associated with write
* @msw_ts: timestamp msw
* @lsw_ts: timestamp lsw
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
- uint32_t msw_ts, uint32_t lsw_ts)
+int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id,
+ uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts)
{
- return __q6asm_run(ac, flags, msw_ts, lsw_ts, false);
+ return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, false);
}
EXPORT_SYMBOL_GPL(q6asm_run_nowait);
@@ -966,6 +1071,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait);
* q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration
*
* @ac: audio client pointer
+ * @stream_id: stream id
* @rate: audio sample rate
* @channels: number of audio channels.
* @channel_map: channel map pointer
@@ -974,6 +1080,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait);
* Return: Will be an negative value on error or zero on success
*/
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t stream_id,
uint32_t rate, uint32_t channels,
u8 channel_map[PCM_MAX_NUM_CHANNEL],
uint16_t bits_per_sample)
@@ -981,18 +1088,15 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
struct asm_multi_channel_pcm_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
u8 *channel_mapping;
- void *p;
- int rc, pkt_size;
-
- pkt_size = APR_HDR_SIZE + sizeof(*fmt);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
pkt = p;
fmt = p + APR_HDR_SIZE;
- q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id);
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
@@ -1008,23 +1112,229 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
} else {
if (q6dsp_map_channels(channel_mapping, channels)) {
dev_err(ac->dev, " map channels failed %d\n", channels);
- rc = -EINVAL;
- goto err;
+ return -EINVAL;
}
}
- rc = q6asm_ac_send_cmd_sync(ac, pkt);
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);
+
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_flac_cfg *cfg)
+{
+ struct asm_flac_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->is_stream_info_present = cfg->stream_info_present;
+ fmt->num_channels = cfg->ch_cfg;
+ fmt->min_blk_size = cfg->min_blk_size;
+ fmt->max_blk_size = cfg->max_blk_size;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->min_frame_size = cfg->min_frame_size;
+ fmt->max_frame_size = cfg->max_frame_size;
+ fmt->sample_size = cfg->sample_size;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac);
+
+int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_wma_cfg *cfg)
+{
+ struct asm_wmastdv9_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->fmtag = cfg->fmtag;
+ fmt->num_channels = cfg->num_channels;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->bytes_per_sec = cfg->bytes_per_sec;
+ fmt->blk_align = cfg->block_align;
+ fmt->bits_per_sample = cfg->bits_per_sample;
+ fmt->channel_mask = cfg->channel_mask;
+ fmt->enc_options = cfg->enc_options;
+ fmt->reserved = 0;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9);
+
+int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_wma_cfg *cfg)
+{
+ struct asm_wmaprov10_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->fmtag = cfg->fmtag;
+ fmt->num_channels = cfg->num_channels;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->bytes_per_sec = cfg->bytes_per_sec;
+ fmt->blk_align = cfg->block_align;
+ fmt->bits_per_sample = cfg->bits_per_sample;
+ fmt->channel_mask = cfg->channel_mask;
+ fmt->enc_options = cfg->enc_options;
+ fmt->advanced_enc_options1 = cfg->adv_enc_options;
+ fmt->advanced_enc_options2 = cfg->adv_enc_options2;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10);
+
+int q6asm_stream_media_format_block_alac(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_alac_cfg *cfg)
+{
+ struct asm_alac_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+
+ fmt->frame_length = cfg->frame_length;
+ fmt->compatible_version = cfg->compatible_version;
+ fmt->bit_depth = cfg->bit_depth;
+ fmt->num_channels = cfg->num_channels;
+ fmt->max_run = cfg->max_run;
+ fmt->max_frame_bytes = cfg->max_frame_bytes;
+ fmt->avg_bit_rate = cfg->avg_bit_rate;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->channel_layout_tag = cfg->channel_layout_tag;
+ fmt->pb = cfg->pb;
+ fmt->mb = cfg->mb;
+ fmt->kb = cfg->kb;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac);
+
+int q6asm_stream_media_format_block_ape(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_ape_cfg *cfg)
+{
+ struct asm_ape_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+
+ fmt->compatible_version = cfg->compatible_version;
+ fmt->compression_level = cfg->compression_level;
+ fmt->format_flags = cfg->format_flags;
+ fmt->blocks_per_frame = cfg->blocks_per_frame;
+ fmt->final_frame_blocks = cfg->final_frame_blocks;
+ fmt->total_frames = cfg->total_frames;
+ fmt->bits_per_sample = cfg->bits_per_sample;
+ fmt->num_channels = cfg->num_channels;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->seek_table_present = cfg->seek_table_present;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape);
+
+static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_id,
+ uint32_t cmd,
+ uint32_t num_samples)
+{
+ uint32_t *samples;
+ struct apr_pkt *pkt;
+ int rc, pkt_size = APR_HDR_SIZE + sizeof(uint32_t);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ samples = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = cmd;
+ *samples = num_samples;
+ rc = apr_send_pkt(ac->adev, pkt);
+ if (rc == pkt_size)
+ rc = 0;
-err:
- kfree(pkt);
return rc;
}
-EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);
+
+int q6asm_stream_remove_initial_silence(struct audio_client *ac,
+ uint32_t stream_id,
+ uint32_t initial_samples)
+{
+ return q6asm_stream_remove_silence(ac, stream_id,
+ ASM_DATA_CMD_REMOVE_INITIAL_SILENCE,
+ initial_samples);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_remove_initial_silence);
+
+int q6asm_stream_remove_trailing_silence(struct audio_client *ac, uint32_t stream_id,
+ uint32_t trailing_samples)
+{
+ return q6asm_stream_remove_silence(ac, stream_id,
+ ASM_DATA_CMD_REMOVE_TRAILING_SILENCE,
+ trailing_samples);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_remove_trailing_silence);
/**
* q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture
*
* @ac: audio client pointer
+ * @stream_id: stream id
* @rate: audio sample rate
* @channels: number of audio channels.
* @bits_per_sample: bits per sample
@@ -1032,23 +1342,22 @@ EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);
* Return: Will be an negative value on error or zero on success
*/
int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
- uint32_t rate, uint32_t channels, uint16_t bits_per_sample)
+ uint32_t stream_id, uint32_t rate,
+ uint32_t channels,
+ uint16_t bits_per_sample)
{
struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg;
struct apr_pkt *pkt;
u8 *channel_mapping;
u32 frames_per_buf = 0;
- int pkt_size, rc;
- void *p;
-
- pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ int pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
pkt = p;
enc_cfg = p + APR_HDR_SIZE;
- q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id);
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
@@ -1063,38 +1372,32 @@ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
enc_cfg->is_signed = 1;
channel_mapping = enc_cfg->channel_mapping;
- if (q6dsp_map_channels(channel_mapping, channels)) {
- rc = -EINVAL;
- goto err;
- }
+ if (q6dsp_map_channels(channel_mapping, channels))
+ return -EINVAL;
- rc = q6asm_ac_send_cmd_sync(ac, pkt);
-err:
- kfree(pkt);
- return rc;
+ return q6asm_ac_send_cmd_sync(ac, pkt);
}
EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support);
+
/**
* q6asm_read() - read data of period size from audio client
*
* @ac: audio client pointer
+ * @stream_id: stream id
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_read(struct audio_client *ac)
+int q6asm_read(struct audio_client *ac, uint32_t stream_id)
{
struct asm_data_cmd_read_v2 *read;
struct audio_port_data *port;
struct audio_buffer *ab;
struct apr_pkt *pkt;
unsigned long flags;
- int pkt_size;
+ int pkt_size = APR_HDR_SIZE + sizeof(*read);
int rc = 0;
- void *p;
-
- pkt_size = APR_HDR_SIZE + sizeof(*read);
- p = kzalloc(pkt_size, GFP_ATOMIC);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1103,7 +1406,7 @@ int q6asm_read(struct audio_client *ac)
spin_lock_irqsave(&ac->lock, flags);
port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];
- q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id);
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id);
ab = &port->buf[port->dsp_buf];
pkt->hdr.opcode = ASM_DATA_CMD_READ_V2;
read->buf_addr_lsw = lower_32_bits(ab->phys);
@@ -1126,28 +1429,24 @@ int q6asm_read(struct audio_client *ac)
else
pr_err("read op[0x%x]rc[%d]\n", pkt->hdr.opcode, rc);
- kfree(pkt);
return rc;
}
EXPORT_SYMBOL_GPL(q6asm_read);
-static int __q6asm_open_read(struct audio_client *ac,
+static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
uint32_t format, uint16_t bits_per_sample)
{
struct asm_stream_cmd_open_read_v3 *open;
struct apr_pkt *pkt;
- int pkt_size, rc;
- void *p;
-
- pkt_size = APR_HDR_SIZE + sizeof(*open);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ int pkt_size = APR_HDR_SIZE + sizeof(*open);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
pkt = p;
open = p + APR_HDR_SIZE;
- q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id);
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3;
/* Stream prio : High, provide meta info with encoded frames */
open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX;
@@ -1168,25 +1467,23 @@ static int __q6asm_open_read(struct audio_client *ac,
pr_err("Invalid format[%d]\n", format);
}
- rc = q6asm_ac_send_cmd_sync(ac, pkt);
-
- kfree(pkt);
- return rc;
+ return q6asm_ac_send_cmd_sync(ac, pkt);
}
/**
* q6asm_open_read() - Open audio client for reading
*
* @ac: audio client pointer
+ * @stream_id: stream id
* @format: audio sample format
* @bits_per_sample: bits per sample
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_open_read(struct audio_client *ac, uint32_t format,
- uint16_t bits_per_sample)
+int q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
+ uint32_t format, uint16_t bits_per_sample)
{
- return __q6asm_open_read(ac, format, bits_per_sample);
+ return __q6asm_open_read(ac, stream_id, format, bits_per_sample);
}
EXPORT_SYMBOL_GPL(q6asm_open_read);
@@ -1194,27 +1491,25 @@ EXPORT_SYMBOL_GPL(q6asm_open_read);
* q6asm_write_async() - non blocking write
*
* @ac: audio client pointer
- * @len: lenght in bytes
+ * @stream_id: stream id
+ * @len: length in bytes
* @msw_ts: timestamp msw
* @lsw_ts: timestamp lsw
* @wflags: flags associated with write
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
- uint32_t lsw_ts, uint32_t wflags)
+int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len,
+ uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags)
{
struct asm_data_cmd_write_v2 *write;
struct audio_port_data *port;
struct audio_buffer *ab;
unsigned long flags;
struct apr_pkt *pkt;
- int pkt_size;
+ int pkt_size = APR_HDR_SIZE + sizeof(*write);
int rc = 0;
- void *p;
-
- pkt_size = APR_HDR_SIZE + sizeof(*write);
- p = kzalloc(pkt_size, GFP_ATOMIC);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1223,10 +1518,10 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
spin_lock_irqsave(&ac->lock, flags);
port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
- q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id);
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id);
ab = &port->buf[port->dsp_buf];
- pkt->hdr.token = port->dsp_buf;
+ pkt->hdr.token = port->dsp_buf | (len << ASM_WRITE_TOKEN_LEN_SHIFT);
pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2;
write->buf_addr_lsw = lower_32_bits(ab->phys);
write->buf_addr_msw = upper_32_bits(ab->phys);
@@ -1237,10 +1532,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
write->mem_map_handle =
ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle;
- if (wflags == NO_TIMESTAMP)
- write->flags = (wflags & 0x800000FF);
- else
- write->flags = (0x80000000 | wflags);
+ write->flags = wflags;
port->dsp_buf++;
@@ -1252,27 +1544,24 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
if (rc == pkt_size)
rc = 0;
- kfree(pkt);
return rc;
}
EXPORT_SYMBOL_GPL(q6asm_write_async);
static void q6asm_reset_buf_state(struct audio_client *ac)
{
- struct audio_port_data *port = NULL;
- unsigned long flags;
+ struct audio_port_data *port;
- spin_lock_irqsave(&ac->lock, flags);
+ guard(spinlock_irqsave)(&ac->lock);
port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
port->dsp_buf = 0;
port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];
port->dsp_buf = 0;
- spin_unlock_irqrestore(&ac->lock, flags);
}
-static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait)
+static int __q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd,
+ bool wait)
{
- int stream_id = ac->stream_id;
struct apr_pkt pkt;
int rc;
@@ -1319,13 +1608,14 @@ static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait)
* q6asm_cmd() - run cmd on audio client
*
* @ac: audio client pointer
+ * @stream_id: stream id
* @cmd: command to run on audio client.
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_cmd(struct audio_client *ac, int cmd)
+int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd)
{
- return __q6asm_cmd(ac, cmd, true);
+ return __q6asm_cmd(ac, stream_id, cmd, true);
}
EXPORT_SYMBOL_GPL(q6asm_cmd);
@@ -1333,13 +1623,14 @@ EXPORT_SYMBOL_GPL(q6asm_cmd);
* q6asm_cmd_nowait() - non blocking, run cmd on audio client
*
* @ac: audio client pointer
+ * @stream_id: stream id
* @cmd: command to run on audio client.
*
* Return: Will be an negative value on error or zero on success
*/
-int q6asm_cmd_nowait(struct audio_client *ac, int cmd)
+int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd)
{
- return __q6asm_cmd(ac, cmd, false);
+ return __q6asm_cmd(ac, stream_id, cmd, false);
}
EXPORT_SYMBOL_GPL(q6asm_cmd_nowait);
@@ -1360,24 +1651,19 @@ static int q6asm_probe(struct apr_device *adev)
spin_lock_init(&q6asm->slock);
dev_set_drvdata(dev, q6asm);
- return of_platform_populate(dev->of_node, NULL, NULL, dev);
+ return devm_of_platform_populate(dev);
}
-static int q6asm_remove(struct apr_device *adev)
-{
- of_platform_depopulate(&adev->dev);
-
- return 0;
-}
+#ifdef CONFIG_OF
static const struct of_device_id q6asm_device_id[] = {
{ .compatible = "qcom,q6asm" },
{},
};
MODULE_DEVICE_TABLE(of, q6asm_device_id);
+#endif
static struct apr_driver qcom_q6asm_driver = {
.probe = q6asm_probe,
- .remove = q6asm_remove,
.callback = q6asm_srvc_callback,
.driver = {
.name = "qcom-q6asm",