cregit-Linux how code gets into the kernel

Release 4.18 sound/soc/qcom/qdsp6/q6asm.c

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
// Copyright (c) 2018, Linaro Limited

#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/soc/qcom/apr.h>
#include <linux/device.h>
#include <linux/of_platform.h>
#include <linux/spinlock.h>
#include <linux/kref.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <uapi/sound/asound.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include "q6asm.h"
#include "q6core.h"
#include "q6dsp-errno.h"
#include "q6dsp-common.h"


#define ASM_STREAM_CMD_CLOSE			0x00010BCD

#define ASM_STREAM_CMD_FLUSH			0x00010BCE

#define ASM_SESSION_CMD_PAUSE			0x00010BD3

#define ASM_DATA_CMD_EOS			0x00010BDB

#define ASM_NULL_POPP_TOPOLOGY			0x00010C68

#define ASM_STREAM_CMD_FLUSH_READBUFS		0x00010C09

#define ASM_STREAM_CMD_SET_ENCDEC_PARAM		0x00010C10

#define ASM_STREAM_POSTPROC_TOPO_ID_NONE	0x00010C68

#define ASM_CMD_SHARED_MEM_MAP_REGIONS		0x00010D92

#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS	0x00010D93

#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS	0x00010D94

#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2	0x00010D98

#define ASM_DATA_EVENT_WRITE_DONE_V2		0x00010D99

#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2	0x00010DA3

#define ASM_SESSION_CMD_RUN_V2			0x00010DAA

#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2	0x00010DA5

#define ASM_DATA_CMD_WRITE_V2			0x00010DAB

#define ASM_DATA_CMD_READ_V2			0x00010DAC

#define ASM_SESSION_CMD_SUSPEND			0x00010DEC

#define ASM_STREAM_CMD_OPEN_WRITE_V3		0x00010DB3

#define ASM_STREAM_CMD_OPEN_READ_V3                 0x00010DB4

#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A

#define ASM_STREAM_CMD_OPEN_READWRITE_V2        0x00010D8D



#define ASM_LEGACY_STREAM_SESSION	0
/* Bit shift for the stream_perf_mode subfield. */

#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ              29

#define ASM_END_POINT_DEVICE_MATRIX	0

#define ASM_DEFAULT_APP_TYPE		0

#define ASM_SYNC_IO_MODE		0x0001

#define ASM_ASYNC_IO_MODE		0x0002

#define ASM_TUN_READ_IO_MODE		0x0004	
/* tunnel read write mode */

#define ASM_TUN_WRITE_IO_MODE		0x0008	
/* tunnel read write mode */

#define ASM_SHIFT_GAPLESS_MODE_FLAG	31

#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL	3


struct avs_cmd_shared_mem_map_regions {
	
u16 mem_pool_id;
	
u16 num_regions;
	
u32 property_flag;

} __packed;


struct avs_shared_map_region_payload {
	
u32 shm_addr_lsw;
	
u32 shm_addr_msw;
	
u32 mem_size_bytes;

} __packed;


struct avs_cmd_shared_mem_unmap_regions {
	
u32 mem_map_handle;

} __packed;


struct asm_data_cmd_media_fmt_update_v2 {
	
u32 fmt_blk_size;

} __packed;


struct asm_multi_channel_pcm_fmt_blk_v2 {
	
struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
	
u16 num_channels;
	
u16 bits_per_sample;
	
u32 sample_rate;
	
u16 is_signed;
	
u16 reserved;
	
u8 channel_mapping[PCM_MAX_NUM_CHANNEL];

} __packed;


struct asm_stream_cmd_set_encdec_param {
	
u32                  param_id;
	
u32                  param_size;

} __packed;


struct asm_enc_cfg_blk_param_v2 {
	
u32                  frames_per_buf;
	
u32                  enc_cfg_blk_size;

} __packed;


struct asm_multi_channel_pcm_enc_cfg_v2 {
	
struct asm_stream_cmd_set_encdec_param  encdec;
	
struct asm_enc_cfg_blk_param_v2	encblk;
	
uint16_t  num_channels;
	
uint16_t  bits_per_sample;
	
uint32_t  sample_rate;
	
uint16_t  is_signed;
	
uint16_t  reserved;
	
uint8_t   channel_mapping[8];

} __packed;


struct asm_data_cmd_read_v2 {
	
u32                  buf_addr_lsw;
	
u32                  buf_addr_msw;
	
u32                  mem_map_handle;
	
u32                  buf_size;
	
u32                  seq_id;

} __packed;


struct asm_data_cmd_read_v2_done {
	
u32	status;
	
u32	buf_addr_lsw;
	
u32	buf_addr_msw;
};


struct asm_stream_cmd_open_read_v3 {
	
u32                    mode_flags;
	
u32                    src_endpointype;
	
u32                    preprocopo_id;
	
u32                    enc_cfg_id;
	
u16                    bits_per_sample;
	
u16                    reserved;

} __packed;


struct asm_data_cmd_write_v2 {
	
u32 buf_addr_lsw;
	
u32 buf_addr_msw;
	
u32 mem_map_handle;
	
u32 buf_size;
	
u32 seq_id;
	
u32 timestamp_lsw;
	
u32 timestamp_msw;
	
u32 flags;

} __packed;


struct asm_stream_cmd_open_write_v3 {
	
uint32_t mode_flags;
	
uint16_t sink_endpointype;
	
uint16_t bits_per_sample;
	
uint32_t postprocopo_id;
	
uint32_t dec_fmt_id;

} __packed;


struct asm_session_cmd_run_v2 {
	
u32 flags;
	
u32 time_lsw;
	
u32 time_msw;

} __packed;


struct audio_buffer {
	
phys_addr_t phys;
	
uint32_t size;		/* size of buffer */
};


struct audio_port_data {
	
struct audio_buffer *buf;
	
uint32_t num_periods;
	
uint32_t dsp_buf;
	
uint32_t mem_map_handle;
};


struct q6asm {
	
struct apr_device *adev;
	
struct device *dev;
	
struct q6core_svc_api_info ainfo;
	
wait_queue_head_t mem_wait;
	
struct platform_device *pcmdev;
	
spinlock_t slock;
	
struct audio_client *session[MAX_SESSIONS + 1];
	
struct platform_device *pdev_dais;
};


struct audio_client {
	
int session;
	
q6asm_cb cb;
	
void *priv;
	
uint32_t io_mode;
	
struct apr_device *adev;
	
struct mutex cmd_lock;
	
spinlock_t lock;
	
struct kref refcount;
	/* idx:1 out port, 0: in port */
	
struct audio_port_data port[2];
	
wait_queue_head_t cmd_wait;
	
struct aprv2_ibasic_rsp_result_t result;
	
int perf_mode;
	
int stream_id;
	
struct q6asm *q6asm;
	
struct device *dev;
};


static inline void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, uint32_t pkt_size, bool cmd_flg, uint32_t stream_id) { hdr->hdr_field = APR_SEQ_CMD_HDR_FIELD; hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id); hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id); hdr->pkt_size = pkt_size; if (cmd_flg) hdr->token = ac->session; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla90100.00%1100.00%
Total90100.00%1100.00%


static int q6asm_apr_send_session_pkt(struct q6asm *a, struct audio_client *ac, struct apr_pkt *pkt, uint32_t rsp_opcode) { struct apr_hdr *hdr = &pkt->hdr; int rc; mutex_lock(&ac->cmd_lock); ac->result.opcode = 0; ac->result.status = 0; rc = apr_send_pkt(a->adev, pkt); if (rc < 0) goto err; if (rsp_opcode) rc = wait_event_timeout(a->mem_wait, (ac->result.opcode == hdr->opcode) || (ac->result.opcode == rsp_opcode), 5 * HZ); else rc = wait_event_timeout(a->mem_wait, (ac->result.opcode == hdr->opcode), 5 * HZ); if (!rc) { dev_err(a->dev, "CMD timeout\n"); rc = -ETIMEDOUT; } else if (ac->result.status > 0) { dev_err(a->dev, "DSP returned error[%x]\n", ac->result.status); rc = -EINVAL; } err: mutex_unlock(&ac->cmd_lock); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla213100.00%1100.00%
Total213100.00%1100.00%


static int __q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add, int dir) { struct avs_cmd_shared_mem_unmap_regions *mem_unmap; struct q6asm *a = dev_get_drvdata(ac->dev->parent); struct apr_pkt *pkt; int rc, pkt_size; void *p; if (ac->port[dir].mem_map_handle == 0) { dev_err(ac->dev, "invalid mem handle\n"); return -EINVAL; } pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap); p = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; pkt = p; mem_unmap = p + APR_HDR_SIZE; pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD; pkt->hdr.src_port = 0; pkt->hdr.dest_port = 0; pkt->hdr.pkt_size = pkt_size; pkt->hdr.token = ((ac->session << 8) | dir); pkt->hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS; mem_unmap->mem_map_handle = ac->port[dir].mem_map_handle; rc = q6asm_apr_send_session_pkt(a, ac, pkt, 0); if (rc < 0) { kfree(pkt); return rc; } ac->port[dir].mem_map_handle = 0; kfree(pkt); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla235100.00%1100.00%
Total235100.00%1100.00%


static void q6asm_audio_client_free_buf(struct audio_client *ac, struct audio_port_data *port) { unsigned long flags; spin_lock_irqsave(&ac->lock, flags); port->num_periods = 0; kfree(port->buf); port->buf = NULL; spin_unlock_irqrestore(&ac->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla59100.00%1100.00%
Total59100.00%1100.00%

/** * q6asm_unmap_memory_regions() - unmap memory regions in the dsp. * * @dir: direction of audio stream * @ac: audio client instanace * * Return: Will be an negative value on failure or zero on success */
int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac) { struct audio_port_data *port; int cnt = 0; int rc = 0; port = &ac->port[dir]; if (!port->buf) { rc = -EINVAL; goto err; } cnt = port->num_periods - 1; if (cnt >= 0) { rc = __q6asm_memory_unmap(ac, port->buf[dir].phys, dir); if (rc < 0) { dev_err(ac->dev, "%s: Memory_unmap_regions failed %d\n", __func__, rc); goto err; } } q6asm_audio_client_free_buf(ac, port); err: return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla126100.00%1100.00%
Total126100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_unmap_memory_regions);
static int __q6asm_memory_map_regions(struct audio_client *ac, int dir, size_t period_sz, unsigned int periods, bool is_contiguous) { struct avs_cmd_shared_mem_map_regions *cmd = NULL; struct avs_shared_map_region_payload *mregions = NULL; struct q6asm *a = dev_get_drvdata(ac->dev->parent); struct audio_port_data *port = NULL; struct audio_buffer *ab = NULL; struct apr_pkt *pkt; void *p; unsigned long flags; uint32_t num_regions, buf_sz; int rc, i, pkt_size; if (is_contiguous) { num_regions = 1; buf_sz = period_sz * periods; } else { buf_sz = period_sz; num_regions = periods; } /* DSP expects size should be aligned to 4K */ buf_sz = ALIGN(buf_sz, 4096); pkt_size = APR_HDR_SIZE + sizeof(*cmd) + (sizeof(*mregions) * num_regions); p = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; pkt = p; cmd = p + APR_HDR_SIZE; mregions = p + APR_HDR_SIZE + sizeof(*cmd); pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD; pkt->hdr.src_port = 0; pkt->hdr.dest_port = 0; pkt->hdr.pkt_size = pkt_size; pkt->hdr.token = ((ac->session << 8) | dir); pkt->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS; cmd->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; cmd->num_regions = num_regions; cmd->property_flag = 0x00; spin_lock_irqsave(&ac->lock, flags); port = &ac->port[dir]; for (i = 0; i < num_regions; i++) { ab = &port->buf[i]; mregions->shm_addr_lsw = lower_32_bits(ab->phys); mregions->shm_addr_msw = upper_32_bits(ab->phys); mregions->mem_size_bytes = buf_sz; ++mregions; } spin_unlock_irqrestore(&ac->lock, flags); rc = q6asm_apr_send_session_pkt(a, ac, pkt, ASM_CMDRSP_SHARED_MEM_MAP_REGIONS); kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla371100.00%1100.00%
Total371100.00%1100.00%

/** * q6asm_map_memory_regions() - map memory regions in the dsp. * * @dir: direction of audio stream * @ac: audio client instanace * @phys: physcial address that needs mapping. * @period_sz: audio period size * @periods: number of periods * * Return: Will be an negative value on failure or zero on success */
int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac, phys_addr_t phys, size_t period_sz, unsigned int periods) { struct audio_buffer *buf; unsigned long flags; int cnt; int rc; spin_lock_irqsave(&ac->lock, flags); if (ac->port[dir].buf) { dev_err(ac->dev, "Buffer already allocated\n"); spin_unlock_irqrestore(&ac->lock, flags); return 0; } buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_ATOMIC); if (!buf) { spin_unlock_irqrestore(&ac->lock, flags); return -ENOMEM; } ac->port[dir].buf = buf; buf[0].phys = phys; buf[0].size = period_sz; for (cnt = 1; cnt < periods; cnt++) { if (period_sz > 0) { buf[cnt].phys = buf[0].phys + (cnt * period_sz); buf[cnt].size = period_sz; } } ac->port[dir].num_periods = periods; spin_unlock_irqrestore(&ac->lock, flags); rc = __q6asm_memory_map_regions(ac, dir, period_sz, periods, 1); if (rc < 0) { dev_err(ac->dev, "Memory_map_regions failed\n"); q6asm_audio_client_free_buf(ac, &ac->port[dir]); } return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla274100.00%2100.00%
Total274100.00%2100.00%

EXPORT_SYMBOL_GPL(q6asm_map_memory_regions);
static void q6asm_audio_client_release(struct kref *ref) { struct audio_client *ac; struct q6asm *a; unsigned long flags; ac = container_of(ref, struct audio_client, refcount); a = ac->q6asm; spin_lock_irqsave(&a->slock, flags); a->session[ac->session] = NULL; spin_unlock_irqrestore(&a->slock, flags); kfree(ac); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla79100.00%1100.00%
Total79100.00%1100.00%

/** * q6asm_audio_client_free() - Freee allocated audio client * * @ac: audio client to free */
void q6asm_audio_client_free(struct audio_client *ac) { kref_put(&ac->refcount, q6asm_audio_client_release); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla20100.00%1100.00%
Total20100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_audio_client_free);
static struct audio_client *q6asm_get_audio_client(struct q6asm *a, int session_id) { struct audio_client *ac = NULL; unsigned long flags; spin_lock_irqsave(&a->slock, flags); if ((session_id <= 0) || (session_id > MAX_SESSIONS)) { dev_err(a->dev, "invalid session: %d\n", session_id); goto err; } /* check for valid session */ if (!a->session[session_id]) goto err; else if (a->session[session_id]->session != session_id) goto err; ac = a->session[session_id]; kref_get(&ac->refcount); err: spin_unlock_irqrestore(&a->slock, flags); return ac; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla130100.00%1100.00%
Total130100.00%1100.00%


static int32_t q6asm_stream_callback(struct apr_device *adev, struct apr_resp_pkt *data, int session_id) { struct q6asm *q6asm = dev_get_drvdata(&adev->dev); struct aprv2_ibasic_rsp_result_t *result; struct apr_hdr *hdr = &data->hdr; struct audio_port_data *port; struct audio_client *ac; uint32_t client_event = 0; int ret = 0; ac = q6asm_get_audio_client(q6asm, session_id); if (!ac)/* Audio client might already be freed by now */ return 0; result = data->payload; switch (hdr->opcode) { case APR_BASIC_RSP_RESULT: switch (result->opcode) { case ASM_SESSION_CMD_PAUSE: client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE; break; case ASM_SESSION_CMD_SUSPEND: client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE; break; case ASM_DATA_CMD_EOS: client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE; break; case ASM_STREAM_CMD_FLUSH: client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE; break; case ASM_SESSION_CMD_RUN_V2: client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE; break; case ASM_STREAM_CMD_CLOSE: client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE; break; case ASM_STREAM_CMD_FLUSH_READBUFS: client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE; break; case ASM_STREAM_CMD_OPEN_WRITE_V3: case ASM_STREAM_CMD_OPEN_READ_V3: case ASM_STREAM_CMD_OPEN_READWRITE_V2: case ASM_STREAM_CMD_SET_ENCDEC_PARAM: case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: if (result->status != 0) { dev_err(ac->dev, "cmd = 0x%x returned error = 0x%x\n", result->opcode, result->status); ac->result = *result; wake_up(&ac->cmd_wait); ret = 0; goto done; } break; default: dev_err(ac->dev, "command[0x%x] not expecting rsp\n", result->opcode); break; } ac->result = *result; wake_up(&ac->cmd_wait); if (ac->cb) ac->cb(client_event, hdr->token, data->payload, ac->priv); ret = 0; goto done; case ASM_DATA_EVENT_WRITE_DONE_V2: client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE; if (ac->io_mode & ASM_SYNC_IO_MODE) { phys_addr_t phys; unsigned long flags; spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; if (!port->buf) { spin_unlock_irqrestore(&ac->lock, flags); ret = 0; goto done; } phys = port->buf[hdr->token].phys; if (lower_32_bits(phys) != result->opcode || upper_32_bits(phys) != result->status) { dev_err(ac->dev, "Expected addr %pa\n", &port->buf[hdr->token].phys); spin_unlock_irqrestore(&ac->lock, flags); ret = -EINVAL; goto done; } spin_unlock_irqrestore(&ac->lock, flags); } break; case ASM_DATA_EVENT_READ_DONE_V2: client_event = ASM_CLIENT_EVENT_DATA_READ_DONE; if (ac->io_mode & ASM_SYNC_IO_MODE) { struct asm_data_cmd_read_v2_done *done = data->payload; unsigned long flags; phys_addr_t phys; spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; if (!port->buf) { spin_unlock_irqrestore(&ac->lock, flags); ret = 0; goto done; } phys = port->buf[hdr->token].phys; if (upper_32_bits(phys) != done->buf_addr_msw || lower_32_bits(phys) != done->buf_addr_lsw) { dev_err(ac->dev, "Expected addr %pa %08x-%08x\n", &port->buf[hdr->token].phys, done->buf_addr_lsw, done->buf_addr_msw); spin_unlock_irqrestore(&ac->lock, flags); ret = -EINVAL; goto done; } spin_unlock_irqrestore(&ac->lock, flags); } break; } if (ac->cb) ac->cb(client_event, hdr->token, data->payload, ac->priv); done: kref_put(&ac->refcount, q6asm_audio_client_release); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla660100.00%3100.00%
Total660100.00%3100.00%


static int q6asm_srvc_callback(struct apr_device *adev, struct apr_resp_pkt *data) { struct q6asm *q6asm = dev_get_drvdata(&adev->dev); struct aprv2_ibasic_rsp_result_t *result; struct audio_port_data *port; struct audio_client *ac = NULL; struct apr_hdr *hdr = &data->hdr; struct q6asm *a; uint32_t sid = 0; uint32_t dir = 0; int session_id; session_id = (hdr->dest_port >> 8) & 0xFF; if (session_id) return q6asm_stream_callback(adev, data, session_id); sid = (hdr->token >> 8) & 0x0F; ac = q6asm_get_audio_client(q6asm, sid); if (!ac) { dev_err(&adev->dev, "Audio Client not active\n"); return 0; } a = dev_get_drvdata(ac->dev->parent); dir = (hdr->token & 0x0F); port = &ac->port[dir]; result = data->payload; switch (hdr->opcode) { case APR_BASIC_RSP_RESULT: switch (result->opcode) { case ASM_CMD_SHARED_MEM_MAP_REGIONS: case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: ac->result = *result; wake_up(&a->mem_wait); break; default: dev_err(&adev->dev, "command[0x%x] not expecting rsp\n", result->opcode); break; } goto done; case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS: ac->result.status = 0; ac->result.opcode = hdr->opcode; port->mem_map_handle = result->opcode; wake_up(&a->mem_wait); break; case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: ac->result.opcode = hdr->opcode; ac->result.status = 0; port->mem_map_handle = 0; wake_up(&a->mem_wait); break; default: dev_dbg(&adev->dev, "command[0x%x]success [0x%x]\n", result->opcode, result->status); break; } if (ac->cb) ac->cb(hdr->opcode, hdr->token, data->payload, ac->priv); done: kref_put(&ac->refcount, q6asm_audio_client_release); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla374100.00%1100.00%
Total374100.00%1100.00%

/** * q6asm_get_session_id() - get session id for audio client * * @c: audio client pointer * * Return: Will be an session id of the audio client. */
int q6asm_get_session_id(struct audio_client *c) { return c->session; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla15100.00%1100.00%
Total15100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_get_session_id); /** * q6asm_audio_client_alloc() - Allocate a new audio client * * @dev: Pointer to asm child device. * @cb: event callback. * @priv: private data associated with this client. * @stream_id: stream id * @perf_mode: performace mode for this client * * Return: Will be an error pointer on error or a valid audio client * on success. */
struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, void *priv, int stream_id, int perf_mode) { struct q6asm *a = dev_get_drvdata(dev->parent); struct audio_client *ac; unsigned long flags; ac = q6asm_get_audio_client(a, stream_id + 1); if (ac) { dev_err(dev, "Audio Client already active\n"); return ac; } ac = kzalloc(sizeof(*ac), GFP_KERNEL); if (!ac) return ERR_PTR(-ENOMEM); spin_lock_irqsave(&a->slock, flags); a->session[stream_id + 1] = ac; spin_unlock_irqrestore(&a->slock, flags); ac->session = stream_id + 1; ac->cb = cb; ac->dev = dev; ac->q6asm = a; ac->priv = priv; ac->io_mode = ASM_SYNC_IO_MODE; ac->perf_mode = perf_mode; /* DSP expects stream id from 1 */ ac->stream_id = 1; ac->adev = a->adev; kref_init(&ac->refcount); init_waitqueue_head(&ac->cmd_wait); mutex_init(&ac->cmd_lock); spin_lock_init(&ac->lock); return ac; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla223100.00%1100.00%
Total223100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc);
static int q6asm_ac_send_cmd_sync(struct audio_client *ac, struct apr_pkt *pkt) { struct apr_hdr *hdr = &pkt->hdr; int rc; mutex_lock(&ac->cmd_lock); ac->result.opcode = 0; ac->result.status = 0; rc = apr_send_pkt(ac->adev, pkt); if (rc < 0) goto err; rc = wait_event_timeout(ac->cmd_wait, (ac->result.opcode == hdr->opcode), 5 * HZ); if (!rc) { dev_err(ac->dev, "CMD timeout\n"); rc = -ETIMEDOUT; goto err; } if (ac->result.status > 0) { dev_err(ac->dev, "DSP returned error[%x]\n", ac->result.status); rc = -EINVAL; } else { rc = 0; } err: mutex_unlock(&ac->cmd_lock); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla174100.00%1100.00%
Total174100.00%1100.00%

/** * q6asm_open_write() - Open audio client for writing * * @ac: audio client pointer * @format: audio sample format * @bits_per_sample: bits per sample * * Return: Will be an negative value on error or zero on success */
int q6asm_open_write(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { struct asm_stream_cmd_open_write_v3 *open; struct apr_pkt *pkt; void *p; int rc, pkt_size; pkt_size = APR_HDR_SIZE + sizeof(*open); p = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; pkt = p; open = p + APR_HDR_SIZE; q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; open->mode_flags = 0x00; open->mode_flags |= ASM_LEGACY_STREAM_SESSION; /* source endpoint : matrix */ open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; open->bits_per_sample = bits_per_sample; open->postprocopo_id = ASM_NULL_POPP_TOPOLOGY; switch (format) { case FORMAT_LINEAR_PCM: open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; break; default: dev_err(ac->dev, "Invalid format 0x%x\n", format); rc = -EINVAL; goto err; } rc = q6asm_ac_send_cmd_sync(ac, pkt); if (rc < 0) goto err; ac->io_mode |= ASM_TUN_WRITE_IO_MODE; err: kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla200100.00%1100.00%
Total200100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_open_write);
static int __q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts, bool wait) { struct asm_session_cmd_run_v2 *run; struct apr_pkt *pkt; int pkt_size, rc; void *p; pkt_size = APR_HDR_SIZE + sizeof(*run); p = kzalloc(pkt_size, GFP_ATOMIC); if (!p) return -ENOMEM; pkt = p; run = p + APR_HDR_SIZE; q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2; run->flags = flags; run->time_lsw = lsw_ts; run->time_msw = msw_ts; if (wait) { rc = q6asm_ac_send_cmd_sync(ac, pkt); } else { rc = apr_send_pkt(ac->adev, pkt); if (rc == pkt_size) rc = 0; } kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla171100.00%1100.00%
Total171100.00%1100.00%

/** * q6asm_run() - start the audio client * * @ac: audio client pointer * @flags: flags associated with write * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * * Return: Will be an negative value on error or zero on success */
int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { return __q6asm_run(ac, flags, msw_ts, lsw_ts, true); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla33100.00%1100.00%
Total33100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_run); /** * q6asm_run_nowait() - start the audio client withou blocking * * @ac: audio client pointer * @flags: flags associated with write * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * * Return: Will be an negative value on error or zero on success */
int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { return __q6asm_run(ac, flags, msw_ts, lsw_ts, false); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla33100.00%1100.00%
Total33100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_run_nowait); /** * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration * * @ac: audio client pointer * @rate: audio sample rate * @channels: number of audio channels. * @channel_map: channel map pointer * @bits_per_sample: bits per sample * * Return: Will be an negative value on error or zero on success */
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample) { struct asm_multi_channel_pcm_fmt_blk_v2 *fmt; struct apr_pkt *pkt; u8 *channel_mapping; void *p; int rc, pkt_size; pkt_size = APR_HDR_SIZE + sizeof(*fmt); p = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; pkt = p; fmt = p + APR_HDR_SIZE; q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); fmt->num_channels = channels; fmt->bits_per_sample = bits_per_sample; fmt->sample_rate = rate; fmt->is_signed = 1; channel_mapping = fmt->channel_mapping; if (channel_map) { memcpy(channel_mapping, channel_map, PCM_MAX_NUM_CHANNEL); } else { if (q6dsp_map_channels(channel_mapping, channels)) { dev_err(ac->dev, " map channels failed %d\n", channels); rc = -EINVAL; goto err; } } rc = q6asm_ac_send_cmd_sync(ac, pkt); err: kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla228100.00%1100.00%
Total228100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); /** * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture * * @ac: audio client pointer * @rate: audio sample rate * @channels: number of audio channels. * @bits_per_sample: bits per sample * * Return: Will be an negative value on error or zero on success */
int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, uint32_t rate, uint32_t channels, uint16_t bits_per_sample) { struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg; struct apr_pkt *pkt; u8 *channel_mapping; u32 frames_per_buf = 0; int pkt_size, rc; void *p; pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg); p = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; pkt = p; enc_cfg = p + APR_HDR_SIZE; q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; enc_cfg->encdec.param_size = sizeof(*enc_cfg) - sizeof(enc_cfg->encdec); enc_cfg->encblk.frames_per_buf = frames_per_buf; enc_cfg->encblk.enc_cfg_blk_size = enc_cfg->encdec.param_size - sizeof(struct asm_enc_cfg_blk_param_v2); enc_cfg->num_channels = channels; enc_cfg->bits_per_sample = bits_per_sample; enc_cfg->sample_rate = rate; enc_cfg->is_signed = 1; channel_mapping = enc_cfg->channel_mapping; if (q6dsp_map_channels(channel_mapping, channels)) { rc = -EINVAL; goto err; } rc = q6asm_ac_send_cmd_sync(ac, pkt); err: kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla232100.00%1100.00%
Total232100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support); /** * q6asm_read() - read data of period size from audio client * * @ac: audio client pointer * * Return: Will be an negative value on error or zero on success */
int q6asm_read(struct audio_client *ac) { struct asm_data_cmd_read_v2 *read; struct audio_port_data *port; struct audio_buffer *ab; struct apr_pkt *pkt; unsigned long flags; int pkt_size; int rc = 0; void *p; pkt_size = APR_HDR_SIZE + sizeof(*read); p = kzalloc(pkt_size, GFP_ATOMIC); if (!p) return -ENOMEM; pkt = p; read = p + APR_HDR_SIZE; spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); ab = &port->buf[port->dsp_buf]; pkt->hdr.opcode = ASM_DATA_CMD_READ_V2; read->buf_addr_lsw = lower_32_bits(ab->phys); read->buf_addr_msw = upper_32_bits(ab->phys); read->mem_map_handle = port->mem_map_handle; read->buf_size = ab->size; read->seq_id = port->dsp_buf; pkt->hdr.token = port->dsp_buf; port->dsp_buf++; if (port->dsp_buf >= port->num_periods) port->dsp_buf = 0; spin_unlock_irqrestore(&ac->lock, flags); rc = apr_send_pkt(ac->adev, pkt); if (rc == pkt_size) rc = 0; else pr_err("read op[0x%x]rc[%d]\n", pkt->hdr.opcode, rc); kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla272100.00%1100.00%
Total272100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_read);
static int __q6asm_open_read(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { struct asm_stream_cmd_open_read_v3 *open; struct apr_pkt *pkt; int pkt_size, rc; void *p; pkt_size = APR_HDR_SIZE + sizeof(*open); p = kzalloc(pkt_size, GFP_KERNEL); if (!p) return -ENOMEM; pkt = p; open = p + APR_HDR_SIZE; q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; /* Stream prio : High, provide meta info with encoded frames */ open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX; open->preprocopo_id = ASM_STREAM_POSTPROC_TOPO_ID_NONE; open->bits_per_sample = bits_per_sample; open->mode_flags = 0x0; open->mode_flags |= ASM_LEGACY_STREAM_SESSION << ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ; switch (format) { case FORMAT_LINEAR_PCM: open->mode_flags |= 0x00; open->enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; break; default: pr_err("Invalid format[%d]\n", format); } rc = q6asm_ac_send_cmd_sync(ac, pkt); kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla180100.00%1100.00%
Total180100.00%1100.00%

/** * q6asm_open_read() - Open audio client for reading * * @ac: audio client pointer * @format: audio sample format * @bits_per_sample: bits per sample * * Return: Will be an negative value on error or zero on success */
int q6asm_open_read(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { return __q6asm_open_read(ac, format, bits_per_sample); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla26100.00%1100.00%
Total26100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_open_read); /** * q6asm_write_async() - non blocking write * * @ac: audio client pointer * @len: lenght in bytes * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * @wflags: flags associated with write * * Return: Will be an negative value on error or zero on success */
int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags) { struct asm_data_cmd_write_v2 *write; struct audio_port_data *port; struct audio_buffer *ab; unsigned long flags; struct apr_pkt *pkt; int pkt_size; int rc = 0; void *p; pkt_size = APR_HDR_SIZE + sizeof(*write); p = kzalloc(pkt_size, GFP_ATOMIC); if (!p) return -ENOMEM; pkt = p; write = p + APR_HDR_SIZE; spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); ab = &port->buf[port->dsp_buf]; pkt->hdr.token = port->dsp_buf; pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2; write->buf_addr_lsw = lower_32_bits(ab->phys); write->buf_addr_msw = upper_32_bits(ab->phys); write->buf_size = len; write->seq_id = port->dsp_buf; write->timestamp_lsw = lsw_ts; write->timestamp_msw = msw_ts; write->mem_map_handle = ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle; if (wflags == NO_TIMESTAMP) write->flags = (wflags & 0x800000FF); else write->flags = (0x80000000 | wflags); port->dsp_buf++; if (port->dsp_buf >= port->num_periods) port->dsp_buf = 0; spin_unlock_irqrestore(&ac->lock, flags); rc = apr_send_pkt(ac->adev, pkt); if (rc == pkt_size) rc = 0; kfree(pkt); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla312100.00%1100.00%
Total312100.00%1100.00%

EXPORT_SYMBOL_GPL(q6asm_write_async);
static void q6asm_reset_buf_state(struct audio_client *ac) { struct audio_port_data *port = NULL; unsigned long flags; spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; port->dsp_buf = 0; port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; port->dsp_buf = 0; spin_unlock_irqrestore(&ac->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla74100.00%2100.00%
Total74100.00%2100.00%


static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) { int stream_id = ac->stream_id; struct apr_pkt pkt; int rc; q6asm_add_hdr(ac, &pkt.hdr, APR_HDR_SIZE, true, stream_id); switch (cmd) { case CMD_PAUSE: pkt.hdr.opcode = ASM_SESSION_CMD_PAUSE; break; case CMD_SUSPEND: pkt.hdr.opcode = ASM_SESSION_CMD_SUSPEND; break; case CMD_FLUSH: pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH; break; case CMD_OUT_FLUSH: pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS; break; case CMD_EOS: pkt.hdr.opcode = ASM_DATA_CMD_EOS; break; case CMD_CLOSE: pkt.hdr.opcode = ASM_STREAM_CMD_CLOSE; break; default: return -EINVAL; } if (wait) rc = q6asm_ac_send_cmd_sync(ac, &pkt); else return apr_send_pkt(ac->adev, &pkt); if (rc < 0) return rc; if (cmd == CMD_FLUSH) q6asm_reset_buf_state(ac); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla179100.00%2100.00%
Total179100.00%2100.00%

/** * q6asm_cmd() - run cmd on audio client * * @ac: audio client pointer * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */
int q6asm_cmd(struct audio_client *ac, int cmd) { return __q6asm_cmd(ac, cmd, true); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla23100.00%2100.00%
Total23100.00%2100.00%

EXPORT_SYMBOL_GPL(q6asm_cmd); /** * q6asm_cmd_nowait() - non blocking, run cmd on audio client * * @ac: audio client pointer * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */
int q6asm_cmd_nowait(struct audio_client *ac, int cmd) { return __q6asm_cmd(ac, cmd, false); }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla23100.00%2100.00%
Total23100.00%2100.00%

EXPORT_SYMBOL_GPL(q6asm_cmd_nowait);
static int q6asm_probe(struct apr_device *adev) { struct device *dev = &adev->dev; struct device_node *dais_np; struct q6asm *q6asm; q6asm = devm_kzalloc(dev, sizeof(*q6asm), GFP_KERNEL); if (!q6asm) return -ENOMEM; q6core_get_svc_api_info(adev->svc_id, &q6asm->ainfo); q6asm->dev = dev; q6asm->adev = adev; init_waitqueue_head(&q6asm->mem_wait); spin_lock_init(&q6asm->slock); dev_set_drvdata(dev, q6asm); dais_np = of_get_child_by_name(dev->of_node, "dais"); if (dais_np) { q6asm->pdev_dais = of_platform_device_create(dais_np, "q6asm-dai", dev); of_node_put(dais_np); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla140100.00%1100.00%
Total140100.00%1100.00%


static int q6asm_remove(struct apr_device *adev) { struct q6asm *q6asm = dev_get_drvdata(&adev->dev); if (q6asm->pdev_dais) of_platform_device_destroy(&q6asm->pdev_dais->dev, NULL); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla45100.00%1100.00%
Total45100.00%1100.00%

static const struct of_device_id q6asm_device_id[] = { { .compatible = "qcom,q6asm" }, {}, }; MODULE_DEVICE_TABLE(of, q6asm_device_id); static struct apr_driver qcom_q6asm_driver = { .probe = q6asm_probe, .remove = q6asm_remove, .callback = q6asm_srvc_callback, .driver = { .name = "qcom-q6asm", .of_match_table = of_match_ptr(q6asm_device_id), }, }; module_apr_driver(qcom_q6asm_driver); MODULE_DESCRIPTION("Q6 Audio Stream Manager driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Srinivas Kandagatla5980100.00%3100.00%
Total5980100.00%3100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.