[alsa-devel] [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver.
Add support for Intel Smart Sound Technology (SST) audio DSPs. This driver provides the low level IO, reset, boot and IRQ management for Intel audio DSPs. These files make up the low level part of the SST audio driver stack and will be used by many Intel SST cores like Haswell, Broadwell and Baytrail.
SST DSPs expose a memory mapped region (shim) for config and control. The shim layout is mostly shared without much modification across cores and this driver provides a uniform API to access the shim and to enable basic shim functions. It also provides functionality to abstract some shim functions for cores with different shim features.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- sound/soc/intel/sst-dsp-priv.h | 291 ++++++++++++++++++++++++++++++++ sound/soc/intel/sst-dsp.c | 370 +++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-dsp.h | 226 +++++++++++++++++++++++++ 3 files changed, 887 insertions(+) create mode 100644 sound/soc/intel/sst-dsp-priv.h create mode 100644 sound/soc/intel/sst-dsp.c create mode 100644 sound/soc/intel/sst-dsp.h
diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h new file mode 100644 index 0000000..c4db4c7 --- /dev/null +++ b/sound/soc/intel/sst-dsp-priv.h @@ -0,0 +1,291 @@ +/* + * Intel Smart Sound Technology + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SOUND_SOC_SST_DSP_PRIV_H +#define __SOUND_SOC_SST_DSP_PRIV_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> + +struct sst_mem_block; +struct sst_module; +struct sst_fw; + +/* + * DSP Operations exported by platform Audio DSP driver. + */ +struct sst_ops { + /* DSP core boot / reset */ + void (*boot)(struct sst_dsp *); + void (*reset)(struct sst_dsp *); + + /* Shim IO */ + void (*write)(void __iomem *addr, u32 offset, u32 value); + u32 (*read)(void __iomem *addr, u32 offset); + void (*write64)(void __iomem *addr, u32 offset, u64 value); + u64 (*read64)(void __iomem *addr, u32 offset); + + /* DSP I/DRAM IO */ + void (*ram_read)(struct sst_dsp *sst, void *dest, void *src, size_t bytes); + void (*ram_write)(struct sst_dsp *sst, void *dest, void *src, size_t bytes); + + void (*dump)(struct sst_dsp *); + + /* IRQ handlers */ + irqreturn_t (*irq_handler)(int irq, void *context); + + /* SST init and free */ + int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata); + void (*free)(struct sst_dsp *sst); + + /* FW module parser/loader */ + int (*parse_fw)(struct sst_fw *sst_fw); +}; + +/* + * Audio DSP memory offsets and addresses. + */ +struct sst_addr { + u32 lpe_base; + u32 shim_offset; + u32 iram_offset; + void __iomem *lpe; + void __iomem *shim; + void __iomem *pci_cfg; + void __iomem *fw_ext; +}; + +/* + * Audio DSP Mailbox configuration. + */ +struct sst_mailbox { + void __iomem *in_base; + void __iomem *out_base; + size_t in_size; + size_t out_size; +}; + +/* + * Audio DSP Firmware data types. + */ +enum sst_data_type { + SST_DATA_M = 0, /* module block data */ + SST_DATA_P = 1, /* peristant data (text, data) */ + SST_DATA_S = 2, /* scratch data (usually buffers) */ +}; + +/* + * Audio DSP memory block types. + */ +enum sst_mem_type { + SST_MEM_IRAM = 0, + SST_MEM_DRAM = 1, + SST_MEM_ANY = 2, + SST_MEM_CACHE= 3, +}; + +/* + * Audio DSP Generic Firmware File. + * + * SST Firmware files can consist of 1..N modules. This generic structure is + * used to manage each firmware file and it's modules regardless of SST firmware + * type. A SST driver may load multiple FW files. + */ +struct sst_fw { + struct sst_dsp *dsp; + + /* base addresses of FW file data */ + dma_addr_t dmable_fw_paddr; /* physical address of fw data */ + void *dma_buf; /* virtual address of fw data */ + u32 size; /* size of fw data */ + + /* lists */ + struct list_head list; /* DSP list of FW */ + struct list_head module_list; /* FW list of modules */ + + void *private; /* core doesn't touch this */ +}; + +/* + * Audio DSP Generic Module data. + * + * This is used to dsecribe any sections of persistent (text and data) and + * scratch (buffers) of module data in ADSP memory space. + */ +struct sst_module_data { + + enum sst_mem_type type; /* destination memory type */ + enum sst_data_type data_type; /* type of module data */ + + u32 size; /* size in bytes */ + u32 offset; /* offset in FW file */ + u32 data_offset; /* offset in ADSP memory space */ + void *data; /* module data */ +}; + +/* + * Audio DSP Generic Module Template. + * + * Used to define and register a new FW module. This data is extracted from + * FW module header information. + */ +struct sst_module_template { + u32 id; + u32 entry; /* entry point */ + struct sst_module_data s; /* scratch data */ + struct sst_module_data p; /* peristant data */ +}; + +/* + * Audio DSP Generic Module. + * + * Each Firmware file can consist of 1..N modules. A module can span multiple + * ADSP memory blocks. The simplest FW will be a file with 1 module. + */ +struct sst_module { + struct sst_dsp *dsp; + struct sst_fw *sst_fw; /* parent FW we belong too */ + + /* module configuration */ + u32 id; + u32 entry; /* module entry point */ + u32 offset; /* module offset in firmware file */ + u32 size; /* module size */ + struct sst_module_data s; /* scratch data */ + struct sst_module_data p; /* peristant data */ + + /* runtime */ + u32 usage_count; /* can be unloaded if count == 0 */ + void *private; /* core doesn't touch this */ + + /* lists */ + struct list_head block_list; /* Module list of blocks in use */ + struct list_head list; /* DSP list of modules */ + struct list_head list_fw; /* FW list of modules */ +}; + +/* + * SST Memory Block operations. + */ +struct sst_block_ops { + int (*enable)(struct sst_mem_block *block); + int (*disable)(struct sst_mem_block *block); +}; + +/* + * SST Generic Memory Block. + * + * SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be + * power gated. + */ +struct sst_mem_block { + struct sst_dsp *dsp; + struct sst_module *module; /* module that uses this block */ + + /* block config */ + u32 offset; /* offset from base */ + u32 size; /* block size */ + u32 index; /* block index 0..N */ + enum sst_mem_type type; /* block memory type IRAM/DRAM */ + struct sst_block_ops *ops; /* block operations, if any */ + + /* block status */ + enum sst_data_type data_type; /* data type held in this block */ + u32 bytes_used; /* bytes in use by modules */ + void *private; /* generic core does not touch this */ + int users; /* number of modules using this block */ + + /* block lists */ + struct list_head module_list; /* Module list of blocks */ + struct list_head list; /* Map list of free/used blocks */ +}; + +/* + * Generic SST Shim Interface. + */ +struct sst_dsp { + + /* runtime */ + struct sst_dsp_device *sst_dev; + spinlock_t spinlock; /* IPC locking */ + struct mutex mutex; /* DSP FW lock */ + struct device *dev; + void *thread_context; + int irq; + u32 id; + + /* list of free and used ADSP memory blocks */ + struct list_head used_block_list; + struct list_head free_block_list; + + /* operations */ + struct sst_ops *ops; + + /* debug FS */ + struct dentry *debugfs_root; + + /* base addresses */ + struct sst_addr addr; + + /* mailbox */ + struct sst_mailbox mailbox; + + /* SST FW files loaded and their modules */ + struct list_head module_list; + struct list_head fw_list; + + /* platform data */ + struct sst_pdata *pdata; + + /* DMA FW loading */ + struct sst_dma *dma; + bool fw_use_dma; +}; + +/* Create/Free FW files - can contain multiple modules */ +struct sst_fw *sst_fw_new(struct sst_dsp *dsp, + const struct firmware *fw, void *private); +void sst_fw_free(struct sst_fw *sst_fw); +void sst_fw_free_all(struct sst_dsp *dsp); + +/* Create/Free firmware modules */ +struct sst_module *sst_module_new(struct sst_fw *sst_fw, + struct sst_module_template *template, void *private); +void sst_module_free(struct sst_module *sst_module); +int sst_module_insert(struct sst_module *sst_module); +int sst_module_remove(struct sst_module *sst_module); +int sst_module_insert_fixed_block(struct sst_module *module, + struct sst_module_data *data); +struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id); + +/* allocate/free pesistent/scratch memory regions managed by drv */ +struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp); +void sst_mem_block_free_scratch(struct sst_dsp *dsp, + struct sst_module *scratch); + +/* Register the DSPs memory blocks - would be nice to read from ACPI */ +struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, + u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, + void *private); +void sst_mem_block_unregister_all(struct sst_dsp *dsp); + +/* DMA */ +int sst_dma_new(struct sst_dsp *sst); +void sst_dma_free(struct sst_dma *dma); + +#endif diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c new file mode 100644 index 0000000..beef2ef --- /dev/null +++ b/sound/soc/intel/sst-dsp.c @@ -0,0 +1,370 @@ +/* + * Intel Smart Sound Technology (SST) DSP Core Driver + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/slab.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" + +#define CREATE_TRACE_POINTS +#include <trace/events/sst.h> + +/* Public API */ +void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sst->spinlock, flags); + sst->ops->write(sst->addr.shim, offset, value); + spin_unlock_irqrestore(&sst->spinlock, flags); +} +EXPORT_SYMBOL(sst_dsp_shim_write); + +u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&sst->spinlock, flags); + val = sst->ops->read(sst->addr.shim, offset); + spin_unlock_irqrestore(&sst->spinlock, flags); + + return val; +} +EXPORT_SYMBOL(sst_dsp_shim_read); + +void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sst->spinlock, flags); + sst->ops->write64(sst->addr.shim, offset, value); + spin_unlock_irqrestore(&sst->spinlock, flags); +} +EXPORT_SYMBOL(sst_dsp_shim_write64); + +u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset) +{ + unsigned long flags; + u64 val; + + spin_lock_irqsave(&sst->spinlock, flags); + val = sst->ops->read64(sst->addr.shim, offset); + spin_unlock_irqrestore(&sst->spinlock, flags); + + return val; +} +EXPORT_SYMBOL(sst_dsp_shim_read64); + +void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) +{ + sst->ops->write(sst->addr.shim, offset, value); +} +EXPORT_SYMBOL(sst_dsp_shim_write_unlocked); + +u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) +{ + return sst->ops->read(sst->addr.shim, offset); +} +EXPORT_SYMBOL(sst_dsp_shim_read_unlocked); + +void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) +{ + sst->ops->write64(sst->addr.shim, offset, value); +} +EXPORT_SYMBOL(sst_dsp_shim_write64_unlocked); + +u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) +{ + return sst->ops->read64(sst->addr.shim, offset); +} +EXPORT_SYMBOL(sst_dsp_shim_read64_unlocked); + +int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + u32 old, new; + + spin_lock_irqsave(&sst->spinlock, flags); + old = sst_dsp_shim_read_unlocked(sst, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + sst_dsp_shim_write_unlocked(sst, offset, new); + + spin_unlock_irqrestore(&sst->spinlock, flags); + return change; +} +EXPORT_SYMBOL(sst_dsp_shim_update_bits); + +int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value) +{ + unsigned long flags; + bool change; + u64 old, new; + + spin_lock_irqsave(&sst->spinlock, flags); + old = sst_dsp_shim_read64_unlocked(sst, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + sst_dsp_shim_write64_unlocked(sst, offset, new); + + spin_unlock_irqrestore(&sst->spinlock, flags); + return change; +} +EXPORT_SYMBOL(sst_dsp_shim_update_bits64); + +int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + ret = sst_dsp_shim_read_unlocked(sst, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + sst_dsp_shim_write_unlocked(sst, offset, new); + + return change; +} +EXPORT_SYMBOL(sst_dsp_shim_update_bits_unlocked); + +int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value) +{ + bool change; + u64 old, new; + + old = sst_dsp_shim_read64_unlocked(sst, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + sst_dsp_shim_write64_unlocked(sst, offset, new); + + return change; +} +EXPORT_SYMBOL(sst_dsp_shim_update_bits64_unlocked); + +void sst_dsp_dump(struct sst_dsp *sst) +{ + sst->ops->dump(sst); +} +EXPORT_SYMBOL(sst_dsp_dump); + +void sst_dsp_reset(struct sst_dsp *sst) +{ + sst->ops->reset(sst); +} +EXPORT_SYMBOL(sst_dsp_reset); + +int sst_dsp_boot(struct sst_dsp *sst) +{ + sst->ops->boot(sst); + return 0; +} +EXPORT_SYMBOL(sst_dsp_boot); + +void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) +{ + sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY); + trace_sst_ipc_msg_tx(msg); +} +EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx); + +u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp) +{ + u32 msg; + + msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); + trace_sst_ipc_msg_rx(msg); + + return msg; +} +EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx); + +void sst_dsp_write(struct sst_dsp *sst, void *src, u32 dest_offset, + size_t bytes) +{ + sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes); +} +EXPORT_SYMBOL(sst_dsp_write); + +void sst_dsp_read(struct sst_dsp *sst, void *dest, u32 src_offset, + size_t bytes) +{ + sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes); +} +EXPORT_SYMBOL(sst_dsp_read); + +int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size, + u32 outbox_offset, size_t outbox_size) +{ + sst->mailbox.in_base = sst->addr.lpe + inbox_offset; + sst->mailbox.out_base = sst->addr.lpe + outbox_offset; + sst->mailbox.in_size = inbox_size; + sst->mailbox.out_size = outbox_size; + return 0; +} +EXPORT_SYMBOL(sst_dsp_mailbox_init); + +void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes) +{ + int i; + + trace_sst_ipc_outbox_write(bytes); + + memcpy_toio(sst->mailbox.out_base, message, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_outbox_wdata(i, *(uint32_t *)(message + i)); +} +EXPORT_SYMBOL(sst_dsp_outbox_write); + +void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes) +{ + int i; + + trace_sst_ipc_outbox_read(bytes); + + memcpy_fromio(message, sst->mailbox.out_base, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_outbox_rdata(i, *(uint32_t *)(message + i)); +} +EXPORT_SYMBOL(sst_dsp_outbox_read); + +void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes) +{ + int i; + + trace_sst_ipc_inbox_write(bytes); + + memcpy_toio(sst->mailbox.in_base, message, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_inbox_wdata(i, *(uint32_t *)(message + i)); +} +EXPORT_SYMBOL(sst_dsp_inbox_write); + +void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) +{ + int i; + + trace_sst_ipc_inbox_read(bytes); + + memcpy_fromio(message, sst->mailbox.in_base, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_inbox_rdata(i, *(uint32_t *)(message + i)); +} +EXPORT_SYMBOL(sst_dsp_inbox_read); + +void *sst_dsp_get_thread_context(struct sst_dsp *sst) +{ + return sst->thread_context; +} +EXPORT_SYMBOL(sst_dsp_get_thread_context); + +struct sst_dsp *sst_dsp_new(struct device *dev, + struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) +{ + struct sst_dsp *sst; + int err; + + dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id); + + sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); + if (sst == NULL) + return NULL; + + spin_lock_init(&sst->spinlock); + mutex_init(&sst->mutex); + sst->dev = dev; + sst->thread_context = sst_dev->thread_context; + sst->sst_dev = sst_dev; + sst->id = pdata->id; + sst->irq = pdata->irq; + sst->ops = sst_dev->ops; + sst->pdata = pdata; + INIT_LIST_HEAD(&sst->used_block_list); + INIT_LIST_HEAD(&sst->free_block_list); + INIT_LIST_HEAD(&sst->module_list); + INIT_LIST_HEAD(&sst->fw_list); + + /* Initialise SST Audio DSP */ + if (sst->ops->init) { + err = sst->ops->init(sst, pdata); + if (err < 0) + return NULL; + } + + /* Register the ISR */ + err = request_threaded_irq(sst->irq, sst->ops->irq_handler, + sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); + if (err) + goto irq_err; + + /* Register the FW loader DMA controller if we have one */ + if (pdata->dma_engine) + err = sst_dma_new(sst); + if (err) + goto dma_err; + + return sst; + +dma_err: + free_irq(sst->irq, sst); +irq_err: + if (sst->ops->free) + sst->ops->free(sst); + + return NULL; +} +EXPORT_SYMBOL(sst_dsp_new); + +void sst_dsp_free(struct sst_dsp *sst) +{ + if (sst->pdata->dma_engine) + sst_dma_free(sst->dma); + free_irq(sst->irq, sst); + if (sst->ops->free) + sst->ops->free(sst); +} +EXPORT_SYMBOL(sst_dsp_free); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("Intel SST Core"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h new file mode 100644 index 0000000..8ea3cbe --- /dev/null +++ b/sound/soc/intel/sst-dsp.h @@ -0,0 +1,226 @@ +/* + * Intel Smart Sound Technology (SST) Core + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SOUND_SOC_SST_DSP_H +#define __SOUND_SOC_SST_DSP_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> + +/* SST Device IDs */ +#define SST_DEV_ID_LYNX_POINT 0x33C8 +#define SST_DEV_ID_WILDCAT_POINT 0x3438 + +/* Supported SST DMA Devices */ +#define SST_DMA_TYPE_DW 1 +#define SST_DMA_TYPE_MID 2 + +/* SST Shim register map + * The register naming can differ between products. Some products also + * contain extra functionality. + */ +#define SST_CSR 0x00 +#define SST_PISR 0x08 +#define SST_PIMR 0x10 +#define SST_ISRX 0x18 +#define SST_ISRD 0x20 +#define SST_IMRX 0x28 +#define SST_IMRD 0x30 +#define SST_IPCX 0x38 /* IPC IA -> SST */ +#define SST_IPCD 0x40 /* IPC SST -> IA */ +#define SST_ISRSC 0x48 +#define SST_ISRLPESC 0x50 +#define SST_IMRSC 0x58 +#define SST_IMRLPESC 0x60 +#define SST_IPCSC 0x68 +#define SST_IPCLPESC 0x70 +#define SST_CLKCTL 0x78 +#define SST_CSR2 0x80 +#define SST_LTRC 0xE0 +#define SST_HDMC 0xE8 +#define SST_DBGO 0xF0 + +#define SST_SHIM_SIZE 0x100 +#define SST_PWMCTRL 0x1000 + +/* SST Shim Register bits + * The register bit naming can differ between products. Some products also + * contain extra functionality. + */ + +/* CSR / CS */ +#define SST_CSR_RST (0x1 << 1) +#define SST_CSR_SBCS0 (0x1 << 2) +#define SST_CSR_SBCS1 (0x1 << 3) +#define SST_CSR_DCS(x) (x << 4) +#define SST_CSR_DCS_MASK (0x7 << 4) +#define SST_CSR_STALL (0x1 << 10) +#define SST_CSR_S0IOCS (0x1 << 21) +#define SST_CSR_S1IOCS (0x1 << 23) +#define SST_CSR_LPCS (0x1 << 31) + +/* ISRX / ISC */ +#define SST_ISRX_BUSY (0x1 << 1) +#define SST_ISRX_DONE (0x1 << 0) + +/* ISRD / ISD */ +#define SST_ISRD_BUSY (0x1 << 1) +#define SST_ISRD_DONE (0x1 << 0) + +/* IMRX / IMC */ +#define SST_IMRX_BUSY (0x1 << 1) +#define SST_IMRX_DONE (0x1 << 0) + +/* IPCX / IPCC */ +#define SST_IPCX_DONE (0x1 << 30) +#define SST_IPCX_BUSY (0x1 << 31) + +/* IPCD */ +#define SST_IPCD_DONE (0x1 << 30) +#define SST_IPCD_BUSY (0x1 << 31) + +/* CLKCTL */ +#define SST_CLKCTL_SMOS(x) (x << 24) +#define SST_CLKCTL_MASK (3 << 24) +#define SST_CLKCTL_DCPLCG (1 << 18) +#define SST_CLKCTL_SCOE1 (1 << 17) +#define SST_CLKCTL_SCOE0 (1 << 16) + +/* CSR2 / CS2 */ +#define SST_CSR2_SDFD_SSP0 (1 << 1) +#define SST_CSR2_SDFD_SSP1 (1 << 2) + +/* LTRC */ +#define SST_LTRC_VAL(x) (x << 0) + +/* HDMC */ +#define SST_HDMC_HDDA0(x) (x << 0) +#define SST_HDMC_HDDA1(x) (x << 7) + + +/* SST Vendor Defined Registers and bits */ +#define SST_VDRTCTL0 0xa0 +#define SST_VDRTCTL1 0xa4 +#define SST_VDRTCTL2 0xa8 +#define SST_VDRTCTL3 0xaC + +/* VDRTCTL0 */ +#define SST_VDRTCL0_DSRAMPGE_SHIFT 16 +#define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT) +#define SST_VDRTCL0_ISRAMPGE_SHIFT 6 +#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT) + +struct sst_dsp; + +/* + * SST Device. + * + * This structure is populated by the SST core driver. + */ +struct sst_dsp_device { + /* Mandatory fields */ + struct sst_ops *ops; + irqreturn_t (*thread)(int irq, void *context); + void *thread_context; +}; + +/* + * SST Platform Data. + */ +struct sst_pdata { + /* ACPI data */ + u32 lpe_base; + u32 lpe_size; + u32 pcicfg_base; + u32 pcicfg_size; + int irq; + + /* Firmware */ + const char *fw_filename; + u32 fw_base; + u32 fw_size; + + /* DMA */ + u32 dma_base; + u32 dma_size; + int dma_engine; + + /* DSP */ + u32 id; + void *dsp; +}; + +/* Initialization */ +struct sst_dsp *sst_dsp_new(struct device *dev, + struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); +void sst_dsp_free(struct sst_dsp *sst); + +/* SHIM Read / Write */ +void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); +u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value); +void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value); +u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value); + +/* SHIM Read / Write Unlocked for callers already holding sst lock */ +void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value); +u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value); +void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value); +u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value); + +/* Size optimised DRAM/IRAM memcpy */ +void sst_dsp_write(struct sst_dsp *sst, void *src, u32 dest_offset, + size_t bytes); +void sst_dsp_bzero(struct sst_dsp *sst, u32 src_offset, size_t bytes); +void sst_dsp_read(struct sst_dsp *sst, void *dest, u32 src_offset, + size_t bytes); + +/* DSP reset & boot */ +void sst_dsp_reset(struct sst_dsp *sst); +int sst_dsp_boot(struct sst_dsp *sst); + +/* Msg IO */ +void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); +u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp); +void *sst_dsp_get_thread_context(struct sst_dsp *sst); + +/* Mailbox management */ +int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset, + size_t inbox_size, u32 outbox_offset, size_t outbox_size); +void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes); + +/* DMA FW maangement */ +int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t src_addr, + dma_addr_t dstn_addr, size_t size); +int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id); +void sst_dsp_dma_put_channel(struct sst_dsp *dsp); + +/* Debug */ +void sst_dsp_dump(struct sst_dsp *sst); + +#endif
From: Jarkko Nikula jarkko.nikula@linux.intel.com
Most of the SST devices will be exposed as ACPI devices. It makes sense to avoid duplication of the driver enumeration logic and concentrate the functionality into a single ACPI SST enumeration file.
Idea of this loader is to parse data we get from ACPI and to be able to load needed other SST drivers and ASoC machine driver runtime based on single ACPI ID what BIOS gives to us.
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- sound/soc/intel/sst-acpi.c | 212 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 sound/soc/intel/sst-acpi.c
diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/sst-acpi.c new file mode 100644 index 0000000..72eedae --- /dev/null +++ b/sound/soc/intel/sst-acpi.c @@ -0,0 +1,212 @@ +/* + * Intel SST loader on ACPI systems + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "sst-dsp.h" + +#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 +#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 +#define SST_LPT_DSP_DMA_SIZE (1024 - 1) + +/* Descriptor for setting up SST platform data */ +struct sst_acpi_desc { + const char *drv_name; + /* Platform resource indexes. Must set to -1 if not used */ + int resindex_lpe_base; + int resindex_pcicfg_base; + int resindex_fw_base; + int irqindex_host_ipc; + int resindex_dma_base; + /* Unique number identifying the SST core on platform */ + int sst_id; + /* firmware file name */ + const char *fw_filename; + /* DMA only valid when resindex_dma_base != -1*/ + int dma_engine; + int dma_size; +}; + +/* Descriptor for SST ASoC machine driver */ +struct sst_acpi_mach { + const char *drv_name; + struct sst_acpi_desc *res_desc; +}; + +struct sst_acpi_priv { + struct platform_device *pdev_mach; + struct platform_device *pdev_pcm; + struct sst_pdata sst_pdata; + struct sst_acpi_desc *desc; +}; + +static int sst_acpi_probe(struct platform_device *pdev) +{ + const struct acpi_device_id *id; + struct device *dev = &pdev->dev; + struct sst_acpi_priv *sst_acpi; + struct sst_pdata *sst_pdata; + struct sst_acpi_mach *mach; + struct sst_acpi_desc *desc; + struct resource *mmio; + int ret = 0; + + sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL); + if (sst_acpi == NULL) + return -ENOMEM; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + mach = (struct sst_acpi_mach *)id->driver_data; + desc = mach->res_desc; + sst_pdata = &sst_acpi->sst_pdata; + sst_pdata->id = desc->sst_id; + sst_pdata->fw_filename = desc->fw_filename; + sst_acpi->desc = desc; + + if (desc->resindex_dma_base >= 0) { + sst_pdata->dma_engine = desc->dma_engine; + sst_pdata->dma_base = desc->resindex_dma_base; + sst_pdata->dma_size = desc->dma_size; + } + + if (desc->irqindex_host_ipc >= 0) + sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + + if (desc->resindex_lpe_base >= 0) { + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + sst_pdata->lpe_base = mmio->start; + sst_pdata->lpe_size = resource_size(mmio); + } + } + + if (desc->resindex_pcicfg_base >= 0) { + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + sst_pdata->pcicfg_base = mmio->start; + sst_pdata->pcicfg_size = resource_size(mmio); + } + } + + if (desc->resindex_fw_base >= 0) { + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_fw_base); + if (mmio) { + sst_pdata->fw_base = mmio->start; + sst_pdata->fw_size = resource_size(mmio); + } + } + + /* register PCM and DAI driver */ + sst_acpi->pdev_pcm = + platform_device_register_data(dev, desc->drv_name, -1, + sst_pdata, sizeof(*sst_pdata)); + if (IS_ERR(sst_acpi->pdev_pcm)) + return PTR_ERR(sst_acpi->pdev_pcm); + + /* register machine driver */ + platform_set_drvdata(pdev, sst_acpi); + + sst_acpi->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sst_pdata, sizeof(*sst_pdata)); + if (IS_ERR(sst_acpi->pdev_mach)) { + ret = PTR_ERR(sst_acpi->pdev_mach); + goto sst_err; + } + + return ret; + +sst_err: + platform_device_unregister(sst_acpi->pdev_pcm); + return ret; +} + +static int sst_acpi_remove(struct platform_device *pdev) +{ + struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); + + platform_device_unregister(sst_acpi->pdev_mach); + platform_device_unregister(sst_acpi->pdev_pcm); + + return 0; +} + +static struct sst_acpi_desc sst_acpi_haswell_desc = { + .drv_name = "haswell-pcm-audio", + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_fw_base = -1, + .irqindex_host_ipc = 0, + .sst_id = SST_DEV_ID_LYNX_POINT, + .fw_filename = "IntcSST1.bin", + .dma_engine = SST_DMA_TYPE_DW, + .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET, + .dma_size = SST_LPT_DSP_DMA_SIZE, +}; + +static struct sst_acpi_desc sst_acpi_broadwell_desc = { + .drv_name = "haswell-pcm-audio", + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_fw_base = -1, + .irqindex_host_ipc = 0, + .sst_id = SST_DEV_ID_WILDCAT_POINT, + .fw_filename = "IntcSST2.bin", + .dma_engine = SST_DMA_TYPE_DW, + .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET, + .dma_size = SST_LPT_DSP_DMA_SIZE, +}; + +static struct sst_acpi_mach haswell_mach = { + .drv_name = "haswell-audio", + .res_desc = &sst_acpi_haswell_desc, +}; + +static struct sst_acpi_mach broadwell_mach = { + .drv_name = "broadwell-audio", + .res_desc = &sst_acpi_broadwell_desc, +}; + +static struct acpi_device_id sst_acpi_match[] = { + { "INT33C8", (unsigned long)&haswell_mach }, + { "INT3438", (unsigned long)&broadwell_mach }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sst_acpi_match); + +static struct platform_driver sst_acpi_driver = { + .probe = sst_acpi_probe, + .remove = sst_acpi_remove, + .driver = { + .name = "sst-acpi", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(sst_acpi_match), + }, +}; +module_platform_driver(sst_acpi_driver); + +MODULE_AUTHOR("Jarkko Nikula jarkko.nikula@linux.intel.com"); +MODULE_DESCRIPTION("Intel SST loader on ACPI systems"); +MODULE_LICENSE("GPL v2");
Hi
On 02/13/2014 09:15 PM, Liam Girdwood wrote:
+static struct sst_acpi_desc sst_acpi_haswell_desc = {
- .drv_name = "haswell-pcm-audio",
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = 1,
- .resindex_fw_base = -1,
- .irqindex_host_ipc = 0,
- .sst_id = SST_DEV_ID_LYNX_POINT,
- .fw_filename = "IntcSST1.bin",
Commenting myself - we need to move firmware files under intel/
Provide services for Intel SST drivers to load SST modular firmware.
SST Firmware can be made up of several modules. These modules can exist within any of the compatible SST memory blocks. Provide a generic memory block and firmware module manager that can be used with any SST firmware and core.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- sound/soc/intel/sst-firmware.c | 805 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 805 insertions(+) create mode 100644 sound/soc/intel/sst-firmware.c
diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c new file mode 100644 index 0000000..8126b46 --- /dev/null +++ b/sound/soc/intel/sst-firmware.c @@ -0,0 +1,805 @@ +/* + * Intel SST Firmware Loader + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/firmware.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/pci.h> + +/* supported DMA engine drivers */ +#include <linux/dw_dmac.h> + +#include <asm/page.h> +#include <asm/pgtable.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" + +#define SST_DMA_RESOURCES 2 +#define SST_DSP_DMA_MAX_BURST 0x3 + +struct sst_dma { + struct sst_dsp *sst; + + struct platform_device *dma_dev; + struct resource dma_resource[SST_DMA_RESOURCES]; + struct dma_async_tx_descriptor *desc; + struct dma_chan *ch; +}; + +static void sst_memcpy32(void *dest, void *src, int bytes) +{ + int i; + + /* copy one 32 bit word at a time as 64 bit access is not supported */ + for (i = 0; i < bytes; i += 4) + memcpy_toio(dest + i, src + i, 4); +} + +static void sst_dma_transfer_complete(void *arg) +{ + struct sst_dsp *sst = (struct sst_dsp *)arg; + + dev_dbg(sst->dev, "DMA: callback\n"); +} + +int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t src_addr, + dma_addr_t dest_addr, size_t size) +{ + struct dma_async_tx_descriptor *desc; + struct sst_dma *dma = sst->dma; + + if (dma->ch == NULL) { + dev_err(sst->dev, "error: no DMA channel\n"); + return -ENODEV; + } + + dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n", + (unsigned long)src_addr, (unsigned long)dest_addr, size); + + desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr, + src_addr, size, DMA_CTRL_ACK); + if (!desc){ + dev_err(sst->dev, "error: dma prep memcpy failed\n"); + return -EINVAL; + } + + desc->callback = sst_dma_transfer_complete; + desc->callback_param = sst; + + desc->tx_submit(desc); + dma_wait_for_async_tx(desc); + + return 0; +} +EXPORT_SYMBOL_GPL(sst_dsp_dma_copy); + +static bool dma_chan_filter(struct dma_chan *chan, void *param) +{ + struct sst_dsp *dsp = (struct sst_dsp *)param; + struct sst_dma *dma = dsp->dma; + + /* only accept channels from this device */ + if (chan->device->dev != &dma->dma_dev->dev) + return false; + + /* todo: add chan_id testing */ + return true; +} + +int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) +{ + struct sst_dma *dma = dsp->dma; + struct dma_slave_config slave; + dma_cap_mask_t mask; + int ret; + + /* The Intel MID DMA engine driver needs the slave config set but + * Synopsis DMA engine driver safely ignores the slave config */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_MEMCPY, mask); + + dma->ch = dma_request_channel(mask, dma_chan_filter, dsp); + if (dma->ch == NULL) { + dev_err(dsp->dev, "error: DMA request channel failed\n"); + return -EIO; + } + + memset(&slave, 0, sizeof(slave)); + slave.direction = DMA_MEM_TO_DEV; + slave.src_addr_width = + slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST; + + ret = dmaengine_slave_config(dma->ch, &slave); + if (ret) { + dev_err(dsp->dev, "error: unable to set DMA slave config %d\n", + ret); + dma_release_channel(dma->ch); + dma->ch = NULL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel); + +void sst_dsp_dma_put_channel(struct sst_dsp *dsp) +{ + struct sst_dma *dma = dsp->dma; + + dma_release_channel(dma->ch); + dma->ch = NULL; +} +EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel); + +/* platform data for DesignWare DMA Engine */ +static struct dw_dma_platform_data dw_pdata = { + .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, + .chan_priority = CHAN_PRIORITY_ASCENDING, +}; + +int sst_dma_new(struct sst_dsp *sst) +{ + struct sst_pdata *sst_pdata = sst->pdata; + struct sst_dma *dma; + const char *dma_dev_name; + size_t dma_pdata_size; + void *dma_pdata; + + /* configure the correct platform data for whatever DMA engine + * is attached to the ADSP IP. */ + switch (sst->pdata->dma_engine) { + case SST_DMA_TYPE_DW: + dma_pdata = &dw_pdata; + dma_pdata_size = sizeof(dw_pdata); + dma_dev_name = "dw_dmac"; + break; + case SST_DMA_TYPE_MID: + dma_pdata = NULL; + dma_pdata_size = 0; + dma_dev_name = "Intel MID DMA"; + break; + default: + dev_err(sst->dev, "error: invalid DMA engine %d\n", + sst->pdata->dma_engine); + return -EINVAL; + } + + dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + dma->sst = sst; + sst->dma = dma; + + dma->dma_resource[0].start = sst->addr.lpe_base + + sst_pdata->dma_base; + dma->dma_resource[0].end = sst->addr.lpe_base + + sst_pdata->dma_base + + sst_pdata->dma_size; + dma->dma_resource[0].flags = IORESOURCE_MEM; + dma->dma_resource[1].start = sst_pdata->irq; + dma->dma_resource[1].end = sst_pdata->irq; + dma->dma_resource[1].flags = IORESOURCE_IRQ; + + /* now register DMA engine device */ + dma->dma_dev = platform_device_register_resndata(sst->dev, + dma_dev_name, -1, dma->dma_resource, 2, + dma_pdata, dma_pdata_size); + + if (dma->dma_dev == NULL) { + dev_err(sst->dev, "error: DMA device register failed\n"); + return -ENODEV; + } + + sst->fw_use_dma = true; + return 0; +} +EXPORT_SYMBOL(sst_dma_new); + +void sst_dma_free(struct sst_dma *dma) +{ + if (dma->ch) + dma_release_channel(dma->ch); + platform_device_unregister(dma->dma_dev); +} +EXPORT_SYMBOL(sst_dma_free); + +/* create new generic firmware object */ +struct sst_fw *sst_fw_new(struct sst_dsp *dsp, + const struct firmware *fw, void *private) +{ + struct sst_fw *sst_fw; + int err; + + if (!dsp->ops->parse_fw) + return NULL; + + sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL); + if (sst_fw == NULL) + return NULL; + + sst_fw->dsp = dsp; + sst_fw->private = private; + sst_fw->size = fw->size; + + err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32)); + if (err < 0) { + kfree(sst_fw); + return NULL; + } + + /* allocate DMA buffer to store FW data */ + sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size, + &sst_fw->dmable_fw_paddr, GFP_DMA); + if (!sst_fw->dma_buf) { + dev_err(dsp->dev, "error: DMA alloc failed\n"); + kfree(sst_fw); + return NULL; + } + + /* copy FW data to DMA-able memory */ + memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size); + release_firmware(fw); + + if (dsp->fw_use_dma) { + err = sst_dsp_dma_get_channel(dsp, 0); + if (err < 0) + goto chan_err; + } + + /* call core specific FW paser to load FW data into DSP */ + err = dsp->ops->parse_fw(sst_fw); + if (err < 0) { + dev_err(dsp->dev, "error: parse fw failed %d\n", err); + goto parse_err; + } + + if (dsp->fw_use_dma) + sst_dsp_dma_put_channel(dsp); + + mutex_lock(&dsp->mutex); + list_add(&sst_fw->list, &dsp->fw_list); + mutex_unlock(&dsp->mutex); + + return sst_fw; + +parse_err: + if (dsp->fw_use_dma) + sst_dsp_dma_put_channel(dsp); +chan_err: + dma_free_coherent(dsp->dev, sst_fw->size, + sst_fw->dma_buf, + sst_fw->dmable_fw_paddr); + kfree(sst_fw); + return NULL; +} +EXPORT_SYMBOL_GPL(sst_fw_new); + +/* free single firmware object */ +void sst_fw_free(struct sst_fw *sst_fw) +{ + struct sst_dsp *dsp = sst_fw->dsp; + + mutex_lock(&dsp->mutex); + list_del(&sst_fw->list); + mutex_unlock(&dsp->mutex); + + dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf, + sst_fw->dmable_fw_paddr); + kfree(sst_fw); +} +EXPORT_SYMBOL_GPL(sst_fw_free); + +/* free all firmware objects */ +void sst_fw_free_all(struct sst_dsp *dsp) +{ + struct sst_fw *sst_fw, *t; + + mutex_lock(&dsp->mutex); + list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) { + + list_del(&sst_fw->list); + dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf, + sst_fw->dmable_fw_paddr); + kfree(sst_fw); + } + mutex_unlock(&dsp->mutex); +} +EXPORT_SYMBOL_GPL(sst_fw_free_all); + +/* create a new SST generic module from FW template */ +struct sst_module *sst_module_new(struct sst_fw *sst_fw, + struct sst_module_template *template, void *private) +{ + struct sst_dsp *dsp = sst_fw->dsp; + struct sst_module *sst_module; + + sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL); + if (sst_module == NULL) + return NULL; + + sst_module->id = template->id; + sst_module->dsp = dsp; + sst_module->sst_fw = sst_fw; + + memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data)); + memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data)); + + INIT_LIST_HEAD(&sst_module->block_list); + + mutex_lock(&dsp->mutex); + list_add(&sst_module->list, &dsp->module_list); + mutex_unlock(&dsp->mutex); + + return sst_module; +} +EXPORT_SYMBOL_GPL(sst_module_new); + +/* free firmware module and remove from available list */ +void sst_module_free(struct sst_module *sst_module) +{ + struct sst_dsp *dsp = sst_module->dsp; + + mutex_lock(&dsp->mutex); + list_del(&sst_module->list); + mutex_unlock(&dsp->mutex); + + kfree(sst_module); +} +EXPORT_SYMBOL_GPL(sst_module_free); + +/* allocate contiguous free DSP blocks - callers hold locks */ +static int block_alloc_contiguous(struct sst_module *module, + struct sst_module_data *data, u32 next_offset, int size) +{ + struct sst_dsp *dsp = module->dsp; + struct sst_mem_block *block, *tmp; + int ret; + + /* find first free blocks that can hold module */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + + /* ignore blocks that dont match type */ + if (block->type != data->type) + continue; + + /* is block next after parent ? */ + if (next_offset == block->offset) { + + /* do we need more blocks */ + if (size > block->size) { + ret = block_alloc_contiguous(module, + data, block->offset + block->size, + size - block->size); + if (ret < 0) + return ret; + } + + /* add block to module */ + block->data_type = data->data_type; + block->bytes_used = block->size; + list_move(&block->list, &dsp->used_block_list); + list_add(&block->module_list, &module->block_list); + dev_dbg(dsp->dev, " module %d added block %d:%d\n", + module->id, block->type, block->index); + return 0; + } + } + + /* free any allocated blocks on failure */ + list_for_each_entry_safe(block, tmp, &module->block_list, module_list) { + list_del(&block->module_list); + list_move(&block->list, &dsp->free_block_list); + } + return -ENOMEM; +} + +/* allocate free DSP blocks for module data - callers hold locks */ +static int block_alloc(struct sst_module *module, + struct sst_module_data *data) +{ + struct sst_dsp *dsp = module->dsp; + struct sst_mem_block *block, *tmp; + int ret = 0; + + if (data->size == 0) + return 0; + + /* find first free whole blocks that can hold module */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + + /* ignore blocks with wrong type */ + if (block->type != data->type) + continue; + + if (data->size > block->size) + continue; + + data->offset = block->offset; + block->data_type = data->data_type; + block->bytes_used = data->size % block->size; + list_add(&block->module_list, &module->block_list); + list_move(&block->list, &dsp->used_block_list); + dev_dbg(dsp->dev, " *module %d added block %d:%d\n", + module->id, block->type, block->index); + return 0; + } + + /* then find free multiple blocks that can hold module */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + + /* ignore blocks with wrong type */ + if (block->type != data->type) + continue; + + /* do we span > 1 blocks */ + if (data->size > block->size) { + ret = block_alloc_contiguous(module, data, + block->offset + block->size, + data->size - block->size); + if (ret == 0) + return ret; + } + } + + /* not enough free block space */ + return -ENOMEM; +} + +/* remove module from memory - callers hold locks */ +static void block_module_remove(struct sst_module *module) +{ + struct sst_mem_block *block, *tmp; + struct sst_dsp *dsp = module->dsp; + int err; + + /* disable each block */ + list_for_each_entry(block, &module->block_list, module_list) { + + if (block->ops && block->ops->disable) { + err = block->ops->disable(block); + if (err < 0) + dev_err(dsp->dev, + "error: cant disable block %d:%d\n", + block->type, block->index); + } + } + + /* mark each block as free */ + list_for_each_entry_safe(block, tmp, &module->block_list, module_list) { + list_del(&block->module_list); + list_move(&block->list, &dsp->free_block_list); + } +} + +/* prepare the memory block to receive data from host - callers hold locks */ +static int block_module_prepare(struct sst_module *module) +{ + struct sst_mem_block *block; + int ret = 0; + + /* enable each block so that's it'e ready for module P/S data */ + list_for_each_entry(block, &module->block_list, module_list) { + + if (block->ops && block->ops->enable) + ret = block->ops->enable(block); + if (ret < 0) { + dev_err(module->dsp->dev, + "error: cant disable block %d:%d\n", + block->type, block->index); + goto err; + } + } + return ret; + +err: + list_for_each_entry(block, &module->block_list, module_list) { + if (block->ops && block->ops->disable) + block->ops->disable(block); + } + return ret; +} + +/* allocate memory blocks for static module addresses - callers hold locks */ +static int block_alloc_fixed(struct sst_module *module, + struct sst_module_data *data) +{ + struct sst_dsp *dsp = module->dsp; + struct sst_mem_block *block, *tmp; + u32 end = data->offset + data->size, block_end; + int err; + + /* only IRAM/DRAM blocks are managed */ + if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM) + return 0; + + /* are blocks already attached to this module */ + list_for_each_entry_safe(block, tmp, &module->block_list, module_list) { + + /* force compacting mem blocks of the same data_type */ + if (block->data_type != data->data_type) + continue; + + block_end = block->offset + block->size; + + /* find block that holds section */ + if (data->offset >= block->offset && end < block_end) + return 0; + + /* does block span more than 1 section */ + if (data->offset >= block->offset && data->offset < block_end) { + + err = block_alloc_contiguous(module, data, + block->offset + block->size, + data->size - block->size + data->offset - block->offset); + if (err < 0) + return -ENOMEM; + + /* module already owns blocks */ + return 0; + } + } + + /* find first free blocks that can hold section in free list */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + block_end = block->offset + block->size; + + /* find block that holds section */ + if (data->offset >= block->offset && end < block_end) { + + /* add block */ + block->data_type = data->data_type; + list_move(&block->list, &dsp->used_block_list); + list_add(&block->module_list, &module->block_list); + return 0; + } + + /* does block span more than 1 section */ + if (data->offset >= block->offset && data->offset < block_end) { + + err = block_alloc_contiguous(module, data, + block->offset + block->size, + data->size - block->size); + if (err < 0) + return -ENOMEM; + + /* add block */ + block->data_type = data->data_type; + list_move(&block->list, &dsp->used_block_list); + list_add(&block->module_list, &module->block_list); + return 0; + } + + } + + return -ENOMEM; +} + +/* Load fixed module data into DSP memory blocks */ +int sst_module_insert_fixed_block(struct sst_module *module, + struct sst_module_data *data) +{ + struct sst_dsp *dsp = module->dsp; + struct sst_fw *sst_fw = module->sst_fw; + int ret; + + mutex_lock(&dsp->mutex); + + /* alloc blocks that includes this section */ + ret = block_alloc_fixed(module, data); + if (ret < 0) { + dev_err(dsp->dev, + "error: no free blocks for section at offset 0x%x size 0x%x\n", + data->offset, data->size); + mutex_unlock(&dsp->mutex); + return -ENOMEM; + } + + /* prepare DSP blocks for module copy */ + ret = block_module_prepare(module); + if (ret < 0) { + dev_err(dsp->dev, "error: fw module prepare failed\n"); + goto err; + } + + /* copy partial module data to blocks */ + if (dsp->fw_use_dma) { + ret = sst_dsp_dma_copy(dsp, + sst_fw->dmable_fw_paddr + data->data_offset, + dsp->addr.lpe_base + data->offset, data->size); + if (ret < 0) { + dev_err(dsp->dev, "error: module copy failed\n"); + goto err; + } + } else + sst_memcpy32(dsp->addr.lpe + data->offset, data->data, + data->size); + + mutex_unlock(&dsp->mutex); + return ret; + +err: + block_module_remove(module); + mutex_unlock(&dsp->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block); + +/* Unload entire module from DSP memory */ +int sst_block_module_remove(struct sst_module *module) +{ + struct sst_dsp *dsp = module->dsp; + + mutex_lock(&dsp->mutex); + block_module_remove(module); + mutex_unlock(&dsp->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(sst_block_module_remove); + +/* register a DSP memory block for use with FW based modules */ +struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, + u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, + void *private) +{ + struct sst_mem_block *block; + + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (block == NULL) + return NULL; + + block->offset = offset; + block->size = size; + block->index = index; + block->type = type; + block->dsp = dsp; + block->private = private; + block->ops = ops; + + mutex_lock(&dsp->mutex); + list_add(&block->list, &dsp->free_block_list); + mutex_unlock(&dsp->mutex); + + return block; +} +EXPORT_SYMBOL_GPL(sst_mem_block_register); + +/* unregister all DSP memory blocks */ +void sst_mem_block_unregister_all(struct sst_dsp *dsp) +{ + struct sst_mem_block *block, *tmp; + + mutex_lock(&dsp->mutex); + + /* unregister used blocks */ + list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) { + list_del(&block->list); + kfree(block); + } + + /* unregister free blocks */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + list_del(&block->list); + kfree(block); + } + + mutex_unlock(&dsp->mutex); +} +EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all); + +/* allocate scratch buffer blocks */ +struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp) +{ + struct sst_module *sst_module, *scratch; + struct sst_mem_block *block, *tmp; + u32 block_size; + int ret = 0; + + scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL); + if (scratch == NULL) + return NULL; + + mutex_lock(&dsp->mutex); + + /* calculate required scratch size */ + list_for_each_entry(sst_module, &dsp->module_list, list) { + if (scratch->s.size > sst_module->s.size) + scratch->s.size = scratch->s.size; + else + scratch->s.size = sst_module->s.size; + } + + dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n", + scratch->s.size); + + /* init scratch module */ + scratch->dsp = dsp; + scratch->s.type = SST_MEM_DRAM; + scratch->s.data_type = SST_DATA_S; + INIT_LIST_HEAD(&scratch->block_list); + + /* check free blocks before looking at used blocks for space */ + if (!list_empty(&dsp->free_block_list)) + block = list_first_entry(&dsp->free_block_list, + struct sst_mem_block, list); + else + block = list_first_entry(&dsp->used_block_list, + struct sst_mem_block, list); + block_size = block->size; + + /* allocate blocks for module scratch buffers */ + dev_dbg(dsp->dev, "allocating scratch blocks\n"); + ret = block_alloc(scratch, &scratch->s); + if (ret < 0) { + dev_err(dsp->dev, "error: can't alloc scratch blocks\n"); + goto err; + } + + /* assign the same offset of scratch to each module */ + list_for_each_entry(sst_module, &dsp->module_list, list) + sst_module->s.offset = scratch->s.offset; + + mutex_unlock(&dsp->mutex); + return scratch; + +err: + list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list) + list_del(&block->module_list); + mutex_unlock(&dsp->mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch); + +/* free all scratch blocks */ +void sst_mem_block_free_scratch(struct sst_dsp *dsp, + struct sst_module *scratch) +{ + struct sst_mem_block *block, *tmp; + + mutex_lock(&dsp->mutex); + + list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list) + list_del(&block->module_list); + + mutex_unlock(&dsp->mutex); +} +EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch); + +/* get a module from it's unique ID */ +struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id) +{ + struct sst_module *module; + + mutex_lock(&dsp->mutex); + + list_for_each_entry(module, &dsp->module_list, list) { + if (module->id == id) { + mutex_unlock(&dsp->mutex); + return module; + } + } + + mutex_unlock(&dsp->mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(sst_module_get_from_id);
At Thu, 13 Feb 2014 19:15:28 +0000, Liam Girdwood wrote:
Provide services for Intel SST drivers to load SST modular firmware.
SST Firmware can be made up of several modules. These modules can exist within any of the compatible SST memory blocks. Provide a generic memory block and firmware module manager that can be used with any SST firmware and core.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com
sound/soc/intel/sst-firmware.c | 805 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 805 insertions(+) create mode 100644 sound/soc/intel/sst-firmware.c
diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c new file mode 100644 index 0000000..8126b46 --- /dev/null +++ b/sound/soc/intel/sst-firmware.c @@ -0,0 +1,805 @@ +/*
- Intel SST Firmware Loader
- Copyright (C) 2013, Intel Corporation. All rights reserved.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License version
- 2 as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- */
+#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/firmware.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/pci.h>
+/* supported DMA engine drivers */ +#include <linux/dw_dmac.h>
+#include <asm/page.h> +#include <asm/pgtable.h>
+#include "sst-dsp.h" +#include "sst-dsp-priv.h"
+#define SST_DMA_RESOURCES 2 +#define SST_DSP_DMA_MAX_BURST 0x3
+struct sst_dma {
- struct sst_dsp *sst;
- struct platform_device *dma_dev;
- struct resource dma_resource[SST_DMA_RESOURCES];
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *ch;
+};
+static void sst_memcpy32(void *dest, void *src, int bytes) +{
- int i;
- /* copy one 32 bit word at a time as 64 bit access is not supported */
- for (i = 0; i < bytes; i += 4)
memcpy_toio(dest + i, src + i, 4);
+}
+static void sst_dma_transfer_complete(void *arg) +{
- struct sst_dsp *sst = (struct sst_dsp *)arg;
- dev_dbg(sst->dev, "DMA: callback\n");
+}
+int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t src_addr,
- dma_addr_t dest_addr, size_t size)
+{
- struct dma_async_tx_descriptor *desc;
- struct sst_dma *dma = sst->dma;
- if (dma->ch == NULL) {
dev_err(sst->dev, "error: no DMA channel\n");
return -ENODEV;
- }
- dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
(unsigned long)src_addr, (unsigned long)dest_addr, size);
- desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
src_addr, size, DMA_CTRL_ACK);
- if (!desc){
dev_err(sst->dev, "error: dma prep memcpy failed\n");
return -EINVAL;
- }
- desc->callback = sst_dma_transfer_complete;
- desc->callback_param = sst;
- desc->tx_submit(desc);
- dma_wait_for_async_tx(desc);
- return 0;
+} +EXPORT_SYMBOL_GPL(sst_dsp_dma_copy);
+static bool dma_chan_filter(struct dma_chan *chan, void *param) +{
- struct sst_dsp *dsp = (struct sst_dsp *)param;
- struct sst_dma *dma = dsp->dma;
- /* only accept channels from this device */
- if (chan->device->dev != &dma->dma_dev->dev)
return false;
- /* todo: add chan_id testing */
- return true;
+}
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) +{
- struct sst_dma *dma = dsp->dma;
- struct dma_slave_config slave;
- dma_cap_mask_t mask;
- int ret;
- /* The Intel MID DMA engine driver needs the slave config set but
* Synopsis DMA engine driver safely ignores the slave config */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_cap_set(DMA_MEMCPY, mask);
- dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
- if (dma->ch == NULL) {
dev_err(dsp->dev, "error: DMA request channel failed\n");
return -EIO;
- }
- memset(&slave, 0, sizeof(slave));
- slave.direction = DMA_MEM_TO_DEV;
- slave.src_addr_width =
slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
- ret = dmaengine_slave_config(dma->ch, &slave);
- if (ret) {
dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
ret);
dma_release_channel(dma->ch);
dma->ch = NULL;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp) +{
- struct sst_dma *dma = dsp->dma;
- dma_release_channel(dma->ch);
- dma->ch = NULL;
+} +EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
+/* platform data for DesignWare DMA Engine */ +static struct dw_dma_platform_data dw_pdata = {
- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
- .chan_priority = CHAN_PRIORITY_ASCENDING,
+};
+int sst_dma_new(struct sst_dsp *sst) +{
- struct sst_pdata *sst_pdata = sst->pdata;
- struct sst_dma *dma;
- const char *dma_dev_name;
- size_t dma_pdata_size;
- void *dma_pdata;
- /* configure the correct platform data for whatever DMA engine
- is attached to the ADSP IP. */
- switch (sst->pdata->dma_engine) {
- case SST_DMA_TYPE_DW:
dma_pdata = &dw_pdata;
dma_pdata_size = sizeof(dw_pdata);
dma_dev_name = "dw_dmac";
break;
- case SST_DMA_TYPE_MID:
dma_pdata = NULL;
dma_pdata_size = 0;
dma_dev_name = "Intel MID DMA";
break;
- default:
dev_err(sst->dev, "error: invalid DMA engine %d\n",
sst->pdata->dma_engine);
return -EINVAL;
- }
- dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
- if (!dma)
return -ENOMEM;
- dma->sst = sst;
- sst->dma = dma;
- dma->dma_resource[0].start = sst->addr.lpe_base +
sst_pdata->dma_base;
- dma->dma_resource[0].end = sst->addr.lpe_base +
sst_pdata->dma_base +
sst_pdata->dma_size;
- dma->dma_resource[0].flags = IORESOURCE_MEM;
- dma->dma_resource[1].start = sst_pdata->irq;
- dma->dma_resource[1].end = sst_pdata->irq;
- dma->dma_resource[1].flags = IORESOURCE_IRQ;
- /* now register DMA engine device */
- dma->dma_dev = platform_device_register_resndata(sst->dev,
dma_dev_name, -1, dma->dma_resource, 2,
dma_pdata, dma_pdata_size);
- if (dma->dma_dev == NULL) {
dev_err(sst->dev, "error: DMA device register failed\n");
return -ENODEV;
- }
- sst->fw_use_dma = true;
- return 0;
+} +EXPORT_SYMBOL(sst_dma_new);
+void sst_dma_free(struct sst_dma *dma) +{
- if (dma->ch)
dma_release_channel(dma->ch);
- platform_device_unregister(dma->dma_dev);
+} +EXPORT_SYMBOL(sst_dma_free);
+/* create new generic firmware object */ +struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
- const struct firmware *fw, void *private)
+{
- struct sst_fw *sst_fw;
- int err;
- if (!dsp->ops->parse_fw)
return NULL;
- sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL);
- if (sst_fw == NULL)
return NULL;
- sst_fw->dsp = dsp;
- sst_fw->private = private;
- sst_fw->size = fw->size;
- err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32));
- if (err < 0) {
kfree(sst_fw);
return NULL;
- }
- /* allocate DMA buffer to store FW data */
- sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size,
&sst_fw->dmable_fw_paddr, GFP_DMA);
- if (!sst_fw->dma_buf) {
dev_err(dsp->dev, "error: DMA alloc failed\n");
kfree(sst_fw);
return NULL;
- }
- /* copy FW data to DMA-able memory */
- memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
- release_firmware(fw);
Should fw object be really released here? For example, if this function returns NULL after this point, the caller doesn't know whether fw was released or not. It'd be more consistent to release it in the caller side, or always release no matter whether the function succeeds or not.
- if (dsp->fw_use_dma) {
err = sst_dsp_dma_get_channel(dsp, 0);
if (err < 0)
goto chan_err;
- }
- /* call core specific FW paser to load FW data into DSP */
- err = dsp->ops->parse_fw(sst_fw);
- if (err < 0) {
dev_err(dsp->dev, "error: parse fw failed %d\n", err);
goto parse_err;
- }
- if (dsp->fw_use_dma)
sst_dsp_dma_put_channel(dsp);
- mutex_lock(&dsp->mutex);
- list_add(&sst_fw->list, &dsp->fw_list);
- mutex_unlock(&dsp->mutex);
- return sst_fw;
+parse_err:
- if (dsp->fw_use_dma)
sst_dsp_dma_put_channel(dsp);
+chan_err:
- dma_free_coherent(dsp->dev, sst_fw->size,
sst_fw->dma_buf,
sst_fw->dmable_fw_paddr);
- kfree(sst_fw);
- return NULL;
+} +EXPORT_SYMBOL_GPL(sst_fw_new);
+/* free single firmware object */ +void sst_fw_free(struct sst_fw *sst_fw) +{
- struct sst_dsp *dsp = sst_fw->dsp;
- mutex_lock(&dsp->mutex);
- list_del(&sst_fw->list);
- mutex_unlock(&dsp->mutex);
- dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
sst_fw->dmable_fw_paddr);
- kfree(sst_fw);
+} +EXPORT_SYMBOL_GPL(sst_fw_free);
+/* free all firmware objects */ +void sst_fw_free_all(struct sst_dsp *dsp) +{
- struct sst_fw *sst_fw, *t;
- mutex_lock(&dsp->mutex);
- list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
list_del(&sst_fw->list);
dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
sst_fw->dmable_fw_paddr);
kfree(sst_fw);
- }
- mutex_unlock(&dsp->mutex);
+} +EXPORT_SYMBOL_GPL(sst_fw_free_all);
+/* create a new SST generic module from FW template */ +struct sst_module *sst_module_new(struct sst_fw *sst_fw,
- struct sst_module_template *template, void *private)
+{
- struct sst_dsp *dsp = sst_fw->dsp;
- struct sst_module *sst_module;
- sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL);
- if (sst_module == NULL)
return NULL;
- sst_module->id = template->id;
- sst_module->dsp = dsp;
- sst_module->sst_fw = sst_fw;
- memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data));
- memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data));
- INIT_LIST_HEAD(&sst_module->block_list);
- mutex_lock(&dsp->mutex);
- list_add(&sst_module->list, &dsp->module_list);
- mutex_unlock(&dsp->mutex);
- return sst_module;
+} +EXPORT_SYMBOL_GPL(sst_module_new);
+/* free firmware module and remove from available list */ +void sst_module_free(struct sst_module *sst_module) +{
- struct sst_dsp *dsp = sst_module->dsp;
- mutex_lock(&dsp->mutex);
- list_del(&sst_module->list);
- mutex_unlock(&dsp->mutex);
- kfree(sst_module);
+} +EXPORT_SYMBOL_GPL(sst_module_free);
+/* allocate contiguous free DSP blocks - callers hold locks */ +static int block_alloc_contiguous(struct sst_module *module,
- struct sst_module_data *data, u32 next_offset, int size)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_mem_block *block, *tmp;
- int ret;
- /* find first free blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks that dont match type */
if (block->type != data->type)
continue;
/* is block next after parent ? */
if (next_offset == block->offset) {
/* do we need more blocks */
if (size > block->size) {
ret = block_alloc_contiguous(module,
data, block->offset + block->size,
size - block->size);
if (ret < 0)
return ret;
How many contiguous blocks can be? The recursive call for each one block doesn't look scaling.
Takashi
}
/* add block to module */
block->data_type = data->data_type;
block->bytes_used = block->size;
list_move(&block->list, &dsp->used_block_list);
list_add(&block->module_list, &module->block_list);
dev_dbg(dsp->dev, " module %d added block %d:%d\n",
module->id, block->type, block->index);
return 0;
}
- }
- /* free any allocated blocks on failure */
- list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
list_del(&block->module_list);
list_move(&block->list, &dsp->free_block_list);
- }
- return -ENOMEM;
+}
+/* allocate free DSP blocks for module data - callers hold locks */ +static int block_alloc(struct sst_module *module,
- struct sst_module_data *data)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_mem_block *block, *tmp;
- int ret = 0;
- if (data->size == 0)
return 0;
- /* find first free whole blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks with wrong type */
if (block->type != data->type)
continue;
if (data->size > block->size)
continue;
data->offset = block->offset;
block->data_type = data->data_type;
block->bytes_used = data->size % block->size;
list_add(&block->module_list, &module->block_list);
list_move(&block->list, &dsp->used_block_list);
dev_dbg(dsp->dev, " *module %d added block %d:%d\n",
module->id, block->type, block->index);
return 0;
- }
- /* then find free multiple blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks with wrong type */
if (block->type != data->type)
continue;
/* do we span > 1 blocks */
if (data->size > block->size) {
ret = block_alloc_contiguous(module, data,
block->offset + block->size,
data->size - block->size);
if (ret == 0)
return ret;
}
- }
- /* not enough free block space */
- return -ENOMEM;
+}
+/* remove module from memory - callers hold locks */ +static void block_module_remove(struct sst_module *module) +{
- struct sst_mem_block *block, *tmp;
- struct sst_dsp *dsp = module->dsp;
- int err;
- /* disable each block */
- list_for_each_entry(block, &module->block_list, module_list) {
if (block->ops && block->ops->disable) {
err = block->ops->disable(block);
if (err < 0)
dev_err(dsp->dev,
"error: cant disable block %d:%d\n",
block->type, block->index);
}
- }
- /* mark each block as free */
- list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
list_del(&block->module_list);
list_move(&block->list, &dsp->free_block_list);
- }
+}
+/* prepare the memory block to receive data from host - callers hold locks */ +static int block_module_prepare(struct sst_module *module) +{
- struct sst_mem_block *block;
- int ret = 0;
- /* enable each block so that's it'e ready for module P/S data */
- list_for_each_entry(block, &module->block_list, module_list) {
if (block->ops && block->ops->enable)
ret = block->ops->enable(block);
if (ret < 0) {
dev_err(module->dsp->dev,
"error: cant disable block %d:%d\n",
block->type, block->index);
goto err;
}
- }
- return ret;
+err:
- list_for_each_entry(block, &module->block_list, module_list) {
if (block->ops && block->ops->disable)
block->ops->disable(block);
- }
- return ret;
+}
+/* allocate memory blocks for static module addresses - callers hold locks */ +static int block_alloc_fixed(struct sst_module *module,
- struct sst_module_data *data)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_mem_block *block, *tmp;
- u32 end = data->offset + data->size, block_end;
- int err;
- /* only IRAM/DRAM blocks are managed */
- if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM)
return 0;
- /* are blocks already attached to this module */
- list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
/* force compacting mem blocks of the same data_type */
if (block->data_type != data->data_type)
continue;
block_end = block->offset + block->size;
/* find block that holds section */
if (data->offset >= block->offset && end < block_end)
return 0;
/* does block span more than 1 section */
if (data->offset >= block->offset && data->offset < block_end) {
err = block_alloc_contiguous(module, data,
block->offset + block->size,
data->size - block->size + data->offset - block->offset);
if (err < 0)
return -ENOMEM;
/* module already owns blocks */
return 0;
}
- }
- /* find first free blocks that can hold section in free list */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
block_end = block->offset + block->size;
/* find block that holds section */
if (data->offset >= block->offset && end < block_end) {
/* add block */
block->data_type = data->data_type;
list_move(&block->list, &dsp->used_block_list);
list_add(&block->module_list, &module->block_list);
return 0;
}
/* does block span more than 1 section */
if (data->offset >= block->offset && data->offset < block_end) {
err = block_alloc_contiguous(module, data,
block->offset + block->size,
data->size - block->size);
if (err < 0)
return -ENOMEM;
/* add block */
block->data_type = data->data_type;
list_move(&block->list, &dsp->used_block_list);
list_add(&block->module_list, &module->block_list);
return 0;
}
- }
- return -ENOMEM;
+}
+/* Load fixed module data into DSP memory blocks */ +int sst_module_insert_fixed_block(struct sst_module *module,
- struct sst_module_data *data)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_fw *sst_fw = module->sst_fw;
- int ret;
- mutex_lock(&dsp->mutex);
- /* alloc blocks that includes this section */
- ret = block_alloc_fixed(module, data);
- if (ret < 0) {
dev_err(dsp->dev,
"error: no free blocks for section at offset 0x%x size 0x%x\n",
data->offset, data->size);
mutex_unlock(&dsp->mutex);
return -ENOMEM;
- }
- /* prepare DSP blocks for module copy */
- ret = block_module_prepare(module);
- if (ret < 0) {
dev_err(dsp->dev, "error: fw module prepare failed\n");
goto err;
- }
- /* copy partial module data to blocks */
- if (dsp->fw_use_dma) {
ret = sst_dsp_dma_copy(dsp,
sst_fw->dmable_fw_paddr + data->data_offset,
dsp->addr.lpe_base + data->offset, data->size);
if (ret < 0) {
dev_err(dsp->dev, "error: module copy failed\n");
goto err;
}
- } else
sst_memcpy32(dsp->addr.lpe + data->offset, data->data,
data->size);
- mutex_unlock(&dsp->mutex);
- return ret;
+err:
- block_module_remove(module);
- mutex_unlock(&dsp->mutex);
- return ret;
+} +EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block);
+/* Unload entire module from DSP memory */ +int sst_block_module_remove(struct sst_module *module) +{
- struct sst_dsp *dsp = module->dsp;
- mutex_lock(&dsp->mutex);
- block_module_remove(module);
- mutex_unlock(&dsp->mutex);
- return 0;
+} +EXPORT_SYMBOL_GPL(sst_block_module_remove);
+/* register a DSP memory block for use with FW based modules */ +struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
- u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
- void *private)
+{
- struct sst_mem_block *block;
- block = kzalloc(sizeof(*block), GFP_KERNEL);
- if (block == NULL)
return NULL;
- block->offset = offset;
- block->size = size;
- block->index = index;
- block->type = type;
- block->dsp = dsp;
- block->private = private;
- block->ops = ops;
- mutex_lock(&dsp->mutex);
- list_add(&block->list, &dsp->free_block_list);
- mutex_unlock(&dsp->mutex);
- return block;
+} +EXPORT_SYMBOL_GPL(sst_mem_block_register);
+/* unregister all DSP memory blocks */ +void sst_mem_block_unregister_all(struct sst_dsp *dsp) +{
- struct sst_mem_block *block, *tmp;
- mutex_lock(&dsp->mutex);
- /* unregister used blocks */
- list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) {
list_del(&block->list);
kfree(block);
- }
- /* unregister free blocks */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
list_del(&block->list);
kfree(block);
- }
- mutex_unlock(&dsp->mutex);
+} +EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
+/* allocate scratch buffer blocks */ +struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp) +{
- struct sst_module *sst_module, *scratch;
- struct sst_mem_block *block, *tmp;
- u32 block_size;
- int ret = 0;
- scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL);
- if (scratch == NULL)
return NULL;
- mutex_lock(&dsp->mutex);
- /* calculate required scratch size */
- list_for_each_entry(sst_module, &dsp->module_list, list) {
if (scratch->s.size > sst_module->s.size)
scratch->s.size = scratch->s.size;
else
scratch->s.size = sst_module->s.size;
- }
- dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n",
scratch->s.size);
- /* init scratch module */
- scratch->dsp = dsp;
- scratch->s.type = SST_MEM_DRAM;
- scratch->s.data_type = SST_DATA_S;
- INIT_LIST_HEAD(&scratch->block_list);
- /* check free blocks before looking at used blocks for space */
- if (!list_empty(&dsp->free_block_list))
block = list_first_entry(&dsp->free_block_list,
struct sst_mem_block, list);
- else
block = list_first_entry(&dsp->used_block_list,
struct sst_mem_block, list);
- block_size = block->size;
- /* allocate blocks for module scratch buffers */
- dev_dbg(dsp->dev, "allocating scratch blocks\n");
- ret = block_alloc(scratch, &scratch->s);
- if (ret < 0) {
dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
goto err;
- }
- /* assign the same offset of scratch to each module */
- list_for_each_entry(sst_module, &dsp->module_list, list)
sst_module->s.offset = scratch->s.offset;
- mutex_unlock(&dsp->mutex);
- return scratch;
+err:
- list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
list_del(&block->module_list);
- mutex_unlock(&dsp->mutex);
- return NULL;
+} +EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch);
+/* free all scratch blocks */ +void sst_mem_block_free_scratch(struct sst_dsp *dsp,
- struct sst_module *scratch)
+{
- struct sst_mem_block *block, *tmp;
- mutex_lock(&dsp->mutex);
- list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
list_del(&block->module_list);
- mutex_unlock(&dsp->mutex);
+} +EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch);
+/* get a module from it's unique ID */ +struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id) +{
- struct sst_module *module;
- mutex_lock(&dsp->mutex);
- list_for_each_entry(module, &dsp->module_list, list) {
if (module->id == id) {
mutex_unlock(&dsp->mutex);
return module;
}
- }
- mutex_unlock(&dsp->mutex);
- return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_get_from_id);
1.8.3.2
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Fri, 2014-02-14 at 09:55 +0100, Takashi Iwai wrote:
At Thu, 13 Feb 2014 19:15:28 +0000, Liam Girdwood wrote:
+/* create new generic firmware object */ +struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
- const struct firmware *fw, void *private)
+{
- struct sst_fw *sst_fw;
- int err;
- if (!dsp->ops->parse_fw)
return NULL;
- sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL);
- if (sst_fw == NULL)
return NULL;
- sst_fw->dsp = dsp;
- sst_fw->private = private;
- sst_fw->size = fw->size;
- err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32));
- if (err < 0) {
kfree(sst_fw);
return NULL;
- }
- /* allocate DMA buffer to store FW data */
- sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size,
&sst_fw->dmable_fw_paddr, GFP_DMA);
- if (!sst_fw->dma_buf) {
dev_err(dsp->dev, "error: DMA alloc failed\n");
kfree(sst_fw);
return NULL;
- }
- /* copy FW data to DMA-able memory */
- memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
- release_firmware(fw);
Should fw object be really released here? For example, if this function returns NULL after this point, the caller doesn't know whether fw was released or not. It'd be more consistent to release it in the caller side, or always release no matter whether the function succeeds or not.
Yep, your right. I'll fix this in V2. Fwiw, we only need to take a copy of the FW and store that copy in DMA-able memory (32bit addressable). So the DMA memory will be used for context restore (from resume), DMA firmware loading etc.
+/* allocate contiguous free DSP blocks - callers hold locks */ +static int block_alloc_contiguous(struct sst_module *module,
- struct sst_module_data *data, u32 next_offset, int size)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_mem_block *block, *tmp;
- int ret;
- /* find first free blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks that dont match type */
if (block->type != data->type)
continue;
/* is block next after parent ? */
if (next_offset == block->offset) {
/* do we need more blocks */
if (size > block->size) {
ret = block_alloc_contiguous(module,
data, block->offset + block->size,
size - block->size);
if (ret < 0)
return ret;
How many contiguous blocks can be?
In theory, the whole DSP memory can be allocated as a contiguous block, but in practice it's only really a few blocks contiguous per module atm.
The recursive call for each one block doesn't look scaling.
It seems to work atm, but I'll update this too for V2.
Liam
On Fri, 2014-02-14 at 09:38 +0000, Liam Girdwood wrote:
On Fri, 2014-02-14 at 09:55 +0100, Takashi Iwai wrote:
At Thu, 13 Feb 2014 19:15:28 +0000, Liam Girdwood wrote:
+/* allocate contiguous free DSP blocks - callers hold locks */ +static int block_alloc_contiguous(struct sst_module *module,
- struct sst_module_data *data, u32 next_offset, int size)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_mem_block *block, *tmp;
- int ret;
- /* find first free blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks that dont match type */
if (block->type != data->type)
continue;
/* is block next after parent ? */
if (next_offset == block->offset) {
/* do we need more blocks */
if (size > block->size) {
ret = block_alloc_contiguous(module,
data, block->offset + block->size,
size - block->size);
if (ret < 0)
return ret;
How many contiguous blocks can be?
In theory, the whole DSP memory can be allocated as a contiguous block, but in practice it's only really a few blocks contiguous per module atm.
The recursive call for each one block doesn't look scaling.
I've double checked the logic here and added more comments and debug. I may be missing something though, but it does work and is designed to deal with unordered blocks (on address) in the list.
The recursion basically checks the whole list each time for the next block in address order. It then checks if the required size > current block size. We decrement size on each by block size and increment address by block size on every call. If the required size < current block size we allocate the block and come back up the stack allocating the other blocks in the sequence.
From the debug we get :-
[ 6.733916] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:4 at offset 0xc0000 [ 6.733925] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:3 at offset 0xb8000 [ 6.733930] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:2 at offset 0xb0000 [ 6.733936] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:1 at offset 0xa8000 [ 6.810179] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:1 at offset 0x88000 [ 6.810189] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:0 at offset 0x80000 [ 6.857452] haswell-pcm-audio haswell-pcm-audio: scratch buffer required is 56320 bytes [ 6.857461] haswell-pcm-audio haswell-pcm-audio: allocating scratch blocks [ 6.857469] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:6 at offset 0x70000 [ 6.860268] haswell-pcm-audio haswell-pcm-audio: FW loaded: type 172 - version: 0.0 build 1
Thanks
Liam
--------------------------------------------------------------------- Intel Corporation (UK) Limited Registered No. 1134945 (England) Registered Office: Pipers Way, Swindon SN3 1RJ VAT No: 860 2173 47
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.
At Fri, 14 Feb 2014 16:11:15 +0000, Liam Girdwood wrote:
On Fri, 2014-02-14 at 09:38 +0000, Liam Girdwood wrote:
On Fri, 2014-02-14 at 09:55 +0100, Takashi Iwai wrote:
At Thu, 13 Feb 2014 19:15:28 +0000, Liam Girdwood wrote:
+/* allocate contiguous free DSP blocks - callers hold locks */ +static int block_alloc_contiguous(struct sst_module *module,
- struct sst_module_data *data, u32 next_offset, int size)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_mem_block *block, *tmp;
- int ret;
- /* find first free blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks that dont match type */
if (block->type != data->type)
continue;
/* is block next after parent ? */
if (next_offset == block->offset) {
/* do we need more blocks */
if (size > block->size) {
ret = block_alloc_contiguous(module,
data, block->offset + block->size,
size - block->size);
if (ret < 0)
return ret;
How many contiguous blocks can be?
In theory, the whole DSP memory can be allocated as a contiguous block, but in practice it's only really a few blocks contiguous per module atm.
The recursive call for each one block doesn't look scaling.
I've double checked the logic here and added more comments and debug. I may be missing something though, but it does work and is designed to deal with unordered blocks (on address) in the list.
The recursion basically checks the whole list each time for the next block in address order. It then checks if the required size > current block size. We decrement size on each by block size and increment address by block size on every call. If the required size < current block size we allocate the block and come back up the stack allocating the other blocks in the sequence.
From the debug we get :-
[ 6.733916] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:4 at offset 0xc0000 [ 6.733925] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:3 at offset 0xb8000 [ 6.733930] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:2 at offset 0xb0000 [ 6.733936] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:1 at offset 0xa8000 [ 6.810179] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:1 at offset 0x88000 [ 6.810189] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:0 at offset 0x80000 [ 6.857452] haswell-pcm-audio haswell-pcm-audio: scratch buffer required is 56320 bytes [ 6.857461] haswell-pcm-audio haswell-pcm-audio: allocating scratch blocks [ 6.857469] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:6 at offset 0x70000 [ 6.860268] haswell-pcm-audio haswell-pcm-audio: FW loaded: type 172 - version: 0.0 build 1
Well, the code is a bit confusing because of the comment:
static int block_alloc_contiguous(struct sst_module *module, struct sst_module_data *data, u32 next_offset, int size) { ..... /* find first free blocks that can hold module */ list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
Actually, this loop doesn't look for the first free blocks but rather looks for the block that matches with the given type and the offset.
/* ignore blocks that dont match type */ if (block->type != data->type) continue;
/* is block next after parent ? */ if (next_offset == block->offset) {
Then, it tries allocating the next block if the size of the found block isn't big enough:
/* do we need more blocks */ if (size > block->size) { ret = block_alloc_contiguous(module, data, block->offset + block->size, size - block->size); if (ret < 0) return ret;
So, here is the recursion, and it means that at maximum (size / block_size) level recursions may happen.
My point is that this doesn't have to be recursion. It can be implemented via a flat loop as simply as well. For example, a code like below would work, too.
struct block *find_block(int type, int offset) { struct block *block; list_for_each_entry(block, &free_blocks, list) { if (block->type == type && block->offset == offset) return block; } return NULL; }
int alloc_contiguous(int type, int offset, int size) { struct list_head tmp = LIST_HEAD_INIT(tmp); struct block *block;
while (size > 0) { block = find_block(type, offset); if (!block) { list_splice(&tmp, &free_list); return -ENOMEM; } list_move_tail(&block->list, &tmp); offset += block->size; size -= block->size; }
list_for_each_entry(block, &tmp, list) { block->data_type = xxx; .... } list_splice(&tmp, &used_list); return 0; }
BTW, this is still suboptimal; we can remember the last failed point and rescan from there at next.
Or, if you manage the sorted list (tree) instead of a plain list, things will be also easier, I suppose.
Takashi
On Fri, 2014-02-14 at 18:06 +0100, Takashi Iwai wrote:
At Fri, 14 Feb 2014 16:11:15 +0000, Liam Girdwood wrote:
On Fri, 2014-02-14 at 09:38 +0000, Liam Girdwood wrote:
On Fri, 2014-02-14 at 09:55 +0100, Takashi Iwai wrote:
At Thu, 13 Feb 2014 19:15:28 +0000, Liam Girdwood wrote:
+/* allocate contiguous free DSP blocks - callers hold locks */ +static int block_alloc_contiguous(struct sst_module *module,
- struct sst_module_data *data, u32 next_offset, int size)
+{
- struct sst_dsp *dsp = module->dsp;
- struct sst_mem_block *block, *tmp;
- int ret;
- /* find first free blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks that dont match type */
if (block->type != data->type)
continue;
/* is block next after parent ? */
if (next_offset == block->offset) {
/* do we need more blocks */
if (size > block->size) {
ret = block_alloc_contiguous(module,
data, block->offset + block->size,
size - block->size);
if (ret < 0)
return ret;
How many contiguous blocks can be?
In theory, the whole DSP memory can be allocated as a contiguous block, but in practice it's only really a few blocks contiguous per module atm.
The recursive call for each one block doesn't look scaling.
I've double checked the logic here and added more comments and debug. I may be missing something though, but it does work and is designed to deal with unordered blocks (on address) in the list.
The recursion basically checks the whole list each time for the next block in address order. It then checks if the required size > current block size. We decrement size on each by block size and increment address by block size on every call. If the required size < current block size we allocate the block and come back up the stack allocating the other blocks in the sequence.
From the debug we get :-
[ 6.733916] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:4 at offset 0xc0000 [ 6.733925] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:3 at offset 0xb8000 [ 6.733930] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:2 at offset 0xb0000 [ 6.733936] haswell-pcm-audio haswell-pcm-audio: module 0 added block 0:1 at offset 0xa8000 [ 6.810179] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:1 at offset 0x88000 [ 6.810189] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:0 at offset 0x80000 [ 6.857452] haswell-pcm-audio haswell-pcm-audio: scratch buffer required is 56320 bytes [ 6.857461] haswell-pcm-audio haswell-pcm-audio: allocating scratch blocks [ 6.857469] haswell-pcm-audio haswell-pcm-audio: module 0 added block 1:6 at offset 0x70000 [ 6.860268] haswell-pcm-audio haswell-pcm-audio: FW loaded: type 172 - version: 0.0 build 1
Well, the code is a bit confusing because of the comment:
I agree, and I had updated the comments for V2.
static int block_alloc_contiguous(struct sst_module *module, struct sst_module_data *data, u32 next_offset, int size) { ..... /* find first free blocks that can hold module */ list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
Actually, this loop doesn't look for the first free blocks but rather looks for the block that matches with the given type and the offset.
/* ignore blocks that dont match type */ if (block->type != data->type) continue; /* is block next after parent ? */ if (next_offset == block->offset) {
Then, it tries allocating the next block if the size of the found block isn't big enough:
/* do we need more blocks */ if (size > block->size) { ret = block_alloc_contiguous(module, data, block->offset + block->size, size - block->size); if (ret < 0) return ret;
So, here is the recursion, and it means that at maximum (size / block_size) level recursions may happen.
My point is that this doesn't have to be recursion. It can be implemented via a flat loop as simply as well. For example, a code like below would work, too.
struct block *find_block(int type, int offset) { struct block *block; list_for_each_entry(block, &free_blocks, list) { if (block->type == type && block->offset == offset) return block; } return NULL; }
int alloc_contiguous(int type, int offset, int size) { struct list_head tmp = LIST_HEAD_INIT(tmp); struct block *block;
while (size > 0) { block = find_block(type, offset); if (!block) { list_splice(&tmp, &free_list); return -ENOMEM; } list_move_tail(&block->list, &tmp); offset += block->size; size -= block->size; }
list_for_each_entry(block, &tmp, list) { block->data_type = xxx; .... } list_splice(&tmp, &used_list); return 0; }
BTW, this is still suboptimal; we can remember the last failed point and rescan from there at next.
Or, if you manage the sorted list (tree) instead of a plain list, things will be also easier, I suppose.
Agree it would be faster if ordered, but this code will be infrequently used so the emphasis was more on ease of development/testing. I'll remove the recursion for V2.
Thanks
Liam
Provide a trace mechanism for debugging generic SST IPC messages.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- include/trace/events/sst.h | 148 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 include/trace/events/sst.h
diff --git a/include/trace/events/sst.h b/include/trace/events/sst.h new file mode 100644 index 0000000..1a0d6b3 --- /dev/null +++ b/include/trace/events/sst.h @@ -0,0 +1,148 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sst + +#if !defined(_TRACE_SST_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SST_H + +#include <linux/types.h> +#include <linux/ktime.h> +#include <linux/tracepoint.h> + +DECLARE_EVENT_CLASS(sst_ipc_msg, + + TP_PROTO(unsigned int val), + + TP_ARGS(val), + + TP_STRUCT__entry( + __field( unsigned int, val ) + ), + + TP_fast_assign( + __entry->val = val; + ), + + TP_printk("0x%8.8x", (unsigned int)__entry->val) +); + +DEFINE_EVENT(sst_ipc_msg, sst_ipc_msg_tx, + + TP_PROTO(unsigned int val), + + TP_ARGS(val) + +); + +DEFINE_EVENT(sst_ipc_msg, sst_ipc_msg_rx, + + TP_PROTO(unsigned int val), + + TP_ARGS(val) + +); + +DECLARE_EVENT_CLASS(sst_ipc_mailbox, + + TP_PROTO(unsigned int offset, unsigned int val), + + TP_ARGS(offset, val), + + TP_STRUCT__entry( + __field( unsigned int, offset ) + __field( unsigned int, val ) + ), + + TP_fast_assign( + __entry->offset = offset; + __entry->val = val; + ), + + TP_printk(" 0x%4.4x = 0x%8.8x", + (unsigned int)__entry->offset, (unsigned int)__entry->val) +); + +DEFINE_EVENT(sst_ipc_mailbox, sst_ipc_inbox_rdata, + + TP_PROTO(unsigned int offset, unsigned int val), + + TP_ARGS(offset, val) + +); + +DEFINE_EVENT(sst_ipc_mailbox, sst_ipc_inbox_wdata, + + TP_PROTO(unsigned int offset, unsigned int val), + + TP_ARGS(offset, val) + +); + +DEFINE_EVENT(sst_ipc_mailbox, sst_ipc_outbox_rdata, + + TP_PROTO(unsigned int offset, unsigned int val), + + TP_ARGS(offset, val) + +); + +DEFINE_EVENT(sst_ipc_mailbox, sst_ipc_outbox_wdata, + + TP_PROTO(unsigned int offset, unsigned int val), + + TP_ARGS(offset, val) + +); + +DECLARE_EVENT_CLASS(sst_ipc_mailbox_info, + + TP_PROTO(unsigned int size), + + TP_ARGS(size), + + TP_STRUCT__entry( + __field( unsigned int, size ) + ), + + TP_fast_assign( + __entry->size = size; + ), + + TP_printk("Mailbox bytes 0x%8.8x", (unsigned int)__entry->size) +); + +DEFINE_EVENT(sst_ipc_mailbox_info, sst_ipc_inbox_read, + + TP_PROTO(unsigned int size), + + TP_ARGS(size) + +); + +DEFINE_EVENT(sst_ipc_mailbox_info, sst_ipc_inbox_write, + + TP_PROTO(unsigned int size), + + TP_ARGS(size) + +); + +DEFINE_EVENT(sst_ipc_mailbox_info, sst_ipc_outbox_read, + + TP_PROTO(unsigned int size), + + TP_ARGS(size) + +); + +DEFINE_EVENT(sst_ipc_mailbox_info, sst_ipc_outbox_write, + + TP_PROTO(unsigned int size), + + TP_ARGS(size) + +); + +#endif /* _TRACE_SST_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h>
This adds kernel build support for Intel SST core audio.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- sound/soc/intel/Kconfig | 14 ++++++++++++++ sound/soc/intel/Makefile | 7 +++++++ 2 files changed, 21 insertions(+)
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 4d9d0a5..0b51451 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -11,3 +11,17 @@ config SND_MFLD_MACHINE
config SND_SST_MFLD_PLATFORM tristate + +config SND_SOC_INTEL_SST + tristate "ASoC support for Intel(R) Smart Sound Technology" + select SND_SOC_INTEL_SST_ACPI if ACPI + help + This adds support for Intel(R) Smart Sound Technology (SST). + Say Y if you have such a device + If unsure select "N". + +config SND_SOC_INTEL_SST_ACPI + tristate + +config SND_SOC_INTEL_HASWELL + tristate diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index eb899fc..cf47100 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,5 +1,12 @@ +# Core support +snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-acpi-objs := sst-acpi.o + snd-soc-sst-mfld-platform-objs := sst-mfld-platform.o snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o + +obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o +obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
At Thu, 13 Feb 2014 19:15:30 +0000, Liam Girdwood wrote:
This adds kernel build support for Intel SST core audio.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com
sound/soc/intel/Kconfig | 14 ++++++++++++++ sound/soc/intel/Makefile | 7 +++++++ 2 files changed, 21 insertions(+)
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 4d9d0a5..0b51451 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -11,3 +11,17 @@ config SND_MFLD_MACHINE
config SND_SST_MFLD_PLATFORM tristate
+config SND_SOC_INTEL_SST
- tristate "ASoC support for Intel(R) Smart Sound Technology"
- select SND_SOC_INTEL_SST_ACPI if ACPI
- help
This adds support for Intel(R) Smart Sound Technology (SST).
Say Y if you have such a device
If unsure select "N".
+config SND_SOC_INTEL_SST_ACPI
- tristate
+config SND_SOC_INTEL_HASWELL
- tristate
I guess the last one is superfluous? It's used nowhere else.
Takashi
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index eb899fc..cf47100 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,5 +1,12 @@ +# Core support +snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-mfld-platform-objs := sst-mfld-platform.o snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
+obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o
+obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
1.8.3.2
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Fri, 2014-02-14 at 09:56 +0100, Takashi Iwai wrote:
At Thu, 13 Feb 2014 19:15:30 +0000, Liam Girdwood wrote:
This adds kernel build support for Intel SST core audio.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com
sound/soc/intel/Kconfig | 14 ++++++++++++++ sound/soc/intel/Makefile | 7 +++++++ 2 files changed, 21 insertions(+)
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 4d9d0a5..0b51451 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -11,3 +11,17 @@ config SND_MFLD_MACHINE
config SND_SST_MFLD_PLATFORM tristate
+config SND_SOC_INTEL_SST
- tristate "ASoC support for Intel(R) Smart Sound Technology"
- select SND_SOC_INTEL_SST_ACPI if ACPI
- help
This adds support for Intel(R) Smart Sound Technology (SST).
Say Y if you have such a device
If unsure select "N".
+config SND_SOC_INTEL_SST_ACPI
- tristate
+config SND_SOC_INTEL_HASWELL
- tristate
I guess the last one is superfluous? It's used nowhere else.
Yeap, for this series. It's actually part of the Haswell support (next patch series). I had just chopped things down into a smaller patch series for easier review.
Liam
At Thu, 13 Feb 2014 19:15:26 +0000, Liam Girdwood wrote:
Add support for Intel Smart Sound Technology (SST) audio DSPs. This driver provides the low level IO, reset, boot and IRQ management for Intel audio DSPs. These files make up the low level part of the SST audio driver stack and will be used by many Intel SST cores like Haswell, Broadwell and Baytrail.
SST DSPs expose a memory mapped region (shim) for config and control. The shim layout is mostly shared without much modification across cores and this driver provides a uniform API to access the shim and to enable basic shim functions. It also provides functionality to abstract some shim functions for cores with different shim features.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com
sound/soc/intel/sst-dsp-priv.h | 291 ++++++++++++++++++++++++++++++++ sound/soc/intel/sst-dsp.c | 370 +++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-dsp.h | 226 +++++++++++++++++++++++++ 3 files changed, 887 insertions(+) create mode 100644 sound/soc/intel/sst-dsp-priv.h create mode 100644 sound/soc/intel/sst-dsp.c create mode 100644 sound/soc/intel/sst-dsp.h
diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h new file mode 100644 index 0000000..c4db4c7 --- /dev/null +++ b/sound/soc/intel/sst-dsp-priv.h @@ -0,0 +1,291 @@ +/*
- Intel Smart Sound Technology
- Copyright (C) 2013, Intel Corporation. All rights reserved.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License version
- 2 as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- */
+#ifndef __SOUND_SOC_SST_DSP_PRIV_H +#define __SOUND_SOC_SST_DSP_PRIV_H
+#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/firmware.h>
+struct sst_mem_block; +struct sst_module; +struct sst_fw;
+/*
- DSP Operations exported by platform Audio DSP driver.
- */
+struct sst_ops {
- /* DSP core boot / reset */
- void (*boot)(struct sst_dsp *);
- void (*reset)(struct sst_dsp *);
- /* Shim IO */
- void (*write)(void __iomem *addr, u32 offset, u32 value);
- u32 (*read)(void __iomem *addr, u32 offset);
- void (*write64)(void __iomem *addr, u32 offset, u64 value);
- u64 (*read64)(void __iomem *addr, u32 offset);
- /* DSP I/DRAM IO */
- void (*ram_read)(struct sst_dsp *sst, void *dest, void *src, size_t bytes);
- void (*ram_write)(struct sst_dsp *sst, void *dest, void *src, size_t bytes);
- void (*dump)(struct sst_dsp *);
- /* IRQ handlers */
- irqreturn_t (*irq_handler)(int irq, void *context);
- /* SST init and free */
- int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata);
- void (*free)(struct sst_dsp *sst);
- /* FW module parser/loader */
- int (*parse_fw)(struct sst_fw *sst_fw);
+};
+/*
- Audio DSP memory offsets and addresses.
- */
+struct sst_addr {
- u32 lpe_base;
- u32 shim_offset;
- u32 iram_offset;
- void __iomem *lpe;
- void __iomem *shim;
- void __iomem *pci_cfg;
- void __iomem *fw_ext;
+};
+/*
- Audio DSP Mailbox configuration.
- */
+struct sst_mailbox {
- void __iomem *in_base;
- void __iomem *out_base;
- size_t in_size;
- size_t out_size;
+};
+/*
- Audio DSP Firmware data types.
- */
+enum sst_data_type {
- SST_DATA_M = 0, /* module block data */
- SST_DATA_P = 1, /* peristant data (text, data) */
- SST_DATA_S = 2, /* scratch data (usually buffers) */
+};
+/*
- Audio DSP memory block types.
- */
+enum sst_mem_type {
- SST_MEM_IRAM = 0,
- SST_MEM_DRAM = 1,
- SST_MEM_ANY = 2,
- SST_MEM_CACHE= 3,
+};
+/*
- Audio DSP Generic Firmware File.
- SST Firmware files can consist of 1..N modules. This generic structure is
- used to manage each firmware file and it's modules regardless of SST firmware
- type. A SST driver may load multiple FW files.
- */
+struct sst_fw {
- struct sst_dsp *dsp;
- /* base addresses of FW file data */
- dma_addr_t dmable_fw_paddr; /* physical address of fw data */
- void *dma_buf; /* virtual address of fw data */
- u32 size; /* size of fw data */
- /* lists */
- struct list_head list; /* DSP list of FW */
- struct list_head module_list; /* FW list of modules */
- void *private; /* core doesn't touch this */
+};
+/*
- Audio DSP Generic Module data.
- This is used to dsecribe any sections of persistent (text and data) and
- scratch (buffers) of module data in ADSP memory space.
- */
+struct sst_module_data {
- enum sst_mem_type type; /* destination memory type */
- enum sst_data_type data_type; /* type of module data */
- u32 size; /* size in bytes */
- u32 offset; /* offset in FW file */
- u32 data_offset; /* offset in ADSP memory space */
- void *data; /* module data */
+};
+/*
- Audio DSP Generic Module Template.
- Used to define and register a new FW module. This data is extracted from
- FW module header information.
- */
+struct sst_module_template {
- u32 id;
- u32 entry; /* entry point */
- struct sst_module_data s; /* scratch data */
- struct sst_module_data p; /* peristant data */
+};
+/*
- Audio DSP Generic Module.
- Each Firmware file can consist of 1..N modules. A module can span multiple
- ADSP memory blocks. The simplest FW will be a file with 1 module.
- */
+struct sst_module {
- struct sst_dsp *dsp;
- struct sst_fw *sst_fw; /* parent FW we belong too */
- /* module configuration */
- u32 id;
- u32 entry; /* module entry point */
- u32 offset; /* module offset in firmware file */
- u32 size; /* module size */
- struct sst_module_data s; /* scratch data */
- struct sst_module_data p; /* peristant data */
- /* runtime */
- u32 usage_count; /* can be unloaded if count == 0 */
- void *private; /* core doesn't touch this */
- /* lists */
- struct list_head block_list; /* Module list of blocks in use */
- struct list_head list; /* DSP list of modules */
- struct list_head list_fw; /* FW list of modules */
+};
+/*
- SST Memory Block operations.
- */
+struct sst_block_ops {
- int (*enable)(struct sst_mem_block *block);
- int (*disable)(struct sst_mem_block *block);
+};
+/*
- SST Generic Memory Block.
- SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be
- power gated.
- */
+struct sst_mem_block {
- struct sst_dsp *dsp;
- struct sst_module *module; /* module that uses this block */
- /* block config */
- u32 offset; /* offset from base */
- u32 size; /* block size */
- u32 index; /* block index 0..N */
- enum sst_mem_type type; /* block memory type IRAM/DRAM */
- struct sst_block_ops *ops; /* block operations, if any */
- /* block status */
- enum sst_data_type data_type; /* data type held in this block */
- u32 bytes_used; /* bytes in use by modules */
- void *private; /* generic core does not touch this */
- int users; /* number of modules using this block */
- /* block lists */
- struct list_head module_list; /* Module list of blocks */
- struct list_head list; /* Map list of free/used blocks */
+};
+/*
- Generic SST Shim Interface.
- */
+struct sst_dsp {
- /* runtime */
- struct sst_dsp_device *sst_dev;
- spinlock_t spinlock; /* IPC locking */
- struct mutex mutex; /* DSP FW lock */
- struct device *dev;
- void *thread_context;
- int irq;
- u32 id;
- /* list of free and used ADSP memory blocks */
- struct list_head used_block_list;
- struct list_head free_block_list;
- /* operations */
- struct sst_ops *ops;
- /* debug FS */
- struct dentry *debugfs_root;
- /* base addresses */
- struct sst_addr addr;
- /* mailbox */
- struct sst_mailbox mailbox;
- /* SST FW files loaded and their modules */
- struct list_head module_list;
- struct list_head fw_list;
- /* platform data */
- struct sst_pdata *pdata;
- /* DMA FW loading */
- struct sst_dma *dma;
- bool fw_use_dma;
+};
+/* Create/Free FW files - can contain multiple modules */ +struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
- const struct firmware *fw, void *private);
+void sst_fw_free(struct sst_fw *sst_fw); +void sst_fw_free_all(struct sst_dsp *dsp);
+/* Create/Free firmware modules */ +struct sst_module *sst_module_new(struct sst_fw *sst_fw,
- struct sst_module_template *template, void *private);
+void sst_module_free(struct sst_module *sst_module); +int sst_module_insert(struct sst_module *sst_module); +int sst_module_remove(struct sst_module *sst_module); +int sst_module_insert_fixed_block(struct sst_module *module,
- struct sst_module_data *data);
+struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
+/* allocate/free pesistent/scratch memory regions managed by drv */ +struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp); +void sst_mem_block_free_scratch(struct sst_dsp *dsp,
- struct sst_module *scratch);
+/* Register the DSPs memory blocks - would be nice to read from ACPI */ +struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
- u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
- void *private);
+void sst_mem_block_unregister_all(struct sst_dsp *dsp);
+/* DMA */ +int sst_dma_new(struct sst_dsp *sst); +void sst_dma_free(struct sst_dma *dma);
+#endif diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c new file mode 100644 index 0000000..beef2ef --- /dev/null +++ b/sound/soc/intel/sst-dsp.c @@ -0,0 +1,370 @@ +/*
- Intel Smart Sound Technology (SST) DSP Core Driver
- Copyright (C) 2013, Intel Corporation. All rights reserved.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License version
- 2 as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- */
+#include <linux/slab.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h>
+#include "sst-dsp.h" +#include "sst-dsp-priv.h"
+#define CREATE_TRACE_POINTS +#include <trace/events/sst.h>
+/* Public API */ +void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) +{
- unsigned long flags;
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
+} +EXPORT_SYMBOL(sst_dsp_shim_write);
Any reason not to use *_GPL() version?
+u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset) +{
- unsigned long flags;
- u32 val;
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return val;
+} +EXPORT_SYMBOL(sst_dsp_shim_read);
+void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value) +{
- unsigned long flags;
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write64(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
+} +EXPORT_SYMBOL(sst_dsp_shim_write64);
+u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset) +{
- unsigned long flags;
- u64 val;
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read64(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return val;
+} +EXPORT_SYMBOL(sst_dsp_shim_read64);
+void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) +{
- sst->ops->write(sst->addr.shim, offset, value);
+} +EXPORT_SYMBOL(sst_dsp_shim_write_unlocked);
+u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) +{
- return sst->ops->read(sst->addr.shim, offset);
+} +EXPORT_SYMBOL(sst_dsp_shim_read_unlocked);
+void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) +{
- sst->ops->write64(sst->addr.shim, offset, value);
+} +EXPORT_SYMBOL(sst_dsp_shim_write64_unlocked);
+u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) +{
- return sst->ops->read64(sst->addr.shim, offset);
+} +EXPORT_SYMBOL(sst_dsp_shim_read64_unlocked);
+int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
u32 mask, u32 value)
+{
- unsigned long flags;
- bool change;
- u32 old, new;
- spin_lock_irqsave(&sst->spinlock, flags);
- old = sst_dsp_shim_read_unlocked(sst, offset);
- new = (old & (~mask)) | (value & mask);
- change = (old != new);
- if (change)
sst_dsp_shim_write_unlocked(sst, offset, new);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
+} +EXPORT_SYMBOL(sst_dsp_shim_update_bits);
+int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
u64 mask, u64 value)
+{
- unsigned long flags;
- bool change;
- u64 old, new;
- spin_lock_irqsave(&sst->spinlock, flags);
- old = sst_dsp_shim_read64_unlocked(sst, offset);
- new = (old & (~mask)) | (value & mask);
- change = (old != new);
- if (change)
sst_dsp_shim_write64_unlocked(sst, offset, new);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
+} +EXPORT_SYMBOL(sst_dsp_shim_update_bits64);
+int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
u32 mask, u32 value)
+{
- bool change;
- unsigned int old, new;
- u32 ret;
- ret = sst_dsp_shim_read_unlocked(sst, offset);
- old = ret;
- new = (old & (~mask)) | (value & mask);
- change = (old != new);
- if (change)
sst_dsp_shim_write_unlocked(sst, offset, new);
- return change;
+} +EXPORT_SYMBOL(sst_dsp_shim_update_bits_unlocked);
+int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
u64 mask, u64 value)
+{
- bool change;
- u64 old, new;
- old = sst_dsp_shim_read64_unlocked(sst, offset);
- new = (old & (~mask)) | (value & mask);
- change = (old != new);
- if (change)
sst_dsp_shim_write64_unlocked(sst, offset, new);
- return change;
+} +EXPORT_SYMBOL(sst_dsp_shim_update_bits64_unlocked);
The locked versions can be simplified by calling the unlocked function.
+void sst_dsp_dump(struct sst_dsp *sst) +{
- sst->ops->dump(sst);
+} +EXPORT_SYMBOL(sst_dsp_dump);
+void sst_dsp_reset(struct sst_dsp *sst) +{
- sst->ops->reset(sst);
+} +EXPORT_SYMBOL(sst_dsp_reset);
+int sst_dsp_boot(struct sst_dsp *sst) +{
- sst->ops->boot(sst);
- return 0;
+} +EXPORT_SYMBOL(sst_dsp_boot);
+void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) +{
- sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
- trace_sst_ipc_msg_tx(msg);
The trace isn't defined yet in the first patch, so the build fails here. Each commit should be at least compilable.
+} +EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
+u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp) +{
- u32 msg;
- msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
- trace_sst_ipc_msg_rx(msg);
- return msg;
+} +EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
+void sst_dsp_write(struct sst_dsp *sst, void *src, u32 dest_offset,
- size_t bytes)
+{
- sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes);
+} +EXPORT_SYMBOL(sst_dsp_write);
+void sst_dsp_read(struct sst_dsp *sst, void *dest, u32 src_offset,
- size_t bytes)
+{
- sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes);
+} +EXPORT_SYMBOL(sst_dsp_read);
+int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
- u32 outbox_offset, size_t outbox_size)
+{
- sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
- sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
- sst->mailbox.in_size = inbox_size;
- sst->mailbox.out_size = outbox_size;
- return 0;
+} +EXPORT_SYMBOL(sst_dsp_mailbox_init);
+void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes) +{
- int i;
- trace_sst_ipc_outbox_write(bytes);
- memcpy_toio(sst->mailbox.out_base, message, bytes);
- for (i = 0; i < bytes; i += 4)
trace_sst_ipc_outbox_wdata(i, *(uint32_t *)(message + i));
Mix of different types... Use u32.
+} +EXPORT_SYMBOL(sst_dsp_outbox_write);
+void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes) +{
- int i;
- trace_sst_ipc_outbox_read(bytes);
- memcpy_fromio(message, sst->mailbox.out_base, bytes);
- for (i = 0; i < bytes; i += 4)
trace_sst_ipc_outbox_rdata(i, *(uint32_t *)(message + i));
+} +EXPORT_SYMBOL(sst_dsp_outbox_read);
+void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes) +{
- int i;
- trace_sst_ipc_inbox_write(bytes);
- memcpy_toio(sst->mailbox.in_base, message, bytes);
- for (i = 0; i < bytes; i += 4)
trace_sst_ipc_inbox_wdata(i, *(uint32_t *)(message + i));
+} +EXPORT_SYMBOL(sst_dsp_inbox_write);
+void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) +{
- int i;
- trace_sst_ipc_inbox_read(bytes);
- memcpy_fromio(message, sst->mailbox.in_base, bytes);
- for (i = 0; i < bytes; i += 4)
trace_sst_ipc_inbox_rdata(i, *(uint32_t *)(message + i));
+} +EXPORT_SYMBOL(sst_dsp_inbox_read);
+void *sst_dsp_get_thread_context(struct sst_dsp *sst) +{
- return sst->thread_context;
+} +EXPORT_SYMBOL(sst_dsp_get_thread_context);
Hrm, an exported function call just for this is an overkill. Better to use an inline function. I guess inline functions would be good for other simple calls, too, unless the code size significantly grows.
Takashi
+struct sst_dsp *sst_dsp_new(struct device *dev,
- struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
+{
- struct sst_dsp *sst;
- int err;
- dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
- sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
- if (sst == NULL)
return NULL;
- spin_lock_init(&sst->spinlock);
- mutex_init(&sst->mutex);
- sst->dev = dev;
- sst->thread_context = sst_dev->thread_context;
- sst->sst_dev = sst_dev;
- sst->id = pdata->id;
- sst->irq = pdata->irq;
- sst->ops = sst_dev->ops;
- sst->pdata = pdata;
- INIT_LIST_HEAD(&sst->used_block_list);
- INIT_LIST_HEAD(&sst->free_block_list);
- INIT_LIST_HEAD(&sst->module_list);
- INIT_LIST_HEAD(&sst->fw_list);
- /* Initialise SST Audio DSP */
- if (sst->ops->init) {
err = sst->ops->init(sst, pdata);
if (err < 0)
return NULL;
- }
- /* Register the ISR */
- err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
- if (err)
goto irq_err;
- /* Register the FW loader DMA controller if we have one */
- if (pdata->dma_engine)
err = sst_dma_new(sst);
- if (err)
goto dma_err;
- return sst;
+dma_err:
- free_irq(sst->irq, sst);
+irq_err:
- if (sst->ops->free)
sst->ops->free(sst);
- return NULL;
+} +EXPORT_SYMBOL(sst_dsp_new);
+void sst_dsp_free(struct sst_dsp *sst) +{
- if (sst->pdata->dma_engine)
sst_dma_free(sst->dma);
- free_irq(sst->irq, sst);
- if (sst->ops->free)
sst->ops->free(sst);
+} +EXPORT_SYMBOL(sst_dsp_free);
+/* Module information */ +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("Intel SST Core"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h new file mode 100644 index 0000000..8ea3cbe --- /dev/null +++ b/sound/soc/intel/sst-dsp.h @@ -0,0 +1,226 @@ +/*
- Intel Smart Sound Technology (SST) Core
- Copyright (C) 2013, Intel Corporation. All rights reserved.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License version
- 2 as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- */
+#ifndef __SOUND_SOC_SST_DSP_H +#define __SOUND_SOC_SST_DSP_H
+#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h>
+/* SST Device IDs */ +#define SST_DEV_ID_LYNX_POINT 0x33C8 +#define SST_DEV_ID_WILDCAT_POINT 0x3438
+/* Supported SST DMA Devices */ +#define SST_DMA_TYPE_DW 1 +#define SST_DMA_TYPE_MID 2
+/* SST Shim register map
- The register naming can differ between products. Some products also
- contain extra functionality.
- */
+#define SST_CSR 0x00 +#define SST_PISR 0x08 +#define SST_PIMR 0x10 +#define SST_ISRX 0x18 +#define SST_ISRD 0x20 +#define SST_IMRX 0x28 +#define SST_IMRD 0x30 +#define SST_IPCX 0x38 /* IPC IA -> SST */ +#define SST_IPCD 0x40 /* IPC SST -> IA */ +#define SST_ISRSC 0x48 +#define SST_ISRLPESC 0x50 +#define SST_IMRSC 0x58 +#define SST_IMRLPESC 0x60 +#define SST_IPCSC 0x68 +#define SST_IPCLPESC 0x70 +#define SST_CLKCTL 0x78 +#define SST_CSR2 0x80 +#define SST_LTRC 0xE0 +#define SST_HDMC 0xE8 +#define SST_DBGO 0xF0
+#define SST_SHIM_SIZE 0x100 +#define SST_PWMCTRL 0x1000
+/* SST Shim Register bits
- The register bit naming can differ between products. Some products also
- contain extra functionality.
- */
+/* CSR / CS */ +#define SST_CSR_RST (0x1 << 1) +#define SST_CSR_SBCS0 (0x1 << 2) +#define SST_CSR_SBCS1 (0x1 << 3) +#define SST_CSR_DCS(x) (x << 4) +#define SST_CSR_DCS_MASK (0x7 << 4) +#define SST_CSR_STALL (0x1 << 10) +#define SST_CSR_S0IOCS (0x1 << 21) +#define SST_CSR_S1IOCS (0x1 << 23) +#define SST_CSR_LPCS (0x1 << 31)
+/* ISRX / ISC */ +#define SST_ISRX_BUSY (0x1 << 1) +#define SST_ISRX_DONE (0x1 << 0)
+/* ISRD / ISD */ +#define SST_ISRD_BUSY (0x1 << 1) +#define SST_ISRD_DONE (0x1 << 0)
+/* IMRX / IMC */ +#define SST_IMRX_BUSY (0x1 << 1) +#define SST_IMRX_DONE (0x1 << 0)
+/* IPCX / IPCC */ +#define SST_IPCX_DONE (0x1 << 30) +#define SST_IPCX_BUSY (0x1 << 31)
+/* IPCD */ +#define SST_IPCD_DONE (0x1 << 30) +#define SST_IPCD_BUSY (0x1 << 31)
+/* CLKCTL */ +#define SST_CLKCTL_SMOS(x) (x << 24) +#define SST_CLKCTL_MASK (3 << 24) +#define SST_CLKCTL_DCPLCG (1 << 18) +#define SST_CLKCTL_SCOE1 (1 << 17) +#define SST_CLKCTL_SCOE0 (1 << 16)
+/* CSR2 / CS2 */ +#define SST_CSR2_SDFD_SSP0 (1 << 1) +#define SST_CSR2_SDFD_SSP1 (1 << 2)
+/* LTRC */ +#define SST_LTRC_VAL(x) (x << 0)
+/* HDMC */ +#define SST_HDMC_HDDA0(x) (x << 0) +#define SST_HDMC_HDDA1(x) (x << 7)
+/* SST Vendor Defined Registers and bits */ +#define SST_VDRTCTL0 0xa0 +#define SST_VDRTCTL1 0xa4 +#define SST_VDRTCTL2 0xa8 +#define SST_VDRTCTL3 0xaC
+/* VDRTCTL0 */ +#define SST_VDRTCL0_DSRAMPGE_SHIFT 16 +#define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT) +#define SST_VDRTCL0_ISRAMPGE_SHIFT 6 +#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
+struct sst_dsp;
+/*
- SST Device.
- This structure is populated by the SST core driver.
- */
+struct sst_dsp_device {
- /* Mandatory fields */
- struct sst_ops *ops;
- irqreturn_t (*thread)(int irq, void *context);
- void *thread_context;
+};
+/*
- SST Platform Data.
- */
+struct sst_pdata {
- /* ACPI data */
- u32 lpe_base;
- u32 lpe_size;
- u32 pcicfg_base;
- u32 pcicfg_size;
- int irq;
- /* Firmware */
- const char *fw_filename;
- u32 fw_base;
- u32 fw_size;
- /* DMA */
- u32 dma_base;
- u32 dma_size;
- int dma_engine;
- /* DSP */
- u32 id;
- void *dsp;
+};
+/* Initialization */ +struct sst_dsp *sst_dsp_new(struct device *dev,
- struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
+void sst_dsp_free(struct sst_dsp *sst);
+/* SHIM Read / Write */ +void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); +u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
u32 mask, u32 value);
+void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value); +u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
u64 mask, u64 value);
+/* SHIM Read / Write Unlocked for callers already holding sst lock */ +void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value); +u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
u32 mask, u32 value);
+void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value); +u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
u64 mask, u64 value);
+/* Size optimised DRAM/IRAM memcpy */ +void sst_dsp_write(struct sst_dsp *sst, void *src, u32 dest_offset,
- size_t bytes);
+void sst_dsp_bzero(struct sst_dsp *sst, u32 src_offset, size_t bytes); +void sst_dsp_read(struct sst_dsp *sst, void *dest, u32 src_offset,
- size_t bytes);
+/* DSP reset & boot */ +void sst_dsp_reset(struct sst_dsp *sst); +int sst_dsp_boot(struct sst_dsp *sst);
+/* Msg IO */ +void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); +u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp); +void *sst_dsp_get_thread_context(struct sst_dsp *sst);
+/* Mailbox management */ +int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset,
- size_t inbox_size, u32 outbox_offset, size_t outbox_size);
+void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes);
+/* DMA FW maangement */ +int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t src_addr,
- dma_addr_t dstn_addr, size_t size);
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id); +void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
+/* Debug */ +void sst_dsp_dump(struct sst_dsp *sst);
+#endif
1.8.3.2
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Fri, 2014-02-14 at 09:29 +0100, Takashi Iwai wrote:
At Thu, 13 Feb 2014 19:15:26 +0000, Liam Girdwood wrote:
+/* Public API */ +void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) +{
- unsigned long flags;
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
+} +EXPORT_SYMBOL(sst_dsp_shim_write);
Any reason not to use *_GPL() version?
No that should be GPL. Will fix.
+int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
u64 mask, u64 value)
+{
- bool change;
- u64 old, new;
- old = sst_dsp_shim_read64_unlocked(sst, offset);
- new = (old & (~mask)) | (value & mask);
- change = (old != new);
- if (change)
sst_dsp_shim_write64_unlocked(sst, offset, new);
- return change;
+} +EXPORT_SYMBOL(sst_dsp_shim_update_bits64_unlocked);
The locked versions can be simplified by calling the unlocked function.
True, will change for V2
+void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) +{
- sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
- trace_sst_ipc_msg_tx(msg);
The trace isn't defined yet in the first patch, so the build fails here. Each commit should be at least compilable.
It is build-able for bisect etc, the Make/Kconfig patch is last in this series after the trace patch.
Liam
participants (4)
-
Jarkko Nikula
-
Liam Girdwood
-
Liam Girdwood
-
Takashi Iwai