diff --git a/Documentation/devicetree/bindings/sound/fsl,dsp.txt b/Documentation/devicetree/bindings/sound/fsl,dsp.txt new file mode 100644 index 000000000000..84bc228b4e32 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,dsp.txt @@ -0,0 +1,16 @@ +NXP DSP + +The IP is from Cadence. + +Required properties: + + - compatible : Contains "fsl,imx8qxp-dsp". + - reg : Offset and length of the register set for the device. + +Example: + +dsp: dsp@596e8000 { + compatible = "fsl,imx8qxp-dsp"; + reg = <0x0 0x596e8000 0x0 0x88000>; + status = "okay"; +}; diff --git a/include/uapi/linux/mxc_dsp.h b/include/uapi/linux/mxc_dsp.h new file mode 100644 index 000000000000..040681cc39c8 --- /dev/null +++ b/include/uapi/linux/mxc_dsp.h @@ -0,0 +1,152 @@ +/* + * Copyright 2018 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MXC_DSP_UAPI_H__ +#define __MXC_DSP_UAPI_H__ + +#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 DSP_LOAD_LIB _IOW(DSP_IOC_MAGIC, 5, unsigned int) +#define DSP_UNLOAD_LIB _IOW(DSP_IOC_MAGIC, 6, unsigned int) + + +#define CODEC_MP3_DEC 1 +#define CODEC_AAC_DEC 2 +#define CODEC_DAB_DEC 3 +#define CODEC_MP2_DEC 4 +#define CODEC_BSAC_DEC 5 +#define CODEC_DRM_DEC 6 +#define CODEC_SBC_DEC 7 +#define CODEC_SBC_ENC 8 +#define CODEC_DEMO_DEC 9 + +#define RENDER_ESAI 0x10 +#define RENDER_SAI 0x11 + +enum DSP_ERROR_TYPE { + XA_SUCCESS = 0, + + XA_ERROR_STREAM, + XA_PARA_ERROR, + XA_INSUFFICIENT_MEM, + XA_ERR_UNKNOWN, + XA_PROFILE_NOT_SUPPORT, + XA_INIT_ERR, + XA_NO_OUTPUT, + + XA_NOT_ENOUGH_DATA = 0x100, + XA_CAPIBILITY_CHANGE = 0x200, + XA_END_OF_STREAM = 0x300, /* no output */ +}; + +/* Parameter type to Set /Get */ +enum DSP_ParaType { +/* Set parmameters */ +/* common */ + XA_SAMPLERATE = 0, + XA_CHANNEL, + XA_FRAMED, /* one whole frame input */ + XA_DEPTH, + XA_CODEC_DATA, + XA_BITRATE, + XA_DOWNMIX_STEREO, + XA_STREAM_TYPE, + XA_CHAN_MAP_TABLE, + //UNIA_CHANNEL_MASK, + XA_TO_STEREO, + +/* dedicate for mp3 dec */ + XA_MP3_DEC_CRC_CHECK = 0x120, + XA_MP3_DEC_MCH_ENABLE, + XA_MP3_DEC_NONSTD_STRM_SUPPORT, + +/* dedicate for bsac dec */ + XA_BSAC_DEC_DECODELAYERS = 0x130, + +/* dedicate for aacplus dec */ + XA_AACPLUS_DEC_BDOWNSAMPLE = 0x140, + XA_AACPLUS_DEC_BBITSTREAMDOWNMIX, + XA_AACPLUS_DEC_CHANROUTING, + +/* dedicate for dabplus dec */ + XA_DABPLUS_DEC_BDOWNSAMPLE = 0x150, + XA_DABPLUS_DEC_BBITSTREAMDOWNMIX, + XA_DABPLUS_DEC_CHANROUTING, + +/* dedicate for sbc enc */ + XA_SBC_ENC_SUBBANDS = 0x160, + XA_SBC_ENC_BLOCKS, + XA_SBC_ENC_SNR, + 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 XA_STREAM_DABPLUS_BASE 0x30 +enum DSP_StreamType { + /* AAC/AACPLUS file format */ + XA_STREAM_UNKNOWN = 0, + XA_STREAM_ADTS, + XA_STREAM_ADIF, + XA_STREAM_RAW, + + XA_STREAM_LATM, + XA_STREAM_LATM_OUTOFBAND_CONFIG, + XA_STREAM_LOAS, + + /* DABPLUS file format */ + XA_STREAM_DABPLUS_RAW_SIDEINFO = XA_STREAM_DABPLUS_BASE, + XA_STREAM_DABPLUS, + + /* BSAC file raw format */ + XA_STREAM_BSAC_RAW, + +}; + +/* sbc_enc-specific channel modes */ +enum DSP_SbcEncChmode { + XA_CHMODE_MONO = 0, + XA_CHMODE_DUAL = 1, + XA_CHMODE_STEREO = 2, + XA_CHMODE_JOINT = 3, +}; + +struct shmem_info { + unsigned int phys_addr; + unsigned int size; +}; + +#endif/* __MXC_DSP_UAPI_H__ */ diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index f6302a139d3b..208250dcd876 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -84,6 +84,15 @@ config SND_SOC_FSL_EASRC destination sample rate. It is a new design module compare with the old ASRC. +config SND_SOC_FSL_DSP + tristate "dsp module support" + select SND_SOC_COMPRESS + help + Say Y if you want to add hifi 4 support for the Freescale CPUs. + which is a DSP core for audio processing. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + config SND_SOC_FSL_UTILS tristate @@ -471,6 +480,15 @@ config SND_SOC_IMX_PDM_MIC Say Y if you want to add support for SoC Audio support for i.MX boards with PDM microphones on SAI. +config SND_SOC_IMX_DSP + tristate "SoC Audio support for i.MX boards with DSP port" + select SND_SOC_FSL_DSP + select SND_SOC_COMPRESS + help + SoC Audio support for i.MX boards with DSP audio + Say Y if you want to add support for SoC audio on an i.MX board with + IMX DSP. + endif # SND_IMX_SOC endmenu diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index baaf124fd86b..8be44b869a8f 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -14,6 +14,9 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support snd-soc-fsl-audmix-objs := fsl_audmix.o snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o +snd-soc-fsl-dsp-objs := fsl_dsp.o fsl_dsp_proxy.o fsl_dsp_pool.o \ + fsl_dsp_library_load.o fsl_dsp_xaf_api.o fsl_dsp_cpu.o \ + fsl_dsp_platform_compress.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 @@ -27,6 +30,7 @@ snd-soc-fsl-easrc-objs := fsl_easrc.o fsl_easrc_dma.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o +obj-$(CONFIG_SND_SOC_FSL_DSP) += snd-soc-fsl-dsp.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o @@ -77,6 +81,7 @@ snd-soc-imx-ak4458-objs := imx-ak4458.o snd-soc-imx-ak5558-objs := imx-ak5558.o snd-soc-imx-ak4497-objs := imx-ak4497.o snd-soc-imx-micfil-objs := imx-micfil.o +snd-soc-imx-dsp-objs := imx-dsp.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o @@ -99,3 +104,4 @@ obj-$(CONFIG_SND_SOC_IMX_AK4458) += snd-soc-imx-ak4458.o obj-$(CONFIG_SND_SOC_IMX_AK5558) += snd-soc-imx-ak5558.o obj-$(CONFIG_SND_SOC_IMX_AK4497) += snd-soc-imx-ak4497.o obj-$(CONFIG_SND_SOC_IMX_MICFIL) += snd-soc-imx-micfil.o +obj-$(CONFIG_SND_SOC_IMX_DSP) += snd-soc-imx-dsp.o diff --git a/sound/soc/fsl/fsl_dsp.c b/sound/soc/fsl/fsl_dsp.c new file mode 100644 index 000000000000..b3bcf21730d2 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp.c @@ -0,0 +1,1176 @@ +/* + * Freescale DSP driver + * + * Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED. + * Copyright 2018 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Copyright (c) 2001 William L. Pitts + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif +#include +#include +#include + +#include +#include + +#include "fsl_dsp.h" +#include "fsl_dsp_pool.h" +#include "fsl_dsp_xaf_api.h" + +/* ...allocate new client */ +struct xf_client *xf_client_alloc(struct fsl_dsp *dsp_priv) +{ + struct xf_client *client; + u32 id; + + id = dsp_priv->xf_client_map[0].next; + + /* ...try to allocate a client handle */ + if (id != 0) { + /* ...allocate client memory */ + client = kmalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + /* ...advance the head of free clients */ + dsp_priv->xf_client_map[0].next = + dsp_priv->xf_client_map[id].next; + + /* ...put associate client id with given object */ + dsp_priv->xf_client_map[id].client = client; + + /* ...mark client is not yet bound to proxy */ + client->proxy = NULL; + + /* ...save global proxy client identifier */ + client->id = id; + + return client; + } + + /* ...number of clients exceeded */ + return ERR_PTR(-EBUSY); +} + +/* ...recycle client object */ +static inline void xf_client_free(struct xf_client *client) +{ + int id = client->id; + struct fsl_dsp *dsp_priv = (struct fsl_dsp *)client->global; + + /* ...put proxy client id into free clients list */ + dsp_priv->xf_client_map[id].next = dsp_priv->xf_client_map[0].next; + dsp_priv->xf_client_map[0].next = id; + + /* ...destroy client data */ + kfree(client); +} + +/* ...lookup client basing on id */ +struct xf_client *xf_client_lookup(struct fsl_dsp *dsp_priv, u32 id) +{ + if ((id >= XF_CFG_MAX_IPC_CLIENTS) || + (dsp_priv->xf_client_map[id].next < XF_CFG_MAX_IPC_CLIENTS) + ) + return NULL; + else + return dsp_priv->xf_client_map[id].client; +} + +/* ...helper function for retrieving the client handle */ +static inline struct xf_client *xf_get_client(struct file *file) +{ + struct xf_client *client; + u32 id; + + client = (struct xf_client *)file->private_data; + if (!client) + return ERR_PTR(-EINVAL); + + id = client->id; + if (id >= XF_CFG_MAX_IPC_CLIENTS) + return ERR_PTR(-EINVAL); + + return client; +} + +static int fsl_dsp_client_register(struct xf_client *client) +{ + struct fsl_dsp *dsp_priv; + struct device *dev; + + dsp_priv = (struct fsl_dsp *)client->global; + dev = dsp_priv->dev; + + /* ...make sure client is not registered yet */ + if (client->proxy != NULL) { + pr_err("client-%x already registered", client->id); + return -EBUSY; + } + + /* ...complete association (no communication with remote proxy here) */ + client->proxy = &dsp_priv->proxy; + + pr_debug("client-%x registered within proxy", client->id); + + return 0; +} + +/* ...unregister client from shared memory interface */ +static int fsl_dsp_client_unregister(struct xf_client *client) +{ + struct xf_proxy *proxy = client->proxy; + + /* ...make sure client is registered */ + if (proxy == NULL) { + pr_err("client-%x is not registered", client->id); + return -EBUSY; + } + + /* ...just clean proxy reference */ + client->proxy = NULL; + + pr_debug("client-%x registered within proxy", client->id); + + return 0; +} + +static int fsl_dsp_ipc_msg_to_dsp(struct xf_client *client, + void __user *user) +{ + struct fsl_dsp *dsp_priv = (struct fsl_dsp *)client->global; + struct device *dev = dsp_priv->dev; + struct xf_proxy_message msg; + void *buffer; + unsigned long ret = 0; + + ret = copy_from_user(&msg, user, sizeof(struct xf_proxy_message)); + if (ret) { + dev_err(dev, "failed to get message from user space\n"); + return -EFAULT; + } + + /* ...make sure message pointer is sane */ + buffer = xf_proxy_a2b(&dsp_priv->proxy, msg.address); + if (buffer == (void *)-1) + return -EFAULT; + + /* ...put current proxy client into message session id */ + msg.session_id = XF_MSG_AP_FROM_USER(msg.session_id, client->id); + + xf_cmd_send(&dsp_priv->proxy, + msg.session_id, + msg.opcode, + buffer, + msg.length); + + return 0; +} + +static int fsl_dsp_ipc_msg_from_dsp(struct xf_client *client, + void __user *user) +{ + struct fsl_dsp *dsp_priv = (struct fsl_dsp *)client->global; + struct device *dev = dsp_priv->dev; + struct xf_message *m; + struct xf_proxy_message msg; + unsigned long ret = 0; + + m = xf_cmd_recv(&dsp_priv->proxy, &client->wait, &client->queue, 0); + if (IS_ERR(m)) { + xf_unlock(&dsp_priv->proxy.lock); + dev_err(dev, "receiving failed: %d", (int)PTR_ERR(m)); + return PTR_ERR(m); + } + + /* ...check if there is a response available */ + if (m == NULL) + return -EAGAIN; + + /* ...prepare message parameters (lock is taken) */ + msg.session_id = XF_MSG_AP_TO_USER(m->id); + msg.opcode = m->opcode; + msg.length = m->length; + msg.address = xf_proxy_b2a(&dsp_priv->proxy, m->buffer); + msg.ret = m->ret; + + /* ...return the message back to a pool and release lock */ + xf_msg_free(&dsp_priv->proxy, m); + xf_unlock(&dsp_priv->proxy.lock); + + ret = copy_to_user(user, &msg, sizeof(struct xf_proxy_message)); + if (ret) { + dev_err(dev, "failed to response message to user space\n"); + return -EFAULT; + } + + return 0; +} + +static int fsl_dsp_get_shmem_info(struct xf_client *client, + void __user *user) +{ + struct fsl_dsp *dsp_priv = (struct fsl_dsp *)client->global; + struct device *dev = dsp_priv->dev; + struct shmem_info mem_info; + unsigned long ret = 0; + + mem_info.phys_addr = dsp_priv->scratch_buf_phys; + mem_info.size = dsp_priv->scratch_buf_size; + + ret = copy_to_user(user, &mem_info, sizeof(struct shmem_info)); + if (ret) { + dev_err(dev, "failed to response message to user space\n"); + return -EFAULT; + } + + return ret; +} + +static struct miscdevice dsp_miscdev = { + .name = "mxc_hifi4", + .minor = MISC_DYNAMIC_MINOR, +}; + +static long fsl_dsp_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct xf_client *client; + struct fsl_dsp *dsp_priv; + struct xf_proxy *proxy; + struct device *dev; + void __user *user; + long ret = 0; + + /* ...basic sanity checks */ + client = xf_get_client(file); + if (IS_ERR(client)) + return PTR_ERR(client); + + dsp_priv = (struct fsl_dsp *)client->global; + proxy = &dsp_priv->proxy; + dev = dsp_priv->dev; + user = (void __user *)arg; + + mutex_lock(&dsp_priv->dsp_mutex); + + if (!proxy->is_ready) { + mutex_unlock(&dsp_priv->dsp_mutex); + dev_err(dev, "dsp firmware is not ready\n"); + return -EFAULT; + } + + switch (cmd) { + case DSP_CLIENT_REGISTER: + ret = fsl_dsp_client_register(client); + break; + case DSP_CLIENT_UNREGISTER: + ret = fsl_dsp_client_unregister(client); + break; + case DSP_IPC_MSG_SEND: + ret = fsl_dsp_ipc_msg_to_dsp(client, user); + break; + case DSP_IPC_MSG_RECV: + ret = fsl_dsp_ipc_msg_from_dsp(client, user); + break; + case DSP_GET_SHMEM_INFO: + ret = fsl_dsp_get_shmem_info(client, user); + break; + default: + break; + } + + mutex_unlock(&dsp_priv->dsp_mutex); + + return ret; +} + +void resource_release(struct fsl_dsp *dsp_priv) +{ + int i; + + /* ...initialize client association map */ + for (i = 0; i < XF_CFG_MAX_IPC_CLIENTS - 1; i++) + dsp_priv->xf_client_map[i].next = i + 1; + /* ...set list terminator */ + dsp_priv->xf_client_map[i].next = 0; + + /* ...set pointer to shared memory */ + xf_proxy_init(&dsp_priv->proxy); +} + +int fsl_dsp_open_func(struct fsl_dsp *dsp_priv, struct xf_client *client) +{ + struct device *dev = dsp_priv->dev; + int ret = 0; + + /* ...initialize waiting queue */ + init_waitqueue_head(&client->wait); + + /* ...initialize client pending message queue */ + xf_msg_queue_init(&client->queue); + + /* ...mark user data is not mapped */ + client->vm_start = 0; + + /* ...reset mappings counter */ + atomic_set(&client->vm_use, 0); + + client->global = (void *)dsp_priv; + dsp_priv->proxy.is_active = 1; + + pm_runtime_get_sync(dev); + + mutex_lock(&dsp_priv->dsp_mutex); + /* increase reference counter when opening device */ + atomic_long_inc(&dsp_priv->refcnt); + mutex_unlock(&dsp_priv->dsp_mutex); + + return ret; +} + +static int fsl_dsp_open(struct inode *inode, struct file *file) +{ + struct fsl_dsp *dsp_priv = dev_get_drvdata(dsp_miscdev.parent); + struct xf_client *client; + int ret = 0; + + /* ...basic sanity checks */ + if (!inode || !file) + return -EINVAL; + + /* ...allocate new proxy client object */ + client = xf_client_alloc(dsp_priv); + if (IS_ERR(client)) + return PTR_ERR(client); + + fsl_dsp_open_func(dsp_priv, client); + + file->private_data = (void *)client; + + return ret; +} + +int fsl_dsp_close_func(struct xf_client *client) +{ + struct fsl_dsp *dsp_priv; + struct device *dev; + struct xf_proxy *proxy; + + /* ...basic sanity checks */ + proxy = client->proxy; + + /* release all pending messages */ + if (proxy) + xf_msg_free_all(proxy, &client->queue); + + dsp_priv = (struct fsl_dsp *)client->global; + dev = dsp_priv->dev; + pm_runtime_put_sync(dev); + + /* ...recycle client id and release memory */ + xf_client_free(client); + + mutex_lock(&dsp_priv->dsp_mutex); + /* decrease reference counter when closing device */ + atomic_long_dec(&dsp_priv->refcnt); + /* If device is free, reinitialize the resource of + * dsp driver and framework + */ + if (atomic_long_read(&dsp_priv->refcnt) <= 0) { + /* we are closing up, wait for proxy processing + * function to finish */ + cancel_work_sync(&dsp_priv->proxy.work); + resource_release(dsp_priv); + } + + mutex_unlock(&dsp_priv->dsp_mutex); + + return 0; +} + +static int fsl_dsp_close(struct inode *inode, struct file *file) +{ + struct xf_client *client; + + /* ...basic sanity checks */ + client = xf_get_client(file); + if (IS_ERR(client)) + return PTR_ERR(client); + + fsl_dsp_close_func(client); + + return 0; +} + +/* ...wait until data is available in the response queue */ +static unsigned int fsl_dsp_poll(struct file *file, poll_table *wait) +{ + struct xf_proxy *proxy; + struct xf_client *client; + int mask; + + /* ...basic sanity checks */ + client = xf_get_client(file); + if (IS_ERR(client)) + return PTR_ERR(client); + + /* ...get proxy interface */ + proxy = client->proxy; + if (!proxy) + return -EPERM; + + /* ...register client waiting queue */ + poll_wait(file, &client->wait, wait); + + /* ...return current queue state */ + mask = (xf_msg_queue_head(&client->queue) ? POLLIN | POLLRDNORM : 0); + + return mask; +} + +/******************************************************************************* + * Low-level mmap interface + ******************************************************************************/ + +/* ...add reference to shared buffer */ +static void dsp_mmap_open(struct vm_area_struct *vma) +{ + struct xf_client *client = vma->vm_private_data; + + /* ...probably just increase counter of open references? - tbd */ + atomic_inc(&client->vm_use); + + pr_debug("xf_mmap_open: vma = %p, client = %p", vma, client); +} + +/* ...close reference to shared buffer */ +static void dsp_mmap_close(struct vm_area_struct *vma) +{ + struct xf_client *client = vma->vm_private_data; + + pr_debug("xf_mmap_close: vma = %p, b = %p", vma, client); + + /* ...decrement number of mapping */ + atomic_dec(&client->vm_use); +} + +/* ...memory map operations */ +static const struct vm_operations_struct dsp_mmap_ops = { + .open = dsp_mmap_open, + .close = dsp_mmap_close, +}; + +/* ...shared memory mapping */ +static int fsl_dsp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct xf_proxy *proxy; + struct xf_client *client; + unsigned long size; + unsigned long pfn; + int r; + struct fsl_dsp *dsp_priv; + + /* ...basic sanity checks */ + client = xf_get_client(file); + if (IS_ERR(client)) + return PTR_ERR(client); + + /* ...get proxy interface */ + proxy = client->proxy; + if (!proxy) + return -EPERM; + + /* ...check it was not mapped already */ + if (client->vm_start != 0) + return -EBUSY; + + /* ...check mapping flags (tbd) */ + if ((vma->vm_flags & (VM_READ | VM_WRITE | VM_SHARED)) + != (VM_READ | VM_WRITE | VM_SHARED)) + return -EPERM; + + /* ...set memory map operations */ + vma->vm_ops = &dsp_mmap_ops; + + /* ...assign private data */ + client->vm_start = vma->vm_start; + + /* ...set private memory data */ + vma->vm_private_data = client; + + /* ...set page number of shared memory */ + dsp_priv = (struct fsl_dsp *)client->global; + pfn = dsp_priv->scratch_buf_phys >> PAGE_SHIFT; + size = dsp_priv->scratch_buf_size; + + /* ...remap shared memory to user-space */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); + if (r != 0) { + pr_err("mapping failed: %d", r); + return r; + } + + /* ...system-specific hook for registering shared memory mapping */ + return 0; +} + +void *memset_dsp(void *dest, int c, size_t count) +{ + uint *dl = (uint *)dest; + void *dl_1, *dl_2; + size_t align = 4; + size_t n, n1, n2; + + /* while all data is aligned (common case), copy a word at a time */ + if ((((ulong)dest) & (sizeof(*dl) - 1)) != 0) { + dl = (unsigned int *)(((ulong)dest + align - 1) & + (~(align - 1))); + dl_1 = dest; + dl_2 = (void *)(((ulong)dest + count) & (~(align - 1))); + n1 = (ulong)dl - (ulong)dl_1; + n2 = (ulong)dest + count - (ulong)dl_2; + n = (count - n1 - n2) / align; + + while (n--) { + writel_relaxed(0, dl); + dl++; + } + while (n1--) { + writeb_relaxed(0, dl_1); + dl_1++; + } + while (n2--) { + writeb_relaxed(0, dl_2); + dl_2++; + } + } else { + n = count / align; + n1 = count - n * align; + dl_1 = dest + n * align; + while (n--) { + writel_relaxed(0, dl); + dl++; + } + while (n1--) { + writeb_relaxed(0, dl_1); + dl_1++; + } + } + + return dest; +} + +void *memcpy_dsp(void *dest, const void *src, size_t count) +{ + unsigned int *dl = (unsigned int *)dest, *sl = (unsigned int *)src; + size_t n = round_up(count, 4) / 4; + + if (src == dest) + return dest; + + /* while all data is aligned (common case), copy a word at a time */ + if ((((ulong)dest | (ulong)src) & (sizeof(*dl) - 1)) != 0) + pr_info("dest %p src %p not 4 bytes aligned\n", dest, src); + + while (n--) { + writel_relaxed(*sl, dl); + dl++; + sl++; + } + + return dest; +} + +static void dsp_load_firmware(const struct firmware *fw, void *context) +{ + struct fsl_dsp *dsp_priv = context; + struct device *dev = dsp_priv->dev; + Elf32_Ehdr *ehdr; /* Elf header structure pointer */ + Elf32_Shdr *shdr; /* Section header structure pointer */ + Elf32_Addr sh_addr; + unsigned char *strtab = 0; /* String table pointer */ + unsigned char *image; /* Binary image pointer */ + int i; /* Loop counter */ + unsigned long addr; + + if (!fw) { + dev_info(dev, "external firmware not found\n"); + return; + } + + addr = (unsigned long)fw->data; + ehdr = (Elf32_Ehdr *)addr; + + /* Find the section header string table for output info */ + shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff + + (ehdr->e_shstrndx * sizeof(Elf32_Shdr))); + + strtab = (unsigned char *)(addr + shdr->sh_offset); + + /* Load each appropriate section */ + for (i = 0; i < ehdr->e_shnum; ++i) { + shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff + + (i * sizeof(Elf32_Shdr))); + + if (!(shdr->sh_flags & SHF_ALLOC) || + shdr->sh_addr == 0 || shdr->sh_size == 0) + continue; + + dev_dbg(dev, "%sing %s @ 0x%08lx (%ld bytes)\n", + (shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load", + &strtab[shdr->sh_name], (unsigned long)shdr->sh_addr, + (long)shdr->sh_size); + + sh_addr = shdr->sh_addr; + + if (shdr->sh_type == SHT_NOBITS) { + memset_dsp((void *)(dsp_priv->sdram_vir_addr + + (sh_addr - dsp_priv->sdram_phys_addr)), + 0, + shdr->sh_size); + } else { + image = (unsigned char *)addr + shdr->sh_offset; + if ((!strcmp(&strtab[shdr->sh_name], ".rodata")) || + (!strcmp(&strtab[shdr->sh_name], ".text")) || + (!strcmp(&strtab[shdr->sh_name], ".data")) || + (!strcmp(&strtab[shdr->sh_name], ".bss")) + ) { + memcpy_dsp((void *)(dsp_priv->sdram_vir_addr + + (sh_addr - dsp_priv->sdram_phys_addr)), + (const void *)image, + shdr->sh_size); + } else { + /* sh_addr is from DSP view, we need to + * fixup addr because we load the firmware from + * the ARM core side + */ + sh_addr -= dsp_priv->fixup_offset; + + memcpy_dsp((void *)(dsp_priv->regs + + (sh_addr - dsp_priv->paddr)), + (const void *)image, + shdr->sh_size); + } + } + } + + /* start the core */ + imx_sc_pm_cpu_start(dsp_priv->dsp_ipcHandle, + IMX_SC_R_DSP, true, dsp_priv->iram); +} + +/* Initialization of the MU code. */ +int dsp_mu_init(struct fsl_dsp *dsp_priv) +{ + struct device *dev = dsp_priv->dev; + struct device_node *np; + unsigned int dsp_mu_id; + u32 irq; + int ret = 0; + + /* + * Get the address of MU to be used for communication with the dsp + */ + np = of_find_compatible_node(NULL, NULL, "fsl,imx8-mu-dsp"); + if (!np) { + dev_err(dev, "Cannot find MU entry in device tree\n"); + return -EINVAL; + } + dsp_priv->mu_base_virtaddr = of_iomap(np, 0); + WARN_ON(!dsp_priv->mu_base_virtaddr); + + ret = of_property_read_u32_index(np, + "fsl,dsp_ap_mu_id", 0, &dsp_mu_id); + if (ret) { + dev_err(dev, "Cannot get mu_id %d\n", ret); + return -EINVAL; + } + + dsp_priv->dsp_mu_id = dsp_mu_id; + + irq = of_irq_get(np, 0); + + ret = devm_request_irq(dsp_priv->dev, irq, fsl_dsp_mu_isr, + IRQF_EARLY_RESUME, "dsp_mu_isr", &dsp_priv->proxy); + if (ret) { + dev_err(dev, "request_irq failed %d, err = %d\n", irq, ret); + return -EINVAL; + } + + return ret; +} + +static const struct file_operations dsp_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = fsl_dsp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = fsl_dsp_ioctl, +#endif + .open = fsl_dsp_open, + .poll = fsl_dsp_poll, + .mmap = fsl_dsp_mmap, + .release = fsl_dsp_close, +}; + +extern struct snd_compr_ops dsp_platform_compr_ops; + +static const struct snd_soc_component_driver dsp_soc_platform_drv = { + .name = FSL_DSP_COMP_NAME, + .compr_ops = &dsp_platform_compr_ops, +}; + +static int fsl_dsp_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *reserved_node; + struct resource reserved_res; + struct fsl_dsp *dsp_priv; + const char *fw_name; + struct resource *res; + void __iomem *regs; + void *buf_virt; + dma_addr_t buf_phys; + int size, offset, i; + int ret; + int num_domains = 0; + char tmp[16]; + + dsp_priv = devm_kzalloc(&pdev->dev, sizeof(*dsp_priv), GFP_KERNEL); + if (!dsp_priv) + return -ENOMEM; + + if (of_device_is_compatible(np, "fsl,imx8qxp-dsp-v1")) + dsp_priv->dsp_board_type = DSP_IMX8QXP_TYPE; + else + dsp_priv->dsp_board_type = DSP_IMX8QM_TYPE; + + dsp_priv->dev = &pdev->dev; + + /* Get the addresses and IRQ */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dsp_priv->paddr = res->start; + dsp_priv->regs = regs; + + dsp_priv->dram0 = dsp_priv->paddr + DRAM0_OFFSET; + dsp_priv->dram1 = dsp_priv->paddr + DRAM1_OFFSET; + dsp_priv->iram = dsp_priv->paddr + IRAM_OFFSET; + dsp_priv->sram = dsp_priv->paddr + SYSRAM_OFFSET; + + ret = imx_scu_get_handle(&dsp_priv->dsp_ipcHandle); + if (ret) { + dev_err(&pdev->dev, "Cannot get scu handle %d\n", ret); + return ret; + }; + + num_domains = of_count_phandle_with_args(np, "power-domains", + "#power-domain-cells"); + for (i = 0; i < num_domains; i++) { + struct device *pd_dev; + struct device_link *link; + + pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i); + if (IS_ERR(pd_dev)) + return PTR_ERR(pd_dev); + + link = device_link_add(&pdev->dev, pd_dev, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (IS_ERR(link)) + return PTR_ERR(link); + } + + if (dsp_priv->dsp_board_type == DSP_IMX8QXP_TYPE) { + ret = imx_sc_misc_set_control(dsp_priv->dsp_ipcHandle, IMX_SC_R_DSP, + IMX_SC_C_OFS_SEL, 1); + if (ret) { + dev_err(&pdev->dev, "Error system address offset source select\n"); + return -EIO; + } + + ret = imx_sc_misc_set_control(dsp_priv->dsp_ipcHandle, IMX_SC_R_DSP, + IMX_SC_C_OFS_PERIPH, 0x5A); + if (ret) { + dev_err(&pdev->dev, "Error system address offset of PERIPH %d\n", + ret); + return -EIO; + } + + ret = imx_sc_misc_set_control(dsp_priv->dsp_ipcHandle, IMX_SC_R_DSP, + IMX_SC_C_OFS_IRQ, 0x51); + if (ret) { + dev_err(&pdev->dev, "Error system address offset of IRQ\n"); + return -EIO; + } + + ret = imx_sc_misc_set_control(dsp_priv->dsp_ipcHandle, IMX_SC_R_DSP, + IMX_SC_C_OFS_AUDIO, 0x80); + if (ret) { + dev_err(&pdev->dev, "Error system address offset of AUDIO\n"); + return -EIO; + } + } else { + ret = imx_sc_misc_set_control(dsp_priv->dsp_ipcHandle, IMX_SC_R_DSP, + IMX_SC_C_OFS_SEL, 0); + if (ret) { + dev_err(&pdev->dev, "Error system address offset source select\n"); + return -EIO; + } + } + + ret = dsp_mu_init(dsp_priv); + if (ret) + return ret; + + ret = of_property_read_string(np, "fsl,dsp-firmware", &fw_name); + dsp_priv->fw_name = fw_name; + + ret = of_property_read_u32(np, "fixup-offset", &dsp_priv->fixup_offset); + + platform_set_drvdata(pdev, dsp_priv); + pm_runtime_enable(&pdev->dev); + + dsp_miscdev.fops = &dsp_fops, + dsp_miscdev.parent = &pdev->dev, + ret = misc_register(&dsp_miscdev); + if (ret) { + dev_err(&pdev->dev, "failed to register misc device %d\n", ret); + return ret; + } + + reserved_node = of_parse_phandle(np, "reserved-region", 0); + if (!reserved_node) { + dev_err(&pdev->dev, "failed to get reserved region node\n"); + return -ENODEV; + } + + if (of_address_to_resource(reserved_node, 0, &reserved_res)) { + dev_err(&pdev->dev, "failed to get reserved region address\n"); + return -EINVAL; + } + + dsp_priv->sdram_phys_addr = reserved_res.start; + dsp_priv->sdram_reserved_size = (reserved_res.end - reserved_res.start) + + 1; + if (dsp_priv->sdram_reserved_size <= 0) { + dev_err(&pdev->dev, "invalid value of reserved region size\n"); + return -EINVAL; + } + + dsp_priv->sdram_vir_addr = ioremap_wc(dsp_priv->sdram_phys_addr, + dsp_priv->sdram_reserved_size); + if (!dsp_priv->sdram_vir_addr) { + dev_err(&pdev->dev, "failed to remap sdram space for dsp firmware\n"); + return -ENXIO; + } + memset_io(dsp_priv->sdram_vir_addr, 0, dsp_priv->sdram_reserved_size); + + size = MSG_BUF_SIZE + DSP_CONFIG_SIZE; + + buf_virt = dma_alloc_coherent(&pdev->dev, size, &buf_phys, GFP_KERNEL); + if (!buf_virt) { + dev_err(&pdev->dev, "failed alloc memory.\n"); + return -ENOMEM; + } + + /* msg ring buffer memory */ + dsp_priv->msg_buf_virt = buf_virt; + dsp_priv->msg_buf_phys = buf_phys; + dsp_priv->msg_buf_size = MSG_BUF_SIZE; + offset = MSG_BUF_SIZE; + + /* keep dsp framework's global data when suspend/resume */ + dsp_priv->dsp_config_virt = buf_virt + offset; + dsp_priv->dsp_config_phys = buf_phys + offset; + dsp_priv->dsp_config_size = DSP_CONFIG_SIZE; + + /* scratch memory for dsp framework. The sdram reserved memory + * is split into two equal parts currently. The front part is + * used to keep the dsp firmware, the other part is considered + * as scratch memory for dsp framework. + */ + dsp_priv->scratch_buf_virt = dsp_priv->sdram_vir_addr + + dsp_priv->sdram_reserved_size / 2; + dsp_priv->scratch_buf_phys = dsp_priv->sdram_phys_addr + + dsp_priv->sdram_reserved_size / 2; + dsp_priv->scratch_buf_size = dsp_priv->sdram_reserved_size / 2; + + /* initialize the reference counter for dsp_priv + * structure + */ + atomic_long_set(&dsp_priv->refcnt, 0); + + /* ...initialize client association map */ + for (i = 0; i < XF_CFG_MAX_IPC_CLIENTS - 1; i++) + dsp_priv->xf_client_map[i].next = i + 1; + /* ...set list terminator */ + dsp_priv->xf_client_map[i].next = 0; + + /* ...set pointer to shared memory */ + xf_proxy_init(&dsp_priv->proxy); + + /* ...initialize mutex */ + mutex_init(&dsp_priv->dsp_mutex); + + ret = devm_snd_soc_register_component(&pdev->dev, &dsp_soc_platform_drv, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "registering soc platform failed\n"); + return ret; + } + + dsp_priv->esai_ipg_clk = devm_clk_get(&pdev->dev, "esai_ipg"); + if (IS_ERR(dsp_priv->esai_ipg_clk)) + dsp_priv->esai_ipg_clk = NULL; + + dsp_priv->esai_mclk = devm_clk_get(&pdev->dev, "esai_mclk"); + if (IS_ERR(dsp_priv->esai_mclk)) + dsp_priv->esai_mclk = NULL; + + dsp_priv->asrc_mem_clk = devm_clk_get(&pdev->dev, "asrc_mem"); + if (IS_ERR(dsp_priv->asrc_mem_clk)) + dsp_priv->asrc_mem_clk = NULL; + + dsp_priv->asrc_ipg_clk = devm_clk_get(&pdev->dev, "asrc_ipg"); + if (IS_ERR(dsp_priv->asrc_ipg_clk)) + dsp_priv->asrc_ipg_clk = NULL; + + for (i = 0; i < 4; i++) { + sprintf(tmp, "asrck_%x", i); + dsp_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp); + if (IS_ERR(dsp_priv->asrck_clk[i])) + dsp_priv->asrck_clk[i] = NULL; + } + + return 0; +} + +static int fsl_dsp_remove(struct platform_device *pdev) +{ + struct fsl_dsp *dsp_priv = platform_get_drvdata(pdev); + int size; + + misc_deregister(&dsp_miscdev); + + size = MSG_BUF_SIZE + DSP_CONFIG_SIZE; + dma_free_coherent(&pdev->dev, size, dsp_priv->msg_buf_virt, + dsp_priv->msg_buf_phys); + if (dsp_priv->sdram_vir_addr) + iounmap(dsp_priv->sdram_vir_addr); + + return 0; +} + +#ifdef CONFIG_PM +static int fsl_dsp_runtime_resume(struct device *dev) +{ + struct fsl_dsp *dsp_priv = dev_get_drvdata(dev); + struct xf_proxy *proxy = &dsp_priv->proxy; + int ret; + int i; + + ret = clk_prepare_enable(dsp_priv->esai_ipg_clk); + if (ret) { + dev_err(dev, "failed to enable esai ipg clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(dsp_priv->esai_mclk); + if (ret) { + dev_err(dev, "failed to enable esai mclk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(dsp_priv->asrc_mem_clk); + if (ret < 0) + dev_err(dev, "Failed to enable asrc_mem_clk ret = %d\n", ret); + + ret = clk_prepare_enable(dsp_priv->asrc_ipg_clk); + if (ret < 0) + dev_err(dev, "Failed to enable asrc_ipg_clk ret = %d\n", ret); + + for (i = 0; i < 4; i++) { + ret = clk_prepare_enable(dsp_priv->asrck_clk[i]); + if (ret < 0) + dev_err(dev, "failed to prepare arc clk %d\n", i); + } + + if (!dsp_priv->dsp_mu_init) { + MU_Init(dsp_priv->mu_base_virtaddr); + MU_EnableRxFullInt(dsp_priv->mu_base_virtaddr, 0); + dsp_priv->dsp_mu_init = 1; + } + + if (!proxy->is_ready) { + init_completion(&proxy->cmd_complete); + + ret = request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, dsp_priv->fw_name, + dev, + GFP_KERNEL, dsp_priv, dsp_load_firmware); + + if (ret) { + dev_err(dev, "failed to load firmware\n"); + return ret; + } + + ret = icm_ack_wait(proxy, 0); + if (ret) + return ret; + + dev_info(dev, "dsp driver registered\n"); + } + + return 0; +} + +static int fsl_dsp_runtime_suspend(struct device *dev) +{ + struct fsl_dsp *dsp_priv = dev_get_drvdata(dev); + struct xf_proxy *proxy = &dsp_priv->proxy; + int i; + + dsp_priv->dsp_mu_init = 0; + proxy->is_ready = 0; + + for (i = 0; i < 4; i++) + clk_disable_unprepare(dsp_priv->asrck_clk[i]); + + clk_disable_unprepare(dsp_priv->asrc_ipg_clk); + clk_disable_unprepare(dsp_priv->asrc_mem_clk); + + clk_disable_unprepare(dsp_priv->esai_mclk); + clk_disable_unprepare(dsp_priv->esai_ipg_clk); + + return 0; +} +#endif /* CONFIG_PM */ + + +#ifdef CONFIG_PM_SLEEP +static int fsl_dsp_suspend(struct device *dev) +{ + struct fsl_dsp *dsp_priv = dev_get_drvdata(dev); + struct xf_proxy *proxy = &dsp_priv->proxy; + int ret = 0; + + if (proxy->is_ready) { + ret = xf_cmd_send_suspend(proxy); + if (ret) { + dev_err(dev, "dsp suspend fail\n"); + return ret; + } + } + + ret = pm_runtime_force_suspend(dev); + + return ret; +} + +static int fsl_dsp_resume(struct device *dev) +{ + struct fsl_dsp *dsp_priv = dev_get_drvdata(dev); + struct xf_proxy *proxy = &dsp_priv->proxy; + int ret = 0; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + if (proxy->is_ready) { + ret = xf_cmd_send_resume(proxy); + if (ret) { + dev_err(dev, "dsp resume fail\n"); + return ret; + } + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_dsp_pm = { + SET_RUNTIME_PM_OPS(fsl_dsp_runtime_suspend, + fsl_dsp_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(fsl_dsp_suspend, fsl_dsp_resume) +}; + +static const struct of_device_id fsl_dsp_ids[] = { + { .compatible = "fsl,imx8qxp-dsp-v1", }, + { .compatible = "fsl,imx8qm-dsp-v1", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_dsp_ids); + +static struct platform_driver fsl_dsp_driver = { + .probe = fsl_dsp_probe, + .remove = fsl_dsp_remove, + .driver = { + .name = "fsl-dsp", + .of_match_table = fsl_dsp_ids, + .pm = &fsl_dsp_pm, + }, +}; +module_platform_driver(fsl_dsp_driver); + +MODULE_DESCRIPTION("Freescale DSP driver"); +MODULE_ALIAS("platform:fsl-dsp"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/fsl/fsl_dsp.h b/sound/soc/fsl/fsl_dsp.h new file mode 100644 index 000000000000..a66b083124ae --- /dev/null +++ b/sound/soc/fsl/fsl_dsp.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT)*/ +/* + * Copyright (C) 2017 Cadence Design Systems, Inc. + * Copyright 2018 NXP + * + */ + +#ifndef FSL_DSP_H +#define FSL_DSP_H +#include +#include +#include "fsl_dsp_proxy.h" +#include "fsl_dsp_platform.h" + + +#define FSL_DSP_COMP_NAME "fsl-dsp-component" + +typedef void (*memcpy_func) (void *dest, const void *src, size_t n); +typedef void (*memset_func) (void *s, int c, size_t n); + +/* ...maximal number of IPC clients per proxy */ +#define XF_CFG_MAX_IPC_CLIENTS (1 << 4) + +enum { + DSP_IMX8QXP_TYPE = 0, + DSP_IMX8QM_TYPE, +}; + +/* ...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 xf_message m; + + struct snd_compr_stream *cstream; + + struct work_struct work; + struct completion compr_complete; + + int input_bytes; + int consume_bytes; +}; + +union xf_client_link { + /* ...index of next client in free list */ + u32 next; + + /* ...reference to proxy data for allocated client */ + struct xf_client *client; +}; + +struct fsl_dsp { + struct device *dev; + const char *fw_name; + void __iomem *regs; + void __iomem *mu_base_virtaddr; + struct imx_sc_ipc *dsp_ipcHandle; + unsigned int dsp_mu_id; + int dsp_mu_init; + atomic_long_t refcnt; + unsigned long paddr; + unsigned long dram0; + unsigned long dram1; + unsigned long iram; + unsigned long sram; + void *sdram_vir_addr; + unsigned long sdram_phys_addr; + int sdram_reserved_size; + void *msg_buf_virt; + dma_addr_t msg_buf_phys; + int msg_buf_size; + void *scratch_buf_virt; + dma_addr_t scratch_buf_phys; + int scratch_buf_size; + void *dsp_config_virt; + dma_addr_t dsp_config_phys; + int dsp_config_size; + int dsp_board_type; + unsigned int fixup_offset; + + /* ...proxy data structures */ + struct xf_proxy proxy; + + /* ...mutex lock */ + struct mutex dsp_mutex; + + struct dsp_data dsp_data; + + /* ...global clients pool (item[0] serves as list terminator) */ + union xf_client_link xf_client_map[XF_CFG_MAX_IPC_CLIENTS]; + + struct clk *esai_ipg_clk; + struct clk *esai_mclk; + struct clk *asrc_mem_clk; + struct clk *asrc_ipg_clk; + struct clk *asrck_clk[4]; +}; + +#define IRAM_OFFSET 0x10000 +#define IRAM_SIZE 2048 + +#define DRAM0_OFFSET 0x0 +#define DRAM0_SIZE 0x8000 + +#define DRAM1_OFFSET 0x8000 +#define DRAM1_SIZE 0x8000 + +#define SYSRAM_OFFSET 0x18000 +#define SYSRAM_SIZE 0x40000 + +#define SYSROM_OFFSET 0x58000 +#define SYSROM_SIZE 0x30000 + +#define MSG_BUF_SIZE 8192 +#define INPUT_BUF_SIZE 4096 +#define OUTPUT_BUF_SIZE 16384 +#define DSP_CONFIG_SIZE 4096 + +void *memcpy_dsp(void *dest, const void *src, size_t count); +void *memset_dsp(void *dest, int c, size_t count); +struct xf_client *xf_client_lookup(struct fsl_dsp *dsp_priv, u32 id); +struct xf_client *xf_client_alloc(struct fsl_dsp *dsp_priv); + +int fsl_dsp_open_func(struct fsl_dsp *dsp_priv, struct xf_client *client); +int fsl_dsp_close_func(struct xf_client *client); + +#endif diff --git a/sound/soc/fsl/fsl_dsp_cpu.c b/sound/soc/fsl/fsl_dsp_cpu.c new file mode 100644 index 000000000000..e97d09ae0c45 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_cpu.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// DSP Audio platform driver +// +// Copyright 2018 NXP + +#include +#include +#include +#include +#include +#include + +#include "fsl_dsp_cpu.h" + +static int dsp_audio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { + return 0; +} + + +static void dsp_audio_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { +} + +static const struct snd_soc_dai_ops dsp_audio_dai_ops = { + .startup = dsp_audio_startup, + .shutdown = dsp_audio_shutdown, +}; + +static struct snd_soc_dai_driver dsp_audio_dai = { + .name = "dsp-audio-cpu-dai", + .compress_new = snd_soc_new_compress, + .ops = &dsp_audio_dai_ops, + .playback = { + .stream_name = "Compress Playback", + .channels_min = 1, + }, +}; + +static const struct snd_soc_component_driver audio_dsp_component = { + .name = "audio-dsp", +}; + +static int dsp_audio_probe(struct platform_device *pdev) +{ + struct fsl_dsp_audio *dsp_audio; + int ret; + + dsp_audio = devm_kzalloc(&pdev->dev, sizeof(*dsp_audio), GFP_KERNEL); + if (dsp_audio == NULL) + return -ENOMEM; + + dev_dbg(&pdev->dev, "probing DSP device....\n"); + + /* intialise sof device */ + dev_set_drvdata(&pdev->dev, dsp_audio); + + pm_runtime_enable(&pdev->dev); + + /* now register audio DSP platform driver */ + ret = snd_soc_register_component(&pdev->dev, &audio_dsp_component, + &dsp_audio_dai, 1); + if (ret < 0) { + dev_err(&pdev->dev, + "error: failed to register DSP DAI driver %d\n", ret); + goto err; + } + + return 0; + +err: + return ret; +} + +static int dsp_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + + +static const struct of_device_id dsp_audio_ids[] = { + { .compatible = "fsl,dsp-audio"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dsp_audio_ids); + +static struct platform_driver dsp_audio_driver = { + .driver = { + .name = "dsp-audio", + .of_match_table = dsp_audio_ids, + }, + .probe = dsp_audio_probe, + .remove = dsp_audio_remove, +}; +module_platform_driver(dsp_audio_driver); diff --git a/sound/soc/fsl/fsl_dsp_cpu.h b/sound/soc/fsl/fsl_dsp_cpu.h new file mode 100644 index 000000000000..dac10b4ab9d0 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_cpu.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DSP Audio DAI header + * + * Copyright 2018 NXP + */ + +#ifndef __FSL_DSP_CPU_H +#define __FSL_DSP_CPU_H + +struct fsl_dsp_audio { + struct platform_device *pdev; +}; + +#endif /*__FSL_DSP_CPU_H*/ + diff --git a/sound/soc/fsl/fsl_dsp_library_load.c b/sound/soc/fsl/fsl_dsp_library_load.c new file mode 100644 index 000000000000..d273f7296e29 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_library_load.c @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2018 NXP +// Copyright (c) 2012-2013 by Tensilica Inc. + +#include +#include +#include + +#include "fsl_dsp.h" +#include "fsl_dsp_library_load.h" + +static Elf32_Half xtlib_host_half(Elf32_Half v, int byteswap) +{ + return (byteswap) ? (v >> 8) | (v << 8) : v; +} + +static Elf32_Word xtlib_host_word(Elf32_Word v, int byteswap) +{ + if (byteswap) { + v = ((v & 0x00FF00FF) << 8) | ((v & 0xFF00FF00) >> 8); + v = (v >> 16) | (v << 16); + } + return v; +} + +static int xtlib_verify_magic(Elf32_Ehdr *header, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Byte magic_no; + + magic_no = header->e_ident[EI_MAG0]; + if (magic_no != 0x7f) + return -1; + + magic_no = header->e_ident[EI_MAG1]; + if (magic_no != 'E') + return -1; + + magic_no = header->e_ident[EI_MAG2]; + if (magic_no != 'L') + return -1; + + magic_no = header->e_ident[EI_MAG3]; + if (magic_no != 'F') + return -1; + + if (header->e_ident[EI_CLASS] != ELFCLASS32) + return -1; + + { + /* determine byte order */ + union { + short s; + char c[sizeof(short)]; + } u; + + u.s = 1; + + if (header->e_ident[EI_DATA] == ELFDATA2LSB) + xtlib_globals->byteswap = u.c[sizeof(short) - 1] == 1; + else if (header->e_ident[EI_DATA] == ELFDATA2MSB) + xtlib_globals->byteswap = u.c[0] == 1; + else + return -1; + } + + return 0; +} + +static void xtlib_load_seg(Elf32_Phdr *pheader, void *src_addr, xt_ptr dst_addr, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Word bytes_to_copy = xtlib_host_word(pheader->p_filesz, + xtlib_globals->byteswap); + Elf32_Word bytes_to_zero = xtlib_host_word(pheader->p_memsz, + xtlib_globals->byteswap) + - bytes_to_copy; + unsigned int i; + char *src_back, *dst_back; + + void *zero_addr = (void *)dst_addr + bytes_to_copy; + + if (bytes_to_copy > 0) { + // memcpy((void *)(dst_addr), src_addr, bytes_to_copy); + src_back = (char *)src_addr; + dst_back = (char *)dst_addr; + for (i = 0; i < bytes_to_copy; i++) + *dst_back++ = *src_back++; + } + + if (bytes_to_zero > 0) { + // memset(zero_addr, 0, bytes_to_zero); + dst_back = (char *)zero_addr; + for (i = 0; i < bytes_to_zero; i++) + *dst_back++ = 0; + } +} + +#define xtlib_xt_half xtlib_host_half +#define xtlib_xt_word xtlib_host_word + +static xt_ptr align_ptr(xt_ptr ptr, xt_uint align) +{ + return (xt_ptr)(((xt_uint)ptr + align - 1) & ~(align - 1)); +} + +static xt_ptr xt_ptr_offs(xt_ptr base, Elf32_Word offs, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + + return (xt_ptr)xtlib_xt_word((xt_uint)base + + xtlib_host_word(offs, xtlib_globals->byteswap), + xtlib_globals->byteswap); +} + +static Elf32_Dyn *find_dynamic_info(Elf32_Ehdr *eheader, + struct lib_info *lib_info) +{ + char *base_addr = (char *)eheader; + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Phdr *pheader = (Elf32_Phdr *)(base_addr + + xtlib_host_word(eheader->e_phoff, + xtlib_globals->byteswap)); + + int seg = 0; + int num = xtlib_host_half(eheader->e_phnum, xtlib_globals->byteswap); + + while (seg < num) { + if (xtlib_host_word(pheader[seg].p_type, + xtlib_globals->byteswap) == PT_DYNAMIC) { + return (Elf32_Dyn *)(base_addr + + xtlib_host_word(pheader[seg].p_offset, + xtlib_globals->byteswap)); + } + seg++; + } + return 0; +} + +static int find_align(Elf32_Ehdr *header, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Shdr *sheader = (Elf32_Shdr *) (((char *)header) + + xtlib_host_word(header->e_shoff, xtlib_globals->byteswap)); + + int sec = 0; + int num = xtlib_host_half(header->e_shnum, xtlib_globals->byteswap); + + int align = 0; + + while (sec < num) { + if (sheader[sec].sh_type != SHT_NULL && + xtlib_host_word(sheader[sec].sh_size, + xtlib_globals->byteswap) > 0) { + int sec_align = + xtlib_host_word(sheader[sec].sh_addralign, + xtlib_globals->byteswap); + if (sec_align > align) + align = sec_align; + } + sec++; + } + + return align; +} + +static int validate_dynamic(Elf32_Ehdr *header, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + + if (xtlib_verify_magic(header, lib_info) != 0) + return XTLIB_NOT_ELF; + + if (xtlib_host_half(header->e_type, + xtlib_globals->byteswap) != ET_DYN) + return XTLIB_NOT_DYNAMIC; + + return XTLIB_NO_ERR; +} + +static int validate_dynamic_splitload(Elf32_Ehdr *header, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Phdr *pheader; + int err = validate_dynamic(header, lib_info); + + if (err != XTLIB_NO_ERR) + return err; + + /* make sure it's split load pi library, expecting three headers, + * code, data and dynamic, for example: + * + *LOAD off 0x00000094 vaddr 0x00000000 paddr 0x00000000 align 2**0 + * filesz 0x00000081 memsz 0x00000081 flags r-x + *LOAD off 0x00000124 vaddr 0x00000084 paddr 0x00000084 align 2**0 + * filesz 0x000001ab memsz 0x000011bc flags rwx + *DYNAMIC off 0x00000124 vaddr 0x00000084 paddr 0x00000084 align 2**2 + * filesz 0x000000a0 memsz 0x000000a0 flags rw- + */ + + if (xtlib_host_half(header->e_phnum, xtlib_globals->byteswap) != 3) + return XTLIB_NOT_SPLITLOAD; + + pheader = (Elf32_Phdr *)((char *)header + + xtlib_host_word(header->e_phoff, xtlib_globals->byteswap)); + + /* LOAD R-X */ + if (xtlib_host_word(pheader[0].p_type, + xtlib_globals->byteswap) != PT_LOAD || + (xtlib_host_word(pheader[0].p_flags, + xtlib_globals->byteswap) + & (PF_R | PF_W | PF_X)) != (PF_R | PF_X)) + return XTLIB_NOT_SPLITLOAD; + + /* LOAD RWX */ + if (xtlib_host_word(pheader[1].p_type, + xtlib_globals->byteswap) != PT_LOAD || + (xtlib_host_word(pheader[1].p_flags, + xtlib_globals->byteswap) + & (PF_R | PF_W | PF_X)) != (PF_R | PF_W | PF_X)) + return XTLIB_NOT_SPLITLOAD; + + /* DYNAMIC RW- */ + if (xtlib_host_word(pheader[2].p_type, + xtlib_globals->byteswap) != PT_DYNAMIC || + (xtlib_host_word(pheader[2].p_flags, + xtlib_globals->byteswap) + & (PF_R | PF_W | PF_X)) != (PF_R | PF_W)) + return XTLIB_NOT_SPLITLOAD; + + return XTLIB_NO_ERR; +} + +static unsigned int +xtlib_split_pi_library_size(struct xtlib_packaged_library *library, + unsigned int *code_size, + unsigned int *data_size, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Phdr *pheader; + Elf32_Ehdr *header = (Elf32_Ehdr *)library; + int align; + int err = validate_dynamic_splitload(header, lib_info); + + if (err != XTLIB_NO_ERR) { + xtlib_globals->err = err; + return err; + } + + align = find_align(header, lib_info); + + pheader = (Elf32_Phdr *)((char *)library + + xtlib_host_word(header->e_phoff, xtlib_globals->byteswap)); + + *code_size = xtlib_host_word(pheader[0].p_memsz, + xtlib_globals->byteswap) + align; + *data_size = xtlib_host_word(pheader[1].p_memsz, + xtlib_globals->byteswap) + align; + + return XTLIB_NO_ERR; +} + +static int get_dyn_info(Elf32_Ehdr *eheader, + xt_ptr dst_addr, xt_uint src_offs, + xt_ptr dst_data_addr, xt_uint src_data_offs, + struct xtlib_pil_info *info, + struct lib_info *lib_info) +{ + unsigned int jmprel = 0; + unsigned int pltrelsz = 0; + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Dyn *dyn_entry = find_dynamic_info(eheader, lib_info); + + if (dyn_entry == 0) + return XTLIB_NO_DYNAMIC_SEGMENT; + + info->dst_addr = (xt_uint)xtlib_xt_word((Elf32_Word)dst_addr, + xtlib_globals->byteswap); + info->src_offs = xtlib_xt_word(src_offs, xtlib_globals->byteswap); + info->dst_data_addr = (xt_uint)xtlib_xt_word( + (Elf32_Word)dst_data_addr + src_data_offs, + xtlib_globals->byteswap); + info->src_data_offs = xtlib_xt_word(src_data_offs, + xtlib_globals->byteswap); + + dst_addr -= src_offs; + dst_data_addr = dst_data_addr + src_data_offs - src_data_offs; + + info->start_sym = xt_ptr_offs(dst_addr, eheader->e_entry, lib_info); + + info->align = xtlib_xt_word(find_align(eheader, lib_info), + xtlib_globals->byteswap); + + info->text_addr = 0; + + while (dyn_entry->d_tag != DT_NULL) { + switch ((Elf32_Sword) xtlib_host_word( + (Elf32_Word)dyn_entry->d_tag, + xtlib_globals->byteswap)) { + case DT_RELA: + info->rel = xt_ptr_offs(dst_data_addr, + dyn_entry->d_un.d_ptr, lib_info); + break; + case DT_RELASZ: + info->rela_count = xtlib_xt_word( + xtlib_host_word(dyn_entry->d_un.d_val, + xtlib_globals->byteswap) / + sizeof(Elf32_Rela), + xtlib_globals->byteswap); + break; + case DT_INIT: + info->init = xt_ptr_offs(dst_addr, + dyn_entry->d_un.d_ptr, lib_info); + break; + case DT_FINI: + info->fini = xt_ptr_offs(dst_addr, + dyn_entry->d_un.d_ptr, lib_info); + break; + case DT_HASH: + info->hash = xt_ptr_offs(dst_data_addr, + dyn_entry->d_un.d_ptr, lib_info); + break; + case DT_SYMTAB: + info->symtab = xt_ptr_offs(dst_data_addr, + dyn_entry->d_un.d_ptr, lib_info); + break; + case DT_STRTAB: + info->strtab = xt_ptr_offs(dst_data_addr, + dyn_entry->d_un.d_ptr, lib_info); + break; + case DT_JMPREL: + jmprel = dyn_entry->d_un.d_val; + break; + case DT_PLTRELSZ: + pltrelsz = dyn_entry->d_un.d_val; + break; + case DT_LOPROC + 2: + info->text_addr = xt_ptr_offs(dst_addr, + dyn_entry->d_un.d_ptr, lib_info); + break; + + default: + /* do nothing */ + break; + } + dyn_entry++; + } + + return XTLIB_NO_ERR; +} + +static xt_ptr +xtlib_load_split_pi_library_common(struct xtlib_packaged_library *library, + xt_ptr destination_code_address, + xt_ptr destination_data_address, + struct xtlib_pil_info *info, + struct lib_info *lib_info) +{ + struct xtlib_loader_globals *xtlib_globals = + &lib_info->xtlib_globals; + Elf32_Ehdr *header = (Elf32_Ehdr *)library; + Elf32_Phdr *pheader; + unsigned int align; + int err = validate_dynamic_splitload(header, lib_info); + xt_ptr destination_code_address_back; + xt_ptr destination_data_address_back; + + if (err != XTLIB_NO_ERR) { + xtlib_globals->err = err; + return 0; + } + + align = find_align(header, lib_info); + + destination_code_address_back = destination_code_address; + destination_data_address_back = destination_data_address; + + destination_code_address = align_ptr(destination_code_address, align); + destination_data_address = align_ptr(destination_data_address, align); + lib_info->code_buf_virt += (destination_code_address - + destination_code_address_back); + lib_info->data_buf_virt += (destination_data_address - + destination_data_address_back); + + pheader = (Elf32_Phdr *)((char *)library + + xtlib_host_word(header->e_phoff, + xtlib_globals->byteswap)); + + err = get_dyn_info(header, + destination_code_address, + xtlib_host_word(pheader[0].p_paddr, + xtlib_globals->byteswap), + destination_data_address, + xtlib_host_word(pheader[1].p_paddr, + xtlib_globals->byteswap), + info, + lib_info); + + if (err != XTLIB_NO_ERR) { + xtlib_globals->err = err; + return 0; + } + + /* loading code */ + xtlib_load_seg(&pheader[0], + (char *)library + xtlib_host_word(pheader[0].p_offset, + xtlib_globals->byteswap), + (xt_ptr)lib_info->code_buf_virt, + lib_info); + + if (info->text_addr == 0) + info->text_addr = + (xt_ptr)xtlib_xt_word((Elf32_Word)destination_code_address, + xtlib_globals->byteswap); + + /* loading data */ + xtlib_load_seg(&pheader[1], + (char *)library + xtlib_host_word(pheader[1].p_offset, + xtlib_globals->byteswap), + (xt_ptr)lib_info->data_buf_virt + + xtlib_host_word(pheader[1].p_paddr, + xtlib_globals->byteswap), + lib_info); + + return (xt_ptr)xtlib_host_word((Elf32_Word)info->start_sym, + xtlib_globals->byteswap); +} + +static 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 *info, + struct lib_info *lib_info) +{ + return xtlib_load_split_pi_library_common(library, + destination_code_address, + destination_data_address, + info, + lib_info); +} + +static long +load_dpu_with_library(struct xf_client *client, struct xf_proxy *proxy, + struct lib_info *lib_info) +{ + struct fsl_dsp *dsp_priv = container_of(proxy, struct fsl_dsp, proxy); + unsigned char *srambuf; + struct lib_dnld_info_t dpulib; + struct file *file; + struct xf_buffer *buf; + Elf32_Phdr *pheader; + Elf32_Ehdr *header; + loff_t pos = 0; + unsigned int align; + int filesize = 0; + long ret_val = 0; + + file = filp_open(lib_info->filename, O_RDONLY, 0); + if (IS_ERR(file)) + return PTR_ERR(file); + + vfs_llseek(file, 0, SEEK_END); + filesize = (int)file->f_pos; + + srambuf = kmalloc(filesize, GFP_KERNEL); + if (!srambuf) + return -ENOMEM; + + vfs_llseek(file, 0, SEEK_SET); + ret_val = kernel_read(file, srambuf, filesize, &pos); + if (ret_val < 0) + return ret_val; + filp_close(file, NULL); + + ret_val = xtlib_split_pi_library_size( + (struct xtlib_packaged_library *)(srambuf), + (unsigned int *)&dpulib.size_code, + (unsigned int *)&dpulib.size_data, + lib_info); + if (ret_val != XTLIB_NO_ERR) + return -EINVAL; + + lib_info->code_buf_size = dpulib.size_code; + lib_info->data_buf_size = dpulib.size_data; + + header = (Elf32_Ehdr *)srambuf; + pheader = (Elf32_Phdr *)((char *)srambuf + + xtlib_host_word(header->e_phoff, + lib_info->xtlib_globals.byteswap)); + + align = find_align(header, lib_info); + ret_val = xf_pool_alloc(client, proxy, 1, dpulib.size_code + align, + XF_POOL_AUX, &lib_info->code_section_pool); + if (ret_val) { + kfree(srambuf); + pr_err("Allocation failure for loading code section\n"); + return -ENOMEM; + } + + ret_val = xf_pool_alloc(client, proxy, 1, + dpulib.size_data + pheader[1].p_paddr + align, + XF_POOL_AUX, &lib_info->data_section_pool); + if (ret_val) { + kfree(srambuf); + pr_err("Allocation failure for loading data section\n"); + return -ENOMEM; + } + + buf = xf_buffer_get(lib_info->code_section_pool); + lib_info->code_buf_virt = xf_buffer_data(buf); + lib_info->code_buf_phys = ((u64)xf_buffer_data(buf) - + (u64)dsp_priv->scratch_buf_virt) + + dsp_priv->scratch_buf_phys; + lib_info->code_buf_size = dpulib.size_code + align; + xf_buffer_put(buf); + + buf = xf_buffer_get(lib_info->data_section_pool); + lib_info->data_buf_virt = xf_buffer_data(buf); + lib_info->data_buf_phys = ((u64)xf_buffer_data(buf) - + (u64)dsp_priv->scratch_buf_virt) + + dsp_priv->scratch_buf_phys; + lib_info->data_buf_size = dpulib.size_data + align + pheader[1].p_paddr; + xf_buffer_put(buf); + + dpulib.pbuf_code = (unsigned long)lib_info->code_buf_phys; + dpulib.pbuf_data = (unsigned long)lib_info->data_buf_phys; + + dpulib.ppil_inf = &lib_info->pil_info; + xtlib_host_load_split_pi_library((struct xtlib_packaged_library *)srambuf, + (xt_ptr)(dpulib.pbuf_code), + (xt_ptr)(dpulib.pbuf_data), + (struct xtlib_pil_info *)dpulib.ppil_inf, + (void *)lib_info); + kfree(srambuf); + + return ret_val; +} + +static long +unload_dpu_with_library(struct xf_client *client, struct xf_proxy *proxy, + struct lib_info *lib_info) +{ + xf_pool_free(client, lib_info->code_section_pool); + xf_pool_free(client, lib_info->data_section_pool); + + return 0; +} + +long xf_load_lib(struct xf_client *client, + struct xf_handle *handle, struct lib_info *lib_info) +{ + void *b = xf_handle_aux(handle); + struct icm_xtlib_pil_info icm_info; + struct xf_proxy *proxy = handle->proxy; + struct xf_message msg; + struct xf_message *rmsg; + long ret_val; + + ret_val = load_dpu_with_library(client, proxy, lib_info); + if (ret_val) + return ret_val; + + memcpy((void *)(&icm_info.pil_info), (void *)(&lib_info->pil_info), + sizeof(struct xtlib_pil_info)); + + icm_info.lib_type = lib_info->lib_type; + + /* ...set message parameters */ + msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), __XF_PORT_SPEC2(handle->id, 0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_LOAD_LIB; + msg.buffer = b; + msg.length = sizeof(struct icm_xtlib_pil_info); + msg.ret = 0; + + /* ...copy lib info */ + memcpy(b, (void *)&icm_info, xf_buffer_length(handle->aux)); + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + if (IS_ERR(rmsg)) + return PTR_ERR(rmsg); + +// xf_msg_free(proxy, rmsg); +// xf_unlock(&proxy->lock); + + return 0; +} + +long xf_unload_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info) +{ + void *b = xf_handle_aux(handle); + struct xf_proxy *proxy = handle->proxy; + struct xf_message msg; + struct xf_message *rmsg; + struct icm_xtlib_pil_info icm_info; + + memset((void *)&icm_info, 0, sizeof(struct icm_xtlib_pil_info)); + icm_info.lib_type = lib_info->lib_type; + + /* ...set message parameters */ + msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0),__XF_PORT_SPEC2(handle->id, 0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_UNLOAD_LIB; + msg.buffer = b; + msg.length = sizeof(struct icm_xtlib_pil_info); + msg.ret = 0; + + /* ...copy lib info */ + memcpy(b, (void *)&icm_info, xf_buffer_length(handle->aux)); + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + if (IS_ERR(rmsg)) + return PTR_ERR(rmsg); + +// xf_msg_free(proxy, rmsg); +// xf_unlock(&proxy->lock); + + return unload_dpu_with_library(client, proxy, lib_info); +} diff --git a/sound/soc/fsl/fsl_dsp_library_load.h b/sound/soc/fsl/fsl_dsp_library_load.h new file mode 100644 index 000000000000..8c14dda20b27 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_library_load.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2018 NXP +// Copyright (c) 2012-2013 by Tensilica Inc. + +#ifndef FSL_DSP_LIBRARY_LOAD_H +#define FSL_DSP_LIBRARY_LOAD_H + +#include "fsl_dsp_pool.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 + +struct xtlib_packaged_library; + +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 +}; + +enum lib_type { + DSP_CODEC_LIB = 1, + DSP_CODEC_WRAP_LIB +}; + +struct xtlib_loader_globals { + int err; + int byteswap; +}; + +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; + unsigned int lib_type; +}; + +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 lib_info { + struct xtlib_pil_info pil_info; + struct xtlib_loader_globals xtlib_globals; + + struct xf_pool *code_section_pool; + struct xf_pool *data_section_pool; + + void *code_buf_virt; + unsigned int code_buf_phys; + unsigned int code_buf_size; + void *data_buf_virt; + unsigned int data_buf_phys; + unsigned int data_buf_size; + + const char *filename; + unsigned int lib_type; +}; + +long xf_load_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info); +long xf_unload_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info); + +#endif diff --git a/sound/soc/fsl/fsl_dsp_platform.h b/sound/soc/fsl/fsl_dsp_platform.h new file mode 100644 index 000000000000..15d085c9f23d --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_platform.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED. + * Copyright 2018 NXP + */ + +#ifndef _FSL_DSP_PLATFORM_H +#define _FSL_DSP_PLATFORM_H + +#include "fsl_dsp_xaf_api.h" + +struct dsp_data { + struct xf_client *client; + struct xaf_pipeline *p_pipe; + struct xaf_pipeline pipeline; + struct xaf_comp component[2]; + int codec_type; + int status; +}; + +#endif /*_FSL_DSP_PLATFORM_H*/ diff --git a/sound/soc/fsl/fsl_dsp_platform_compress.c b/sound/soc/fsl/fsl_dsp_platform_compress.c new file mode 100644 index 000000000000..43b07af3cbfe --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_platform_compress.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// DSP driver compress implementation +// +// Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED. +// Copyright 2018 NXP + +#include +#include +#include +#include + +#include "fsl_dsp.h" +#include "fsl_dsp_platform.h" +#include "fsl_dsp_xaf_api.h" + +#define NUM_CODEC 2 +#define MIN_FRAGMENT 1 +#define MAX_FRAGMENT 1 +#define MIN_FRAGMENT_SIZE (4 * 1024) +#define MAX_FRAGMENT_SIZE (4 * 1024) + +void dsp_platform_process(struct work_struct *w) +{ + struct xf_client *client = container_of(w, struct xf_client, work); + struct xf_proxy *proxy = client->proxy; + struct xf_message *rmsg; + + while (1) { + rmsg = xf_cmd_recv(proxy, &client->wait, &client->queue, 1); + + if (!proxy->is_active || IS_ERR(rmsg)) + return; + if (rmsg->opcode == XF_EMPTY_THIS_BUFFER) { + client->consume_bytes += rmsg->length; + snd_compr_fragment_elapsed(client->cstream); + + if (rmsg->buffer == NULL && rmsg->length == 0) + snd_compr_drain_notify(client->cstream); + + } else { + memcpy(&client->m, rmsg, sizeof(struct xf_message)); + complete(&client->compr_complete); + } + + xf_msg_free(proxy, rmsg); + xf_unlock(&proxy->lock); + } +} + +static int dsp_platform_compr_open(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + + drv->client = xf_client_alloc(dsp_priv); + if (IS_ERR(drv->client)) + return PTR_ERR(drv->client); + + fsl_dsp_open_func(dsp_priv, drv->client); + + drv->client->proxy = &dsp_priv->proxy; + + cpu_dai->driver->ops->startup(NULL, cpu_dai); + + drv->client->cstream = cstream; + + INIT_WORK(&drv->client->work, dsp_platform_process); + + return 0; +} + +static int dsp_platform_compr_free(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + int ret; + + if (cstream->runtime->state != SNDRV_PCM_STATE_PAUSED && + cstream->runtime->state != SNDRV_PCM_STATE_RUNNING && + cstream->runtime->state != SNDRV_PCM_STATE_DRAINING) { + + ret = xaf_comp_delete(drv->client, &drv->component[1]); + if (ret) { + dev_err(component->dev, "Fail to delete component, err = %d\n", ret); + return ret; + } + + ret = xaf_comp_delete(drv->client, &drv->component[0]); + if (ret) { + dev_err(component->dev, "Fail to delete component, err = %d\n", ret); + return ret; + } + } + + cpu_dai->driver->ops->shutdown(NULL, cpu_dai); + + drv->client->proxy->is_active = 0; + wake_up(&drv->client->wait); + cancel_work_sync(&drv->client->work); + + fsl_dsp_close_func(drv->client); + + return 0; +} + +static int dsp_platform_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + /* accroding to the params, load the library and create component*/ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + struct xf_proxy *p_proxy = &dsp_priv->proxy; + struct xf_set_param_msg s_param; + int ret; + + switch (params->codec.id) { + case SND_AUDIOCODEC_MP3: + drv->codec_type = CODEC_MP3_DEC; + break; + case SND_AUDIOCODEC_AAC: + drv->codec_type = CODEC_AAC_DEC; + break; + default: + dev_err(component->dev, "codec not supported, id =%d\n", params->codec.id); + return -EINVAL; + } + + /* ...create auxiliary buffers pool for control commands */ + ret = xf_pool_alloc(drv->client, + p_proxy, + XA_AUX_POOL_SIZE, + XA_AUX_POOL_MSG_LENGTH, + XF_POOL_AUX, + &p_proxy->aux); + if (ret) { + dev_err(component->dev, "xf_pool_alloc failed"); + return ret; + } + + /* ...create pipeline */ + ret = xaf_pipeline_create(&drv->pipeline); + if (ret) { + dev_err(component->dev, "create pipeline error\n"); + goto err_pool_alloc; + } + + /* ...create component */ + ret = xaf_comp_create(drv->client, p_proxy, &drv->component[0], + drv->codec_type); + if (ret) { + dev_err(component->dev, + "create component failed type = %d, err = %d\n", + drv->codec_type, ret); + goto err_pool_alloc; + } + + ret = xaf_comp_create(drv->client, p_proxy, &drv->component[1], + RENDER_ESAI); + if (ret) { + dev_err(component->dev, + "create component failed, type = %d, err = %d\n", + RENDER_ESAI, ret); + goto err_comp0_create; + } + + /* ...add component into pipeline */ + ret = xaf_comp_add(&drv->pipeline, &drv->component[0]); + if (ret) { + dev_err(component->dev, + "add component failed, type = %d, err = %d\n", + drv->codec_type, ret); + goto err_comp1_create; + } + + ret = xaf_comp_add(&drv->pipeline, &drv->component[1]); + if (ret) { + dev_err(component->dev, + "add component failed, type = %d, err = %d\n", + drv->codec_type, ret); + goto err_comp1_create; + } + + drv->client->input_bytes = 0; + drv->client->consume_bytes = 0; + + s_param.id = XA_RENDERER_CONFIG_PARAM_SAMPLE_RATE; + s_param.mixData.value = params->codec.sample_rate; + ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param); + if (ret) { + dev_err(component->dev, + "set param[cmd:0x%x|val:0x%x] error, err = %d\n", + s_param.id, s_param.mixData.value, ret); + goto err_comp1_create; + } + + s_param.id = XA_RENDERER_CONFIG_PARAM_CHANNELS; + s_param.mixData.value = params->codec.ch_out; + ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param); + if (ret) { + dev_err(component->dev, + "set param[cmd:0x%x|val:0x%x] error, err = %d\n", + s_param.id, s_param.mixData.value, ret); + goto err_comp1_create; + } + + s_param.id = XA_RENDERER_CONFIG_PARAM_PCM_WIDTH; + s_param.mixData.value = 16; + ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param); + if (ret) { + dev_err(component->dev, + "set param[cmd:0x%x|val:0x%x] error, err = %d\n", + s_param.id, s_param.mixData.value, ret); + goto err_comp1_create; + } + return 0; + +err_comp1_create: + xaf_comp_delete(drv->client, &drv->component[1]); +err_comp0_create: + xaf_comp_delete(drv->client, &drv->component[0]); +err_pool_alloc: + xf_pool_free(drv->client, p_proxy->aux); + + return ret; +} + +static int dsp_platform_compr_trigger_start(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + struct xaf_comp *p_comp = &drv->component[0]; + int ret; + + ret = xaf_comp_process(drv->client, + p_comp, + p_comp->inptr, + drv->client->input_bytes, + XF_EMPTY_THIS_BUFFER); + + ret = xaf_connect(drv->client, + &drv->component[0], + &drv->component[1], + 1, + OUTBUF_SIZE); + if (ret) { + dev_err(component->dev, "Failed to connect component, err = %d\n", ret); + return ret; + } + + schedule_work(&drv->client->work); + + return 0; +} + +static int dsp_platform_compr_trigger_stop(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + int ret; + + ret = xaf_comp_flush(drv->client, &drv->component[0]); + if (ret) { + dev_err(component->dev, "Fail to flush component, err = %d\n", ret); + return ret; + } + + ret = xaf_comp_flush(drv->client, &drv->component[1]); + if (ret) { + dev_err(component->dev, "Fail to flush component, err = %d\n", ret); + return ret; + } + + ret = xaf_comp_delete(drv->client, &drv->component[0]); + if (ret) { + dev_err(component->dev, "Fail to delete component, err = %d\n", ret); + return ret; + } + + ret = xaf_comp_delete(drv->client, &drv->component[1]); + if (ret) { + dev_err(component->dev, "Fail to delete component, err = %d\n", ret); + return ret; + } + + return 0; +} + +static int dsp_platform_compr_trigger_drain(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + struct xaf_comp *p_comp = &drv->component[0]; + int ret; + + ret = xaf_comp_process(drv->client, p_comp, NULL, 0, + XF_EMPTY_THIS_BUFFER); + + schedule_work(&drv->client->work); + return 0; +} + +static int dsp_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd) +{ + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = dsp_platform_compr_trigger_start(cstream); + break; + case SNDRV_PCM_TRIGGER_STOP: + ret = dsp_platform_compr_trigger_stop(cstream); + break; + case SND_COMPR_TRIGGER_DRAIN: + ret = dsp_platform_compr_trigger_drain(cstream); + break; + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + break; + } + + /*send command*/ + return ret; +} + +static int dsp_platform_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + + tstamp->copied_total = drv->client->input_bytes; + tstamp->byte_offset = drv->client->input_bytes; + tstamp->pcm_frames = 0x900; + tstamp->pcm_io_frames = 0, + tstamp->sampling_rate = 48000; + + return 0; +} + +static int dsp_platform_compr_copy(struct snd_compr_stream *cstream, + char __user *buf, + size_t count) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); + struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); + struct dsp_data *drv = &dsp_priv->dsp_data; + struct xaf_comp *p_comp = &drv->component[0]; + int copied = 0; + int ret; + + if (drv->client->input_bytes == drv->client->consume_bytes) { + if (count > INBUF_SIZE){ + ret = copy_from_user(p_comp->inptr, buf, INBUF_SIZE); + if (ret) { + dev_err(component->dev, "failed to get message from user space\n"); + return -EFAULT; + } + copied = INBUF_SIZE; + } else { + ret = copy_from_user(p_comp->inptr, buf, count); + if (ret) { + dev_err(component->dev, "failed to get message from user space\n"); + return -EFAULT; + } + copied = count; + } + drv->client->input_bytes += copied; + + if (cstream->runtime->state == SNDRV_PCM_STATE_RUNNING) { + ret = xaf_comp_process(drv->client, p_comp, + p_comp->inptr, copied, + XF_EMPTY_THIS_BUFFER); + schedule_work(&drv->client->work); + } + } + + return copied; +} + +static int dsp_platform_compr_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + caps->num_codecs = NUM_CODEC; + caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ + caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ + caps->min_fragments = MIN_FRAGMENT; + caps->max_fragments = MAX_FRAGMENT; + caps->codecs[0] = SND_AUDIOCODEC_MP3; + caps->codecs[1] = SND_AUDIOCODEC_AAC; + + return 0; +} + +static struct snd_compr_codec_caps caps_mp3 = { + .num_descriptors = 1, + .descriptor[0].max_ch = 2, + .descriptor[0].sample_rates[0] = 48000, + .descriptor[0].sample_rates[1] = 44100, + .descriptor[0].sample_rates[2] = 32000, + .descriptor[0].sample_rates[3] = 16000, + .descriptor[0].sample_rates[4] = 8000, + .descriptor[0].num_sample_rates = 5, + .descriptor[0].bit_rate[0] = 320, + .descriptor[0].bit_rate[1] = 192, + .descriptor[0].num_bitrates = 2, + .descriptor[0].profiles = 0, + .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, + .descriptor[0].formats = 0, +}; + +static struct snd_compr_codec_caps caps_aac = { + .num_descriptors = 2, + .descriptor[1].max_ch = 2, + .descriptor[0].sample_rates[0] = 48000, + .descriptor[0].sample_rates[1] = 44100, + .descriptor[0].sample_rates[2] = 32000, + .descriptor[0].sample_rates[3] = 16000, + .descriptor[0].sample_rates[4] = 8000, + .descriptor[0].num_sample_rates = 5, + .descriptor[1].bit_rate[0] = 320, + .descriptor[1].bit_rate[1] = 192, + .descriptor[1].num_bitrates = 2, + .descriptor[1].profiles = 0, + .descriptor[1].modes = 0, + .descriptor[1].formats = + (SND_AUDIOSTREAMFORMAT_MP4ADTS | + SND_AUDIOSTREAMFORMAT_RAW), +}; + +static int dsp_platform_compr_get_codec_caps(struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec) +{ + if (codec->codec == SND_AUDIOCODEC_MP3) + *codec = caps_mp3; + else if (codec->codec == SND_AUDIOCODEC_AAC) + *codec = caps_aac; + else + return -EINVAL; + + return 0; +} + +static int dsp_platform_compr_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + return 0; +} + +const struct snd_compr_ops dsp_platform_compr_ops = { + .open = dsp_platform_compr_open, + .free = dsp_platform_compr_free, + .set_params = dsp_platform_compr_set_params, + .set_metadata = dsp_platform_compr_set_metadata, + .trigger = dsp_platform_compr_trigger, + .pointer = dsp_platform_compr_pointer, + .copy = dsp_platform_compr_copy, + .get_caps = dsp_platform_compr_get_caps, + .get_codec_caps = dsp_platform_compr_get_codec_caps, +}; diff --git a/sound/soc/fsl/fsl_dsp_pool.c b/sound/soc/fsl/fsl_dsp_pool.c new file mode 100644 index 000000000000..637454d97231 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_pool.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Xtensa buffer pool API +// +// Copyright 2018 NXP +// Copyright (c) 2012-2013 by Tensilica Inc. + +#include + +#include "fsl_dsp_pool.h" +#include "fsl_dsp.h" + +/* ...allocate buffer pool */ +int xf_pool_alloc(struct xf_client *client, struct xf_proxy *proxy, + u32 number, u32 length, xf_pool_type_t type, + struct xf_pool **pool) +{ + struct xf_pool *p; + struct xf_buffer *b; + void *data; + struct xf_message msg; + struct xf_message *rmsg; + + /* ...basic sanity checks; number of buffers is positive */ + if (number <=0) + return -EINVAL; + + /* ...get properly aligned buffer length */ + length = ALIGN(length, XF_PROXY_ALIGNMENT); + + p = kzalloc(offsetof(struct xf_pool, buffer) + + number * sizeof(struct xf_buffer), GFP_KERNEL); + if(!p) + return -ENOMEM; + + /* ...prepare command parameters */ + msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_ALLOC; + msg.length = length * number; + msg.buffer = NULL; + msg.ret = 0; + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + if (IS_ERR(rmsg)) { + kfree(p); + return PTR_ERR(rmsg); + } + + p->p = rmsg->buffer; + /* TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); + * xf_unlock(&proxy->lock); */ + + /* ...if operation is failed, do cleanup */ + /* ...set pool parameters */ + p->number = number, p->length = length; + p->proxy = proxy; + + /* ...create individual buffers and link them into free list */ + for (p->free = b = &p->buffer[0], data = p->p; number > 0; + number--, b++) { + /* ...set address of the buffer (no length there) */ + b->address = data; + + /* ...file buffer into the free list */ + b->link.next = b + 1; + + /* ...advance data pointer in contiguous buffer */ + data += length; + } + + /* ...terminate list of buffers (not too good - tbd) */ + b[-1].link.next = NULL; + + /* ...return buffer pointer */ + *pool = p; + + return 0; +} +/* ...buffer pool destruction */ +int xf_pool_free(struct xf_client *client, struct xf_pool *pool) +{ + struct xf_proxy *proxy; + struct xf_message msg; + struct xf_message *rmsg; + + /* ...basic sanity checks; pool is positive */ + if (pool == NULL) + return -EINVAL; + + /* ...get proxy pointer */ + if ((proxy = pool->proxy) == NULL) + return -EINVAL; + + /* ...prepare command parameters */ + msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_FREE; + msg.length = pool->length * pool->number; + msg.buffer = pool->p; + msg.ret = 0; + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + kfree(pool); + if (IS_ERR(rmsg)) + return PTR_ERR(rmsg); + + /* TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); + * xf_unlock(&proxy->lock); */ + + return 0; +} + +/* ...get new buffer from a pool */ +struct xf_buffer *xf_buffer_get(struct xf_pool *pool) +{ + struct xf_buffer *b; + + xf_lock(&pool->proxy->lock); + /* ...take buffer from a head of the free list */ + b = pool->free; + if (b) { + /* ...advance free list head */ + pool->free = b->link.next, b->link.pool = pool; + } + + xf_unlock(&pool->proxy->lock); + return b; +} + +/* ...return buffer back to pool */ +void xf_buffer_put(struct xf_buffer *buffer) +{ + struct xf_pool *pool = buffer->link.pool; + + xf_lock(&pool->proxy->lock); + /* ...use global proxy lock for pool operations protection */ + /* ...put buffer back to a pool */ + buffer->link.next = pool->free, pool->free = buffer; + + xf_unlock(&pool->proxy->lock); +} diff --git a/sound/soc/fsl/fsl_dsp_pool.h b/sound/soc/fsl/fsl_dsp_pool.h new file mode 100644 index 000000000000..4a56262faf7f --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_pool.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Xtensa buffer pool API header + * + * Copyright 2018 NXP + * Copyright (c) 2012-2013 by Tensilica Inc + */ +#ifndef FSL_DSP_POOL_H +#define FSL_DSP_POOL_H + +#include +#include "fsl_dsp_proxy.h" + +/* ...buffer pool type */ +typedef u32 xf_pool_type_t; + +/* ...previous declaration of struct */ +struct xf_buffer; +struct xf_pool; +struct xf_handle; +struct xf_message; +struct xf_client; + +/* ...response callback */ +typedef void (*xf_response_cb)(struct xf_handle *h, struct xf_message *msg); + +/* ...buffer pool type */ +enum xf_pool_type { + XF_POOL_AUX = 0, + XF_POOL_INPUT = 1, + XF_POOL_OUTPUT = 2 +}; + +/* ...buffer link pointer */ +union xf_buffer_link { + /* ...pointer to next free buffer in a pool (for free buffer) */ + struct xf_buffer *next; + /* ...reference to a buffer pool (for allocated buffer) */ + struct xf_pool *pool; +}; + +/* ...buffer descriptor */ +struct xf_buffer { + /* ...virtual address of contiguous buffer */ + void *address; + /* ...link pointer */ + union xf_buffer_link link; +}; + +/* ...buffer pool */ +struct xf_pool { + /* ...reference to proxy data */ + struct xf_proxy *proxy; + /* ...length of individual buffer in a pool */ + u32 length; + /* ...number of buffers in a pool */ + u32 number; + /* ...pointer to pool memory */ + void *p; + /* ...pointer to first free buffer in a pool */ + struct xf_buffer *free; + /* ...individual buffers */ + struct xf_buffer buffer[0]; +}; + +/* component handle */ +struct xf_handle { + /* ...reference to proxy data */ + struct xf_proxy *proxy; + /* ...auxiliary control buffer for control transactions */ + struct xf_buffer *aux; + /* ...global client-id of the component */ + u32 id; + /* ...local client number (think about merging into "id" field - tbd) */ + u32 client; + /* ...response processing hook */ + xf_response_cb response; +}; + +/* ...accessor to buffer data */ +static inline void *xf_buffer_data(struct xf_buffer *buffer) +{ + return buffer->address; +} + +/* ...length of buffer data */ +static inline size_t xf_buffer_length(struct xf_buffer *buffer) +{ + struct xf_pool *pool = buffer->link.pool; + + return (size_t)pool->length; +} + +/* ...component client-id (global scope) */ +static inline u32 xf_handle_id(struct xf_handle *handle) +{ + return handle->id; +} + +/* ...pointer to auxiliary buffer */ +static inline void *xf_handle_aux(struct xf_handle *handle) +{ + return xf_buffer_data(handle->aux); +} + +int xf_pool_alloc(struct xf_client *client, struct xf_proxy *proxy, u32 number, + u32 length, xf_pool_type_t type, struct xf_pool **pool); +int xf_pool_free(struct xf_client *client, struct xf_pool *pool); + +struct xf_buffer *xf_buffer_get(struct xf_pool *pool); +void xf_buffer_put(struct xf_buffer *buffer); + +#endif /* FSL_DSP_POOL_H */ diff --git a/sound/soc/fsl/fsl_dsp_proxy.c b/sound/soc/fsl/fsl_dsp_proxy.c new file mode 100644 index 000000000000..266a50deab91 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_proxy.c @@ -0,0 +1,858 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +// +// DSP proxy driver transfers messages between DSP driver and DSP framework +// +// Copyright 2018 NXP +// Copyright (C) 2017 Cadence Design Systems, Inc. + +#include "fsl_dsp_proxy.h" +#include "fsl_dsp.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; + + /* ...acquire global lock */ + xf_lock(&proxy->lock); + + /* ...try to peek message from the queue */ + m = xf_msg_dequeue(queue); + if (m == NULL) { + /* ...queue is empty; release lock */ + xf_unlock(&proxy->lock); + } + + /* ...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_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, proxy); + + MU_SendMessage(dsp_priv->mu_base_virtaddr, 0, msg); + return 0; +} + +int icm_intr_extended_send(struct xf_proxy *proxy, + u32 msg, + struct dsp_ext_msg *ext_msg) +{ + struct fsl_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, proxy); + struct device *dev = dsp_priv->dev; + union icm_header_t msghdr; + + msghdr.allbits = msg; + if (msghdr.size != 8) + dev_err(dev, "too much ext msg\n"); + + MU_SendMessage(dsp_priv->mu_base_virtaddr, 1, ext_msg->phys); + MU_SendMessage(dsp_priv->mu_base_virtaddr, 2, ext_msg->size); + MU_SendMessage(dsp_priv->mu_base_virtaddr, 0, msg); + + return 0; +} + +int send_dpu_ext_msg_addr(struct xf_proxy *proxy) +{ + struct fsl_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, proxy); + union icm_header_t msghdr; + struct dsp_ext_msg ext_msg; + struct dsp_mem_msg *dpu_ext_msg = + (struct dsp_mem_msg *)((unsigned char *)dsp_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 = dsp_priv->msg_buf_phys + (MSG_BUF_SIZE / 2); + ext_msg.size = sizeof(struct dsp_mem_msg); + + dpu_ext_msg->ext_msg_phys = dsp_priv->msg_buf_phys; + dpu_ext_msg->ext_msg_size = MSG_BUF_SIZE; + dpu_ext_msg->scratch_phys = dsp_priv->scratch_buf_phys; + dpu_ext_msg->scratch_size = dsp_priv->scratch_buf_size; + dpu_ext_msg->dsp_config_phys = dsp_priv->dsp_config_phys; + dpu_ext_msg->dsp_config_size = dsp_priv->dsp_config_size; + dpu_ext_msg->dsp_board_type = dsp_priv->dsp_board_type; + + icm_intr_extended_send(proxy, msghdr.allbits, &ext_msg); + + return ret_val; +} + +long icm_ack_wait(struct xf_proxy *proxy, u32 msg) +{ + struct fsl_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, proxy); + struct device *dev = dsp_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_dsp_mu_isr(int irq, void *dev_id) +{ + struct xf_proxy *proxy = dev_id; + struct fsl_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, proxy); + struct device *dev = dsp_priv->dev; + union icm_header_t msghdr; + u32 reg; + + MU_ReceiveMsg(dsp_priv->mu_base_virtaddr, 0, ®); + 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; + case XF_SUSPEND: + case XF_RESUME: + 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 (dsp_priv->scratch_buf_size) + +/* ...shared memory translation - kernel virtual address to shared address */ +u32 xf_proxy_b2a(struct xf_proxy *proxy, void *b) +{ + struct fsl_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, proxy); + + if (b == NULL) + return XF_PROXY_NULL; + else if ((u32)(b - dsp_priv->scratch_buf_virt) < + dsp_priv->scratch_buf_size) + return (u32)(b - dsp_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_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, proxy); + + if (address < dsp_priv->scratch_buf_size) + return dsp_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_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, 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(dsp_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); + m->ret = response->ret; + + /* ...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); + command->ret = m->ret; + + /* ...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); + } + + if (status) + icm_intr_send(proxy, 0); + + 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_dsp *dsp_priv = container_of(proxy, + struct fsl_dsp, 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 *)dsp_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, cmd_invalid, 0); + XF_PROXY_WRITE(proxy, rsp_read_idx, 0); + XF_PROXY_WRITE(proxy, rsp_write_idx, 0); + XF_PROXY_WRITE(proxy, rsp_invalid, 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 = NULL; + int ret; + + /* ...wait for message reception (take lock on success) */ + ret = wait_event_interruptible(*wq, + (m = xf_msg_received(proxy, queue)) != NULL || !wait + || !proxy->is_active); + if (ret) + return ERR_PTR(-EINTR); + + /* ...return message with a lock taken */ + return m; +} + +struct xf_message *xf_cmd_recv_timeout(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_timeout(*wq, + (m = xf_msg_received(proxy, queue)) != NULL || !wait, + msecs_to_jiffies(1000)); + if (ret < 0) + return ERR_PTR(-EINTR); + + if (ret == 0) + return ERR_PTR(-ETIMEDOUT); + + /* ...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); +} + +struct xf_message *xf_cmd_send_recv_wq(struct xf_proxy *proxy, u32 id, + u32 opcode, void *buffer, u32 length, + wait_queue_head_t *wq, + struct xf_msg_queue *queue) +{ + 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, wq, queue, 1); +} + +struct xf_message *xf_cmd_send_recv_complete(struct xf_client *client, + struct xf_proxy *proxy, + u32 id, u32 opcode, void *buffer, + u32 length, + struct work_struct *work, + struct completion *completion) +{ + struct xf_message *m; + int ret; + + /* ...retrieve message handle (take the lock on success) */ + m = xf_msg_available(proxy); + if (!m) + return ERR_PTR(-EBUSY); + + /* ...fill-in message parameters (lock is taken) */ + m->id = id; + m->opcode = opcode; + m->length = length; + m->buffer = buffer; + m->ret = 0; + + init_completion(completion); + + /* ...submit command to the proxy */ + xf_proxy_command(proxy, m); + + schedule_work(work); + + /* ...wait for message reception (take lock on success) */ + ret = wait_for_completion_timeout(completion, + msecs_to_jiffies(1000)); + if (!ret) + return ERR_PTR(-ETIMEDOUT); + + m = &client->m; + + /* ...return message with a lock taken */ + return m; +} +/* + * 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)) { + xf_unlock(&proxy->lock); + 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); + xf_unlock(&proxy->lock); + + 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)) { + xf_unlock(&proxy->lock); + 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); + xf_unlock(&proxy->lock); + + return ret; +} + +/* + * suspend & resume functions + */ +int xf_cmd_send_suspend(struct xf_proxy *proxy) +{ + union icm_header_t msghdr; + int ret = 0; + + init_completion(&proxy->cmd_complete); + + msghdr.allbits = 0; /* clear all bits; */ + msghdr.ack = 0; + msghdr.intr = 1; + msghdr.msg = XF_SUSPEND; + msghdr.size = 0; + icm_intr_send(proxy, msghdr.allbits); + + /* wait for response here */ + ret = icm_ack_wait(proxy, msghdr.allbits); + + return ret; +} + +int xf_cmd_send_resume(struct xf_proxy *proxy) +{ + union icm_header_t msghdr; + int ret = 0; + + init_completion(&proxy->cmd_complete); + + msghdr.allbits = 0; /* clear all bits; */ + msghdr.ack = 0; + msghdr.intr = 1; + msghdr.msg = XF_RESUME; + msghdr.size = 0; + icm_intr_send(proxy, msghdr.allbits); + + /* wait for response here */ + ret = icm_ack_wait(proxy, msghdr.allbits); + + return ret; +} + +/* ...open component handle */ +int xf_open(struct xf_client *client, struct xf_proxy *proxy, + struct xf_handle *handle, const char *id, u32 core, + xf_response_cb response) +{ + void *b; + struct xf_message msg; + struct xf_message *rmsg; + + /* ...retrieve auxiliary control buffer from proxy - need I */ + handle->aux = xf_buffer_get(proxy->aux); + + b = xf_handle_aux(handle); + + msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_REGISTER; + msg.buffer = b; + msg.length = strlen(id) + 1; + msg.ret = 0; + + /* ...copy component identifier */ + memcpy(b, (void *)id, xf_buffer_length(handle->aux)); + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + + if (IS_ERR(rmsg)) { + xf_buffer_put(handle->aux), handle->aux = NULL; + return PTR_ERR(rmsg); + } + /* ...save received component global client-id */ + handle->id = XF_MSG_SRC(rmsg->id); + /* TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); + * xf_unlock(&proxy->lock); */ + + /* ...if failed, release buffer handle */ + /* ...operation completed successfully; assign handle data */ + handle->response = response; + handle->proxy = proxy; + + return 0; +} + +/* ...close component handle */ +int xf_close(struct xf_client *client, struct xf_handle *handle) +{ + struct xf_proxy *proxy = handle->proxy; + struct xf_message msg; + struct xf_message *rmsg; + + /* ...do I need to take component lock here? guess no - tbd */ + + /* ...buffers and stuff? - tbd */ + + /* ...acquire global proxy lock */ + /* ...unregister component from remote DSP proxy (ignore result code) */ + + msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), handle->id); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_UNREGISTER; + msg.buffer = NULL; + msg.length = 0; + msg.ret = 0; + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + + if (IS_ERR(rmsg)) { + xf_buffer_put(handle->aux), handle->aux = NULL; + return PTR_ERR(rmsg); + } + /* TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); + * xf_unlock(&proxy->lock); */ + + /* ...wipe out proxy pointer */ + handle->proxy = NULL; + + return 0; +} diff --git a/sound/soc/fsl/fsl_dsp_proxy.h b/sound/soc/fsl/fsl_dsp_proxy.h new file mode 100644 index 000000000000..bc9ccf37bc8f --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_proxy.h @@ -0,0 +1,520 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * DSP proxy header - commands/responses from DSP driver to DSP ramework + * + * Copyright 2018 NXP + * Copyright (c) 2017 Cadence Design Systems, Inc. + */ + +#ifndef __FSL_DSP_PROXY_H +#define __FSL_DSP_PROXY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_dsp_pool.h" +#define XF_CFG_MESSAGE_POOL_SIZE 256 + +struct xf_client; + +/******************************************************************************* + * Local proxy data + ******************************************************************************/ + +struct xf_message; +struct xf_handle; +typedef void (*xf_response_cb)(struct xf_handle *h, struct xf_message *msg); + +/* ...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, +}; + +/* ...adjust IPC client of message going from user-space */ +#define XF_MSG_AP_FROM_USER(id, client) (((id) & ~(0xF << 2)) | (client << 2)) + + +#define __XF_PORT_SPEC(core, id, port) ((core) | ((id) << 2) | ((port) << 8)) +#define __XF_PORT_SPEC2(id, port) ((id) | ((port) << 8)) + + +/* ...wipe out IPC client from message going to user-space */ +#define XF_MSG_AP_TO_USER(id) ((id) & ~(0xF << 18)) +#define __XF_AP_PROXY(core) ((core) | 0x8000) +#define __XF_DSP_PROXY(core) ((core) | 0x8000) + +/* ...message id contains source and destination ports specification */ +#define __XF_MSG_ID(src, dst) (((src) & 0xFFFF) | (((dst) & 0xFFFF) << 16)) +#define XF_MSG_SRC(id) (((id) >> 0) & 0xFFFF) +#define XF_MSG_SRC_CORE(id) (((id) >> 0) & 0x3) +#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_CLIENT(id) (((id) >> 22) & 0x1FF) +#define __XF_AP_PROXY(core) ((core) | 0x8000) +#define __XF_DSP_PROXY(core) ((core) | 0x8000) +#define __XF_AP_CLIENT(core, client) ((core) | ((client) << 6) | 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) + 0x10001) & (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; + + /* ...indicate command queue is valid or not */ + u32 cmd_invalid; +}; + +/* ...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; + + /* ...indicate response queue is valid or not */ + u32 rsp_invalid; +}; + +/* ...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_cmd_invalid(shmem) \ + __XF_PROXY_READ_ATOMIC(shmem->local.cmd_invalid) + +#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 + +#define __XF_PROXY_READ_rsp_invalid(shmem) \ + __XF_PROXY_READ_ATOMIC(shmem->remote.rsp_invalid) + +/* ...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_cmd_invalid(shmem, v) \ + __XF_PROXY_WRITE_ATOMIC(shmem->local.cmd_invalid, 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) + +#define __XF_PROXY_WRITE_rsp_invalid(shmem, v) \ + __XF_PROXY_WRITE_ATOMIC(shmem->remote.rsp_invalid, 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; + int is_active; + + /* ...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; + + /* ...auxiliary buffer pool for clients */ + struct xf_pool *aux; +}; + +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 dsp_ext_msg { + u32 phys; + u32 size; +}; + +struct dsp_mem_msg { + u32 ext_msg_phys; + u32 ext_msg_size; + u32 scratch_phys; + u32 scratch_size; + u32 dsp_config_phys; + u32 dsp_config_size; + u32 dsp_board_type; +}; + +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); + +struct xf_message* +xf_cmd_recv_timeout(struct xf_proxy *proxy, wait_queue_head_t *wq, + struct xf_msg_queue *queue, int wait); + +struct xf_message* +xf_cmd_send_recv(struct xf_proxy *proxy, u32 id, u32 opcode, + void *buffer, u32 length); + +struct xf_message* +xf_cmd_send_recv_wq(struct xf_proxy *proxy, u32 id, u32 opcode, void *buffer, + u32 length, wait_queue_head_t *wq, + struct xf_msg_queue *queue); + +struct xf_message* +xf_cmd_send_recv_complete(struct xf_client *client, struct xf_proxy *proxy, + u32 id, u32 opcode, void *buffer, u32 length, + struct work_struct *work, + struct completion *completion); + +/* ...mu interrupt handle */ +irqreturn_t fsl_dsp_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); + +int xf_cmd_send_suspend(struct xf_proxy *proxy); +int xf_cmd_send_resume(struct xf_proxy *proxy); + +int xf_cmd_alloc(struct xf_proxy *proxy, void **buffer, u32 length); +int xf_cmd_free(struct xf_proxy *proxy, void *buffer, u32 length); + +int xf_open(struct xf_client *client, struct xf_proxy *proxy, + struct xf_handle *handle, const char *id, u32 core, + xf_response_cb response); + +int xf_close(struct xf_client *client, struct xf_handle *handle); + + + +/******************************************************************************* + * Opcode composition + ******************************************************************************/ + +/* ...opcode composition with command/response data tags */ +#define __XF_OPCODE(c, r, op) (((c) << 31) | ((r) << 30) | ((op) & 0x3F)) + +/* ...accessors */ +#define XF_OPCODE_CDATA(opcode) ((opcode) & (1 << 31)) +#define XF_OPCODE_RDATA(opcode) ((opcode) & (1 << 30)) +#define XF_OPCODE_TYPE(opcode) ((opcode) & (0x3F)) + +/******************************************************************************* + * Opcode types + ******************************************************************************/ + +/* ...unregister client */ +#define XF_UNREGISTER __XF_OPCODE(0, 0, 0) + +/* ...register client at proxy */ +#define XF_REGISTER __XF_OPCODE(1, 0, 1) + +/* ...port routing command */ +#define XF_ROUTE __XF_OPCODE(1, 0, 2) + +/* ...port unrouting command */ +#define XF_UNROUTE __XF_OPCODE(1, 0, 3) + +/* ...shared buffer allocation */ +#define XF_ALLOC __XF_OPCODE(0, 0, 4) + +/* ...shared buffer freeing */ +#define XF_FREE __XF_OPCODE(0, 0, 5) + +/* ...set component parameters */ +#define XF_SET_PARAM __XF_OPCODE(1, 0, 6) + +/* ...get component parameters */ +#define XF_GET_PARAM __XF_OPCODE(1, 1, 7) + +/* ...input buffer reception */ +#define XF_EMPTY_THIS_BUFFER __XF_OPCODE(1, 0, 8) + +/* ...output buffer reception */ +#define XF_FILL_THIS_BUFFER __XF_OPCODE(0, 1, 9) + +/* ...flush specific port */ +#define XF_FLUSH __XF_OPCODE(0, 0, 10) + +/* ...start component operation */ +#define XF_START __XF_OPCODE(0, 0, 11) + +/* ...stop component operation */ +#define XF_STOP __XF_OPCODE(0, 0, 12) + +/* ...pause component operation */ +#define XF_PAUSE __XF_OPCODE(0, 0, 13) + +/* ...resume component operation */ +#define XF_RESUME __XF_OPCODE(0, 0, 14) + +/* ...resume component operation */ +#define XF_SUSPEND __XF_OPCODE(0, 0, 15) + +/* ...load lib for component operation */ +#define XF_LOAD_LIB __XF_OPCODE(0, 0, 16) + +/* ...unload lib for component operation */ +#define XF_UNLOAD_LIB __XF_OPCODE(0, 0, 17) + +/* ...component output eos operation */ +#define XF_OUTPUT_EOS __XF_OPCODE(0, 0, 18) + +/* ...total amount of supported decoder commands */ +#define __XF_OP_NUM 19 + +#endif diff --git a/sound/soc/fsl/fsl_dsp_xaf_api.c b/sound/soc/fsl/fsl_dsp_xaf_api.c new file mode 100644 index 000000000000..4312a6b29ef1 --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_xaf_api.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +// +// Xtensa Audio Framework API for communication with DSP +// +// Copyright (C) 2017 Cadence Design Systems, Inc. +// Copyright 2018 NXP + +#include "fsl_dsp.h" +#include "fsl_dsp_xaf_api.h" + +/* ...send a command message to component */ +int xf_command(struct xf_client *client, struct xf_handle *handle, + u32 port, u32 opcode, void *buffer, u32 length) +{ + struct xf_proxy *proxy = handle->proxy; + struct xf_message msg; + + /* ...fill-in message parameters */ + msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), + __XF_PORT_SPEC2(handle->id, port)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = opcode; + msg.length = length; + msg.buffer = buffer; + msg.ret = 0; + + /* ...execute command synchronously */ + return xf_cmd_send(proxy, msg.id, msg.opcode, msg.buffer, msg.length); +} + +int xaf_comp_set_config(struct xf_client *client, struct xaf_comp *p_comp, + u32 num_param, void *p_param) +{ + struct xf_handle *p_handle; + struct xf_message msg; + struct xf_message *rmsg; + struct xf_set_param_msg *smsg; + struct xf_set_param_msg *param = (struct xf_set_param_msg *)p_param; + struct xf_proxy *proxy; + u32 i; + + p_handle = &p_comp->handle; + proxy = p_handle->proxy; + + /* ...set persistent stream characteristics */ + smsg = xf_buffer_data(p_handle->aux); + + for (i = 0; i < num_param; i++) { + smsg[i].id = param[i].id; + smsg[i].mixData.value = param[i].mixData.value; + } + + /* ...set command parameters */ + msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), + __XF_PORT_SPEC2(p_handle->id, 0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_SET_PARAM; + msg.length = sizeof(*smsg) * num_param; + msg.buffer = smsg; + msg.ret = 0; + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + + if(IS_ERR(rmsg)) + return PTR_ERR(rmsg); + /* ...save received component global client-id */ + /* TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); + * xf_unlock(&proxy->lock); + */ + + /* ...make sure response is expected */ + if ((rmsg->opcode != XF_SET_PARAM) || (rmsg->buffer != smsg)) { + return -EPIPE; + } + + return 0; +} + +int xaf_comp_get_config(struct xf_client *client, struct xaf_comp *p_comp, + u32 num_param, void *p_param) +{ + + struct xf_handle *p_handle; + struct xf_message msg; + struct xf_message *rmsg; + struct xf_get_param_msg *smsg; + struct xf_get_param_msg *param = (struct xf_get_param_msg *)p_param; + struct xf_proxy *proxy; + u32 i; + + p_handle = &p_comp->handle; + proxy = p_handle->proxy; + + /* ...set persistent stream characteristics */ + smsg = xf_buffer_data(p_handle->aux); + + for (i = 0; i < num_param; i++) + smsg[i].id = param[i].id; + + + msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), + __XF_PORT_SPEC2(p_handle->id, 0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_GET_PARAM; + msg.length = sizeof(*smsg) * num_param; + msg.buffer = smsg; + msg.ret = 0; + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + + /* ...save received component global client-id */ + if(IS_ERR(rmsg)) + return PTR_ERR(rmsg); + + /* TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); + * xf_unlock(&proxy->lock); */ + + /* ...make sure response is expected */ + if ((rmsg->opcode != (u32)XF_GET_PARAM) || (rmsg->buffer != smsg)) { + return -EPIPE; + } + + for (i = 0; i < num_param; i++) + param[i].mixData.value = smsg[i].mixData.value; + + return 0; +} + +int xaf_comp_flush(struct xf_client *client, struct xaf_comp *p_comp) +{ + + struct xf_handle *p_handle; + struct xf_proxy *proxy; + struct xf_message msg; + struct xf_message *rmsg; + + p_handle = &p_comp->handle; + proxy = p_handle->proxy; + + msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), + __XF_PORT_SPEC2(p_handle->id, 0)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_FLUSH; + msg.length = 0; + msg.buffer = NULL; + msg.ret = 0; + + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + + if(IS_ERR(rmsg)) + return PTR_ERR(rmsg); + + /* ...make sure response is expected */ + if ((rmsg->opcode != (u32)XF_FLUSH) || rmsg->buffer) { + return -EPIPE; + } + + return 0; +} + +int xaf_comp_create(struct xf_client *client, struct xf_proxy *proxy, + struct xaf_comp *p_comp, int comp_type) +{ + struct fsl_dsp *dsp_priv = container_of(proxy, struct fsl_dsp, proxy); + char lib_path[200]; + char lib_wrap_path[200]; + struct xf_handle *p_handle; + struct xf_buffer *buf; + int ret = 0; + bool loadlib = true; + + memset((void *)p_comp, 0, sizeof(struct xaf_comp)); + + strcpy(lib_path, "/usr/lib/imx-mm/audio-codec/dsp/"); + strcpy(lib_wrap_path, "/usr/lib/imx-mm/audio-codec/dsp/"); + + p_handle = &p_comp->handle; + + p_comp->comp_type = comp_type; + + if (comp_type == RENDER_ESAI) + loadlib = false; + + if (loadlib) { + p_comp->codec_lib.filename = lib_path; + p_comp->codec_wrap_lib.filename = lib_wrap_path; + p_comp->codec_lib.lib_type = DSP_CODEC_LIB; + } + + switch (comp_type) { + case CODEC_MP3_DEC: + p_comp->dec_id = "audio-decoder/mp3"; + strcat(lib_path, "lib_dsp_mp3_dec.so"); + break; + case CODEC_AAC_DEC: + p_comp->dec_id = "audio-decoder/aac"; + strcat(lib_path, "lib_dsp_aac_dec.so"); + break; + case RENDER_ESAI: + p_comp->dec_id = "renderer/esai"; + break; + + default: + return -EINVAL; + break; + } + + /* ...create decoder component instance (select core-0) */ + ret = xf_open(client, proxy, p_handle, p_comp->dec_id, 0, NULL); + if (ret) { + dev_err(dsp_priv->dev, "create (%s) component error: %d\n", + p_comp->dec_id, ret); + return ret; + } + + if (loadlib) { + strcat(lib_wrap_path, "lib_dsp_codec_wrap.so"); + p_comp->codec_wrap_lib.lib_type = DSP_CODEC_WRAP_LIB; + + /* ...load codec wrapper lib */ + ret = xf_load_lib(client, p_handle, &p_comp->codec_wrap_lib); + if (ret) { + dev_err(dsp_priv->dev, "load codec wrap lib error\n"); + goto err_wrap_load; + } + + /* ...load codec lib */ + ret = xf_load_lib(client, p_handle, &p_comp->codec_lib); + if (ret) { + dev_err(dsp_priv->dev, "load codec lib error\n"); + goto err_codec_load; + } + + /* ...allocate input buffer */ + ret = xf_pool_alloc(client, proxy, 1, INBUF_SIZE, + XF_POOL_INPUT, &p_comp->inpool); + if (ret) { + dev_err(dsp_priv->dev, "alloc input buf error\n"); + goto err_pool_alloc; + } + + /* ...initialize input buffer pointer */ + buf = xf_buffer_get(p_comp->inpool); + p_comp->inptr = xf_buffer_data(buf); + } + + p_comp->active = true; + + return ret; + +err_pool_alloc: + xf_unload_lib(client, p_handle, &p_comp->codec_lib); +err_codec_load: + xf_unload_lib(client, p_handle, &p_comp->codec_wrap_lib); +err_wrap_load: + xf_close(client, p_handle); + + return ret; +} + +int xaf_comp_delete(struct xf_client *client, struct xaf_comp *p_comp) +{ + + struct xf_handle *p_handle; + bool loadlib = true; + u32 ret = 0; + + if (!p_comp->active) + return ret; + + /* mark component as unusable from this point */ + p_comp->active = false; + + if (p_comp->comp_type == RENDER_ESAI) + loadlib = false; + + p_handle = &p_comp->handle; + + if (loadlib) { + /* ...unload codec wrapper library */ + xf_unload_lib(client, p_handle, &p_comp->codec_wrap_lib); + + /* ...unload codec library */ + xf_unload_lib(client, p_handle, &p_comp->codec_lib); + + xf_pool_free(client, p_comp->inpool); + } + + /* ...delete component */ + xf_close(client, p_handle); + + return ret; +} + +int xaf_comp_process(struct xf_client *client, struct xaf_comp *p_comp, void *p_buf, u32 length, u32 flag) +{ + struct xf_handle *p_handle; + u32 ret = 0; + + p_handle = &p_comp->handle; + + switch (flag) { + case XF_FILL_THIS_BUFFER: + /* ...send message to component output port (port-id=1) */ + ret = xf_command(client, p_handle, 1, XF_FILL_THIS_BUFFER, + p_buf, length); + break; + case XF_EMPTY_THIS_BUFFER: + /* ...send message to component input port (port-id=0) */ + ret = xf_command(client, p_handle, 0, XF_EMPTY_THIS_BUFFER, + p_buf, length); + break; + default: + break; + } + + return ret; +} + +/* ...port binding function */ +int xf_route(struct xf_client *client, struct xf_handle *src, u32 src_port, + struct xf_handle *dst, u32 dst_port, u32 num, u32 size, u32 align) +{ + struct xf_proxy *proxy = src->proxy; + struct xf_buffer *b; + struct xf_route_port_msg *m; + struct xf_message msg; + struct xf_message *rmsg; + + /* ...sanity checks - proxy pointers are same */ + if (proxy != dst->proxy) + return -EINVAL; + + /* ...buffer data is sane */ + if (!(num && size && xf_is_power_of_two(align))) + return -EINVAL; + + /* ...get control buffer */ + if ((b = xf_buffer_get(proxy->aux)) == NULL) + return -EBUSY; + + /* ...get message buffer */ + m = xf_buffer_data(b); + + /* ...fill-in message parameters */ + m->src = __XF_PORT_SPEC2(src->id, src_port); + m->dst = __XF_PORT_SPEC2(dst->id, dst_port); + m->alloc_number = num; + m->alloc_size = size; + m->alloc_align = align; + + /* ...set command parameters */ + msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), + __XF_PORT_SPEC2(src->id, src_port)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_ROUTE; + msg.length = sizeof(*m); + msg.buffer = m; + msg.ret = 0; + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + if(IS_ERR(rmsg)) + return PTR_ERR(rmsg); + + /* ...save received component global client-id */ + /* TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); + * xf_unlock(&proxy->lock); */ + + + /* ...synchronously execute command on remote DSP */ + /* XF_CHK_API(xf_proxy_cmd_exec(proxy, &msg)); */ + + /* ...return buffer to proxy */ + xf_buffer_put(b); + + /* ...check result is successful */ + /* XF_CHK_ERR(msg.opcode == XF_ROUTE, -ENOMEM); */ + + return 0; +} + +/* ...port unbinding function */ +int xf_unroute(struct xf_client *client, struct xf_handle *src, u32 src_port) +{ + struct xf_proxy *proxy = src->proxy; + struct xf_buffer *b; + struct xf_unroute_port_msg *m; + struct xf_message msg; + struct xf_message *rmsg; + int r = 0; + + /* ...get control buffer */ + if((b = xf_buffer_get(proxy->aux)) == NULL) + return -EBUSY; + + /* ...get message buffer */ + m = xf_buffer_data(b); + + /* ...fill-in message parameters */ + m->src = __XF_PORT_SPEC2(src->id, src_port); + + /* ...set command parameters */ + msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), + __XF_PORT_SPEC2(src->id, src_port)); + msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); + msg.opcode = XF_UNROUTE; + msg.length = sizeof(*m); + msg.buffer = m; + msg.ret = 0; + + /* ...execute command synchronously */ + rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, + msg.buffer, msg.length, &client->work, + &client->compr_complete); + if (IS_ERR(rmsg)) + return PTR_ERR(rmsg); + /* ...save received component global client-id */ + + /*TODO: review cleanup */ + /* xf_msg_free(proxy, rmsg); */ + /* xf_unlock(&proxy->lock); */ + + /* ...return buffer to proxy */ + xf_buffer_put(b); + + return r; +} + +int xaf_connect(struct xf_client *client, + struct xaf_comp *p_src, + struct xaf_comp *p_dest, + u32 num_buf, + u32 buf_length) +{ + /* ...connect p_src output port with p_dest input port */ + return xf_route(client, &p_src->handle, 0, &p_dest->handle, 0, + num_buf, buf_length, 8); +} + +int xaf_disconnect(struct xf_client *client, struct xaf_comp *p_comp) +{ + /* ...disconnect p_src output port with p_dest input port */ + return xf_unroute(client, &p_comp->handle, 0); + +} + +int xaf_comp_add(struct xaf_pipeline *p_pipe, struct xaf_comp *p_comp) +{ + int ret = 0; + + p_comp->next = p_pipe->comp_chain; + p_comp->pipeline = p_pipe; + p_pipe->comp_chain = p_comp; + + return ret; +} + +int xaf_pipeline_create(struct xaf_pipeline *p_pipe) +{ + int ret = 0; + + memset(p_pipe, 0, sizeof(struct xaf_pipeline)); + + return ret; +} + +int xaf_pipeline_delete(struct xaf_pipeline *p_pipe) +{ + int ret = 0; + + memset(p_pipe, 0, sizeof(struct xaf_pipeline)); + + return ret; +} diff --git a/sound/soc/fsl/fsl_dsp_xaf_api.h b/sound/soc/fsl/fsl_dsp_xaf_api.h new file mode 100644 index 000000000000..2367b6c9c34d --- /dev/null +++ b/sound/soc/fsl/fsl_dsp_xaf_api.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT)*/ +/* + * Xtensa Audio Framework API for communication with DSP + * + * Copyright (C) 2017 Cadence Design Systems, Inc. + * Copyright 2018 NXP + */ +#ifndef FSL_DSP_XAF_API_H +#define FSL_DSP_XAF_API_H + +#include "fsl_dsp_library_load.h" + +/* ...size of auxiliary pool for communication with DSP */ +#define XA_AUX_POOL_SIZE 32 + +/* ...length of auxiliary pool messages */ +#define XA_AUX_POOL_MSG_LENGTH 128 + +/* ...number of max input buffers */ +#define INBUF_SIZE 4096 +#define OUTBUF_SIZE 16384 + +struct xaf_pipeline; + +struct xaf_info_s { + u32 opcode; + void *buf; + u32 length; + u32 ret; +}; + +struct xaf_comp { + struct xaf_comp *next; + + struct xaf_pipeline *pipeline; + struct xf_handle handle; + + const char *dec_id; + int comp_type; + + struct xf_pool *inpool; + struct xf_pool *outpool; + void *inptr; + void *outptr; + + struct lib_info codec_lib; + struct lib_info codec_wrap_lib; + + int active; /* component fully initialized */ +}; + +struct xaf_pipeline { + struct xaf_comp *comp_chain; + + u32 input_eos; + u32 output_eos; +}; + +int xaf_comp_create(struct xf_client *client, struct xf_proxy *p_proxy, + struct xaf_comp *p_comp, int comp_type); +int xaf_comp_delete(struct xf_client *client, struct xaf_comp *p_comp); +int xaf_comp_flush(struct xf_client *client, struct xaf_comp *p_comp); + +int xaf_comp_set_config(struct xf_client *client,struct xaf_comp *p_comp, + u32 num_param, void *p_param); +int xaf_comp_get_config(struct xf_client *client,struct xaf_comp *p_comp, + u32 num_param, void *p_param); + +int xaf_comp_add(struct xaf_pipeline *p_pipe, struct xaf_comp *p_comp); +int xaf_comp_process(struct xf_client *client, struct xaf_comp *p_comp, + void *p_buf, u32 length, u32 flag); +int xaf_comp_get_status(struct xaf_comp *p_comp, struct xaf_info_s *p_info); +int xaf_comp_get_msg_count(struct xaf_comp *p_comp); + +int xaf_connect(struct xf_client *client,struct xaf_comp *p_src, + struct xaf_comp *p_dest, u32 num_buf, u32 buf_length); +int xaf_disconnect(struct xf_client *client,struct xaf_comp *p_comp); + +int xaf_pipeline_create(struct xaf_pipeline *p_pipe); +int xaf_pipeline_delete(struct xaf_pipeline *p_pipe); + +int xaf_pipeline_send_eos(struct xaf_pipeline *p_pipe); + +/* ...port routing command */ +struct __attribute__((__packed__)) xf_route_port_msg { + /* ...source port specification */ + u32 src; + /* ...destination port specification */ + u32 dst; + /* ...number of buffers to allocate */ + u32 alloc_number; + /* ...length of buffer to allocate */ + u32 alloc_size; + /* ...alignment restriction for a buffer */ + u32 alloc_align; +}; + +/* ...port unrouting command */ +struct __attribute__((__packed__)) xf_unroute_port_msg { + /* ...source port specification */ + u32 src; + /* ...destination port specification */ + u32 dst; +}; + +/* ...check if non-zero value is a power-of-two */ +#define xf_is_power_of_two(v) (((v) & ((v) - 1)) == 0) + + +/******************************************************************************* + * bascial message + ******************************************************************************/ +typedef union DATA { + u32 value; + + struct { + u32 size; + u32 channel_table[10]; + } chan_map_tab; + + struct { + u32 samplerate; + u32 width; + u32 depth; + u32 channels; + u32 endian; + u32 interleave; + u32 layout[12]; + u32 chan_pos_set; // indicate if channel position is set outside or use codec default + } outputFormat; +} data_t; + +/* ...component initialization parameter */ +struct __attribute__((__packed__)) xf_set_param_msg { + /* ...index of parameter passed to SET_CONFIG_PARAM call */ + u32 id; + /* ...value of parameter */ + data_t mixData; +}; + +/* ...message body (command/response) */ +struct __attribute__((__packed__)) xf_get_param_msg { + /* ...array of parameters requested */ + u32 id; + /* ...array of parameters values */ + data_t mixData; +}; + +/* ...renderer-specific configuration parameters */ +enum xa_config_param_renderer { + XA_RENDERER_CONFIG_PARAM_CB = 0, + XA_RENDERER_CONFIG_PARAM_STATE = 1, + XA_RENDERER_CONFIG_PARAM_PCM_WIDTH = 2, + XA_RENDERER_CONFIG_PARAM_CHANNELS = 3, + XA_RENDERER_CONFIG_PARAM_SAMPLE_RATE = 4, + XA_RENDERER_CONFIG_PARAM_FRAME_SIZE = 5, + XA_RENDERER_CONFIG_PARAM_NUM = 6, +}; + +#endif /* FSL_DSP_XAF_API_H */ diff --git a/sound/soc/fsl/imx-dsp.c b/sound/soc/fsl/imx-dsp.c new file mode 100644 index 000000000000..f60d55073d8e --- /dev/null +++ b/sound/soc/fsl/imx-dsp.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: (GPL-2.0+ +// +// DSP machine driver +// +// Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED. +// Copyright 2018 NXP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct imx_dsp_audio_data { + struct snd_soc_dai_link dai[2]; + struct snd_soc_card card; +}; + +static int imx_dsp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + u32 dai_format = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBS_CFS; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + 24576000, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "failed to set codec sysclk: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_fmt(codec_dai, dai_format); + if (ret) { + dev_err(rtd->dev, "failed to set codec dai fmt: %d\n", ret); + return ret; + } + + return 0; +} + +static struct snd_soc_ops imx_dsp_ops_be = { + .hw_params = imx_dsp_hw_params, +}; + +static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) { + + struct snd_interval *rate; + struct snd_interval *channels; + struct snd_mask *mask; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + rate->max = rate->min = 48000; + + channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + channels->max = channels->min = 2; + + mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_none(mask); + snd_mask_set(mask, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static const struct snd_soc_dapm_route imx_dsp_audio_map[] = { + {"Playback", NULL, "Compress Playback"},/* dai route for be and fe */ + {"Playback", NULL, "Playback"}, +}; + +static int imx_dsp_audio_probe(struct platform_device *pdev) +{ + struct device_node *cpu_np=NULL, *codec_np=NULL, *platform_np=NULL; + struct snd_soc_dai_link_component *comp; + struct platform_device *cpu_pdev; + struct imx_dsp_audio_data *data; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL); + if (!comp) { + ret = -ENOMEM; + goto fail; + } + + cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + cpu_pdev = of_find_device_by_node(cpu_np); + if (!cpu_pdev) { + dev_err(&pdev->dev, "failed to find rpmsg platform device\n"); + ret = -EINVAL; + goto fail; + } + + codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + platform_np = of_parse_phandle(pdev->dev.of_node, "audio-platform", 0); + if (!platform_np) { + dev_err(&pdev->dev, "platform missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + + data->dai[0].cpus = &comp[0]; + data->dai[0].codecs = &comp[1]; + data->dai[0].platforms = &comp[2]; + + data->dai[0].num_cpus = 1; + data->dai[0].num_codecs = 1; + data->dai[0].num_platforms = 1; + + data->dai[0].name = "dsp hifi fe"; + data->dai[0].stream_name = "dsp hifi fe"; + data->dai[0].codecs->dai_name = "snd-soc-dummy-dai"; + data->dai[0].codecs->name = "snd-soc-dummy"; + data->dai[0].cpus->dai_name = dev_name(&cpu_pdev->dev); + data->dai[0].cpus->of_node = cpu_np; + data->dai[0].platforms->of_node = platform_np; + data->dai[0].playback_only = true; + data->dai[0].capture_only = false; + data->dai[0].dpcm_playback = 1; + data->dai[0].dpcm_capture = 0; + data->dai[0].dynamic = 1, + data->dai[0].ignore_pmdown_time = 1, + data->dai[0].dai_fmt = SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + data->dai[1].cpus = &comp[3]; + data->dai[1].codecs = &comp[4]; + data->dai[1].platforms = &comp[5]; + + data->dai[1].num_cpus = 1; + data->dai[1].num_codecs = 1; + data->dai[1].num_platforms = 1; + + data->dai[1].name = "dsp hifi be"; + data->dai[1].stream_name = "dsp hifi be"; + data->dai[1].codecs->dai_name = "cs42888"; + data->dai[1].codecs->of_node = codec_np; + data->dai[1].cpus->dai_name = "snd-soc-dummy-dai"; + data->dai[1].cpus->name = "snd-soc-dummy"; + data->dai[1].platforms->name = "snd-soc-dummy"; + data->dai[1].playback_only = true; + data->dai[1].capture_only = false; + data->dai[1].dpcm_playback = 1; + data->dai[1].dpcm_capture = 0; + data->dai[1].no_pcm = 1, + data->dai[1].ignore_pmdown_time = 1, + data->dai[1].dai_fmt = SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + data->dai[1].ops = &imx_dsp_ops_be; + data->dai[1].be_hw_params_fixup = be_hw_params_fixup; + + data->card.dapm_routes = imx_dsp_audio_map; + data->card.num_dapm_routes = ARRAY_SIZE(imx_dsp_audio_map); + data->card.num_links = 2; + data->card.dai_link = data->dai; + + data->card.dev = &pdev->dev; + data->card.owner = THIS_MODULE; + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto fail; + + platform_set_drvdata(pdev, &data->card); + snd_soc_card_set_drvdata(&data->card, data); + ret = devm_snd_soc_register_card(&pdev->dev, &data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto fail; + } + +fail: + if (cpu_np) + of_node_put(cpu_np); + if (codec_np) + of_node_put(codec_np); + if (platform_np) + of_node_put(platform_np); + return ret; +} + +static const struct of_device_id imx_dsp_audio_dt_ids[] = { + { .compatible = "fsl,imx-dsp-audio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_dsp_audio_dt_ids); + +static struct platform_driver imx_dsp_audio_driver = { + .driver = { + .name = "imx-dsp-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = imx_dsp_audio_dt_ids, + }, + .probe = imx_dsp_audio_probe, +}; +module_platform_driver(imx_dsp_audio_driver); + +MODULE_LICENSE("GPL v2");