1
0
Fork 0

MLK-17635-1: ASoC: fsl_dsp: change dsp driver to support new dsp framework

The architecture of dsp framework has been changed, the role of
dsp driver is transferring messages between dsp framework and user space
application, so change dsp driver to support this function.

Signed-off-by: Weiguang Kong <weiguang.kong@nxp.com>
pull/10/head
Weiguang Kong 2018-04-04 11:56:10 +08:00 committed by Jason Liu
parent 21d77005e7
commit 118061cbcd
6 changed files with 1448 additions and 2123 deletions

View File

@ -25,18 +25,12 @@
#ifndef __MXC_HIFI4_UAPI_H__
#define __MXC_HIFI4_UAPI_H__
#define HIFI4_IOC_MAGIC 'H'
#define HIFI4_LOAD_CODEC _IOWR(HIFI4_IOC_MAGIC, 0, unsigned int)
#define HIFI4_INIT_CODEC _IOWR(HIFI4_IOC_MAGIC, 1, unsigned int)
#define HIFI4_CODEC_OPEN _IOWR(HIFI4_IOC_MAGIC, 2, unsigned int)
#define HIFI4_CODEC_CLOSE _IOWR(HIFI4_IOC_MAGIC, 3, unsigned int)
#define HIFI4_DECODE_ONE_FRAME _IOW(HIFI4_IOC_MAGIC, 4, unsigned int)
#define HIFI4_UNLOAD_CODEC _IOW(HIFI4_IOC_MAGIC, 5, unsigned int)
#define HIFI4_GET_PCM_PROP _IOW(HIFI4_IOC_MAGIC, 6, unsigned int)
#define HIFI4_SET_CONFIG _IOW(HIFI4_IOC_MAGIC, 7, unsigned int)
#define HIFI4_RESET_CODEC _IOW(HIFI4_IOC_MAGIC, 8, unsigned int)
#define HIFI4_CLIENT_REGISTER _IOW(HIFI4_IOC_MAGIC, 9, unsigned int)
#define HIFI4_CLIENT_UNREGISTER _IOW(HIFI4_IOC_MAGIC, 10, unsigned int)
#define DSP_IOC_MAGIC 'H'
#define DSP_CLIENT_REGISTER _IOW(DSP_IOC_MAGIC, 0, unsigned int)
#define DSP_CLIENT_UNREGISTER _IOW(DSP_IOC_MAGIC, 1, unsigned int)
#define DSP_IPC_MSG_SEND _IOW(DSP_IOC_MAGIC, 2, unsigned int)
#define DSP_IPC_MSG_RECV _IOW(DSP_IOC_MAGIC, 3, unsigned int)
#define DSP_GET_SHMEM_INFO _IOW(DSP_IOC_MAGIC, 4, unsigned int)
#define CODEC_MP3_DEC 1
#define CODEC_AAC_DEC 2
@ -105,6 +99,13 @@ enum HIFI_ParaType {
XA_SBC_ENC_BITPOOL,
XA_SBC_ENC_CHMODE,
/* Get parmameters */
XA_CODEC_DESCRIPTION = 0x200,
XA_OUTPUT_PCM_FORMAT,
XA_CONSUMED_LENGTH,
XA_OUTBUF_ALLOC_SIZE,
XA_CONSUMED_CYCLES,
};
#define HIFI_STREAM_DABPLUS_BASE 0x30
@ -136,45 +137,9 @@ enum HIFI_SbcEncChmode {
XA_CHMODE_JOINT = 3,
};
enum lib_type {
HIFI_CODEC_LIB = 1,
HIFI_CODEC_WRAP_LIB,
};
struct decode_info {
void *in_buf_addr;
int in_buf_size;
int in_buf_off;
void *out_buf_addr;
int out_buf_size;
int out_buf_off;
unsigned int input_over;
unsigned int process_id;
};
struct prop_info {
int samplerate;
int channels;
int bits;
unsigned int consumed_bytes;
unsigned int cycles;
unsigned int process_id;
};
struct binary_info {
int type;
char *file;
unsigned int process_id;
unsigned int lib_type;
};
struct prop_config {
int codec_id; /* codec id */
int cmd; /* command value */
int val; /* parameter value */
int ret; /* executed status of function */
unsigned int process_id;
struct shmem_info {
unsigned int phys_addr;
unsigned int size;
};
#endif/* __MXC_HIFI4_UAPI_H__ */

View File

@ -16,7 +16,7 @@ snd-soc-fsl-acm-objs := fsl_acm.o
snd-soc-fsl-amix-objs := fsl_amix.o
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-dma-workaround-objs := fsl_dma_workaround.o
snd-soc-fsl-hifi4-objs := fsl_hifi4.o
snd-soc-fsl-hifi4-objs := fsl_hifi4.o fsl_hifi4_proxy.o
snd-soc-fsl-sai-objs := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o

File diff suppressed because it is too large Load Diff

View File

@ -9,197 +9,46 @@
*/
#include <uapi/linux/mxc_hifi4.h>
#include "fsl_hifi4_proxy.h"
#define Elf32_Byte unsigned char
#define xt_ptr unsigned long
#define xt_int int
#define xt_uint unsigned int
#define xt_ulong unsigned long
typedef void (*memcpy_func) (void *dest, const void *src, size_t n);
typedef void (*memset_func) (void *s, int c, size_t n);
struct xtlib_packaged_library;
#define MULTI_CODEC_NUM 5
#define MAX_MEM_ALLOCS 50
/* ...maximal number of IPC clients per proxy */
#define XF_CFG_MAX_IPC_CLIENTS (1 << 4)
enum {
XTLIB_NO_ERR = 0,
XTLIB_NOT_ELF = 1,
XTLIB_NOT_DYNAMIC = 2,
XTLIB_NOT_STATIC = 3,
XTLIB_NO_DYNAMIC_SEGMENT = 4,
XTLIB_UNKNOWN_SYMBOL = 5,
XTLIB_NOT_ALIGNED = 6,
XTLIB_NOT_SPLITLOAD = 7,
XTLIB_RELOCATION_ERR = 8
/* ...proxy client data */
struct xf_client {
/* ...pointer to proxy interface */
struct xf_proxy *proxy;
/* ...allocated proxy client id */
u32 id;
/* ...pending response queue */
struct xf_msg_queue queue;
/* ...response waiting queue */
wait_queue_head_t wait;
/* ...virtual memory mapping */
unsigned long vm_start;
/* ...counter of memory mappings (no real use of it yet - tbd) */
atomic_t vm_use;
/* ...global structure pointer */
void *global;
};
struct xtlib_loader_globals {
int err;
int byteswap;
};
union xf_client_link {
/* ...index of next client in free list */
u32 next;
struct icm_base_info_t {
u32 process_id; /* process id of current task */
u32 codec_id; /* codec id */
s32 ret; /* executed status of function */
};
struct icm_cdc_iobuf_t {
struct icm_base_info_t base_info;
u32 inp_addr_sysram; /* init by APU */
u32 inp_buf_size_max; /* init by APU */
u32 inp_cur_offset; /* init by APU, updated by DPU */
u32 out_addr_sysram; /* init by APU */
u32 out_buf_size_max; /* init by APU */
u32 out_cur_offset; /* init by APU, updated by DPU */
u32 input_over; /* indicate external stream is over*/
};
struct icm_prop_config {
struct icm_base_info_t base_info;
u32 cmd; /*set parameter command value*/
u32 val; /*set parameter value*/
};
struct icm_pcm_prop_t {
struct icm_base_info_t base_info;
u32 pcmbytes; /* total bytes in the wav file */
u32 sfreq; /* sample rate */
u32 channels; /* output channels */
u32 bits; /* bits per sample */
u32 consumed_bytes;
u32 cycles;
};
struct xtlib_overlay_info {
u32 start_addr;
u32 codec_type;
};
struct xtlib_pil_info {
xt_uint dst_addr;
xt_uint src_offs;
xt_uint dst_data_addr;
xt_uint src_data_offs;
xt_uint start_sym;
xt_uint text_addr;
xt_uint init;
xt_uint fini;
xt_uint rel;
xt_int rela_count;
xt_uint hash;
xt_uint symtab;
xt_uint strtab;
xt_int align;
};
struct icm_xtlib_pil_info {
struct xtlib_pil_info pil_info;
u32 process_id;
u32 lib_type;
};
union icm_header_t {
struct {
u32 msg:6;
u32 sub_msg:6; /* sub_msg will have ICM_MSG when
* msg=ICM_XXX_ACTION_COMPLETE
*/
u32 rsvd:3; /* reserved */
u32 intr:1; /* intr = 1 when sending msg. */
u32 size:15; /* =size in bytes (excluding header)
* to follow when intr=1,
* =response message when ack=1
*/
u32 ack:1;
};
u32 allbits;
} icm_header_t;
enum icm_action_t {
ICM_CORE_READY = 1,
ICM_PI_LIB_MEM_ALLOC,
ICM_PI_LIB_MEM_FREE,
ICM_PI_LIB_INIT,
ICM_PI_LIB_LOAD,
ICM_PI_LIB_UNLOAD,
ICM_DPU_ACTION_COMPLETE,
ICM_APU_ACTION_COMPLETE,
ICM_OPEN,
ICM_EMPTY_THIS_BUFFER,
ICM_FILL_THIS_BUFFER,
ICM_PAUSE,
ICM_CLOSE,
ICM_GET_PCM_PROP,
ICM_SET_PARA_CONFIG,
ICM_CORE_EXIT,
ICM_EXT_MSG_ADDR,
ICM_RESET,
ICM_SUSPEND,
ICM_RESUME,
};
enum aud_status_t {
AUD_IDLE = 0,
AUD_STOPPED,
AUD_DECODING,
AUD_PAUSED
};
struct lib_dnld_info_t {
unsigned long pbuf_code;
unsigned long pbuf_data;
unsigned int size_code;
unsigned int size_data;
struct xtlib_pil_info *ppil_inf;
unsigned int lib_on_dpu; /* 0: not loaded, 1: loaded. */
};
struct icm_pilib_size_t {
u32 buffer_addr;
u32 buffer_size;
s32 ret;
};
struct icm_process_info {
unsigned int process_id;
unsigned int codec_id;
struct xtlib_pil_info pil_info;
struct xtlib_loader_globals xtlib_globals;
void *in_buf_virt;
dma_addr_t in_buf_phys;
int in_buf_size;
void *out_buf_virt;
dma_addr_t out_buf_phys;
int out_buf_size;
void *code_buf_virt;
dma_addr_t code_buf_phys;
int code_buf_size;
void *data_buf_virt;
dma_addr_t data_buf_phys;
int data_buf_size;
dma_addr_t array_alloc_mem[MAX_MEM_ALLOCS];
int alloc_count;
struct filename *objfile;
char objtype;
unsigned int used;
/* ...reference to proxy data for allocated client */
struct xf_client *client;
};
struct fsl_hifi4 {
@ -229,36 +78,14 @@ struct fsl_hifi4 {
dma_addr_t hifi_config_phys;
int hifi_config_size;
int is_ready;
int is_done;
int ret_status;
/* ...proxy data structures */
struct xf_proxy proxy;
struct icm_cdc_iobuf_t codec_iobuf_info;
struct icm_pcm_prop_t pcm_prop_info;
struct icm_pilib_size_t pilib_buffer_info;
struct completion cmd_complete;
/* ...mutex lock */
struct mutex hifi4_mutex;
struct icm_process_info process_info[MULTI_CODEC_NUM];
};
struct fsl_hifi4_engine {
struct fsl_hifi4 *hifi4_priv;
};
struct hifi4_ext_msg {
u32 phys;
u32 size;
};
struct hifi4_mem_msg {
u32 ext_msg_phys;
u32 ext_msg_size;
u32 scratch_phys;
u32 scratch_size;
u32 hifi_config_phys;
u32 hifi_config_size;
/* ...global clients pool (item[0] serves as list terminator) */
union xf_client_link xf_client_map[XF_CFG_MAX_IPC_CLIENTS];
};
#define IRAM_OFFSET 0x10000
@ -276,7 +103,7 @@ struct hifi4_mem_msg {
#define SYSROM_OFFSET 0x58000
#define SYSROM_SIZE 0x30000
#define MSG_BUF_SIZE 4096
#define MSG_BUF_SIZE 8192
#define INPUT_BUF_SIZE 4096
#define OUTPUT_BUF_SIZE 16384
#define HIFI_CONFIG_SIZE 4096
@ -300,33 +127,13 @@ struct hifi4_mem_msg {
#define SDRAM_BASE_ADDR 0x8e000000
#define SDRAM_BASE_SIZE 0x1ffffff
#define SDRAM_CODEC_LIB_OFFSET 0x1000000
#define SDRAM_SCRATCH_BUF_SIZE 0xffffff
#define SC_C_OFS_SEL 39
#define SC_C_OFS_AUDIO 40
#define SC_C_OFS_PERIPH 41
#define SC_C_OFS_IRQ 42
static void hifi4_load_firmware(const struct firmware *fw, void *context);
u32 icm_intr_send(struct fsl_hifi4 *hifi4_priv, u32 msg);
u32 icm_intr_extended_send(struct fsl_hifi4 *hifi4_priv, u32 msg,
struct hifi4_ext_msg *ext_msg);
int send_dpu_ext_msg_addr(struct fsl_hifi4 *hifi4_priv);
long icm_ack_wait(struct fsl_hifi4 *hifi4_priv, u32 msg);
unsigned int xtlib_split_pi_library_size(
struct xtlib_packaged_library *library,
unsigned int *code_size,
unsigned int *data_size,
struct icm_process_info *process_info);
xt_ptr xtlib_host_load_split_pi_library(
struct xtlib_packaged_library *library,
xt_ptr destination_code_address,
xt_ptr destination_data_address,
struct xtlib_pil_info *lib_info,
memcpy_func mcpy_fn,
memset_func mset_fn,
struct icm_process_info *process_info);
void *memcpy_hifi(void *dest, const void *src, size_t count);
void *memset_hifi(void *dest, int c, size_t count);
struct xf_client *xf_client_lookup(struct fsl_hifi4 *hifi4_priv, u32 id);

View File

@ -0,0 +1,641 @@
/*******************************************************************************
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*
* Copyright (C) 2017 Cadence Design Systems, Inc.
* Copyright 2018 NXP
*
******************************************************************************/
/*******************************************************************************
* fsl_hifi4_proxy.c
*
* DSP proxy driver
*
* DSP proxy driver is used to transfer messages between dsp driver
* and dsp framework
******************************************************************************/
#include <soc/imx8/sc/ipc.h>
#include "fsl_hifi4_proxy.h"
#include "fsl_hifi4.h"
/* ...initialize message queue */
void xf_msg_queue_init(struct xf_msg_queue *queue)
{
queue->head = queue->tail = NULL;
}
/* ...get message queue head */
struct xf_message *xf_msg_queue_head(struct xf_msg_queue *queue)
{
return queue->head;
}
/* ...allocate new message from the pool */
struct xf_message *xf_msg_alloc(struct xf_proxy *proxy)
{
struct xf_message *m = proxy->free;
/* ...make sure we have a free message item */
if (m != NULL) {
/* ...get message from the pool */
proxy->free = m->next, m->next = NULL;
}
return m;
}
/* ...return message to the pool of free items */
void xf_msg_free(struct xf_proxy *proxy, struct xf_message *m)
{
/* ...put message into the head of free items list */
m->next = proxy->free, proxy->free = m;
/* ...notify potential client waiting for message */
wake_up(&proxy->busy);
}
/* ...return all messages from the queue to the pool of free items */
void xf_msg_free_all(struct xf_proxy *proxy, struct xf_msg_queue *queue)
{
struct xf_message *m = queue->head;
/* ...check if there is anything in the queue */
if (m != NULL) {
queue->tail->next = proxy->free;
proxy->free = queue->head;
queue->head = queue->tail = NULL;
/* ...notify potential client waiting for message */
wake_up(&proxy->busy);
}
}
/* ...submit message to a queue */
int xf_msg_enqueue(struct xf_msg_queue *queue, struct xf_message *m)
{
int first = (queue->head == NULL);
/* ...set pointer to next item */
m->next = NULL;
/* ...advance head/tail pointer as required */
if (first)
queue->head = m;
else
queue->tail->next = m;
/* ...new tail points to this message */
queue->tail = m;
return first;
}
/* ...retrieve next message from the per-task queue */
struct xf_message *xf_msg_dequeue(struct xf_msg_queue *queue)
{
struct xf_message *m = queue->head;
/* ...check if there is anything in the queue */
if (m != NULL) {
/* ...pop message from the head of the list */
queue->head = m->next;
if (queue->head == NULL)
queue->tail = NULL;
}
return m;
}
/* ...helper function for requesting execution message from a pool */
struct xf_message *xf_msg_available(struct xf_proxy *proxy)
{
struct xf_message *m;
/* ...acquire global lock */
xf_lock(&proxy->lock);
/* ...try to allocate the message */
m = xf_msg_alloc(proxy);
if (m == NULL) {
/* ...failed to allocate message; release lock */
xf_unlock(&proxy->lock);
}
/* ...if successfully allocated */
return m;
}
/* ...helper function for receiving a message from per-client queue */
struct xf_message *xf_msg_received(struct xf_proxy *proxy,
struct xf_msg_queue *queue)
{
struct xf_message *m;
/* ...try to peek message from the queue */
m = xf_msg_dequeue(queue);
/* ...if message is non-null, lock is held */
return m;
}
/*
* MU related functions
*/
u32 icm_intr_send(struct xf_proxy *proxy, u32 msg)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
MU_SendMessage(hifi4_priv->mu_base_virtaddr, 0, msg);
return 0;
}
int icm_intr_extended_send(struct xf_proxy *proxy,
u32 msg,
struct hifi4_ext_msg *ext_msg)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
struct device *dev = hifi4_priv->dev;
union icm_header_t msghdr;
msghdr.allbits = msg;
if (msghdr.size != 8)
dev_err(dev, "too much ext msg\n");
MU_SendMessage(hifi4_priv->mu_base_virtaddr, 1, ext_msg->phys);
MU_SendMessage(hifi4_priv->mu_base_virtaddr, 2, ext_msg->size);
MU_SendMessage(hifi4_priv->mu_base_virtaddr, 0, msg);
return 0;
}
int send_dpu_ext_msg_addr(struct xf_proxy *proxy)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
union icm_header_t msghdr;
struct hifi4_ext_msg ext_msg;
struct hifi4_mem_msg *dpu_ext_msg =
(struct hifi4_mem_msg *)((unsigned char *)hifi4_priv->msg_buf_virt
+ (MSG_BUF_SIZE / 2));
int ret_val = 0;
msghdr.allbits = 0; /* clear all bits; */
msghdr.ack = 0;
msghdr.intr = 1;
msghdr.msg = ICM_CORE_INIT;
msghdr.size = 8;
ext_msg.phys = hifi4_priv->msg_buf_phys + (MSG_BUF_SIZE / 2);
ext_msg.size = sizeof(struct hifi4_mem_msg);
dpu_ext_msg->ext_msg_phys = hifi4_priv->msg_buf_phys;
dpu_ext_msg->ext_msg_size = MSG_BUF_SIZE;
dpu_ext_msg->scratch_phys = hifi4_priv->scratch_buf_phys;
dpu_ext_msg->scratch_size = hifi4_priv->scratch_buf_size;
dpu_ext_msg->hifi_config_phys = hifi4_priv->hifi_config_phys;
dpu_ext_msg->hifi_config_size = hifi4_priv->hifi_config_size;
icm_intr_extended_send(proxy, msghdr.allbits, &ext_msg);
return ret_val;
}
long icm_ack_wait(struct xf_proxy *proxy, u32 msg)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
struct device *dev = hifi4_priv->dev;
union icm_header_t msghdr;
int err;
msghdr.allbits = msg;
/* wait response from mu */
err = wait_for_completion_timeout(&proxy->cmd_complete,
msecs_to_jiffies(1000));
if (!err) {
dev_err(dev, "icm ack timeout! %x\n", msg);
return -ETIMEDOUT;
}
dev_dbg(dev, "Ack recd for message 0x%08x\n", msghdr.allbits);
return 0;
}
irqreturn_t fsl_hifi4_mu_isr(int irq, void *dev_id)
{
struct xf_proxy *proxy = dev_id;
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
struct device *dev = hifi4_priv->dev;
union icm_header_t msghdr;
u32 reg;
MU_ReceiveMsg(hifi4_priv->mu_base_virtaddr, 0, &reg);
msghdr = (union icm_header_t)reg;
if (msghdr.intr == 1) {
dev_dbg(dev, "INTR: Received ICM intr, msg 0x%08x\n",
msghdr.allbits);
switch (msghdr.msg) {
case ICM_CORE_EXIT:
break;
case ICM_CORE_READY:
send_dpu_ext_msg_addr(proxy);
proxy->is_ready = 1;
complete(&proxy->cmd_complete);
break;
default:
schedule_work(&proxy->work);
break;
}
} else if (msghdr.ack == 1) {
dev_dbg(dev, "INTR: Received ICM ack 0x%08x\n", msghdr.size);
msghdr.ack = 0;
} else {
dev_dbg(dev, "Received false ICM intr 0x%08x\n",
msghdr.allbits);
}
return IRQ_HANDLED;
}
/*
* Proxy related functions
*/
/* ...NULL-address specification */
#define XF_PROXY_NULL (~0U)
#define XF_PROXY_BADADDR SDRAM_SCRATCH_BUF_SIZE
/* ...shared memory translation - kernel virtual address to shared address */
u32 xf_proxy_b2a(struct xf_proxy *proxy, void *b)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
if (b == NULL)
return XF_PROXY_NULL;
else if ((u32)(b - hifi4_priv->scratch_buf_virt) <
SDRAM_SCRATCH_BUF_SIZE)
return (u32)(b - hifi4_priv->scratch_buf_virt);
else
return XF_PROXY_BADADDR;
}
/* ...shared memory translation - shared address to kernel virtual address */
void *xf_proxy_a2b(struct xf_proxy *proxy, u32 address)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
if (address < SDRAM_SCRATCH_BUF_SIZE)
return hifi4_priv->scratch_buf_virt + address;
else if (address == XF_PROXY_NULL)
return NULL;
else
return (void *) -1;
}
/* ...process association between response received and intended client */
static void xf_cmap(struct xf_proxy *proxy, struct xf_message *m)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
u32 id = XF_AP_IPC_CLIENT(m->id);
struct xf_client *client;
/* ...process messages addressed to proxy itself */
if (id == 0) {
/* ...place message into local response queue */
xf_msg_enqueue(&proxy->response, m);
wake_up(&proxy->wait);
return;
}
/* ...make sure the client ID is sane */
client = xf_client_lookup(hifi4_priv, id);
if (!client) {
pr_err("rsp[id:%08x]: client lookup failed", m->id);
xf_msg_free(proxy, m);
return;
}
/* ...make sure client is bound to this proxy interface */
if (client->proxy != proxy) {
pr_err("rsp[id:%08x]: wrong proxy interface", m->id);
xf_msg_free(proxy, m);
return;
}
/* ...place message into local response queue */
if (xf_msg_enqueue(&client->queue, m))
wake_up(&client->wait);
}
/* ...retrieve pending responses from shared memory ring-buffer */
static u32 xf_shmem_process_responses(struct xf_proxy *proxy)
{
struct xf_message *m;
u32 read_idx, write_idx;
int status;
status = 0;
/* ...get current values of read/write pointers in response queue */
read_idx = XF_PROXY_READ(proxy, rsp_read_idx);
write_idx = XF_PROXY_READ(proxy, rsp_write_idx);
/* ...process all committed responses */
while (!XF_QUEUE_EMPTY(read_idx, write_idx)) {
struct xf_proxy_message *response;
/* ...allocate execution message */
m = xf_msg_alloc(proxy);
if (m == NULL)
break;
/* ...mark the interface status has changed */
status |= (XF_QUEUE_FULL(read_idx, write_idx) ? 0x3 : 0x1);
/* ...get oldest not yet processed response */
response = XF_PROXY_RESPONSE(proxy, XF_QUEUE_IDX(read_idx));
/* ...fill message parameters */
m->id = response->session_id;
m->opcode = response->opcode;
m->length = response->length;
m->buffer = xf_proxy_a2b(proxy, response->address);
/* ...advance local reading index copy */
read_idx = XF_QUEUE_ADVANCE_IDX(read_idx);
/* ...update shadow copy of reading index */
XF_PROXY_WRITE(proxy, rsp_read_idx, read_idx);
/* ...submit message to proper client */
xf_cmap(proxy, m);
}
return status;
}
/* ...put pending commands into shared memory ring-buffer */
static u32 xf_shmem_process_commands(struct xf_proxy *proxy)
{
struct xf_message *m;
u32 read_idx, write_idx;
int status = 0;
/* ...get current value of peer read pointer */
write_idx = XF_PROXY_READ(proxy, cmd_write_idx);
read_idx = XF_PROXY_READ(proxy, cmd_read_idx);
/* ...submit any pending commands */
while (!XF_QUEUE_FULL(read_idx, write_idx)) {
struct xf_proxy_message *command;
/* ...check if we have a pending command */
m = xf_msg_dequeue(&proxy->command);
if (m == NULL)
break;
/* ...always mark the interface status has changed */
status |= 0x3;
/* ...select the place for the command */
command = XF_PROXY_COMMAND(proxy, XF_QUEUE_IDX(write_idx));
/* ...put the response message fields */
command->session_id = m->id;
command->opcode = m->opcode;
command->length = m->length;
command->address = xf_proxy_b2a(proxy, m->buffer);
/* ...return message back to the pool */
xf_msg_free(proxy, m);
/* ...advance local writing index copy */
write_idx = XF_QUEUE_ADVANCE_IDX(write_idx);
/* ...update shared copy of queue write pointer */
XF_PROXY_WRITE(proxy, cmd_write_idx, write_idx);
}
return status;
}
/* ...shared memory interface maintenance routine */
void xf_proxy_process(struct work_struct *w)
{
struct xf_proxy *proxy = container_of(w, struct xf_proxy, work);
int status = 0;
/* ...get exclusive access to internal data */
xf_lock(&proxy->lock);
do {
/* ...process outgoing commands first */
status = xf_shmem_process_commands(proxy);
/* ...process all pending responses */
status |= xf_shmem_process_responses(proxy);
} while (status);
/* ...unlock internal proxy data */
xf_unlock(&proxy->lock);
}
/* ...initialize shared memory interface */
int xf_proxy_init(struct xf_proxy *proxy)
{
struct fsl_hifi4 *hifi4_priv = container_of(proxy,
struct fsl_hifi4, proxy);
struct xf_message *m;
int i;
/* ...create a list of all messages in a pool; set head pointer */
proxy->free = &proxy->pool[0];
/* ...put all messages into a single-linked list */
for (i = 0, m = proxy->free; i < XF_CFG_MESSAGE_POOL_SIZE - 1; i++, m++)
m->next = m + 1;
/* ...set list tail pointer */
m->next = NULL;
/* ...initialize proxy lock */
xf_lock_init(&proxy->lock);
/* ...initialize proxy thread message queues */
xf_msg_queue_init(&proxy->command);
xf_msg_queue_init(&proxy->response);
/* ...initialize global busy queue */
init_waitqueue_head(&proxy->busy);
init_waitqueue_head(&proxy->wait);
/* ...create work structure */
INIT_WORK(&proxy->work, xf_proxy_process);
/* ...set pointer to shared memory */
proxy->ipc.shmem = (struct xf_shmem_data *)hifi4_priv->msg_buf_virt;
/* ...initialize shared memory interface */
XF_PROXY_WRITE(proxy, cmd_read_idx, 0);
XF_PROXY_WRITE(proxy, cmd_write_idx, 0);
XF_PROXY_WRITE(proxy, rsp_read_idx, 0);
XF_PROXY_WRITE(proxy, rsp_write_idx, 0);
return 0;
}
/* ...trigger shared memory interface processing */
void xf_proxy_notify(struct xf_proxy *proxy)
{
schedule_work(&proxy->work);
}
/* ...submit a command to proxy pending queue (lock released upon return) */
void xf_proxy_command(struct xf_proxy *proxy, struct xf_message *m)
{
int first;
/* ...submit message to proxy thread */
first = xf_msg_enqueue(&proxy->command, m);
/* ...release the lock */
xf_unlock(&proxy->lock);
/* ...notify thread about command reception */
(first ? xf_proxy_notify(proxy), 1 : 0);
}
/*
* Proxy cmd send and receive functions
*/
int xf_cmd_send(struct xf_proxy *proxy,
u32 id,
u32 opcode,
void *buffer,
u32 length)
{
struct xf_message *m;
int ret;
/* ...retrieve message handle (take the lock on success) */
ret = wait_event_interruptible(proxy->busy,
(m = xf_msg_available(proxy)) != NULL);
if (ret)
return -EINTR;
/* ...fill-in message parameters (lock is taken) */
m->id = id;
m->opcode = opcode;
m->length = length;
m->buffer = buffer;
m->ret = 0;
/* ...submit command to the proxy */
xf_proxy_command(proxy, m);
return 0;
}
struct xf_message *xf_cmd_recv(struct xf_proxy *proxy,
wait_queue_head_t *wq,
struct xf_msg_queue *queue,
int wait)
{
struct xf_message *m;
int ret;
/* ...wait for message reception (take lock on success) */
ret = wait_event_interruptible(*wq,
(m = xf_msg_received(proxy, queue)) != NULL || !wait);
if (ret)
return ERR_PTR(-EINTR);
/* ...return message with a lock taken */
return m;
}
/* ...helper function for synchronous command execution */
struct xf_message *xf_cmd_send_recv(struct xf_proxy *proxy,
u32 id, u32 opcode,
void *buffer,
u32 length)
{
int ret;
/* ...send command to remote proxy */
ret = xf_cmd_send(proxy, id, opcode, buffer, length);
if (ret)
return ERR_PTR(ret);
/* ...wait for message delivery */
return xf_cmd_recv(proxy, &proxy->wait, &proxy->response, 1);
}
/*
* Proxy allocate and free memory functions
*/
/* ...allocate memory buffer for kernel use */
int xf_cmd_alloc(struct xf_proxy *proxy, void **buffer, u32 length)
{
struct xf_message *m;
u32 id = 0;
int ret;
/* ...send command to remote proxy */
m = xf_cmd_send_recv(proxy, id, XF_ALLOC, NULL, length);
if (IS_ERR(m)) {
ret = PTR_ERR(m);
return ret;
}
/* ...check if response is expected */
if (m->opcode == XF_ALLOC && m->buffer != NULL) {
*buffer = m->buffer;
ret = 0;
} else {
ret = -ENOMEM;
}
/* ...free message and release proxy lock */
xf_msg_free(proxy, m);
return ret;
}
/* ...free memory buffer */
int xf_cmd_free(struct xf_proxy *proxy, void *buffer, u32 length)
{
struct xf_message *m;
u32 id = 0;
int ret;
/* ...synchronously execute freeing command */
m = xf_cmd_send_recv(proxy, id, XF_FREE, buffer, length);
if (IS_ERR(m)) {
ret = PTR_ERR(m);
return ret;
}
/* ...check if response is expected */
if (m->opcode == XF_FREE)
ret = 0;
else
ret = -EINVAL;
/* ...free message and release proxy lock */
xf_msg_free(proxy, m);
return ret;
}

View File

@ -0,0 +1,381 @@
/*******************************************************************************
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*
* Copyright (c) 2017 Cadence Design Systems, Inc.
* Copyright 2018 NXP
*
************************************************************/
/************************************************************
* fsl_hifi4_proxy.h
*
* Proxy commmand/response messages
************************************************************/
#ifndef __FSL_HIFI4_PROXY_H
#define __FSL_HIFI4_PROXY_H
#include <linux/wait.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/compiler.h>
#include <linux/dma-mapping.h>
#include <linux/platform_data/dma-imx.h>
#include <linux/mx8_mu.h>
#include <linux/interrupt.h>
#define XF_CFG_MESSAGE_POOL_SIZE 256
/*******************************************************************************
* Local proxy data
******************************************************************************/
/* ...execution message */
struct xf_message {
/* ...pointer to next message in a list */
struct xf_message *next;
/* ...session-id */
u32 id;
/* ...operation code */
u32 opcode;
/* ...length of data buffer */
u32 length;
/* ...translated data pointer */
void *buffer;
/* ...return message status */
u32 ret;
};
/* ...message queue */
struct xf_msg_queue {
/* ...pointer to list head */
struct xf_message *head;
/* ...pointer to list tail */
struct xf_message *tail;
};
struct xf_proxy_message {
/* ...session ID */
u32 session_id;
/* ...proxy API command/response code */
u32 opcode;
/* ...length of attached buffer */
u32 length;
/* ...physical address of message buffer */
u32 address;
/* ...return message status */
u32 ret;
};
/**********************************************************************/
enum icm_action_t {
ICM_CORE_READY = 1,
ICM_CORE_INIT,
ICM_CORE_EXIT,
ICM_SUSPEND,
ICM_RESUME,
};
/* ...adjust IPC client of message going from user-space */
#define XF_MSG_AP_FROM_USER(id, client) (((id) & ~(0xF << 2)) | (client << 2))
/* ...wipe out IPC client from message going to user-space */
#define XF_MSG_AP_TO_USER(id) ((id) & ~(0xF << 18))
/* ...message id contains source and destination ports specification */
#define __XF_MSG_ID(src, dst) (((src) & 0xFFFF) | (((dst) & 0xFFFF) << 16))
#define XF_MSG_SRC_CLIENT(id) (((id) >> 2) & 0x3F)
#define XF_MSG_DST_CLIENT(id) (((id) >> 18) & 0x3F)
/* ...special treatment of AP-proxy destination field */
#define XF_AP_IPC_CLIENT(id) (((id) >> 18) & 0xF)
#define __XF_AP_PROXY(core) ((core) | 0x8000)
#define __XF_DSP_PROXY(core) ((core) | 0x8000)
/* ...opcode composition with command/response data tags */
#define __XF_OPCODE(c, r, op) (((c) << 31) | ((r) << 30) | ((op) & 0x3F))
/* ...shared buffer allocation */
#define XF_ALLOC __XF_OPCODE(0, 0, 4)
/* ...shared buffer freeing */
#define XF_FREE __XF_OPCODE(0, 0, 5)
/* ...resume component operation */
#define XF_RESUME __XF_OPCODE(0, 0, 14)
/* ...resume component operation */
#define XF_SUSPEND __XF_OPCODE(0, 0, 15)
/*******************************************************************************
* Ring buffer support
******************************************************************************/
/* ...cache-line size on DSP */
#define XF_PROXY_ALIGNMENT 64
/* ...total length of shared memory queue (for commands and responses) */
#define XF_PROXY_MESSAGE_QUEUE_LENGTH (1 << 6)
/* ...index mask */
#define XF_PROXY_MESSAGE_QUEUE_MASK 0x3F
/* ...ring-buffer index */
#define __XF_QUEUE_IDX(idx, counter) \
(((idx) & XF_PROXY_MESSAGE_QUEUE_MASK) | ((counter) << 16))
/* ...retrieve ring-buffer index */
#define XF_QUEUE_IDX(idx) \
((idx) & XF_PROXY_MESSAGE_QUEUE_MASK)
/* ...increment ring-buffer index */
#define XF_QUEUE_ADVANCE_IDX(idx) \
(((idx) + 1) & (0xFFFF0000 | XF_PROXY_MESSAGE_QUEUE_MASK))
/* ...test if ring buffer is empty */
#define XF_QUEUE_EMPTY(read, write) \
((read) == (write))
/* ...test if ring buffer is full */
#define XF_QUEUE_FULL(read, write) \
((write) == (read) + (XF_PROXY_MESSAGE_QUEUE_LENGTH << 16))
/* ...basic cache operations */
#define XF_PROXY_INVALIDATE(addr, len) { }
#define XF_PROXY_FLUSH(addr, len) { }
/* ...data managed by host CPU (remote) - in case of shunt it is a IPC layer */
struct xf_proxy_host_data {
/* ...command queue */
struct xf_proxy_message command[XF_PROXY_MESSAGE_QUEUE_LENGTH];
/* ...writing index into command queue */
u32 cmd_write_idx;
/* ...reading index for response queue */
u32 rsp_read_idx;
};
/* ...data managed by DSP (local) */
struct xf_proxy_dsp_data {
/* ...response queue */
struct xf_proxy_message response[XF_PROXY_MESSAGE_QUEUE_LENGTH];
/* ...writing index into response queue */
u32 rsp_write_idx;
/* ...reading index for command queue */
u32 cmd_read_idx;
};
/* ...shared memory data */
struct xf_shmem_data {
/* ...ingoing data (maintained by DSP (local side)) */
struct xf_proxy_host_data local;
/* ...outgoing data (maintained by host CPU (remote side)) */
struct xf_proxy_dsp_data remote;
};
/* ...shared memory data accessor */
#define XF_SHMEM_DATA(proxy) \
((proxy)->ipc.shmem)
/* ...atomic reading */
#define __XF_PROXY_READ_ATOMIC(var) \
({ XF_PROXY_INVALIDATE(&(var), sizeof(var)); \
*(u32 *)&(var); })
/* ...atomic writing */
#define __XF_PROXY_WRITE_ATOMIC(var, value) \
({*(u32 *)&(var) = (value); \
XF_PROXY_FLUSH(&(var), sizeof(var)); \
(value); })
/* ...accessors */
#define XF_PROXY_READ(proxy, field) \
__XF_PROXY_READ_##field(XF_SHMEM_DATA(proxy))
#define XF_PROXY_WRITE(proxy, field, v) \
__XF_PROXY_WRITE_##field(XF_SHMEM_DATA(proxy), (v))
/* ...individual fields reading */
#define __XF_PROXY_READ_cmd_write_idx(shmem) \
__XF_PROXY_READ_ATOMIC(shmem->local.cmd_write_idx)
#define __XF_PROXY_READ_cmd_read_idx(shmem) \
shmem->remote.cmd_read_idx
#define __XF_PROXY_READ_rsp_write_idx(shmem) \
__XF_PROXY_READ_ATOMIC(shmem->remote.rsp_write_idx)
#define __XF_PROXY_READ_rsp_read_idx(shmem) \
shmem->local.rsp_read_idx
/* ...individual fields writings */
#define __XF_PROXY_WRITE_cmd_write_idx(shmem, v) \
__XF_PROXY_WRITE_ATOMIC(shmem->local.cmd_write_idx, v)
#define __XF_PROXY_WRITE_cmd_read_idx(shmem, v) \
__XF_PROXY_WRITE_ATOMIC(shmem->remote.cmd_read_idx, v)
#define __XF_PROXY_WRITE_rsp_read_idx(shmem, v) \
__XF_PROXY_WRITE_ATOMIC(shmem->local.rsp_read_idx, v)
#define __XF_PROXY_WRITE_rsp_write_idx(shmem, v) \
__XF_PROXY_WRITE_ATOMIC(shmem->remote.rsp_write_idx, v)
/* ...command buffer accessor */
#define XF_PROXY_COMMAND(proxy, idx) \
(&XF_SHMEM_DATA(proxy)->local.command[(idx)])
/* ...response buffer accessor */
#define XF_PROXY_RESPONSE(proxy, idx) \
(&XF_SHMEM_DATA(proxy)->remote.response[(idx)])
/*******************************************************************************
* Local proxy data
******************************************************************************/
struct xf_proxy_ipc_data {
/* ...shared memory data pointer */
struct xf_shmem_data __iomem *shmem;
/* ...core identifier */
u32 core;
/* ...IPC registers memory */
void __iomem *regs;
};
/* ...proxy data */
struct xf_proxy {
/* ...IPC layer data */
struct xf_proxy_ipc_data ipc;
/* ...shared memory status change processing item */
struct work_struct work;
struct completion cmd_complete;
int is_ready;
/* ...internal lock */
spinlock_t lock;
/* ...busy queue (for clients waiting ON NOTIFIcation) */
wait_queue_head_t busy;
/* ...waiting queue for synchronous proxy operations */
wait_queue_head_t wait;
/* ...submitted commands queue */
struct xf_msg_queue command;
/* ...pending responses queue */
struct xf_msg_queue response;
/* ...global message pool */
struct xf_message pool[XF_CFG_MESSAGE_POOL_SIZE];
/* ...pointer to first free message in the pool */
struct xf_message *free;
};
union icm_header_t {
struct {
u32 msg:6;
u32 sub_msg:6; // sub_msg will have ICM_MSG
u32 rsvd:3; /* reserved */
u32 intr:1; /* intr = 1 when sending msg. */
u32 size:15; /* =size in bytes (excluding header) */
u32 ack:1; /* response message when ack=1 */
};
u32 allbits;
};
struct hifi4_ext_msg {
u32 phys;
u32 size;
};
struct hifi4_mem_msg {
u32 ext_msg_phys;
u32 ext_msg_size;
u32 scratch_phys;
u32 scratch_size;
u32 hifi_config_phys;
u32 hifi_config_size;
};
static inline void xf_lock_init(spinlock_t *lock)
{
spin_lock_init(lock);
}
static inline void xf_lock(spinlock_t *lock)
{
spin_lock(lock);
}
static inline void xf_unlock(spinlock_t *lock)
{
spin_unlock(lock);
}
/* ...init proxy */
int xf_proxy_init(struct xf_proxy *proxy);
/* ...send message to proxy */
int xf_cmd_send(struct xf_proxy *proxy,
u32 id,
u32 opcode,
void *buffer,
u32 length);
/* ...get message from proxy */
struct xf_message *xf_cmd_recv(struct xf_proxy *proxy,
wait_queue_head_t *wq,
struct xf_msg_queue *queue,
int wait);
/* ...mu interrupt handle */
irqreturn_t fsl_hifi4_mu_isr(int irq, void *dev_id);
/* ...initialize client pending message queue */
void xf_msg_queue_init(struct xf_msg_queue *queue);
/* ...return current queue state */
struct xf_message *xf_msg_queue_head(struct xf_msg_queue *queue);
/* ...return the message back to a pool */
void xf_msg_free(struct xf_proxy *proxy, struct xf_message *m);
/* ...release all pending messages */
void xf_msg_free_all(struct xf_proxy *proxy, struct xf_msg_queue *queue);
/* ...wait mu interrupt */
long icm_ack_wait(struct xf_proxy *proxy, u32 msg);
/* ...shared memory translation - kernel virtual address to shared address */
u32 xf_proxy_b2a(struct xf_proxy *proxy, void *b);
/* ...shared memory translation - shared address to kernel virtual address */
void *xf_proxy_a2b(struct xf_proxy *proxy, u32 address);
#endif