diff options
Diffstat (limited to 'sound/soc/qcom/qdsp6/q6asm.c')
| -rw-r--r-- | sound/soc/qcom/qdsp6/q6asm.c | 588 |
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", |
