162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci// Copyright (c) 2018, Linaro Limited 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/mutex.h> 662306a36Sopenharmony_ci#include <linux/wait.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/soc/qcom/apr.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/of_platform.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <linux/kref.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <uapi/sound/asound.h> 1562306a36Sopenharmony_ci#include <uapi/sound/compress_params.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include "q6asm.h" 2062306a36Sopenharmony_ci#include "q6core.h" 2162306a36Sopenharmony_ci#include "q6dsp-errno.h" 2262306a36Sopenharmony_ci#include "q6dsp-common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define ASM_STREAM_CMD_CLOSE 0x00010BCD 2562306a36Sopenharmony_ci#define ASM_STREAM_CMD_FLUSH 0x00010BCE 2662306a36Sopenharmony_ci#define ASM_SESSION_CMD_PAUSE 0x00010BD3 2762306a36Sopenharmony_ci#define ASM_DATA_CMD_EOS 0x00010BDB 2862306a36Sopenharmony_ci#define ASM_DATA_EVENT_RENDERED_EOS 0x00010C1C 2962306a36Sopenharmony_ci#define ASM_NULL_POPP_TOPOLOGY 0x00010C68 3062306a36Sopenharmony_ci#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09 3162306a36Sopenharmony_ci#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10 3262306a36Sopenharmony_ci#define ASM_STREAM_POSTPROC_TOPO_ID_NONE 0x00010C68 3362306a36Sopenharmony_ci#define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D92 3462306a36Sopenharmony_ci#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D93 3562306a36Sopenharmony_ci#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D94 3662306a36Sopenharmony_ci#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98 3762306a36Sopenharmony_ci#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99 3862306a36Sopenharmony_ci#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3 3962306a36Sopenharmony_ci#define ASM_SESSION_CMD_RUN_V2 0x00010DAA 4062306a36Sopenharmony_ci#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 4162306a36Sopenharmony_ci#define ASM_MEDIA_FMT_MP3 0x00010BE9 4262306a36Sopenharmony_ci#define ASM_MEDIA_FMT_FLAC 0x00010C16 4362306a36Sopenharmony_ci#define ASM_MEDIA_FMT_WMA_V9 0x00010DA8 4462306a36Sopenharmony_ci#define ASM_MEDIA_FMT_WMA_V10 0x00010DA7 4562306a36Sopenharmony_ci#define ASM_DATA_CMD_WRITE_V2 0x00010DAB 4662306a36Sopenharmony_ci#define ASM_DATA_CMD_READ_V2 0x00010DAC 4762306a36Sopenharmony_ci#define ASM_SESSION_CMD_SUSPEND 0x00010DEC 4862306a36Sopenharmony_ci#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB3 4962306a36Sopenharmony_ci#define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB4 5062306a36Sopenharmony_ci#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A 5162306a36Sopenharmony_ci#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D 5262306a36Sopenharmony_ci#define ASM_MEDIA_FMT_ALAC 0x00012f31 5362306a36Sopenharmony_ci#define ASM_MEDIA_FMT_APE 0x00012f32 5462306a36Sopenharmony_ci#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67 5562306a36Sopenharmony_ci#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define ASM_LEGACY_STREAM_SESSION 0 5962306a36Sopenharmony_ci/* Bit shift for the stream_perf_mode subfield. */ 6062306a36Sopenharmony_ci#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ 29 6162306a36Sopenharmony_ci#define ASM_END_POINT_DEVICE_MATRIX 0 6262306a36Sopenharmony_ci#define ASM_DEFAULT_APP_TYPE 0 6362306a36Sopenharmony_ci#define ASM_SYNC_IO_MODE 0x0001 6462306a36Sopenharmony_ci#define ASM_ASYNC_IO_MODE 0x0002 6562306a36Sopenharmony_ci#define ASM_TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ 6662306a36Sopenharmony_ci#define ASM_TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */ 6762306a36Sopenharmony_ci#define ASM_SHIFT_GAPLESS_MODE_FLAG 31 6862306a36Sopenharmony_ci#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct avs_cmd_shared_mem_map_regions { 7162306a36Sopenharmony_ci u16 mem_pool_id; 7262306a36Sopenharmony_ci u16 num_regions; 7362306a36Sopenharmony_ci u32 property_flag; 7462306a36Sopenharmony_ci} __packed; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct avs_shared_map_region_payload { 7762306a36Sopenharmony_ci u32 shm_addr_lsw; 7862306a36Sopenharmony_ci u32 shm_addr_msw; 7962306a36Sopenharmony_ci u32 mem_size_bytes; 8062306a36Sopenharmony_ci} __packed; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct avs_cmd_shared_mem_unmap_regions { 8362306a36Sopenharmony_ci u32 mem_map_handle; 8462306a36Sopenharmony_ci} __packed; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct asm_data_cmd_media_fmt_update_v2 { 8762306a36Sopenharmony_ci u32 fmt_blk_size; 8862306a36Sopenharmony_ci} __packed; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct asm_multi_channel_pcm_fmt_blk_v2 { 9162306a36Sopenharmony_ci struct asm_data_cmd_media_fmt_update_v2 fmt_blk; 9262306a36Sopenharmony_ci u16 num_channels; 9362306a36Sopenharmony_ci u16 bits_per_sample; 9462306a36Sopenharmony_ci u32 sample_rate; 9562306a36Sopenharmony_ci u16 is_signed; 9662306a36Sopenharmony_ci u16 reserved; 9762306a36Sopenharmony_ci u8 channel_mapping[PCM_MAX_NUM_CHANNEL]; 9862306a36Sopenharmony_ci} __packed; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct asm_flac_fmt_blk_v2 { 10162306a36Sopenharmony_ci struct asm_data_cmd_media_fmt_update_v2 fmt_blk; 10262306a36Sopenharmony_ci u16 is_stream_info_present; 10362306a36Sopenharmony_ci u16 num_channels; 10462306a36Sopenharmony_ci u16 min_blk_size; 10562306a36Sopenharmony_ci u16 max_blk_size; 10662306a36Sopenharmony_ci u16 md5_sum[8]; 10762306a36Sopenharmony_ci u32 sample_rate; 10862306a36Sopenharmony_ci u32 min_frame_size; 10962306a36Sopenharmony_ci u32 max_frame_size; 11062306a36Sopenharmony_ci u16 sample_size; 11162306a36Sopenharmony_ci u16 reserved; 11262306a36Sopenharmony_ci} __packed; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct asm_wmastdv9_fmt_blk_v2 { 11562306a36Sopenharmony_ci struct asm_data_cmd_media_fmt_update_v2 fmt_blk; 11662306a36Sopenharmony_ci u16 fmtag; 11762306a36Sopenharmony_ci u16 num_channels; 11862306a36Sopenharmony_ci u32 sample_rate; 11962306a36Sopenharmony_ci u32 bytes_per_sec; 12062306a36Sopenharmony_ci u16 blk_align; 12162306a36Sopenharmony_ci u16 bits_per_sample; 12262306a36Sopenharmony_ci u32 channel_mask; 12362306a36Sopenharmony_ci u16 enc_options; 12462306a36Sopenharmony_ci u16 reserved; 12562306a36Sopenharmony_ci} __packed; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistruct asm_wmaprov10_fmt_blk_v2 { 12862306a36Sopenharmony_ci struct asm_data_cmd_media_fmt_update_v2 fmt_blk; 12962306a36Sopenharmony_ci u16 fmtag; 13062306a36Sopenharmony_ci u16 num_channels; 13162306a36Sopenharmony_ci u32 sample_rate; 13262306a36Sopenharmony_ci u32 bytes_per_sec; 13362306a36Sopenharmony_ci u16 blk_align; 13462306a36Sopenharmony_ci u16 bits_per_sample; 13562306a36Sopenharmony_ci u32 channel_mask; 13662306a36Sopenharmony_ci u16 enc_options; 13762306a36Sopenharmony_ci u16 advanced_enc_options1; 13862306a36Sopenharmony_ci u32 advanced_enc_options2; 13962306a36Sopenharmony_ci} __packed; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistruct asm_alac_fmt_blk_v2 { 14262306a36Sopenharmony_ci struct asm_data_cmd_media_fmt_update_v2 fmt_blk; 14362306a36Sopenharmony_ci u32 frame_length; 14462306a36Sopenharmony_ci u8 compatible_version; 14562306a36Sopenharmony_ci u8 bit_depth; 14662306a36Sopenharmony_ci u8 pb; 14762306a36Sopenharmony_ci u8 mb; 14862306a36Sopenharmony_ci u8 kb; 14962306a36Sopenharmony_ci u8 num_channels; 15062306a36Sopenharmony_ci u16 max_run; 15162306a36Sopenharmony_ci u32 max_frame_bytes; 15262306a36Sopenharmony_ci u32 avg_bit_rate; 15362306a36Sopenharmony_ci u32 sample_rate; 15462306a36Sopenharmony_ci u32 channel_layout_tag; 15562306a36Sopenharmony_ci} __packed; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct asm_ape_fmt_blk_v2 { 15862306a36Sopenharmony_ci struct asm_data_cmd_media_fmt_update_v2 fmt_blk; 15962306a36Sopenharmony_ci u16 compatible_version; 16062306a36Sopenharmony_ci u16 compression_level; 16162306a36Sopenharmony_ci u32 format_flags; 16262306a36Sopenharmony_ci u32 blocks_per_frame; 16362306a36Sopenharmony_ci u32 final_frame_blocks; 16462306a36Sopenharmony_ci u32 total_frames; 16562306a36Sopenharmony_ci u16 bits_per_sample; 16662306a36Sopenharmony_ci u16 num_channels; 16762306a36Sopenharmony_ci u32 sample_rate; 16862306a36Sopenharmony_ci u32 seek_table_present; 16962306a36Sopenharmony_ci} __packed; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistruct asm_stream_cmd_set_encdec_param { 17262306a36Sopenharmony_ci u32 param_id; 17362306a36Sopenharmony_ci u32 param_size; 17462306a36Sopenharmony_ci} __packed; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistruct asm_enc_cfg_blk_param_v2 { 17762306a36Sopenharmony_ci u32 frames_per_buf; 17862306a36Sopenharmony_ci u32 enc_cfg_blk_size; 17962306a36Sopenharmony_ci} __packed; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistruct asm_multi_channel_pcm_enc_cfg_v2 { 18262306a36Sopenharmony_ci struct asm_stream_cmd_set_encdec_param encdec; 18362306a36Sopenharmony_ci struct asm_enc_cfg_blk_param_v2 encblk; 18462306a36Sopenharmony_ci uint16_t num_channels; 18562306a36Sopenharmony_ci uint16_t bits_per_sample; 18662306a36Sopenharmony_ci uint32_t sample_rate; 18762306a36Sopenharmony_ci uint16_t is_signed; 18862306a36Sopenharmony_ci uint16_t reserved; 18962306a36Sopenharmony_ci uint8_t channel_mapping[8]; 19062306a36Sopenharmony_ci} __packed; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistruct asm_data_cmd_read_v2 { 19362306a36Sopenharmony_ci u32 buf_addr_lsw; 19462306a36Sopenharmony_ci u32 buf_addr_msw; 19562306a36Sopenharmony_ci u32 mem_map_handle; 19662306a36Sopenharmony_ci u32 buf_size; 19762306a36Sopenharmony_ci u32 seq_id; 19862306a36Sopenharmony_ci} __packed; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistruct asm_data_cmd_read_v2_done { 20162306a36Sopenharmony_ci u32 status; 20262306a36Sopenharmony_ci u32 buf_addr_lsw; 20362306a36Sopenharmony_ci u32 buf_addr_msw; 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistruct asm_stream_cmd_open_read_v3 { 20762306a36Sopenharmony_ci u32 mode_flags; 20862306a36Sopenharmony_ci u32 src_endpointype; 20962306a36Sopenharmony_ci u32 preprocopo_id; 21062306a36Sopenharmony_ci u32 enc_cfg_id; 21162306a36Sopenharmony_ci u16 bits_per_sample; 21262306a36Sopenharmony_ci u16 reserved; 21362306a36Sopenharmony_ci} __packed; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistruct asm_data_cmd_write_v2 { 21662306a36Sopenharmony_ci u32 buf_addr_lsw; 21762306a36Sopenharmony_ci u32 buf_addr_msw; 21862306a36Sopenharmony_ci u32 mem_map_handle; 21962306a36Sopenharmony_ci u32 buf_size; 22062306a36Sopenharmony_ci u32 seq_id; 22162306a36Sopenharmony_ci u32 timestamp_lsw; 22262306a36Sopenharmony_ci u32 timestamp_msw; 22362306a36Sopenharmony_ci u32 flags; 22462306a36Sopenharmony_ci} __packed; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistruct asm_stream_cmd_open_write_v3 { 22762306a36Sopenharmony_ci uint32_t mode_flags; 22862306a36Sopenharmony_ci uint16_t sink_endpointype; 22962306a36Sopenharmony_ci uint16_t bits_per_sample; 23062306a36Sopenharmony_ci uint32_t postprocopo_id; 23162306a36Sopenharmony_ci uint32_t dec_fmt_id; 23262306a36Sopenharmony_ci} __packed; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistruct asm_session_cmd_run_v2 { 23562306a36Sopenharmony_ci u32 flags; 23662306a36Sopenharmony_ci u32 time_lsw; 23762306a36Sopenharmony_ci u32 time_msw; 23862306a36Sopenharmony_ci} __packed; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistruct audio_buffer { 24162306a36Sopenharmony_ci phys_addr_t phys; 24262306a36Sopenharmony_ci uint32_t size; /* size of buffer */ 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistruct audio_port_data { 24662306a36Sopenharmony_ci struct audio_buffer *buf; 24762306a36Sopenharmony_ci uint32_t num_periods; 24862306a36Sopenharmony_ci uint32_t dsp_buf; 24962306a36Sopenharmony_ci uint32_t mem_map_handle; 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistruct q6asm { 25362306a36Sopenharmony_ci struct apr_device *adev; 25462306a36Sopenharmony_ci struct device *dev; 25562306a36Sopenharmony_ci struct q6core_svc_api_info ainfo; 25662306a36Sopenharmony_ci wait_queue_head_t mem_wait; 25762306a36Sopenharmony_ci spinlock_t slock; 25862306a36Sopenharmony_ci struct audio_client *session[MAX_SESSIONS + 1]; 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistruct audio_client { 26262306a36Sopenharmony_ci int session; 26362306a36Sopenharmony_ci q6asm_cb cb; 26462306a36Sopenharmony_ci void *priv; 26562306a36Sopenharmony_ci uint32_t io_mode; 26662306a36Sopenharmony_ci struct apr_device *adev; 26762306a36Sopenharmony_ci struct mutex cmd_lock; 26862306a36Sopenharmony_ci spinlock_t lock; 26962306a36Sopenharmony_ci struct kref refcount; 27062306a36Sopenharmony_ci /* idx:1 out port, 0: in port */ 27162306a36Sopenharmony_ci struct audio_port_data port[2]; 27262306a36Sopenharmony_ci wait_queue_head_t cmd_wait; 27362306a36Sopenharmony_ci struct aprv2_ibasic_rsp_result_t result; 27462306a36Sopenharmony_ci int perf_mode; 27562306a36Sopenharmony_ci struct q6asm *q6asm; 27662306a36Sopenharmony_ci struct device *dev; 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic inline void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, 28062306a36Sopenharmony_ci uint32_t pkt_size, bool cmd_flg, 28162306a36Sopenharmony_ci uint32_t stream_id) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci hdr->hdr_field = APR_SEQ_CMD_HDR_FIELD; 28462306a36Sopenharmony_ci hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id); 28562306a36Sopenharmony_ci hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id); 28662306a36Sopenharmony_ci hdr->pkt_size = pkt_size; 28762306a36Sopenharmony_ci if (cmd_flg) 28862306a36Sopenharmony_ci hdr->token = ac->session; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int q6asm_apr_send_session_pkt(struct q6asm *a, struct audio_client *ac, 29262306a36Sopenharmony_ci struct apr_pkt *pkt, uint32_t rsp_opcode) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct apr_hdr *hdr = &pkt->hdr; 29562306a36Sopenharmony_ci int rc; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci mutex_lock(&ac->cmd_lock); 29862306a36Sopenharmony_ci ac->result.opcode = 0; 29962306a36Sopenharmony_ci ac->result.status = 0; 30062306a36Sopenharmony_ci rc = apr_send_pkt(a->adev, pkt); 30162306a36Sopenharmony_ci if (rc < 0) 30262306a36Sopenharmony_ci goto err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (rsp_opcode) 30562306a36Sopenharmony_ci rc = wait_event_timeout(a->mem_wait, 30662306a36Sopenharmony_ci (ac->result.opcode == hdr->opcode) || 30762306a36Sopenharmony_ci (ac->result.opcode == rsp_opcode), 30862306a36Sopenharmony_ci 5 * HZ); 30962306a36Sopenharmony_ci else 31062306a36Sopenharmony_ci rc = wait_event_timeout(a->mem_wait, 31162306a36Sopenharmony_ci (ac->result.opcode == hdr->opcode), 31262306a36Sopenharmony_ci 5 * HZ); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!rc) { 31562306a36Sopenharmony_ci dev_err(a->dev, "CMD %x timeout\n", hdr->opcode); 31662306a36Sopenharmony_ci rc = -ETIMEDOUT; 31762306a36Sopenharmony_ci } else if (ac->result.status > 0) { 31862306a36Sopenharmony_ci dev_err(a->dev, "DSP returned error[%x]\n", 31962306a36Sopenharmony_ci ac->result.status); 32062306a36Sopenharmony_ci rc = -EINVAL; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cierr: 32462306a36Sopenharmony_ci mutex_unlock(&ac->cmd_lock); 32562306a36Sopenharmony_ci return rc; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int __q6asm_memory_unmap(struct audio_client *ac, 32962306a36Sopenharmony_ci phys_addr_t buf_add, int dir) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct avs_cmd_shared_mem_unmap_regions *mem_unmap; 33262306a36Sopenharmony_ci struct q6asm *a = dev_get_drvdata(ac->dev->parent); 33362306a36Sopenharmony_ci struct apr_pkt *pkt; 33462306a36Sopenharmony_ci int rc, pkt_size; 33562306a36Sopenharmony_ci void *p; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (ac->port[dir].mem_map_handle == 0) { 33862306a36Sopenharmony_ci dev_err(ac->dev, "invalid mem handle\n"); 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap); 34362306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 34462306a36Sopenharmony_ci if (!p) 34562306a36Sopenharmony_ci return -ENOMEM; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci pkt = p; 34862306a36Sopenharmony_ci mem_unmap = p + APR_HDR_SIZE; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD; 35162306a36Sopenharmony_ci pkt->hdr.src_port = 0; 35262306a36Sopenharmony_ci pkt->hdr.dest_port = 0; 35362306a36Sopenharmony_ci pkt->hdr.pkt_size = pkt_size; 35462306a36Sopenharmony_ci pkt->hdr.token = ((ac->session << 8) | dir); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci pkt->hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS; 35762306a36Sopenharmony_ci mem_unmap->mem_map_handle = ac->port[dir].mem_map_handle; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci rc = q6asm_apr_send_session_pkt(a, ac, pkt, 0); 36062306a36Sopenharmony_ci if (rc < 0) { 36162306a36Sopenharmony_ci kfree(pkt); 36262306a36Sopenharmony_ci return rc; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci ac->port[dir].mem_map_handle = 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci kfree(pkt); 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void q6asm_audio_client_free_buf(struct audio_client *ac, 37362306a36Sopenharmony_ci struct audio_port_data *port) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci unsigned long flags; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 37862306a36Sopenharmony_ci port->num_periods = 0; 37962306a36Sopenharmony_ci kfree(port->buf); 38062306a36Sopenharmony_ci port->buf = NULL; 38162306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * q6asm_unmap_memory_regions() - unmap memory regions in the dsp. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * @dir: direction of audio stream 38862306a36Sopenharmony_ci * @ac: audio client instanace 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Return: Will be an negative value on failure or zero on success 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ciint q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct audio_port_data *port; 39562306a36Sopenharmony_ci int cnt = 0; 39662306a36Sopenharmony_ci int rc = 0; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci port = &ac->port[dir]; 39962306a36Sopenharmony_ci if (!port->buf) { 40062306a36Sopenharmony_ci rc = -EINVAL; 40162306a36Sopenharmony_ci goto err; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci cnt = port->num_periods - 1; 40562306a36Sopenharmony_ci if (cnt >= 0) { 40662306a36Sopenharmony_ci rc = __q6asm_memory_unmap(ac, port->buf[dir].phys, dir); 40762306a36Sopenharmony_ci if (rc < 0) { 40862306a36Sopenharmony_ci dev_err(ac->dev, "%s: Memory_unmap_regions failed %d\n", 40962306a36Sopenharmony_ci __func__, rc); 41062306a36Sopenharmony_ci goto err; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci q6asm_audio_client_free_buf(ac, port); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cierr: 41762306a36Sopenharmony_ci return rc; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_unmap_memory_regions); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int __q6asm_memory_map_regions(struct audio_client *ac, int dir, 42262306a36Sopenharmony_ci size_t period_sz, unsigned int periods, 42362306a36Sopenharmony_ci bool is_contiguous) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct avs_cmd_shared_mem_map_regions *cmd = NULL; 42662306a36Sopenharmony_ci struct avs_shared_map_region_payload *mregions = NULL; 42762306a36Sopenharmony_ci struct q6asm *a = dev_get_drvdata(ac->dev->parent); 42862306a36Sopenharmony_ci struct audio_port_data *port = NULL; 42962306a36Sopenharmony_ci struct audio_buffer *ab = NULL; 43062306a36Sopenharmony_ci struct apr_pkt *pkt; 43162306a36Sopenharmony_ci void *p; 43262306a36Sopenharmony_ci unsigned long flags; 43362306a36Sopenharmony_ci uint32_t num_regions, buf_sz; 43462306a36Sopenharmony_ci int rc, i, pkt_size; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (is_contiguous) { 43762306a36Sopenharmony_ci num_regions = 1; 43862306a36Sopenharmony_ci buf_sz = period_sz * periods; 43962306a36Sopenharmony_ci } else { 44062306a36Sopenharmony_ci buf_sz = period_sz; 44162306a36Sopenharmony_ci num_regions = periods; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* DSP expects size should be aligned to 4K */ 44562306a36Sopenharmony_ci buf_sz = ALIGN(buf_sz, 4096); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*cmd) + 44862306a36Sopenharmony_ci (sizeof(*mregions) * num_regions); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 45162306a36Sopenharmony_ci if (!p) 45262306a36Sopenharmony_ci return -ENOMEM; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci pkt = p; 45562306a36Sopenharmony_ci cmd = p + APR_HDR_SIZE; 45662306a36Sopenharmony_ci mregions = p + APR_HDR_SIZE + sizeof(*cmd); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD; 45962306a36Sopenharmony_ci pkt->hdr.src_port = 0; 46062306a36Sopenharmony_ci pkt->hdr.dest_port = 0; 46162306a36Sopenharmony_ci pkt->hdr.pkt_size = pkt_size; 46262306a36Sopenharmony_ci pkt->hdr.token = ((ac->session << 8) | dir); 46362306a36Sopenharmony_ci pkt->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci cmd->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; 46662306a36Sopenharmony_ci cmd->num_regions = num_regions; 46762306a36Sopenharmony_ci cmd->property_flag = 0x00; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 47062306a36Sopenharmony_ci port = &ac->port[dir]; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci for (i = 0; i < num_regions; i++) { 47362306a36Sopenharmony_ci ab = &port->buf[i]; 47462306a36Sopenharmony_ci mregions->shm_addr_lsw = lower_32_bits(ab->phys); 47562306a36Sopenharmony_ci mregions->shm_addr_msw = upper_32_bits(ab->phys); 47662306a36Sopenharmony_ci mregions->mem_size_bytes = buf_sz; 47762306a36Sopenharmony_ci ++mregions; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci rc = q6asm_apr_send_session_pkt(a, ac, pkt, 48262306a36Sopenharmony_ci ASM_CMDRSP_SHARED_MEM_MAP_REGIONS); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci kfree(pkt); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return rc; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci/** 49062306a36Sopenharmony_ci * q6asm_map_memory_regions() - map memory regions in the dsp. 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * @dir: direction of audio stream 49362306a36Sopenharmony_ci * @ac: audio client instanace 49462306a36Sopenharmony_ci * @phys: physical address that needs mapping. 49562306a36Sopenharmony_ci * @period_sz: audio period size 49662306a36Sopenharmony_ci * @periods: number of periods 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * Return: Will be an negative value on failure or zero on success 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ciint q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac, 50162306a36Sopenharmony_ci phys_addr_t phys, 50262306a36Sopenharmony_ci size_t period_sz, unsigned int periods) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct audio_buffer *buf; 50562306a36Sopenharmony_ci unsigned long flags; 50662306a36Sopenharmony_ci int cnt; 50762306a36Sopenharmony_ci int rc; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 51062306a36Sopenharmony_ci if (ac->port[dir].buf) { 51162306a36Sopenharmony_ci dev_err(ac->dev, "Buffer already allocated\n"); 51262306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci buf = kcalloc(periods, sizeof(*buf), GFP_ATOMIC); 51762306a36Sopenharmony_ci if (!buf) { 51862306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 51962306a36Sopenharmony_ci return -ENOMEM; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ac->port[dir].buf = buf; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci buf[0].phys = phys; 52662306a36Sopenharmony_ci buf[0].size = period_sz; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci for (cnt = 1; cnt < periods; cnt++) { 52962306a36Sopenharmony_ci if (period_sz > 0) { 53062306a36Sopenharmony_ci buf[cnt].phys = buf[0].phys + (cnt * period_sz); 53162306a36Sopenharmony_ci buf[cnt].size = period_sz; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci ac->port[dir].num_periods = periods; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci rc = __q6asm_memory_map_regions(ac, dir, period_sz, periods, 1); 53962306a36Sopenharmony_ci if (rc < 0) { 54062306a36Sopenharmony_ci dev_err(ac->dev, "Memory_map_regions failed\n"); 54162306a36Sopenharmony_ci q6asm_audio_client_free_buf(ac, &ac->port[dir]); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return rc; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_map_memory_regions); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void q6asm_audio_client_release(struct kref *ref) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct audio_client *ac; 55162306a36Sopenharmony_ci struct q6asm *a; 55262306a36Sopenharmony_ci unsigned long flags; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ac = container_of(ref, struct audio_client, refcount); 55562306a36Sopenharmony_ci a = ac->q6asm; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci spin_lock_irqsave(&a->slock, flags); 55862306a36Sopenharmony_ci a->session[ac->session] = NULL; 55962306a36Sopenharmony_ci spin_unlock_irqrestore(&a->slock, flags); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci kfree(ac); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/** 56562306a36Sopenharmony_ci * q6asm_audio_client_free() - Freee allocated audio client 56662306a36Sopenharmony_ci * 56762306a36Sopenharmony_ci * @ac: audio client to free 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_civoid q6asm_audio_client_free(struct audio_client *ac) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci kref_put(&ac->refcount, q6asm_audio_client_release); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_audio_client_free); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic struct audio_client *q6asm_get_audio_client(struct q6asm *a, 57662306a36Sopenharmony_ci int session_id) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct audio_client *ac = NULL; 57962306a36Sopenharmony_ci unsigned long flags; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci spin_lock_irqsave(&a->slock, flags); 58262306a36Sopenharmony_ci if ((session_id <= 0) || (session_id > MAX_SESSIONS)) { 58362306a36Sopenharmony_ci dev_err(a->dev, "invalid session: %d\n", session_id); 58462306a36Sopenharmony_ci goto err; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* check for valid session */ 58862306a36Sopenharmony_ci if (!a->session[session_id]) 58962306a36Sopenharmony_ci goto err; 59062306a36Sopenharmony_ci else if (a->session[session_id]->session != session_id) 59162306a36Sopenharmony_ci goto err; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ac = a->session[session_id]; 59462306a36Sopenharmony_ci kref_get(&ac->refcount); 59562306a36Sopenharmony_cierr: 59662306a36Sopenharmony_ci spin_unlock_irqrestore(&a->slock, flags); 59762306a36Sopenharmony_ci return ac; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int32_t q6asm_stream_callback(struct apr_device *adev, 60162306a36Sopenharmony_ci struct apr_resp_pkt *data, 60262306a36Sopenharmony_ci int session_id) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct q6asm *q6asm = dev_get_drvdata(&adev->dev); 60562306a36Sopenharmony_ci struct aprv2_ibasic_rsp_result_t *result; 60662306a36Sopenharmony_ci struct apr_hdr *hdr = &data->hdr; 60762306a36Sopenharmony_ci struct audio_port_data *port; 60862306a36Sopenharmony_ci struct audio_client *ac; 60962306a36Sopenharmony_ci uint32_t client_event = 0; 61062306a36Sopenharmony_ci int ret = 0; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ac = q6asm_get_audio_client(q6asm, session_id); 61362306a36Sopenharmony_ci if (!ac)/* Audio client might already be freed by now */ 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci result = data->payload; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci switch (hdr->opcode) { 61962306a36Sopenharmony_ci case APR_BASIC_RSP_RESULT: 62062306a36Sopenharmony_ci switch (result->opcode) { 62162306a36Sopenharmony_ci case ASM_SESSION_CMD_PAUSE: 62262306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE; 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci case ASM_SESSION_CMD_SUSPEND: 62562306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci case ASM_STREAM_CMD_FLUSH: 62862306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE; 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci case ASM_SESSION_CMD_RUN_V2: 63162306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE; 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci case ASM_STREAM_CMD_CLOSE: 63462306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case ASM_STREAM_CMD_FLUSH_READBUFS: 63762306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE; 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci case ASM_STREAM_CMD_OPEN_WRITE_V3: 64062306a36Sopenharmony_ci case ASM_STREAM_CMD_OPEN_READ_V3: 64162306a36Sopenharmony_ci case ASM_STREAM_CMD_OPEN_READWRITE_V2: 64262306a36Sopenharmony_ci case ASM_STREAM_CMD_SET_ENCDEC_PARAM: 64362306a36Sopenharmony_ci case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: 64462306a36Sopenharmony_ci case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: 64562306a36Sopenharmony_ci case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: 64662306a36Sopenharmony_ci if (result->status != 0) { 64762306a36Sopenharmony_ci dev_err(ac->dev, 64862306a36Sopenharmony_ci "cmd = 0x%x returned error = 0x%x\n", 64962306a36Sopenharmony_ci result->opcode, result->status); 65062306a36Sopenharmony_ci ac->result = *result; 65162306a36Sopenharmony_ci wake_up(&ac->cmd_wait); 65262306a36Sopenharmony_ci ret = 0; 65362306a36Sopenharmony_ci goto done; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci default: 65762306a36Sopenharmony_ci dev_err(ac->dev, "command[0x%x] not expecting rsp\n", 65862306a36Sopenharmony_ci result->opcode); 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci ac->result = *result; 66362306a36Sopenharmony_ci wake_up(&ac->cmd_wait); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (ac->cb) 66662306a36Sopenharmony_ci ac->cb(client_event, hdr->token, 66762306a36Sopenharmony_ci data->payload, ac->priv); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci ret = 0; 67062306a36Sopenharmony_ci goto done; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci case ASM_DATA_EVENT_WRITE_DONE_V2: 67362306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE; 67462306a36Sopenharmony_ci if (ac->io_mode & ASM_SYNC_IO_MODE) { 67562306a36Sopenharmony_ci phys_addr_t phys; 67662306a36Sopenharmony_ci unsigned long flags; 67762306a36Sopenharmony_ci int token = hdr->token & ASM_WRITE_TOKEN_MASK; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!port->buf) { 68462306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 68562306a36Sopenharmony_ci ret = 0; 68662306a36Sopenharmony_ci goto done; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci phys = port->buf[token].phys; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (lower_32_bits(phys) != result->opcode || 69262306a36Sopenharmony_ci upper_32_bits(phys) != result->status) { 69362306a36Sopenharmony_ci dev_err(ac->dev, "Expected addr %pa\n", 69462306a36Sopenharmony_ci &port->buf[token].phys); 69562306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 69662306a36Sopenharmony_ci ret = -EINVAL; 69762306a36Sopenharmony_ci goto done; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci case ASM_DATA_EVENT_READ_DONE_V2: 70362306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_DATA_READ_DONE; 70462306a36Sopenharmony_ci if (ac->io_mode & ASM_SYNC_IO_MODE) { 70562306a36Sopenharmony_ci struct asm_data_cmd_read_v2_done *done = data->payload; 70662306a36Sopenharmony_ci unsigned long flags; 70762306a36Sopenharmony_ci phys_addr_t phys; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 71062306a36Sopenharmony_ci port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; 71162306a36Sopenharmony_ci if (!port->buf) { 71262306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 71362306a36Sopenharmony_ci ret = 0; 71462306a36Sopenharmony_ci goto done; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci phys = port->buf[hdr->token].phys; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (upper_32_bits(phys) != done->buf_addr_msw || 72062306a36Sopenharmony_ci lower_32_bits(phys) != done->buf_addr_lsw) { 72162306a36Sopenharmony_ci dev_err(ac->dev, "Expected addr %pa %08x-%08x\n", 72262306a36Sopenharmony_ci &port->buf[hdr->token].phys, 72362306a36Sopenharmony_ci done->buf_addr_lsw, 72462306a36Sopenharmony_ci done->buf_addr_msw); 72562306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 72662306a36Sopenharmony_ci ret = -EINVAL; 72762306a36Sopenharmony_ci goto done; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci case ASM_DATA_EVENT_RENDERED_EOS: 73462306a36Sopenharmony_ci client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE; 73562306a36Sopenharmony_ci break; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (ac->cb) 73962306a36Sopenharmony_ci ac->cb(client_event, hdr->token, data->payload, ac->priv); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cidone: 74262306a36Sopenharmony_ci kref_put(&ac->refcount, q6asm_audio_client_release); 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int q6asm_srvc_callback(struct apr_device *adev, 74762306a36Sopenharmony_ci struct apr_resp_pkt *data) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct q6asm *q6asm = dev_get_drvdata(&adev->dev); 75062306a36Sopenharmony_ci struct aprv2_ibasic_rsp_result_t *result; 75162306a36Sopenharmony_ci struct audio_port_data *port; 75262306a36Sopenharmony_ci struct audio_client *ac = NULL; 75362306a36Sopenharmony_ci struct apr_hdr *hdr = &data->hdr; 75462306a36Sopenharmony_ci struct q6asm *a; 75562306a36Sopenharmony_ci uint32_t sid = 0; 75662306a36Sopenharmony_ci uint32_t dir = 0; 75762306a36Sopenharmony_ci int session_id; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci session_id = (hdr->dest_port >> 8) & 0xFF; 76062306a36Sopenharmony_ci if (session_id) 76162306a36Sopenharmony_ci return q6asm_stream_callback(adev, data, session_id); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci sid = (hdr->token >> 8) & 0x0F; 76462306a36Sopenharmony_ci ac = q6asm_get_audio_client(q6asm, sid); 76562306a36Sopenharmony_ci if (!ac) { 76662306a36Sopenharmony_ci dev_err(&adev->dev, "Audio Client not active\n"); 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci a = dev_get_drvdata(ac->dev->parent); 77162306a36Sopenharmony_ci dir = (hdr->token & 0x0F); 77262306a36Sopenharmony_ci port = &ac->port[dir]; 77362306a36Sopenharmony_ci result = data->payload; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci switch (hdr->opcode) { 77662306a36Sopenharmony_ci case APR_BASIC_RSP_RESULT: 77762306a36Sopenharmony_ci switch (result->opcode) { 77862306a36Sopenharmony_ci case ASM_CMD_SHARED_MEM_MAP_REGIONS: 77962306a36Sopenharmony_ci case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: 78062306a36Sopenharmony_ci ac->result = *result; 78162306a36Sopenharmony_ci wake_up(&a->mem_wait); 78262306a36Sopenharmony_ci break; 78362306a36Sopenharmony_ci default: 78462306a36Sopenharmony_ci dev_err(&adev->dev, "command[0x%x] not expecting rsp\n", 78562306a36Sopenharmony_ci result->opcode); 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci goto done; 78962306a36Sopenharmony_ci case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS: 79062306a36Sopenharmony_ci ac->result.status = 0; 79162306a36Sopenharmony_ci ac->result.opcode = hdr->opcode; 79262306a36Sopenharmony_ci port->mem_map_handle = result->opcode; 79362306a36Sopenharmony_ci wake_up(&a->mem_wait); 79462306a36Sopenharmony_ci break; 79562306a36Sopenharmony_ci case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: 79662306a36Sopenharmony_ci ac->result.opcode = hdr->opcode; 79762306a36Sopenharmony_ci ac->result.status = 0; 79862306a36Sopenharmony_ci port->mem_map_handle = 0; 79962306a36Sopenharmony_ci wake_up(&a->mem_wait); 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci default: 80262306a36Sopenharmony_ci dev_dbg(&adev->dev, "command[0x%x]success [0x%x]\n", 80362306a36Sopenharmony_ci result->opcode, result->status); 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (ac->cb) 80862306a36Sopenharmony_ci ac->cb(hdr->opcode, hdr->token, data->payload, ac->priv); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cidone: 81162306a36Sopenharmony_ci kref_put(&ac->refcount, q6asm_audio_client_release); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/** 81762306a36Sopenharmony_ci * q6asm_get_session_id() - get session id for audio client 81862306a36Sopenharmony_ci * 81962306a36Sopenharmony_ci * @c: audio client pointer 82062306a36Sopenharmony_ci * 82162306a36Sopenharmony_ci * Return: Will be an session id of the audio client. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ciint q6asm_get_session_id(struct audio_client *c) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci return c->session; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_get_session_id); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/** 83062306a36Sopenharmony_ci * q6asm_audio_client_alloc() - Allocate a new audio client 83162306a36Sopenharmony_ci * 83262306a36Sopenharmony_ci * @dev: Pointer to asm child device. 83362306a36Sopenharmony_ci * @cb: event callback. 83462306a36Sopenharmony_ci * @priv: private data associated with this client. 83562306a36Sopenharmony_ci * @session_id: session id 83662306a36Sopenharmony_ci * @perf_mode: performace mode for this client 83762306a36Sopenharmony_ci * 83862306a36Sopenharmony_ci * Return: Will be an error pointer on error or a valid audio client 83962306a36Sopenharmony_ci * on success. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_cistruct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, 84262306a36Sopenharmony_ci void *priv, int session_id, 84362306a36Sopenharmony_ci int perf_mode) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct q6asm *a = dev_get_drvdata(dev->parent); 84662306a36Sopenharmony_ci struct audio_client *ac; 84762306a36Sopenharmony_ci unsigned long flags; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci ac = q6asm_get_audio_client(a, session_id + 1); 85062306a36Sopenharmony_ci if (ac) { 85162306a36Sopenharmony_ci dev_err(dev, "Audio Client already active\n"); 85262306a36Sopenharmony_ci return ac; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci ac = kzalloc(sizeof(*ac), GFP_KERNEL); 85662306a36Sopenharmony_ci if (!ac) 85762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci spin_lock_irqsave(&a->slock, flags); 86062306a36Sopenharmony_ci a->session[session_id + 1] = ac; 86162306a36Sopenharmony_ci spin_unlock_irqrestore(&a->slock, flags); 86262306a36Sopenharmony_ci ac->session = session_id + 1; 86362306a36Sopenharmony_ci ac->cb = cb; 86462306a36Sopenharmony_ci ac->dev = dev; 86562306a36Sopenharmony_ci ac->q6asm = a; 86662306a36Sopenharmony_ci ac->priv = priv; 86762306a36Sopenharmony_ci ac->io_mode = ASM_SYNC_IO_MODE; 86862306a36Sopenharmony_ci ac->perf_mode = perf_mode; 86962306a36Sopenharmony_ci ac->adev = a->adev; 87062306a36Sopenharmony_ci kref_init(&ac->refcount); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci init_waitqueue_head(&ac->cmd_wait); 87362306a36Sopenharmony_ci mutex_init(&ac->cmd_lock); 87462306a36Sopenharmony_ci spin_lock_init(&ac->lock); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return ac; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_audio_client_alloc); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic int q6asm_ac_send_cmd_sync(struct audio_client *ac, struct apr_pkt *pkt) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct apr_hdr *hdr = &pkt->hdr; 88362306a36Sopenharmony_ci int rc; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci mutex_lock(&ac->cmd_lock); 88662306a36Sopenharmony_ci ac->result.opcode = 0; 88762306a36Sopenharmony_ci ac->result.status = 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci rc = apr_send_pkt(ac->adev, pkt); 89062306a36Sopenharmony_ci if (rc < 0) 89162306a36Sopenharmony_ci goto err; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci rc = wait_event_timeout(ac->cmd_wait, 89462306a36Sopenharmony_ci (ac->result.opcode == hdr->opcode), 5 * HZ); 89562306a36Sopenharmony_ci if (!rc) { 89662306a36Sopenharmony_ci dev_err(ac->dev, "CMD %x timeout\n", hdr->opcode); 89762306a36Sopenharmony_ci rc = -ETIMEDOUT; 89862306a36Sopenharmony_ci goto err; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (ac->result.status > 0) { 90262306a36Sopenharmony_ci dev_err(ac->dev, "DSP returned error[%x]\n", 90362306a36Sopenharmony_ci ac->result.status); 90462306a36Sopenharmony_ci rc = -EINVAL; 90562306a36Sopenharmony_ci } else { 90662306a36Sopenharmony_ci rc = 0; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cierr: 91162306a36Sopenharmony_ci mutex_unlock(&ac->cmd_lock); 91262306a36Sopenharmony_ci return rc; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/** 91662306a36Sopenharmony_ci * q6asm_open_write() - Open audio client for writing 91762306a36Sopenharmony_ci * @ac: audio client pointer 91862306a36Sopenharmony_ci * @stream_id: stream id of q6asm session 91962306a36Sopenharmony_ci * @format: audio sample format 92062306a36Sopenharmony_ci * @codec_profile: compressed format profile 92162306a36Sopenharmony_ci * @bits_per_sample: bits per sample 92262306a36Sopenharmony_ci * @is_gapless: flag to indicate if this is a gapless stream 92362306a36Sopenharmony_ci * 92462306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ciint q6asm_open_write(struct audio_client *ac, uint32_t stream_id, 92762306a36Sopenharmony_ci uint32_t format, u32 codec_profile, 92862306a36Sopenharmony_ci uint16_t bits_per_sample, bool is_gapless) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct asm_stream_cmd_open_write_v3 *open; 93162306a36Sopenharmony_ci struct apr_pkt *pkt; 93262306a36Sopenharmony_ci void *p; 93362306a36Sopenharmony_ci int rc, pkt_size; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*open); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 93862306a36Sopenharmony_ci if (!p) 93962306a36Sopenharmony_ci return -ENOMEM; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci pkt = p; 94262306a36Sopenharmony_ci open = p + APR_HDR_SIZE; 94362306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; 94662306a36Sopenharmony_ci open->mode_flags = 0x00; 94762306a36Sopenharmony_ci open->mode_flags |= ASM_LEGACY_STREAM_SESSION; 94862306a36Sopenharmony_ci if (is_gapless) 94962306a36Sopenharmony_ci open->mode_flags |= BIT(ASM_SHIFT_GAPLESS_MODE_FLAG); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* source endpoint : matrix */ 95262306a36Sopenharmony_ci open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; 95362306a36Sopenharmony_ci open->bits_per_sample = bits_per_sample; 95462306a36Sopenharmony_ci open->postprocopo_id = ASM_NULL_POPP_TOPOLOGY; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci switch (format) { 95762306a36Sopenharmony_ci case SND_AUDIOCODEC_MP3: 95862306a36Sopenharmony_ci open->dec_fmt_id = ASM_MEDIA_FMT_MP3; 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci case FORMAT_LINEAR_PCM: 96162306a36Sopenharmony_ci open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; 96262306a36Sopenharmony_ci break; 96362306a36Sopenharmony_ci case SND_AUDIOCODEC_FLAC: 96462306a36Sopenharmony_ci open->dec_fmt_id = ASM_MEDIA_FMT_FLAC; 96562306a36Sopenharmony_ci break; 96662306a36Sopenharmony_ci case SND_AUDIOCODEC_WMA: 96762306a36Sopenharmony_ci switch (codec_profile) { 96862306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA9: 96962306a36Sopenharmony_ci open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V9; 97062306a36Sopenharmony_ci break; 97162306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA10: 97262306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA9_PRO: 97362306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA9_LOSSLESS: 97462306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA10_LOSSLESS: 97562306a36Sopenharmony_ci open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V10; 97662306a36Sopenharmony_ci break; 97762306a36Sopenharmony_ci default: 97862306a36Sopenharmony_ci dev_err(ac->dev, "Invalid codec profile 0x%x\n", 97962306a36Sopenharmony_ci codec_profile); 98062306a36Sopenharmony_ci rc = -EINVAL; 98162306a36Sopenharmony_ci goto err; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci case SND_AUDIOCODEC_ALAC: 98562306a36Sopenharmony_ci open->dec_fmt_id = ASM_MEDIA_FMT_ALAC; 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci case SND_AUDIOCODEC_APE: 98862306a36Sopenharmony_ci open->dec_fmt_id = ASM_MEDIA_FMT_APE; 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci default: 99162306a36Sopenharmony_ci dev_err(ac->dev, "Invalid format 0x%x\n", format); 99262306a36Sopenharmony_ci rc = -EINVAL; 99362306a36Sopenharmony_ci goto err; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 99762306a36Sopenharmony_ci if (rc < 0) 99862306a36Sopenharmony_ci goto err; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci ac->io_mode |= ASM_TUN_WRITE_IO_MODE; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cierr: 100362306a36Sopenharmony_ci kfree(pkt); 100462306a36Sopenharmony_ci return rc; 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_open_write); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic int __q6asm_run(struct audio_client *ac, uint32_t stream_id, 100962306a36Sopenharmony_ci uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts, 101062306a36Sopenharmony_ci bool wait) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct asm_session_cmd_run_v2 *run; 101362306a36Sopenharmony_ci struct apr_pkt *pkt; 101462306a36Sopenharmony_ci int pkt_size, rc; 101562306a36Sopenharmony_ci void *p; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*run); 101862306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_ATOMIC); 101962306a36Sopenharmony_ci if (!p) 102062306a36Sopenharmony_ci return -ENOMEM; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci pkt = p; 102362306a36Sopenharmony_ci run = p + APR_HDR_SIZE; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2; 102862306a36Sopenharmony_ci run->flags = flags; 102962306a36Sopenharmony_ci run->time_lsw = lsw_ts; 103062306a36Sopenharmony_ci run->time_msw = msw_ts; 103162306a36Sopenharmony_ci if (wait) { 103262306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 103362306a36Sopenharmony_ci } else { 103462306a36Sopenharmony_ci rc = apr_send_pkt(ac->adev, pkt); 103562306a36Sopenharmony_ci if (rc == pkt_size) 103662306a36Sopenharmony_ci rc = 0; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci kfree(pkt); 104062306a36Sopenharmony_ci return rc; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci/** 104462306a36Sopenharmony_ci * q6asm_run() - start the audio client 104562306a36Sopenharmony_ci * 104662306a36Sopenharmony_ci * @ac: audio client pointer 104762306a36Sopenharmony_ci * @stream_id: stream id of q6asm session 104862306a36Sopenharmony_ci * @flags: flags associated with write 104962306a36Sopenharmony_ci * @msw_ts: timestamp msw 105062306a36Sopenharmony_ci * @lsw_ts: timestamp lsw 105162306a36Sopenharmony_ci * 105262306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ciint q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, 105562306a36Sopenharmony_ci uint32_t msw_ts, uint32_t lsw_ts) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, true); 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_run); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci/** 106262306a36Sopenharmony_ci * q6asm_run_nowait() - start the audio client withou blocking 106362306a36Sopenharmony_ci * 106462306a36Sopenharmony_ci * @ac: audio client pointer 106562306a36Sopenharmony_ci * @stream_id: stream id 106662306a36Sopenharmony_ci * @flags: flags associated with write 106762306a36Sopenharmony_ci * @msw_ts: timestamp msw 106862306a36Sopenharmony_ci * @lsw_ts: timestamp lsw 106962306a36Sopenharmony_ci * 107062306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_ciint q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, 107362306a36Sopenharmony_ci uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, false); 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_run_nowait); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/** 108062306a36Sopenharmony_ci * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration 108162306a36Sopenharmony_ci * 108262306a36Sopenharmony_ci * @ac: audio client pointer 108362306a36Sopenharmony_ci * @stream_id: stream id 108462306a36Sopenharmony_ci * @rate: audio sample rate 108562306a36Sopenharmony_ci * @channels: number of audio channels. 108662306a36Sopenharmony_ci * @channel_map: channel map pointer 108762306a36Sopenharmony_ci * @bits_per_sample: bits per sample 108862306a36Sopenharmony_ci * 108962306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 109062306a36Sopenharmony_ci */ 109162306a36Sopenharmony_ciint q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, 109262306a36Sopenharmony_ci uint32_t stream_id, 109362306a36Sopenharmony_ci uint32_t rate, uint32_t channels, 109462306a36Sopenharmony_ci u8 channel_map[PCM_MAX_NUM_CHANNEL], 109562306a36Sopenharmony_ci uint16_t bits_per_sample) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct asm_multi_channel_pcm_fmt_blk_v2 *fmt; 109862306a36Sopenharmony_ci struct apr_pkt *pkt; 109962306a36Sopenharmony_ci u8 *channel_mapping; 110062306a36Sopenharmony_ci void *p; 110162306a36Sopenharmony_ci int rc, pkt_size; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*fmt); 110462306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 110562306a36Sopenharmony_ci if (!p) 110662306a36Sopenharmony_ci return -ENOMEM; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci pkt = p; 110962306a36Sopenharmony_ci fmt = p + APR_HDR_SIZE; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; 111462306a36Sopenharmony_ci fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); 111562306a36Sopenharmony_ci fmt->num_channels = channels; 111662306a36Sopenharmony_ci fmt->bits_per_sample = bits_per_sample; 111762306a36Sopenharmony_ci fmt->sample_rate = rate; 111862306a36Sopenharmony_ci fmt->is_signed = 1; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci channel_mapping = fmt->channel_mapping; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (channel_map) { 112362306a36Sopenharmony_ci memcpy(channel_mapping, channel_map, PCM_MAX_NUM_CHANNEL); 112462306a36Sopenharmony_ci } else { 112562306a36Sopenharmony_ci if (q6dsp_map_channels(channel_mapping, channels)) { 112662306a36Sopenharmony_ci dev_err(ac->dev, " map channels failed %d\n", channels); 112762306a36Sopenharmony_ci rc = -EINVAL; 112862306a36Sopenharmony_ci goto err; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cierr: 113562306a36Sopenharmony_ci kfree(pkt); 113662306a36Sopenharmony_ci return rc; 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ciint q6asm_stream_media_format_block_flac(struct audio_client *ac, 114162306a36Sopenharmony_ci uint32_t stream_id, 114262306a36Sopenharmony_ci struct q6asm_flac_cfg *cfg) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci struct asm_flac_fmt_blk_v2 *fmt; 114562306a36Sopenharmony_ci struct apr_pkt *pkt; 114662306a36Sopenharmony_ci void *p; 114762306a36Sopenharmony_ci int rc, pkt_size; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*fmt); 115062306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 115162306a36Sopenharmony_ci if (!p) 115262306a36Sopenharmony_ci return -ENOMEM; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci pkt = p; 115562306a36Sopenharmony_ci fmt = p + APR_HDR_SIZE; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; 116062306a36Sopenharmony_ci fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); 116162306a36Sopenharmony_ci fmt->is_stream_info_present = cfg->stream_info_present; 116262306a36Sopenharmony_ci fmt->num_channels = cfg->ch_cfg; 116362306a36Sopenharmony_ci fmt->min_blk_size = cfg->min_blk_size; 116462306a36Sopenharmony_ci fmt->max_blk_size = cfg->max_blk_size; 116562306a36Sopenharmony_ci fmt->sample_rate = cfg->sample_rate; 116662306a36Sopenharmony_ci fmt->min_frame_size = cfg->min_frame_size; 116762306a36Sopenharmony_ci fmt->max_frame_size = cfg->max_frame_size; 116862306a36Sopenharmony_ci fmt->sample_size = cfg->sample_size; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 117162306a36Sopenharmony_ci kfree(pkt); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci return rc; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ciint q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, 117862306a36Sopenharmony_ci uint32_t stream_id, 117962306a36Sopenharmony_ci struct q6asm_wma_cfg *cfg) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct asm_wmastdv9_fmt_blk_v2 *fmt; 118262306a36Sopenharmony_ci struct apr_pkt *pkt; 118362306a36Sopenharmony_ci void *p; 118462306a36Sopenharmony_ci int rc, pkt_size; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*fmt); 118762306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 118862306a36Sopenharmony_ci if (!p) 118962306a36Sopenharmony_ci return -ENOMEM; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci pkt = p; 119262306a36Sopenharmony_ci fmt = p + APR_HDR_SIZE; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; 119762306a36Sopenharmony_ci fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); 119862306a36Sopenharmony_ci fmt->fmtag = cfg->fmtag; 119962306a36Sopenharmony_ci fmt->num_channels = cfg->num_channels; 120062306a36Sopenharmony_ci fmt->sample_rate = cfg->sample_rate; 120162306a36Sopenharmony_ci fmt->bytes_per_sec = cfg->bytes_per_sec; 120262306a36Sopenharmony_ci fmt->blk_align = cfg->block_align; 120362306a36Sopenharmony_ci fmt->bits_per_sample = cfg->bits_per_sample; 120462306a36Sopenharmony_ci fmt->channel_mask = cfg->channel_mask; 120562306a36Sopenharmony_ci fmt->enc_options = cfg->enc_options; 120662306a36Sopenharmony_ci fmt->reserved = 0; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 120962306a36Sopenharmony_ci kfree(pkt); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci return rc; 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ciint q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, 121662306a36Sopenharmony_ci uint32_t stream_id, 121762306a36Sopenharmony_ci struct q6asm_wma_cfg *cfg) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci struct asm_wmaprov10_fmt_blk_v2 *fmt; 122062306a36Sopenharmony_ci struct apr_pkt *pkt; 122162306a36Sopenharmony_ci void *p; 122262306a36Sopenharmony_ci int rc, pkt_size; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*fmt); 122562306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 122662306a36Sopenharmony_ci if (!p) 122762306a36Sopenharmony_ci return -ENOMEM; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci pkt = p; 123062306a36Sopenharmony_ci fmt = p + APR_HDR_SIZE; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; 123562306a36Sopenharmony_ci fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); 123662306a36Sopenharmony_ci fmt->fmtag = cfg->fmtag; 123762306a36Sopenharmony_ci fmt->num_channels = cfg->num_channels; 123862306a36Sopenharmony_ci fmt->sample_rate = cfg->sample_rate; 123962306a36Sopenharmony_ci fmt->bytes_per_sec = cfg->bytes_per_sec; 124062306a36Sopenharmony_ci fmt->blk_align = cfg->block_align; 124162306a36Sopenharmony_ci fmt->bits_per_sample = cfg->bits_per_sample; 124262306a36Sopenharmony_ci fmt->channel_mask = cfg->channel_mask; 124362306a36Sopenharmony_ci fmt->enc_options = cfg->enc_options; 124462306a36Sopenharmony_ci fmt->advanced_enc_options1 = cfg->adv_enc_options; 124562306a36Sopenharmony_ci fmt->advanced_enc_options2 = cfg->adv_enc_options2; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 124862306a36Sopenharmony_ci kfree(pkt); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci return rc; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ciint q6asm_stream_media_format_block_alac(struct audio_client *ac, 125562306a36Sopenharmony_ci uint32_t stream_id, 125662306a36Sopenharmony_ci struct q6asm_alac_cfg *cfg) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci struct asm_alac_fmt_blk_v2 *fmt; 125962306a36Sopenharmony_ci struct apr_pkt *pkt; 126062306a36Sopenharmony_ci void *p; 126162306a36Sopenharmony_ci int rc, pkt_size; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*fmt); 126462306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 126562306a36Sopenharmony_ci if (!p) 126662306a36Sopenharmony_ci return -ENOMEM; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci pkt = p; 126962306a36Sopenharmony_ci fmt = p + APR_HDR_SIZE; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; 127462306a36Sopenharmony_ci fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci fmt->frame_length = cfg->frame_length; 127762306a36Sopenharmony_ci fmt->compatible_version = cfg->compatible_version; 127862306a36Sopenharmony_ci fmt->bit_depth = cfg->bit_depth; 127962306a36Sopenharmony_ci fmt->num_channels = cfg->num_channels; 128062306a36Sopenharmony_ci fmt->max_run = cfg->max_run; 128162306a36Sopenharmony_ci fmt->max_frame_bytes = cfg->max_frame_bytes; 128262306a36Sopenharmony_ci fmt->avg_bit_rate = cfg->avg_bit_rate; 128362306a36Sopenharmony_ci fmt->sample_rate = cfg->sample_rate; 128462306a36Sopenharmony_ci fmt->channel_layout_tag = cfg->channel_layout_tag; 128562306a36Sopenharmony_ci fmt->pb = cfg->pb; 128662306a36Sopenharmony_ci fmt->mb = cfg->mb; 128762306a36Sopenharmony_ci fmt->kb = cfg->kb; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 129062306a36Sopenharmony_ci kfree(pkt); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci return rc; 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ciint q6asm_stream_media_format_block_ape(struct audio_client *ac, 129762306a36Sopenharmony_ci uint32_t stream_id, 129862306a36Sopenharmony_ci struct q6asm_ape_cfg *cfg) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct asm_ape_fmt_blk_v2 *fmt; 130162306a36Sopenharmony_ci struct apr_pkt *pkt; 130262306a36Sopenharmony_ci void *p; 130362306a36Sopenharmony_ci int rc, pkt_size; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*fmt); 130662306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 130762306a36Sopenharmony_ci if (!p) 130862306a36Sopenharmony_ci return -ENOMEM; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci pkt = p; 131162306a36Sopenharmony_ci fmt = p + APR_HDR_SIZE; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; 131662306a36Sopenharmony_ci fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci fmt->compatible_version = cfg->compatible_version; 131962306a36Sopenharmony_ci fmt->compression_level = cfg->compression_level; 132062306a36Sopenharmony_ci fmt->format_flags = cfg->format_flags; 132162306a36Sopenharmony_ci fmt->blocks_per_frame = cfg->blocks_per_frame; 132262306a36Sopenharmony_ci fmt->final_frame_blocks = cfg->final_frame_blocks; 132362306a36Sopenharmony_ci fmt->total_frames = cfg->total_frames; 132462306a36Sopenharmony_ci fmt->bits_per_sample = cfg->bits_per_sample; 132562306a36Sopenharmony_ci fmt->num_channels = cfg->num_channels; 132662306a36Sopenharmony_ci fmt->sample_rate = cfg->sample_rate; 132762306a36Sopenharmony_ci fmt->seek_table_present = cfg->seek_table_present; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 133062306a36Sopenharmony_ci kfree(pkt); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci return rc; 133362306a36Sopenharmony_ci} 133462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_id, 133762306a36Sopenharmony_ci uint32_t cmd, 133862306a36Sopenharmony_ci uint32_t num_samples) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci uint32_t *samples; 134162306a36Sopenharmony_ci struct apr_pkt *pkt; 134262306a36Sopenharmony_ci void *p; 134362306a36Sopenharmony_ci int rc, pkt_size; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(uint32_t); 134662306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_ATOMIC); 134762306a36Sopenharmony_ci if (!p) 134862306a36Sopenharmony_ci return -ENOMEM; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci pkt = p; 135162306a36Sopenharmony_ci samples = p + APR_HDR_SIZE; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci pkt->hdr.opcode = cmd; 135662306a36Sopenharmony_ci *samples = num_samples; 135762306a36Sopenharmony_ci rc = apr_send_pkt(ac->adev, pkt); 135862306a36Sopenharmony_ci if (rc == pkt_size) 135962306a36Sopenharmony_ci rc = 0; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci kfree(pkt); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci return rc; 136462306a36Sopenharmony_ci} 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ciint q6asm_stream_remove_initial_silence(struct audio_client *ac, 136762306a36Sopenharmony_ci uint32_t stream_id, 136862306a36Sopenharmony_ci uint32_t initial_samples) 136962306a36Sopenharmony_ci{ 137062306a36Sopenharmony_ci return q6asm_stream_remove_silence(ac, stream_id, 137162306a36Sopenharmony_ci ASM_DATA_CMD_REMOVE_INITIAL_SILENCE, 137262306a36Sopenharmony_ci initial_samples); 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_stream_remove_initial_silence); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ciint q6asm_stream_remove_trailing_silence(struct audio_client *ac, uint32_t stream_id, 137762306a36Sopenharmony_ci uint32_t trailing_samples) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci return q6asm_stream_remove_silence(ac, stream_id, 138062306a36Sopenharmony_ci ASM_DATA_CMD_REMOVE_TRAILING_SILENCE, 138162306a36Sopenharmony_ci trailing_samples); 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_stream_remove_trailing_silence); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci/** 138662306a36Sopenharmony_ci * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture 138762306a36Sopenharmony_ci * 138862306a36Sopenharmony_ci * @ac: audio client pointer 138962306a36Sopenharmony_ci * @stream_id: stream id 139062306a36Sopenharmony_ci * @rate: audio sample rate 139162306a36Sopenharmony_ci * @channels: number of audio channels. 139262306a36Sopenharmony_ci * @bits_per_sample: bits per sample 139362306a36Sopenharmony_ci * 139462306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 139562306a36Sopenharmony_ci */ 139662306a36Sopenharmony_ciint q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, 139762306a36Sopenharmony_ci uint32_t stream_id, uint32_t rate, 139862306a36Sopenharmony_ci uint32_t channels, 139962306a36Sopenharmony_ci uint16_t bits_per_sample) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg; 140262306a36Sopenharmony_ci struct apr_pkt *pkt; 140362306a36Sopenharmony_ci u8 *channel_mapping; 140462306a36Sopenharmony_ci u32 frames_per_buf = 0; 140562306a36Sopenharmony_ci int pkt_size, rc; 140662306a36Sopenharmony_ci void *p; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg); 140962306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 141062306a36Sopenharmony_ci if (!p) 141162306a36Sopenharmony_ci return -ENOMEM; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci pkt = p; 141462306a36Sopenharmony_ci enc_cfg = p + APR_HDR_SIZE; 141562306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; 141862306a36Sopenharmony_ci enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; 141962306a36Sopenharmony_ci enc_cfg->encdec.param_size = sizeof(*enc_cfg) - sizeof(enc_cfg->encdec); 142062306a36Sopenharmony_ci enc_cfg->encblk.frames_per_buf = frames_per_buf; 142162306a36Sopenharmony_ci enc_cfg->encblk.enc_cfg_blk_size = enc_cfg->encdec.param_size - 142262306a36Sopenharmony_ci sizeof(struct asm_enc_cfg_blk_param_v2); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci enc_cfg->num_channels = channels; 142562306a36Sopenharmony_ci enc_cfg->bits_per_sample = bits_per_sample; 142662306a36Sopenharmony_ci enc_cfg->sample_rate = rate; 142762306a36Sopenharmony_ci enc_cfg->is_signed = 1; 142862306a36Sopenharmony_ci channel_mapping = enc_cfg->channel_mapping; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (q6dsp_map_channels(channel_mapping, channels)) { 143162306a36Sopenharmony_ci rc = -EINVAL; 143262306a36Sopenharmony_ci goto err; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 143662306a36Sopenharmony_cierr: 143762306a36Sopenharmony_ci kfree(pkt); 143862306a36Sopenharmony_ci return rc; 143962306a36Sopenharmony_ci} 144062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci/** 144462306a36Sopenharmony_ci * q6asm_read() - read data of period size from audio client 144562306a36Sopenharmony_ci * 144662306a36Sopenharmony_ci * @ac: audio client pointer 144762306a36Sopenharmony_ci * @stream_id: stream id 144862306a36Sopenharmony_ci * 144962306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_ciint q6asm_read(struct audio_client *ac, uint32_t stream_id) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci struct asm_data_cmd_read_v2 *read; 145462306a36Sopenharmony_ci struct audio_port_data *port; 145562306a36Sopenharmony_ci struct audio_buffer *ab; 145662306a36Sopenharmony_ci struct apr_pkt *pkt; 145762306a36Sopenharmony_ci unsigned long flags; 145862306a36Sopenharmony_ci int pkt_size; 145962306a36Sopenharmony_ci int rc = 0; 146062306a36Sopenharmony_ci void *p; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*read); 146362306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_ATOMIC); 146462306a36Sopenharmony_ci if (!p) 146562306a36Sopenharmony_ci return -ENOMEM; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci pkt = p; 146862306a36Sopenharmony_ci read = p + APR_HDR_SIZE; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 147162306a36Sopenharmony_ci port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; 147262306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); 147362306a36Sopenharmony_ci ab = &port->buf[port->dsp_buf]; 147462306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_READ_V2; 147562306a36Sopenharmony_ci read->buf_addr_lsw = lower_32_bits(ab->phys); 147662306a36Sopenharmony_ci read->buf_addr_msw = upper_32_bits(ab->phys); 147762306a36Sopenharmony_ci read->mem_map_handle = port->mem_map_handle; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci read->buf_size = ab->size; 148062306a36Sopenharmony_ci read->seq_id = port->dsp_buf; 148162306a36Sopenharmony_ci pkt->hdr.token = port->dsp_buf; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci port->dsp_buf++; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (port->dsp_buf >= port->num_periods) 148662306a36Sopenharmony_ci port->dsp_buf = 0; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 148962306a36Sopenharmony_ci rc = apr_send_pkt(ac->adev, pkt); 149062306a36Sopenharmony_ci if (rc == pkt_size) 149162306a36Sopenharmony_ci rc = 0; 149262306a36Sopenharmony_ci else 149362306a36Sopenharmony_ci pr_err("read op[0x%x]rc[%d]\n", pkt->hdr.opcode, rc); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci kfree(pkt); 149662306a36Sopenharmony_ci return rc; 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_read); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id, 150162306a36Sopenharmony_ci uint32_t format, uint16_t bits_per_sample) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci struct asm_stream_cmd_open_read_v3 *open; 150462306a36Sopenharmony_ci struct apr_pkt *pkt; 150562306a36Sopenharmony_ci int pkt_size, rc; 150662306a36Sopenharmony_ci void *p; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*open); 150962306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_KERNEL); 151062306a36Sopenharmony_ci if (!p) 151162306a36Sopenharmony_ci return -ENOMEM; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci pkt = p; 151462306a36Sopenharmony_ci open = p + APR_HDR_SIZE; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); 151762306a36Sopenharmony_ci pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; 151862306a36Sopenharmony_ci /* Stream prio : High, provide meta info with encoded frames */ 151962306a36Sopenharmony_ci open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci open->preprocopo_id = ASM_STREAM_POSTPROC_TOPO_ID_NONE; 152262306a36Sopenharmony_ci open->bits_per_sample = bits_per_sample; 152362306a36Sopenharmony_ci open->mode_flags = 0x0; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci open->mode_flags |= ASM_LEGACY_STREAM_SESSION << 152662306a36Sopenharmony_ci ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci switch (format) { 152962306a36Sopenharmony_ci case FORMAT_LINEAR_PCM: 153062306a36Sopenharmony_ci open->mode_flags |= 0x00; 153162306a36Sopenharmony_ci open->enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; 153262306a36Sopenharmony_ci break; 153362306a36Sopenharmony_ci default: 153462306a36Sopenharmony_ci pr_err("Invalid format[%d]\n", format); 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, pkt); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci kfree(pkt); 154062306a36Sopenharmony_ci return rc; 154162306a36Sopenharmony_ci} 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci/** 154462306a36Sopenharmony_ci * q6asm_open_read() - Open audio client for reading 154562306a36Sopenharmony_ci * 154662306a36Sopenharmony_ci * @ac: audio client pointer 154762306a36Sopenharmony_ci * @stream_id: stream id 154862306a36Sopenharmony_ci * @format: audio sample format 154962306a36Sopenharmony_ci * @bits_per_sample: bits per sample 155062306a36Sopenharmony_ci * 155162306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 155262306a36Sopenharmony_ci */ 155362306a36Sopenharmony_ciint q6asm_open_read(struct audio_client *ac, uint32_t stream_id, 155462306a36Sopenharmony_ci uint32_t format, uint16_t bits_per_sample) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci return __q6asm_open_read(ac, stream_id, format, bits_per_sample); 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_open_read); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci/** 156162306a36Sopenharmony_ci * q6asm_write_async() - non blocking write 156262306a36Sopenharmony_ci * 156362306a36Sopenharmony_ci * @ac: audio client pointer 156462306a36Sopenharmony_ci * @stream_id: stream id 156562306a36Sopenharmony_ci * @len: length in bytes 156662306a36Sopenharmony_ci * @msw_ts: timestamp msw 156762306a36Sopenharmony_ci * @lsw_ts: timestamp lsw 156862306a36Sopenharmony_ci * @wflags: flags associated with write 156962306a36Sopenharmony_ci * 157062306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_ciint q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, 157362306a36Sopenharmony_ci uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci struct asm_data_cmd_write_v2 *write; 157662306a36Sopenharmony_ci struct audio_port_data *port; 157762306a36Sopenharmony_ci struct audio_buffer *ab; 157862306a36Sopenharmony_ci unsigned long flags; 157962306a36Sopenharmony_ci struct apr_pkt *pkt; 158062306a36Sopenharmony_ci int pkt_size; 158162306a36Sopenharmony_ci int rc = 0; 158262306a36Sopenharmony_ci void *p; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci pkt_size = APR_HDR_SIZE + sizeof(*write); 158562306a36Sopenharmony_ci p = kzalloc(pkt_size, GFP_ATOMIC); 158662306a36Sopenharmony_ci if (!p) 158762306a36Sopenharmony_ci return -ENOMEM; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci pkt = p; 159062306a36Sopenharmony_ci write = p + APR_HDR_SIZE; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 159362306a36Sopenharmony_ci port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; 159462306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci ab = &port->buf[port->dsp_buf]; 159762306a36Sopenharmony_ci pkt->hdr.token = port->dsp_buf | (len << ASM_WRITE_TOKEN_LEN_SHIFT); 159862306a36Sopenharmony_ci pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2; 159962306a36Sopenharmony_ci write->buf_addr_lsw = lower_32_bits(ab->phys); 160062306a36Sopenharmony_ci write->buf_addr_msw = upper_32_bits(ab->phys); 160162306a36Sopenharmony_ci write->buf_size = len; 160262306a36Sopenharmony_ci write->seq_id = port->dsp_buf; 160362306a36Sopenharmony_ci write->timestamp_lsw = lsw_ts; 160462306a36Sopenharmony_ci write->timestamp_msw = msw_ts; 160562306a36Sopenharmony_ci write->mem_map_handle = 160662306a36Sopenharmony_ci ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci write->flags = wflags; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci port->dsp_buf++; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (port->dsp_buf >= port->num_periods) 161362306a36Sopenharmony_ci port->dsp_buf = 0; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 161662306a36Sopenharmony_ci rc = apr_send_pkt(ac->adev, pkt); 161762306a36Sopenharmony_ci if (rc == pkt_size) 161862306a36Sopenharmony_ci rc = 0; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci kfree(pkt); 162162306a36Sopenharmony_ci return rc; 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_write_async); 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_cistatic void q6asm_reset_buf_state(struct audio_client *ac) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci struct audio_port_data *port; 162862306a36Sopenharmony_ci unsigned long flags; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 163162306a36Sopenharmony_ci port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; 163262306a36Sopenharmony_ci port->dsp_buf = 0; 163362306a36Sopenharmony_ci port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; 163462306a36Sopenharmony_ci port->dsp_buf = 0; 163562306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cistatic int __q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd, 163962306a36Sopenharmony_ci bool wait) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct apr_pkt pkt; 164262306a36Sopenharmony_ci int rc; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci q6asm_add_hdr(ac, &pkt.hdr, APR_HDR_SIZE, true, stream_id); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci switch (cmd) { 164762306a36Sopenharmony_ci case CMD_PAUSE: 164862306a36Sopenharmony_ci pkt.hdr.opcode = ASM_SESSION_CMD_PAUSE; 164962306a36Sopenharmony_ci break; 165062306a36Sopenharmony_ci case CMD_SUSPEND: 165162306a36Sopenharmony_ci pkt.hdr.opcode = ASM_SESSION_CMD_SUSPEND; 165262306a36Sopenharmony_ci break; 165362306a36Sopenharmony_ci case CMD_FLUSH: 165462306a36Sopenharmony_ci pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH; 165562306a36Sopenharmony_ci break; 165662306a36Sopenharmony_ci case CMD_OUT_FLUSH: 165762306a36Sopenharmony_ci pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS; 165862306a36Sopenharmony_ci break; 165962306a36Sopenharmony_ci case CMD_EOS: 166062306a36Sopenharmony_ci pkt.hdr.opcode = ASM_DATA_CMD_EOS; 166162306a36Sopenharmony_ci break; 166262306a36Sopenharmony_ci case CMD_CLOSE: 166362306a36Sopenharmony_ci pkt.hdr.opcode = ASM_STREAM_CMD_CLOSE; 166462306a36Sopenharmony_ci break; 166562306a36Sopenharmony_ci default: 166662306a36Sopenharmony_ci return -EINVAL; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci if (wait) 167062306a36Sopenharmony_ci rc = q6asm_ac_send_cmd_sync(ac, &pkt); 167162306a36Sopenharmony_ci else 167262306a36Sopenharmony_ci return apr_send_pkt(ac->adev, &pkt); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci if (rc < 0) 167562306a36Sopenharmony_ci return rc; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (cmd == CMD_FLUSH) 167862306a36Sopenharmony_ci q6asm_reset_buf_state(ac); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci return 0; 168162306a36Sopenharmony_ci} 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci/** 168462306a36Sopenharmony_ci * q6asm_cmd() - run cmd on audio client 168562306a36Sopenharmony_ci * 168662306a36Sopenharmony_ci * @ac: audio client pointer 168762306a36Sopenharmony_ci * @stream_id: stream id 168862306a36Sopenharmony_ci * @cmd: command to run on audio client. 168962306a36Sopenharmony_ci * 169062306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 169162306a36Sopenharmony_ci */ 169262306a36Sopenharmony_ciint q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci return __q6asm_cmd(ac, stream_id, cmd, true); 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_cmd); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci/** 169962306a36Sopenharmony_ci * q6asm_cmd_nowait() - non blocking, run cmd on audio client 170062306a36Sopenharmony_ci * 170162306a36Sopenharmony_ci * @ac: audio client pointer 170262306a36Sopenharmony_ci * @stream_id: stream id 170362306a36Sopenharmony_ci * @cmd: command to run on audio client. 170462306a36Sopenharmony_ci * 170562306a36Sopenharmony_ci * Return: Will be an negative value on error or zero on success 170662306a36Sopenharmony_ci */ 170762306a36Sopenharmony_ciint q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci return __q6asm_cmd(ac, stream_id, cmd, false); 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(q6asm_cmd_nowait); 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_cistatic int q6asm_probe(struct apr_device *adev) 171462306a36Sopenharmony_ci{ 171562306a36Sopenharmony_ci struct device *dev = &adev->dev; 171662306a36Sopenharmony_ci struct q6asm *q6asm; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci q6asm = devm_kzalloc(dev, sizeof(*q6asm), GFP_KERNEL); 171962306a36Sopenharmony_ci if (!q6asm) 172062306a36Sopenharmony_ci return -ENOMEM; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci q6core_get_svc_api_info(adev->svc_id, &q6asm->ainfo); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci q6asm->dev = dev; 172562306a36Sopenharmony_ci q6asm->adev = adev; 172662306a36Sopenharmony_ci init_waitqueue_head(&q6asm->mem_wait); 172762306a36Sopenharmony_ci spin_lock_init(&q6asm->slock); 172862306a36Sopenharmony_ci dev_set_drvdata(dev, q6asm); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci return devm_of_platform_populate(dev); 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci#ifdef CONFIG_OF 173462306a36Sopenharmony_cistatic const struct of_device_id q6asm_device_id[] = { 173562306a36Sopenharmony_ci { .compatible = "qcom,q6asm" }, 173662306a36Sopenharmony_ci {}, 173762306a36Sopenharmony_ci}; 173862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, q6asm_device_id); 173962306a36Sopenharmony_ci#endif 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_cistatic struct apr_driver qcom_q6asm_driver = { 174262306a36Sopenharmony_ci .probe = q6asm_probe, 174362306a36Sopenharmony_ci .callback = q6asm_srvc_callback, 174462306a36Sopenharmony_ci .driver = { 174562306a36Sopenharmony_ci .name = "qcom-q6asm", 174662306a36Sopenharmony_ci .of_match_table = of_match_ptr(q6asm_device_id), 174762306a36Sopenharmony_ci }, 174862306a36Sopenharmony_ci}; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cimodule_apr_driver(qcom_q6asm_driver); 175162306a36Sopenharmony_ciMODULE_DESCRIPTION("Q6 Audio Stream Manager driver"); 175262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1753