1
0
Fork 0

Merge remote-tracking branch 'origin/audio/dsp' into audio/next

* origin/audio/dsp: (78 commits)
  MLK-22815-1: ASoC: fsl: dsp: Expand parameter msg size
  MLK-21144 ASoC: fsl: Fix crash with multiple open/close
  ASoC: fsl_dsp: Use new compatible string for FSL DSP
  ASoC: fsl: dsp: remove unused types.h
  MLK-21985-6 ASoC: imx-dsp: fix build for next 20190607 upgrade
  ...
5.4-rM2-2.2.x-imx-squashed
Dong Aisheng 2019-12-02 18:00:40 +08:00
commit 5d9b52c341
19 changed files with 5377 additions and 0 deletions

View File

@ -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";
};

View File

@ -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__ */

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 <uapi/linux/mxc_dsp.h>
#include <linux/firmware/imx/ipc.h>
#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

View File

@ -0,0 +1,97 @@
// SPDX-License-Identifier: GPL-2.0+
//
// DSP Audio platform driver
//
// Copyright 2018 NXP
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include <sound/core.h>
#include <sound/compress_driver.h>
#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);

View File

@ -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*/

View File

@ -0,0 +1,642 @@
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2018 NXP
// Copyright (c) 2012-2013 by Tensilica Inc.
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/elf.h>
#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);
}

View File

@ -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

View File

@ -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*/

View File

@ -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 <linux/pm_runtime.h>
#include <sound/soc.h>
#include <sound/core.h>
#include <sound/compress_driver.h>
#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,
};

View File

@ -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 <linux/slab.h>
#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);
}

View File

@ -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 <linux/types.h>
#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 */

View File

@ -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, &reg);
msghdr = (union icm_header_t)reg;
if (msghdr.intr == 1) {
dev_dbg(dev, "INTR: Received ICM intr, msg 0x%08x\n",
msghdr.allbits);
switch (msghdr.msg) {
case ICM_CORE_EXIT:
break;
case ICM_CORE_READY:
send_dpu_ext_msg_addr(proxy);
proxy->is_ready = 1;
complete(&proxy->cmd_complete);
break;
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;
}

View File

@ -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 <linux/wait.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/compiler.h>
#include <linux/dma-mapping.h>
#include <linux/platform_data/dma-imx.h>
#include <linux/mx8_mu.h>
#include <linux/interrupt.h>
#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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 <linux/module.h>
#include <linux/of_platform.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/control.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
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");