1754 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.3// Copyright (c) 2018, Linaro Limited4 5#include <dt-bindings/sound/qcom,q6asm.h>6#include <linux/mutex.h>7#include <linux/wait.h>8#include <linux/module.h>9#include <linux/soc/qcom/apr.h>10#include <linux/device.h>11#include <linux/of_platform.h>12#include <linux/spinlock.h>13#include <linux/kref.h>14#include <linux/of.h>15#include <uapi/sound/asound.h>16#include <uapi/sound/compress_params.h>17#include <linux/delay.h>18#include <linux/slab.h>19#include <linux/mm.h>20#include "q6asm.h"21#include "q6core.h"22#include "q6dsp-errno.h"23#include "q6dsp-common.h"24 25#define ASM_STREAM_CMD_CLOSE 0x00010BCD26#define ASM_STREAM_CMD_FLUSH 0x00010BCE27#define ASM_SESSION_CMD_PAUSE 0x00010BD328#define ASM_DATA_CMD_EOS 0x00010BDB29#define ASM_DATA_EVENT_RENDERED_EOS 0x00010C1C30#define ASM_NULL_POPP_TOPOLOGY 0x00010C6831#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C0932#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C1033#define ASM_STREAM_POSTPROC_TOPO_ID_NONE 0x00010C6834#define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D9235#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D9336#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D9437#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D9838#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D9939#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA340#define ASM_SESSION_CMD_RUN_V2 0x00010DAA41#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA542#define ASM_MEDIA_FMT_MP3 0x00010BE943#define ASM_MEDIA_FMT_FLAC 0x00010C1644#define ASM_MEDIA_FMT_WMA_V9 0x00010DA845#define ASM_MEDIA_FMT_WMA_V10 0x00010DA746#define ASM_DATA_CMD_WRITE_V2 0x00010DAB47#define ASM_DATA_CMD_READ_V2 0x00010DAC48#define ASM_SESSION_CMD_SUSPEND 0x00010DEC49#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB350#define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB451#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A52#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D53#define ASM_MEDIA_FMT_ALAC 0x00012f3154#define ASM_MEDIA_FMT_APE 0x00012f3255#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D6756#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D6857 58 59#define ASM_LEGACY_STREAM_SESSION 060/* Bit shift for the stream_perf_mode subfield. */61#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ 2962#define ASM_END_POINT_DEVICE_MATRIX 063#define ASM_DEFAULT_APP_TYPE 064#define ASM_SYNC_IO_MODE 0x000165#define ASM_ASYNC_IO_MODE 0x000266#define ASM_TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */67#define ASM_TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */68#define ASM_SHIFT_GAPLESS_MODE_FLAG 3169#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 370 71struct avs_cmd_shared_mem_map_regions {72 u16 mem_pool_id;73 u16 num_regions;74 u32 property_flag;75} __packed;76 77struct avs_shared_map_region_payload {78 u32 shm_addr_lsw;79 u32 shm_addr_msw;80 u32 mem_size_bytes;81} __packed;82 83struct avs_cmd_shared_mem_unmap_regions {84 u32 mem_map_handle;85} __packed;86 87struct asm_data_cmd_media_fmt_update_v2 {88 u32 fmt_blk_size;89} __packed;90 91struct asm_multi_channel_pcm_fmt_blk_v2 {92 struct asm_data_cmd_media_fmt_update_v2 fmt_blk;93 u16 num_channels;94 u16 bits_per_sample;95 u32 sample_rate;96 u16 is_signed;97 u16 reserved;98 u8 channel_mapping[PCM_MAX_NUM_CHANNEL];99} __packed;100 101struct asm_flac_fmt_blk_v2 {102 struct asm_data_cmd_media_fmt_update_v2 fmt_blk;103 u16 is_stream_info_present;104 u16 num_channels;105 u16 min_blk_size;106 u16 max_blk_size;107 u16 md5_sum[8];108 u32 sample_rate;109 u32 min_frame_size;110 u32 max_frame_size;111 u16 sample_size;112 u16 reserved;113} __packed;114 115struct asm_wmastdv9_fmt_blk_v2 {116 struct asm_data_cmd_media_fmt_update_v2 fmt_blk;117 u16 fmtag;118 u16 num_channels;119 u32 sample_rate;120 u32 bytes_per_sec;121 u16 blk_align;122 u16 bits_per_sample;123 u32 channel_mask;124 u16 enc_options;125 u16 reserved;126} __packed;127 128struct asm_wmaprov10_fmt_blk_v2 {129 struct asm_data_cmd_media_fmt_update_v2 fmt_blk;130 u16 fmtag;131 u16 num_channels;132 u32 sample_rate;133 u32 bytes_per_sec;134 u16 blk_align;135 u16 bits_per_sample;136 u32 channel_mask;137 u16 enc_options;138 u16 advanced_enc_options1;139 u32 advanced_enc_options2;140} __packed;141 142struct asm_alac_fmt_blk_v2 {143 struct asm_data_cmd_media_fmt_update_v2 fmt_blk;144 u32 frame_length;145 u8 compatible_version;146 u8 bit_depth;147 u8 pb;148 u8 mb;149 u8 kb;150 u8 num_channels;151 u16 max_run;152 u32 max_frame_bytes;153 u32 avg_bit_rate;154 u32 sample_rate;155 u32 channel_layout_tag;156} __packed;157 158struct asm_ape_fmt_blk_v2 {159 struct asm_data_cmd_media_fmt_update_v2 fmt_blk;160 u16 compatible_version;161 u16 compression_level;162 u32 format_flags;163 u32 blocks_per_frame;164 u32 final_frame_blocks;165 u32 total_frames;166 u16 bits_per_sample;167 u16 num_channels;168 u32 sample_rate;169 u32 seek_table_present;170} __packed;171 172struct asm_stream_cmd_set_encdec_param {173 u32 param_id;174 u32 param_size;175} __packed;176 177struct asm_enc_cfg_blk_param_v2 {178 u32 frames_per_buf;179 u32 enc_cfg_blk_size;180} __packed;181 182struct asm_multi_channel_pcm_enc_cfg_v2 {183 struct asm_stream_cmd_set_encdec_param encdec;184 struct asm_enc_cfg_blk_param_v2 encblk;185 uint16_t num_channels;186 uint16_t bits_per_sample;187 uint32_t sample_rate;188 uint16_t is_signed;189 uint16_t reserved;190 uint8_t channel_mapping[8];191} __packed;192 193struct asm_data_cmd_read_v2 {194 u32 buf_addr_lsw;195 u32 buf_addr_msw;196 u32 mem_map_handle;197 u32 buf_size;198 u32 seq_id;199} __packed;200 201struct asm_data_cmd_read_v2_done {202 u32 status;203 u32 buf_addr_lsw;204 u32 buf_addr_msw;205};206 207struct asm_stream_cmd_open_read_v3 {208 u32 mode_flags;209 u32 src_endpointype;210 u32 preprocopo_id;211 u32 enc_cfg_id;212 u16 bits_per_sample;213 u16 reserved;214} __packed;215 216struct asm_data_cmd_write_v2 {217 u32 buf_addr_lsw;218 u32 buf_addr_msw;219 u32 mem_map_handle;220 u32 buf_size;221 u32 seq_id;222 u32 timestamp_lsw;223 u32 timestamp_msw;224 u32 flags;225} __packed;226 227struct asm_stream_cmd_open_write_v3 {228 uint32_t mode_flags;229 uint16_t sink_endpointype;230 uint16_t bits_per_sample;231 uint32_t postprocopo_id;232 uint32_t dec_fmt_id;233} __packed;234 235struct asm_session_cmd_run_v2 {236 u32 flags;237 u32 time_lsw;238 u32 time_msw;239} __packed;240 241struct audio_buffer {242 phys_addr_t phys;243 uint32_t size; /* size of buffer */244};245 246struct audio_port_data {247 struct audio_buffer *buf;248 uint32_t num_periods;249 uint32_t dsp_buf;250 uint32_t mem_map_handle;251};252 253struct q6asm {254 struct apr_device *adev;255 struct device *dev;256 struct q6core_svc_api_info ainfo;257 wait_queue_head_t mem_wait;258 spinlock_t slock;259 struct audio_client *session[MAX_SESSIONS + 1];260};261 262struct audio_client {263 int session;264 q6asm_cb cb;265 void *priv;266 uint32_t io_mode;267 struct apr_device *adev;268 struct mutex cmd_lock;269 spinlock_t lock;270 struct kref refcount;271 /* idx:1 out port, 0: in port */272 struct audio_port_data port[2];273 wait_queue_head_t cmd_wait;274 struct aprv2_ibasic_rsp_result_t result;275 int perf_mode;276 struct q6asm *q6asm;277 struct device *dev;278};279 280static inline void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,281 uint32_t pkt_size, bool cmd_flg,282 uint32_t stream_id)283{284 hdr->hdr_field = APR_SEQ_CMD_HDR_FIELD;285 hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);286 hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);287 hdr->pkt_size = pkt_size;288 if (cmd_flg)289 hdr->token = ac->session;290}291 292static int q6asm_apr_send_session_pkt(struct q6asm *a, struct audio_client *ac,293 struct apr_pkt *pkt, uint32_t rsp_opcode)294{295 struct apr_hdr *hdr = &pkt->hdr;296 int rc;297 298 mutex_lock(&ac->cmd_lock);299 ac->result.opcode = 0;300 ac->result.status = 0;301 rc = apr_send_pkt(a->adev, pkt);302 if (rc < 0)303 goto err;304 305 if (rsp_opcode)306 rc = wait_event_timeout(a->mem_wait,307 (ac->result.opcode == hdr->opcode) ||308 (ac->result.opcode == rsp_opcode),309 5 * HZ);310 else311 rc = wait_event_timeout(a->mem_wait,312 (ac->result.opcode == hdr->opcode),313 5 * HZ);314 315 if (!rc) {316 dev_err(a->dev, "CMD %x timeout\n", hdr->opcode);317 rc = -ETIMEDOUT;318 } else if (ac->result.status > 0) {319 dev_err(a->dev, "DSP returned error[%x]\n",320 ac->result.status);321 rc = -EINVAL;322 }323 324err:325 mutex_unlock(&ac->cmd_lock);326 return rc;327}328 329static int __q6asm_memory_unmap(struct audio_client *ac,330 phys_addr_t buf_add, int dir)331{332 struct avs_cmd_shared_mem_unmap_regions *mem_unmap;333 struct q6asm *a = dev_get_drvdata(ac->dev->parent);334 struct apr_pkt *pkt;335 int rc, pkt_size;336 void *p;337 338 if (ac->port[dir].mem_map_handle == 0) {339 dev_err(ac->dev, "invalid mem handle\n");340 return -EINVAL;341 }342 343 pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap);344 p = kzalloc(pkt_size, GFP_KERNEL);345 if (!p)346 return -ENOMEM;347 348 pkt = p;349 mem_unmap = p + APR_HDR_SIZE;350 351 pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD;352 pkt->hdr.src_port = 0;353 pkt->hdr.dest_port = 0;354 pkt->hdr.pkt_size = pkt_size;355 pkt->hdr.token = ((ac->session << 8) | dir);356 357 pkt->hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;358 mem_unmap->mem_map_handle = ac->port[dir].mem_map_handle;359 360 rc = q6asm_apr_send_session_pkt(a, ac, pkt, 0);361 if (rc < 0) {362 kfree(pkt);363 return rc;364 }365 366 ac->port[dir].mem_map_handle = 0;367 368 kfree(pkt);369 return 0;370}371 372 373static void q6asm_audio_client_free_buf(struct audio_client *ac,374 struct audio_port_data *port)375{376 unsigned long flags;377 378 spin_lock_irqsave(&ac->lock, flags);379 port->num_periods = 0;380 kfree(port->buf);381 port->buf = NULL;382 spin_unlock_irqrestore(&ac->lock, flags);383}384 385/**386 * q6asm_unmap_memory_regions() - unmap memory regions in the dsp.387 *388 * @dir: direction of audio stream389 * @ac: audio client instanace390 *391 * Return: Will be an negative value on failure or zero on success392 */393int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac)394{395 struct audio_port_data *port;396 int cnt = 0;397 int rc = 0;398 399 port = &ac->port[dir];400 if (!port->buf) {401 rc = -EINVAL;402 goto err;403 }404 405 cnt = port->num_periods - 1;406 if (cnt >= 0) {407 rc = __q6asm_memory_unmap(ac, port->buf[dir].phys, dir);408 if (rc < 0) {409 dev_err(ac->dev, "%s: Memory_unmap_regions failed %d\n",410 __func__, rc);411 goto err;412 }413 }414 415 q6asm_audio_client_free_buf(ac, port);416 417err:418 return rc;419}420EXPORT_SYMBOL_GPL(q6asm_unmap_memory_regions);421 422static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,423 size_t period_sz, unsigned int periods,424 bool is_contiguous)425{426 struct avs_cmd_shared_mem_map_regions *cmd = NULL;427 struct avs_shared_map_region_payload *mregions = NULL;428 struct q6asm *a = dev_get_drvdata(ac->dev->parent);429 struct audio_port_data *port = NULL;430 struct audio_buffer *ab = NULL;431 struct apr_pkt *pkt;432 void *p;433 unsigned long flags;434 uint32_t num_regions, buf_sz;435 int rc, i, pkt_size;436 437 if (is_contiguous) {438 num_regions = 1;439 buf_sz = period_sz * periods;440 } else {441 buf_sz = period_sz;442 num_regions = periods;443 }444 445 /* DSP expects size should be aligned to 4K */446 buf_sz = ALIGN(buf_sz, 4096);447 448 pkt_size = APR_HDR_SIZE + sizeof(*cmd) +449 (sizeof(*mregions) * num_regions);450 451 p = kzalloc(pkt_size, GFP_KERNEL);452 if (!p)453 return -ENOMEM;454 455 pkt = p;456 cmd = p + APR_HDR_SIZE;457 mregions = p + APR_HDR_SIZE + sizeof(*cmd);458 459 pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD;460 pkt->hdr.src_port = 0;461 pkt->hdr.dest_port = 0;462 pkt->hdr.pkt_size = pkt_size;463 pkt->hdr.token = ((ac->session << 8) | dir);464 pkt->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;465 466 cmd->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;467 cmd->num_regions = num_regions;468 cmd->property_flag = 0x00;469 470 spin_lock_irqsave(&ac->lock, flags);471 port = &ac->port[dir];472 473 for (i = 0; i < num_regions; i++) {474 ab = &port->buf[i];475 mregions->shm_addr_lsw = lower_32_bits(ab->phys);476 mregions->shm_addr_msw = upper_32_bits(ab->phys);477 mregions->mem_size_bytes = buf_sz;478 ++mregions;479 }480 spin_unlock_irqrestore(&ac->lock, flags);481 482 rc = q6asm_apr_send_session_pkt(a, ac, pkt,483 ASM_CMDRSP_SHARED_MEM_MAP_REGIONS);484 485 kfree(pkt);486 487 return rc;488}489 490/**491 * q6asm_map_memory_regions() - map memory regions in the dsp.492 *493 * @dir: direction of audio stream494 * @ac: audio client instanace495 * @phys: physical address that needs mapping.496 * @period_sz: audio period size497 * @periods: number of periods498 *499 * Return: Will be an negative value on failure or zero on success500 */501int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac,502 phys_addr_t phys,503 size_t period_sz, unsigned int periods)504{505 struct audio_buffer *buf;506 unsigned long flags;507 int cnt;508 int rc;509 510 spin_lock_irqsave(&ac->lock, flags);511 if (ac->port[dir].buf) {512 dev_err(ac->dev, "Buffer already allocated\n");513 spin_unlock_irqrestore(&ac->lock, flags);514 return 0;515 }516 517 buf = kcalloc(periods, sizeof(*buf), GFP_ATOMIC);518 if (!buf) {519 spin_unlock_irqrestore(&ac->lock, flags);520 return -ENOMEM;521 }522 523 524 ac->port[dir].buf = buf;525 526 buf[0].phys = phys;527 buf[0].size = period_sz;528 529 for (cnt = 1; cnt < periods; cnt++) {530 if (period_sz > 0) {531 buf[cnt].phys = buf[0].phys + (cnt * period_sz);532 buf[cnt].size = period_sz;533 }534 }535 ac->port[dir].num_periods = periods;536 537 spin_unlock_irqrestore(&ac->lock, flags);538 539 rc = __q6asm_memory_map_regions(ac, dir, period_sz, periods, 1);540 if (rc < 0) {541 dev_err(ac->dev, "Memory_map_regions failed\n");542 q6asm_audio_client_free_buf(ac, &ac->port[dir]);543 }544 545 return rc;546}547EXPORT_SYMBOL_GPL(q6asm_map_memory_regions);548 549static void q6asm_audio_client_release(struct kref *ref)550{551 struct audio_client *ac;552 struct q6asm *a;553 unsigned long flags;554 555 ac = container_of(ref, struct audio_client, refcount);556 a = ac->q6asm;557 558 spin_lock_irqsave(&a->slock, flags);559 a->session[ac->session] = NULL;560 spin_unlock_irqrestore(&a->slock, flags);561 562 kfree(ac);563}564 565/**566 * q6asm_audio_client_free() - Freee allocated audio client567 *568 * @ac: audio client to free569 */570void q6asm_audio_client_free(struct audio_client *ac)571{572 kref_put(&ac->refcount, q6asm_audio_client_release);573}574EXPORT_SYMBOL_GPL(q6asm_audio_client_free);575 576static struct audio_client *q6asm_get_audio_client(struct q6asm *a,577 int session_id)578{579 struct audio_client *ac = NULL;580 unsigned long flags;581 582 spin_lock_irqsave(&a->slock, flags);583 if ((session_id <= 0) || (session_id > MAX_SESSIONS)) {584 dev_err(a->dev, "invalid session: %d\n", session_id);585 goto err;586 }587 588 /* check for valid session */589 if (!a->session[session_id])590 goto err;591 else if (a->session[session_id]->session != session_id)592 goto err;593 594 ac = a->session[session_id];595 kref_get(&ac->refcount);596err:597 spin_unlock_irqrestore(&a->slock, flags);598 return ac;599}600 601static int32_t q6asm_stream_callback(struct apr_device *adev,602 struct apr_resp_pkt *data,603 int session_id)604{605 struct q6asm *q6asm = dev_get_drvdata(&adev->dev);606 struct aprv2_ibasic_rsp_result_t *result;607 struct apr_hdr *hdr = &data->hdr;608 struct audio_port_data *port;609 struct audio_client *ac;610 uint32_t client_event = 0;611 int ret = 0;612 613 ac = q6asm_get_audio_client(q6asm, session_id);614 if (!ac)/* Audio client might already be freed by now */615 return 0;616 617 result = data->payload;618 619 switch (hdr->opcode) {620 case APR_BASIC_RSP_RESULT:621 switch (result->opcode) {622 case ASM_SESSION_CMD_PAUSE:623 client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE;624 break;625 case ASM_SESSION_CMD_SUSPEND:626 client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE;627 break;628 case ASM_STREAM_CMD_FLUSH:629 client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE;630 break;631 case ASM_SESSION_CMD_RUN_V2:632 client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE;633 break;634 case ASM_STREAM_CMD_CLOSE:635 client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE;636 break;637 case ASM_STREAM_CMD_FLUSH_READBUFS:638 client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE;639 break;640 case ASM_STREAM_CMD_OPEN_WRITE_V3:641 case ASM_STREAM_CMD_OPEN_READ_V3:642 case ASM_STREAM_CMD_OPEN_READWRITE_V2:643 case ASM_STREAM_CMD_SET_ENCDEC_PARAM:644 case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:645 case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:646 case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:647 if (result->status != 0) {648 dev_err(ac->dev,649 "cmd = 0x%x returned error = 0x%x\n",650 result->opcode, result->status);651 ac->result = *result;652 wake_up(&ac->cmd_wait);653 ret = 0;654 goto done;655 }656 break;657 default:658 dev_err(ac->dev, "command[0x%x] not expecting rsp\n",659 result->opcode);660 break;661 }662 663 ac->result = *result;664 wake_up(&ac->cmd_wait);665 666 if (ac->cb)667 ac->cb(client_event, hdr->token,668 data->payload, ac->priv);669 670 ret = 0;671 goto done;672 673 case ASM_DATA_EVENT_WRITE_DONE_V2:674 client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE;675 if (ac->io_mode & ASM_SYNC_IO_MODE) {676 phys_addr_t phys;677 unsigned long flags;678 int token = hdr->token & ASM_WRITE_TOKEN_MASK;679 680 spin_lock_irqsave(&ac->lock, flags);681 682 port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];683 684 if (!port->buf) {685 spin_unlock_irqrestore(&ac->lock, flags);686 ret = 0;687 goto done;688 }689 690 phys = port->buf[token].phys;691 692 if (lower_32_bits(phys) != result->opcode ||693 upper_32_bits(phys) != result->status) {694 dev_err(ac->dev, "Expected addr %pa\n",695 &port->buf[token].phys);696 spin_unlock_irqrestore(&ac->lock, flags);697 ret = -EINVAL;698 goto done;699 }700 spin_unlock_irqrestore(&ac->lock, flags);701 }702 break;703 case ASM_DATA_EVENT_READ_DONE_V2:704 client_event = ASM_CLIENT_EVENT_DATA_READ_DONE;705 if (ac->io_mode & ASM_SYNC_IO_MODE) {706 struct asm_data_cmd_read_v2_done *done = data->payload;707 unsigned long flags;708 phys_addr_t phys;709 710 spin_lock_irqsave(&ac->lock, flags);711 port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];712 if (!port->buf) {713 spin_unlock_irqrestore(&ac->lock, flags);714 ret = 0;715 goto done;716 }717 718 phys = port->buf[hdr->token].phys;719 720 if (upper_32_bits(phys) != done->buf_addr_msw ||721 lower_32_bits(phys) != done->buf_addr_lsw) {722 dev_err(ac->dev, "Expected addr %pa %08x-%08x\n",723 &port->buf[hdr->token].phys,724 done->buf_addr_lsw,725 done->buf_addr_msw);726 spin_unlock_irqrestore(&ac->lock, flags);727 ret = -EINVAL;728 goto done;729 }730 spin_unlock_irqrestore(&ac->lock, flags);731 }732 733 break;734 case ASM_DATA_EVENT_RENDERED_EOS:735 client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;736 break;737 }738 739 if (ac->cb)740 ac->cb(client_event, hdr->token, data->payload, ac->priv);741 742done:743 kref_put(&ac->refcount, q6asm_audio_client_release);744 return ret;745}746 747static int q6asm_srvc_callback(struct apr_device *adev,748 struct apr_resp_pkt *data)749{750 struct q6asm *q6asm = dev_get_drvdata(&adev->dev);751 struct aprv2_ibasic_rsp_result_t *result;752 struct audio_port_data *port;753 struct audio_client *ac = NULL;754 struct apr_hdr *hdr = &data->hdr;755 struct q6asm *a;756 uint32_t sid = 0;757 uint32_t dir = 0;758 int session_id;759 760 session_id = (hdr->dest_port >> 8) & 0xFF;761 if (session_id)762 return q6asm_stream_callback(adev, data, session_id);763 764 sid = (hdr->token >> 8) & 0x0F;765 ac = q6asm_get_audio_client(q6asm, sid);766 if (!ac) {767 dev_err(&adev->dev, "Audio Client not active\n");768 return 0;769 }770 771 a = dev_get_drvdata(ac->dev->parent);772 dir = (hdr->token & 0x0F);773 port = &ac->port[dir];774 result = data->payload;775 776 switch (hdr->opcode) {777 case APR_BASIC_RSP_RESULT:778 switch (result->opcode) {779 case ASM_CMD_SHARED_MEM_MAP_REGIONS:780 case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:781 ac->result = *result;782 wake_up(&a->mem_wait);783 break;784 default:785 dev_err(&adev->dev, "command[0x%x] not expecting rsp\n",786 result->opcode);787 break;788 }789 goto done;790 case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS:791 ac->result.status = 0;792 ac->result.opcode = hdr->opcode;793 port->mem_map_handle = result->opcode;794 wake_up(&a->mem_wait);795 break;796 case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:797 ac->result.opcode = hdr->opcode;798 ac->result.status = 0;799 port->mem_map_handle = 0;800 wake_up(&a->mem_wait);801 break;802 default:803 dev_dbg(&adev->dev, "command[0x%x]success [0x%x]\n",804 result->opcode, result->status);805 break;806 }807 808 if (ac->cb)809 ac->cb(hdr->opcode, hdr->token, data->payload, ac->priv);810 811done:812 kref_put(&ac->refcount, q6asm_audio_client_release);813 814 return 0;815}816 817/**818 * q6asm_get_session_id() - get session id for audio client819 *820 * @c: audio client pointer821 *822 * Return: Will be an session id of the audio client.823 */824int q6asm_get_session_id(struct audio_client *c)825{826 return c->session;827}828EXPORT_SYMBOL_GPL(q6asm_get_session_id);829 830/**831 * q6asm_audio_client_alloc() - Allocate a new audio client832 *833 * @dev: Pointer to asm child device.834 * @cb: event callback.835 * @priv: private data associated with this client.836 * @session_id: session id837 * @perf_mode: performace mode for this client838 *839 * Return: Will be an error pointer on error or a valid audio client840 * on success.841 */842struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb,843 void *priv, int session_id,844 int perf_mode)845{846 struct q6asm *a = dev_get_drvdata(dev->parent);847 struct audio_client *ac;848 unsigned long flags;849 850 ac = q6asm_get_audio_client(a, session_id + 1);851 if (ac) {852 dev_err(dev, "Audio Client already active\n");853 return ac;854 }855 856 ac = kzalloc(sizeof(*ac), GFP_KERNEL);857 if (!ac)858 return ERR_PTR(-ENOMEM);859 860 spin_lock_irqsave(&a->slock, flags);861 a->session[session_id + 1] = ac;862 spin_unlock_irqrestore(&a->slock, flags);863 ac->session = session_id + 1;864 ac->cb = cb;865 ac->dev = dev;866 ac->q6asm = a;867 ac->priv = priv;868 ac->io_mode = ASM_SYNC_IO_MODE;869 ac->perf_mode = perf_mode;870 ac->adev = a->adev;871 kref_init(&ac->refcount);872 873 init_waitqueue_head(&ac->cmd_wait);874 mutex_init(&ac->cmd_lock);875 spin_lock_init(&ac->lock);876 877 return ac;878}879EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc);880 881static int q6asm_ac_send_cmd_sync(struct audio_client *ac, struct apr_pkt *pkt)882{883 struct apr_hdr *hdr = &pkt->hdr;884 int rc;885 886 mutex_lock(&ac->cmd_lock);887 ac->result.opcode = 0;888 ac->result.status = 0;889 890 rc = apr_send_pkt(ac->adev, pkt);891 if (rc < 0)892 goto err;893 894 rc = wait_event_timeout(ac->cmd_wait,895 (ac->result.opcode == hdr->opcode), 5 * HZ);896 if (!rc) {897 dev_err(ac->dev, "CMD %x timeout\n", hdr->opcode);898 rc = -ETIMEDOUT;899 goto err;900 }901 902 if (ac->result.status > 0) {903 dev_err(ac->dev, "DSP returned error[%x]\n",904 ac->result.status);905 rc = -EINVAL;906 } else {907 rc = 0;908 }909 910 911err:912 mutex_unlock(&ac->cmd_lock);913 return rc;914}915 916/**917 * q6asm_open_write() - Open audio client for writing918 * @ac: audio client pointer919 * @stream_id: stream id of q6asm session920 * @format: audio sample format921 * @codec_profile: compressed format profile922 * @bits_per_sample: bits per sample923 * @is_gapless: flag to indicate if this is a gapless stream924 *925 * Return: Will be an negative value on error or zero on success926 */927int q6asm_open_write(struct audio_client *ac, uint32_t stream_id,928 uint32_t format, u32 codec_profile,929 uint16_t bits_per_sample, bool is_gapless)930{931 struct asm_stream_cmd_open_write_v3 *open;932 struct apr_pkt *pkt;933 void *p;934 int rc, pkt_size;935 936 pkt_size = APR_HDR_SIZE + sizeof(*open);937 938 p = kzalloc(pkt_size, GFP_KERNEL);939 if (!p)940 return -ENOMEM;941 942 pkt = p;943 open = p + APR_HDR_SIZE;944 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);945 946 pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;947 open->mode_flags = 0x00;948 open->mode_flags |= ASM_LEGACY_STREAM_SESSION;949 if (is_gapless)950 open->mode_flags |= BIT(ASM_SHIFT_GAPLESS_MODE_FLAG);951 952 /* source endpoint : matrix */953 open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;954 open->bits_per_sample = bits_per_sample;955 open->postprocopo_id = ASM_NULL_POPP_TOPOLOGY;956 957 switch (format) {958 case SND_AUDIOCODEC_MP3:959 open->dec_fmt_id = ASM_MEDIA_FMT_MP3;960 break;961 case FORMAT_LINEAR_PCM:962 open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;963 break;964 case SND_AUDIOCODEC_FLAC:965 open->dec_fmt_id = ASM_MEDIA_FMT_FLAC;966 break;967 case SND_AUDIOCODEC_WMA:968 switch (codec_profile) {969 case SND_AUDIOPROFILE_WMA9:970 open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V9;971 break;972 case SND_AUDIOPROFILE_WMA10:973 case SND_AUDIOPROFILE_WMA9_PRO:974 case SND_AUDIOPROFILE_WMA9_LOSSLESS:975 case SND_AUDIOPROFILE_WMA10_LOSSLESS:976 open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V10;977 break;978 default:979 dev_err(ac->dev, "Invalid codec profile 0x%x\n",980 codec_profile);981 rc = -EINVAL;982 goto err;983 }984 break;985 case SND_AUDIOCODEC_ALAC:986 open->dec_fmt_id = ASM_MEDIA_FMT_ALAC;987 break;988 case SND_AUDIOCODEC_APE:989 open->dec_fmt_id = ASM_MEDIA_FMT_APE;990 break;991 default:992 dev_err(ac->dev, "Invalid format 0x%x\n", format);993 rc = -EINVAL;994 goto err;995 }996 997 rc = q6asm_ac_send_cmd_sync(ac, pkt);998 if (rc < 0)999 goto err;1000 1001 ac->io_mode |= ASM_TUN_WRITE_IO_MODE;1002 1003err:1004 kfree(pkt);1005 return rc;1006}1007EXPORT_SYMBOL_GPL(q6asm_open_write);1008 1009static int __q6asm_run(struct audio_client *ac, uint32_t stream_id,1010 uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts,1011 bool wait)1012{1013 struct asm_session_cmd_run_v2 *run;1014 struct apr_pkt *pkt;1015 int pkt_size, rc;1016 void *p;1017 1018 pkt_size = APR_HDR_SIZE + sizeof(*run);1019 p = kzalloc(pkt_size, GFP_ATOMIC);1020 if (!p)1021 return -ENOMEM;1022 1023 pkt = p;1024 run = p + APR_HDR_SIZE;1025 1026 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1027 1028 pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2;1029 run->flags = flags;1030 run->time_lsw = lsw_ts;1031 run->time_msw = msw_ts;1032 if (wait) {1033 rc = q6asm_ac_send_cmd_sync(ac, pkt);1034 } else {1035 rc = apr_send_pkt(ac->adev, pkt);1036 if (rc == pkt_size)1037 rc = 0;1038 }1039 1040 kfree(pkt);1041 return rc;1042}1043 1044/**1045 * q6asm_run() - start the audio client1046 *1047 * @ac: audio client pointer1048 * @stream_id: stream id of q6asm session1049 * @flags: flags associated with write1050 * @msw_ts: timestamp msw1051 * @lsw_ts: timestamp lsw1052 *1053 * Return: Will be an negative value on error or zero on success1054 */1055int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags,1056 uint32_t msw_ts, uint32_t lsw_ts)1057{1058 return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, true);1059}1060EXPORT_SYMBOL_GPL(q6asm_run);1061 1062/**1063 * q6asm_run_nowait() - start the audio client withou blocking1064 *1065 * @ac: audio client pointer1066 * @stream_id: stream id1067 * @flags: flags associated with write1068 * @msw_ts: timestamp msw1069 * @lsw_ts: timestamp lsw1070 *1071 * Return: Will be an negative value on error or zero on success1072 */1073int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id,1074 uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts)1075{1076 return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, false);1077}1078EXPORT_SYMBOL_GPL(q6asm_run_nowait);1079 1080/**1081 * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration1082 *1083 * @ac: audio client pointer1084 * @stream_id: stream id1085 * @rate: audio sample rate1086 * @channels: number of audio channels.1087 * @channel_map: channel map pointer1088 * @bits_per_sample: bits per sample1089 *1090 * Return: Will be an negative value on error or zero on success1091 */1092int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,1093 uint32_t stream_id,1094 uint32_t rate, uint32_t channels,1095 u8 channel_map[PCM_MAX_NUM_CHANNEL],1096 uint16_t bits_per_sample)1097{1098 struct asm_multi_channel_pcm_fmt_blk_v2 *fmt;1099 struct apr_pkt *pkt;1100 u8 *channel_mapping;1101 void *p;1102 int rc, pkt_size;1103 1104 pkt_size = APR_HDR_SIZE + sizeof(*fmt);1105 p = kzalloc(pkt_size, GFP_KERNEL);1106 if (!p)1107 return -ENOMEM;1108 1109 pkt = p;1110 fmt = p + APR_HDR_SIZE;1111 1112 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1113 1114 pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;1115 fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);1116 fmt->num_channels = channels;1117 fmt->bits_per_sample = bits_per_sample;1118 fmt->sample_rate = rate;1119 fmt->is_signed = 1;1120 1121 channel_mapping = fmt->channel_mapping;1122 1123 if (channel_map) {1124 memcpy(channel_mapping, channel_map, PCM_MAX_NUM_CHANNEL);1125 } else {1126 if (q6dsp_map_channels(channel_mapping, channels)) {1127 dev_err(ac->dev, " map channels failed %d\n", channels);1128 rc = -EINVAL;1129 goto err;1130 }1131 }1132 1133 rc = q6asm_ac_send_cmd_sync(ac, pkt);1134 1135err:1136 kfree(pkt);1137 return rc;1138}1139EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);1140 1141int q6asm_stream_media_format_block_flac(struct audio_client *ac,1142 uint32_t stream_id,1143 struct q6asm_flac_cfg *cfg)1144{1145 struct asm_flac_fmt_blk_v2 *fmt;1146 struct apr_pkt *pkt;1147 void *p;1148 int rc, pkt_size;1149 1150 pkt_size = APR_HDR_SIZE + sizeof(*fmt);1151 p = kzalloc(pkt_size, GFP_KERNEL);1152 if (!p)1153 return -ENOMEM;1154 1155 pkt = p;1156 fmt = p + APR_HDR_SIZE;1157 1158 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1159 1160 pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;1161 fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);1162 fmt->is_stream_info_present = cfg->stream_info_present;1163 fmt->num_channels = cfg->ch_cfg;1164 fmt->min_blk_size = cfg->min_blk_size;1165 fmt->max_blk_size = cfg->max_blk_size;1166 fmt->sample_rate = cfg->sample_rate;1167 fmt->min_frame_size = cfg->min_frame_size;1168 fmt->max_frame_size = cfg->max_frame_size;1169 fmt->sample_size = cfg->sample_size;1170 1171 rc = q6asm_ac_send_cmd_sync(ac, pkt);1172 kfree(pkt);1173 1174 return rc;1175}1176EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac);1177 1178int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac,1179 uint32_t stream_id,1180 struct q6asm_wma_cfg *cfg)1181{1182 struct asm_wmastdv9_fmt_blk_v2 *fmt;1183 struct apr_pkt *pkt;1184 void *p;1185 int rc, pkt_size;1186 1187 pkt_size = APR_HDR_SIZE + sizeof(*fmt);1188 p = kzalloc(pkt_size, GFP_KERNEL);1189 if (!p)1190 return -ENOMEM;1191 1192 pkt = p;1193 fmt = p + APR_HDR_SIZE;1194 1195 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1196 1197 pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;1198 fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);1199 fmt->fmtag = cfg->fmtag;1200 fmt->num_channels = cfg->num_channels;1201 fmt->sample_rate = cfg->sample_rate;1202 fmt->bytes_per_sec = cfg->bytes_per_sec;1203 fmt->blk_align = cfg->block_align;1204 fmt->bits_per_sample = cfg->bits_per_sample;1205 fmt->channel_mask = cfg->channel_mask;1206 fmt->enc_options = cfg->enc_options;1207 fmt->reserved = 0;1208 1209 rc = q6asm_ac_send_cmd_sync(ac, pkt);1210 kfree(pkt);1211 1212 return rc;1213}1214EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9);1215 1216int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac,1217 uint32_t stream_id,1218 struct q6asm_wma_cfg *cfg)1219{1220 struct asm_wmaprov10_fmt_blk_v2 *fmt;1221 struct apr_pkt *pkt;1222 void *p;1223 int rc, pkt_size;1224 1225 pkt_size = APR_HDR_SIZE + sizeof(*fmt);1226 p = kzalloc(pkt_size, GFP_KERNEL);1227 if (!p)1228 return -ENOMEM;1229 1230 pkt = p;1231 fmt = p + APR_HDR_SIZE;1232 1233 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1234 1235 pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;1236 fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);1237 fmt->fmtag = cfg->fmtag;1238 fmt->num_channels = cfg->num_channels;1239 fmt->sample_rate = cfg->sample_rate;1240 fmt->bytes_per_sec = cfg->bytes_per_sec;1241 fmt->blk_align = cfg->block_align;1242 fmt->bits_per_sample = cfg->bits_per_sample;1243 fmt->channel_mask = cfg->channel_mask;1244 fmt->enc_options = cfg->enc_options;1245 fmt->advanced_enc_options1 = cfg->adv_enc_options;1246 fmt->advanced_enc_options2 = cfg->adv_enc_options2;1247 1248 rc = q6asm_ac_send_cmd_sync(ac, pkt);1249 kfree(pkt);1250 1251 return rc;1252}1253EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10);1254 1255int q6asm_stream_media_format_block_alac(struct audio_client *ac,1256 uint32_t stream_id,1257 struct q6asm_alac_cfg *cfg)1258{1259 struct asm_alac_fmt_blk_v2 *fmt;1260 struct apr_pkt *pkt;1261 void *p;1262 int rc, pkt_size;1263 1264 pkt_size = APR_HDR_SIZE + sizeof(*fmt);1265 p = kzalloc(pkt_size, GFP_KERNEL);1266 if (!p)1267 return -ENOMEM;1268 1269 pkt = p;1270 fmt = p + APR_HDR_SIZE;1271 1272 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1273 1274 pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;1275 fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);1276 1277 fmt->frame_length = cfg->frame_length;1278 fmt->compatible_version = cfg->compatible_version;1279 fmt->bit_depth = cfg->bit_depth;1280 fmt->num_channels = cfg->num_channels;1281 fmt->max_run = cfg->max_run;1282 fmt->max_frame_bytes = cfg->max_frame_bytes;1283 fmt->avg_bit_rate = cfg->avg_bit_rate;1284 fmt->sample_rate = cfg->sample_rate;1285 fmt->channel_layout_tag = cfg->channel_layout_tag;1286 fmt->pb = cfg->pb;1287 fmt->mb = cfg->mb;1288 fmt->kb = cfg->kb;1289 1290 rc = q6asm_ac_send_cmd_sync(ac, pkt);1291 kfree(pkt);1292 1293 return rc;1294}1295EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac);1296 1297int q6asm_stream_media_format_block_ape(struct audio_client *ac,1298 uint32_t stream_id,1299 struct q6asm_ape_cfg *cfg)1300{1301 struct asm_ape_fmt_blk_v2 *fmt;1302 struct apr_pkt *pkt;1303 void *p;1304 int rc, pkt_size;1305 1306 pkt_size = APR_HDR_SIZE + sizeof(*fmt);1307 p = kzalloc(pkt_size, GFP_KERNEL);1308 if (!p)1309 return -ENOMEM;1310 1311 pkt = p;1312 fmt = p + APR_HDR_SIZE;1313 1314 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1315 1316 pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;1317 fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);1318 1319 fmt->compatible_version = cfg->compatible_version;1320 fmt->compression_level = cfg->compression_level;1321 fmt->format_flags = cfg->format_flags;1322 fmt->blocks_per_frame = cfg->blocks_per_frame;1323 fmt->final_frame_blocks = cfg->final_frame_blocks;1324 fmt->total_frames = cfg->total_frames;1325 fmt->bits_per_sample = cfg->bits_per_sample;1326 fmt->num_channels = cfg->num_channels;1327 fmt->sample_rate = cfg->sample_rate;1328 fmt->seek_table_present = cfg->seek_table_present;1329 1330 rc = q6asm_ac_send_cmd_sync(ac, pkt);1331 kfree(pkt);1332 1333 return rc;1334}1335EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape);1336 1337static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_id,1338 uint32_t cmd,1339 uint32_t num_samples)1340{1341 uint32_t *samples;1342 struct apr_pkt *pkt;1343 void *p;1344 int rc, pkt_size;1345 1346 pkt_size = APR_HDR_SIZE + sizeof(uint32_t);1347 p = kzalloc(pkt_size, GFP_ATOMIC);1348 if (!p)1349 return -ENOMEM;1350 1351 pkt = p;1352 samples = p + APR_HDR_SIZE;1353 1354 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1355 1356 pkt->hdr.opcode = cmd;1357 *samples = num_samples;1358 rc = apr_send_pkt(ac->adev, pkt);1359 if (rc == pkt_size)1360 rc = 0;1361 1362 kfree(pkt);1363 1364 return rc;1365}1366 1367int q6asm_stream_remove_initial_silence(struct audio_client *ac,1368 uint32_t stream_id,1369 uint32_t initial_samples)1370{1371 return q6asm_stream_remove_silence(ac, stream_id,1372 ASM_DATA_CMD_REMOVE_INITIAL_SILENCE,1373 initial_samples);1374}1375EXPORT_SYMBOL_GPL(q6asm_stream_remove_initial_silence);1376 1377int q6asm_stream_remove_trailing_silence(struct audio_client *ac, uint32_t stream_id,1378 uint32_t trailing_samples)1379{1380 return q6asm_stream_remove_silence(ac, stream_id,1381 ASM_DATA_CMD_REMOVE_TRAILING_SILENCE,1382 trailing_samples);1383}1384EXPORT_SYMBOL_GPL(q6asm_stream_remove_trailing_silence);1385 1386/**1387 * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture1388 *1389 * @ac: audio client pointer1390 * @stream_id: stream id1391 * @rate: audio sample rate1392 * @channels: number of audio channels.1393 * @bits_per_sample: bits per sample1394 *1395 * Return: Will be an negative value on error or zero on success1396 */1397int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,1398 uint32_t stream_id, uint32_t rate,1399 uint32_t channels,1400 uint16_t bits_per_sample)1401{1402 struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg;1403 struct apr_pkt *pkt;1404 u8 *channel_mapping;1405 u32 frames_per_buf = 0;1406 int pkt_size, rc;1407 void *p;1408 1409 pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg);1410 p = kzalloc(pkt_size, GFP_KERNEL);1411 if (!p)1412 return -ENOMEM;1413 1414 pkt = p;1415 enc_cfg = p + APR_HDR_SIZE;1416 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1417 1418 pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;1419 enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;1420 enc_cfg->encdec.param_size = sizeof(*enc_cfg) - sizeof(enc_cfg->encdec);1421 enc_cfg->encblk.frames_per_buf = frames_per_buf;1422 enc_cfg->encblk.enc_cfg_blk_size = enc_cfg->encdec.param_size -1423 sizeof(struct asm_enc_cfg_blk_param_v2);1424 1425 enc_cfg->num_channels = channels;1426 enc_cfg->bits_per_sample = bits_per_sample;1427 enc_cfg->sample_rate = rate;1428 enc_cfg->is_signed = 1;1429 channel_mapping = enc_cfg->channel_mapping;1430 1431 if (q6dsp_map_channels(channel_mapping, channels)) {1432 rc = -EINVAL;1433 goto err;1434 }1435 1436 rc = q6asm_ac_send_cmd_sync(ac, pkt);1437err:1438 kfree(pkt);1439 return rc;1440}1441EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support);1442 1443 1444/**1445 * q6asm_read() - read data of period size from audio client1446 *1447 * @ac: audio client pointer1448 * @stream_id: stream id1449 *1450 * Return: Will be an negative value on error or zero on success1451 */1452int q6asm_read(struct audio_client *ac, uint32_t stream_id)1453{1454 struct asm_data_cmd_read_v2 *read;1455 struct audio_port_data *port;1456 struct audio_buffer *ab;1457 struct apr_pkt *pkt;1458 unsigned long flags;1459 int pkt_size;1460 int rc = 0;1461 void *p;1462 1463 pkt_size = APR_HDR_SIZE + sizeof(*read);1464 p = kzalloc(pkt_size, GFP_ATOMIC);1465 if (!p)1466 return -ENOMEM;1467 1468 pkt = p;1469 read = p + APR_HDR_SIZE;1470 1471 spin_lock_irqsave(&ac->lock, flags);1472 port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];1473 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id);1474 ab = &port->buf[port->dsp_buf];1475 pkt->hdr.opcode = ASM_DATA_CMD_READ_V2;1476 read->buf_addr_lsw = lower_32_bits(ab->phys);1477 read->buf_addr_msw = upper_32_bits(ab->phys);1478 read->mem_map_handle = port->mem_map_handle;1479 1480 read->buf_size = ab->size;1481 read->seq_id = port->dsp_buf;1482 pkt->hdr.token = port->dsp_buf;1483 1484 port->dsp_buf++;1485 1486 if (port->dsp_buf >= port->num_periods)1487 port->dsp_buf = 0;1488 1489 spin_unlock_irqrestore(&ac->lock, flags);1490 rc = apr_send_pkt(ac->adev, pkt);1491 if (rc == pkt_size)1492 rc = 0;1493 else1494 pr_err("read op[0x%x]rc[%d]\n", pkt->hdr.opcode, rc);1495 1496 kfree(pkt);1497 return rc;1498}1499EXPORT_SYMBOL_GPL(q6asm_read);1500 1501static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id,1502 uint32_t format, uint16_t bits_per_sample)1503{1504 struct asm_stream_cmd_open_read_v3 *open;1505 struct apr_pkt *pkt;1506 int pkt_size, rc;1507 void *p;1508 1509 pkt_size = APR_HDR_SIZE + sizeof(*open);1510 p = kzalloc(pkt_size, GFP_KERNEL);1511 if (!p)1512 return -ENOMEM;1513 1514 pkt = p;1515 open = p + APR_HDR_SIZE;1516 1517 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);1518 pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3;1519 /* Stream prio : High, provide meta info with encoded frames */1520 open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX;1521 1522 open->preprocopo_id = ASM_STREAM_POSTPROC_TOPO_ID_NONE;1523 open->bits_per_sample = bits_per_sample;1524 open->mode_flags = 0x0;1525 1526 open->mode_flags |= ASM_LEGACY_STREAM_SESSION <<1527 ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ;1528 1529 switch (format) {1530 case FORMAT_LINEAR_PCM:1531 open->mode_flags |= 0x00;1532 open->enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;1533 break;1534 default:1535 pr_err("Invalid format[%d]\n", format);1536 }1537 1538 rc = q6asm_ac_send_cmd_sync(ac, pkt);1539 1540 kfree(pkt);1541 return rc;1542}1543 1544/**1545 * q6asm_open_read() - Open audio client for reading1546 *1547 * @ac: audio client pointer1548 * @stream_id: stream id1549 * @format: audio sample format1550 * @bits_per_sample: bits per sample1551 *1552 * Return: Will be an negative value on error or zero on success1553 */1554int q6asm_open_read(struct audio_client *ac, uint32_t stream_id,1555 uint32_t format, uint16_t bits_per_sample)1556{1557 return __q6asm_open_read(ac, stream_id, format, bits_per_sample);1558}1559EXPORT_SYMBOL_GPL(q6asm_open_read);1560 1561/**1562 * q6asm_write_async() - non blocking write1563 *1564 * @ac: audio client pointer1565 * @stream_id: stream id1566 * @len: length in bytes1567 * @msw_ts: timestamp msw1568 * @lsw_ts: timestamp lsw1569 * @wflags: flags associated with write1570 *1571 * Return: Will be an negative value on error or zero on success1572 */1573int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len,1574 uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags)1575{1576 struct asm_data_cmd_write_v2 *write;1577 struct audio_port_data *port;1578 struct audio_buffer *ab;1579 unsigned long flags;1580 struct apr_pkt *pkt;1581 int pkt_size;1582 int rc = 0;1583 void *p;1584 1585 pkt_size = APR_HDR_SIZE + sizeof(*write);1586 p = kzalloc(pkt_size, GFP_ATOMIC);1587 if (!p)1588 return -ENOMEM;1589 1590 pkt = p;1591 write = p + APR_HDR_SIZE;1592 1593 spin_lock_irqsave(&ac->lock, flags);1594 port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];1595 q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id);1596 1597 ab = &port->buf[port->dsp_buf];1598 pkt->hdr.token = port->dsp_buf | (len << ASM_WRITE_TOKEN_LEN_SHIFT);1599 pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2;1600 write->buf_addr_lsw = lower_32_bits(ab->phys);1601 write->buf_addr_msw = upper_32_bits(ab->phys);1602 write->buf_size = len;1603 write->seq_id = port->dsp_buf;1604 write->timestamp_lsw = lsw_ts;1605 write->timestamp_msw = msw_ts;1606 write->mem_map_handle =1607 ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle;1608 1609 write->flags = wflags;1610 1611 port->dsp_buf++;1612 1613 if (port->dsp_buf >= port->num_periods)1614 port->dsp_buf = 0;1615 1616 spin_unlock_irqrestore(&ac->lock, flags);1617 rc = apr_send_pkt(ac->adev, pkt);1618 if (rc == pkt_size)1619 rc = 0;1620 1621 kfree(pkt);1622 return rc;1623}1624EXPORT_SYMBOL_GPL(q6asm_write_async);1625 1626static void q6asm_reset_buf_state(struct audio_client *ac)1627{1628 struct audio_port_data *port;1629 unsigned long flags;1630 1631 spin_lock_irqsave(&ac->lock, flags);1632 port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];1633 port->dsp_buf = 0;1634 port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];1635 port->dsp_buf = 0;1636 spin_unlock_irqrestore(&ac->lock, flags);1637}1638 1639static int __q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd,1640 bool wait)1641{1642 struct apr_pkt pkt;1643 int rc;1644 1645 q6asm_add_hdr(ac, &pkt.hdr, APR_HDR_SIZE, true, stream_id);1646 1647 switch (cmd) {1648 case CMD_PAUSE:1649 pkt.hdr.opcode = ASM_SESSION_CMD_PAUSE;1650 break;1651 case CMD_SUSPEND:1652 pkt.hdr.opcode = ASM_SESSION_CMD_SUSPEND;1653 break;1654 case CMD_FLUSH:1655 pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH;1656 break;1657 case CMD_OUT_FLUSH:1658 pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;1659 break;1660 case CMD_EOS:1661 pkt.hdr.opcode = ASM_DATA_CMD_EOS;1662 break;1663 case CMD_CLOSE:1664 pkt.hdr.opcode = ASM_STREAM_CMD_CLOSE;1665 break;1666 default:1667 return -EINVAL;1668 }1669 1670 if (wait)1671 rc = q6asm_ac_send_cmd_sync(ac, &pkt);1672 else1673 return apr_send_pkt(ac->adev, &pkt);1674 1675 if (rc < 0)1676 return rc;1677 1678 if (cmd == CMD_FLUSH)1679 q6asm_reset_buf_state(ac);1680 1681 return 0;1682}1683 1684/**1685 * q6asm_cmd() - run cmd on audio client1686 *1687 * @ac: audio client pointer1688 * @stream_id: stream id1689 * @cmd: command to run on audio client.1690 *1691 * Return: Will be an negative value on error or zero on success1692 */1693int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd)1694{1695 return __q6asm_cmd(ac, stream_id, cmd, true);1696}1697EXPORT_SYMBOL_GPL(q6asm_cmd);1698 1699/**1700 * q6asm_cmd_nowait() - non blocking, run cmd on audio client1701 *1702 * @ac: audio client pointer1703 * @stream_id: stream id1704 * @cmd: command to run on audio client.1705 *1706 * Return: Will be an negative value on error or zero on success1707 */1708int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd)1709{1710 return __q6asm_cmd(ac, stream_id, cmd, false);1711}1712EXPORT_SYMBOL_GPL(q6asm_cmd_nowait);1713 1714static int q6asm_probe(struct apr_device *adev)1715{1716 struct device *dev = &adev->dev;1717 struct q6asm *q6asm;1718 1719 q6asm = devm_kzalloc(dev, sizeof(*q6asm), GFP_KERNEL);1720 if (!q6asm)1721 return -ENOMEM;1722 1723 q6core_get_svc_api_info(adev->svc_id, &q6asm->ainfo);1724 1725 q6asm->dev = dev;1726 q6asm->adev = adev;1727 init_waitqueue_head(&q6asm->mem_wait);1728 spin_lock_init(&q6asm->slock);1729 dev_set_drvdata(dev, q6asm);1730 1731 return devm_of_platform_populate(dev);1732}1733 1734#ifdef CONFIG_OF1735static const struct of_device_id q6asm_device_id[] = {1736 { .compatible = "qcom,q6asm" },1737 {},1738};1739MODULE_DEVICE_TABLE(of, q6asm_device_id);1740#endif1741 1742static struct apr_driver qcom_q6asm_driver = {1743 .probe = q6asm_probe,1744 .callback = q6asm_srvc_callback,1745 .driver = {1746 .name = "qcom-q6asm",1747 .of_match_table = of_match_ptr(q6asm_device_id),1748 },1749};1750 1751module_apr_driver(qcom_q6asm_driver);1752MODULE_DESCRIPTION("Q6 Audio Stream Manager driver");1753MODULE_LICENSE("GPL v2");1754