All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver.
@ 2014-02-13 19:15 Liam Girdwood
  2014-02-13 19:15 ` [PATCH 2/5] ASoC: Intel: Add common SST driver loader on ACPI systems Liam Girdwood
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: Liam Girdwood @ 2014-02-13 19:15 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, alsa-devel, Benson Leung

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

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/5] ASoC: Intel: Add common SST driver loader on ACPI systems
  2014-02-13 19:15 [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Liam Girdwood
@ 2014-02-13 19:15 ` Liam Girdwood
  2014-02-14 10:30   ` Jarkko Nikula
  2014-02-13 19:15 ` [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader Liam Girdwood
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Liam Girdwood @ 2014-02-13 19:15 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, alsa-devel, Jarkko Nikula, Benson Leung

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");
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader.
  2014-02-13 19:15 [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Liam Girdwood
  2014-02-13 19:15 ` [PATCH 2/5] ASoC: Intel: Add common SST driver loader on ACPI systems Liam Girdwood
@ 2014-02-13 19:15 ` Liam Girdwood
  2014-02-14  8:55   ` Takashi Iwai
  2014-02-13 19:15 ` [PATCH 4/5] ASoC: Intel: Add trace support for generic SST IPC messages Liam Girdwood
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Liam Girdwood @ 2014-02-13 19:15 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, alsa-devel, Benson Leung

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);
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/5] ASoC: Intel: Add trace support for generic SST IPC messages.
  2014-02-13 19:15 [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Liam Girdwood
  2014-02-13 19:15 ` [PATCH 2/5] ASoC: Intel: Add common SST driver loader on ACPI systems Liam Girdwood
  2014-02-13 19:15 ` [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader Liam Girdwood
@ 2014-02-13 19:15 ` Liam Girdwood
  2014-02-13 19:15 ` [PATCH 5/5] ASoC: Intel: Add build support for Intel SST DSP core Liam Girdwood
  2014-02-14  8:29 ` [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Takashi Iwai
  4 siblings, 0 replies; 15+ messages in thread
From: Liam Girdwood @ 2014-02-13 19:15 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, alsa-devel, Benson Leung

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

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 5/5] ASoC: Intel: Add build support for Intel SST DSP core.
  2014-02-13 19:15 [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Liam Girdwood
                   ` (2 preceding siblings ...)
  2014-02-13 19:15 ` [PATCH 4/5] ASoC: Intel: Add trace support for generic SST IPC messages Liam Girdwood
@ 2014-02-13 19:15 ` Liam Girdwood
  2014-02-14  8:56   ` Takashi Iwai
  2014-02-14  8:29 ` [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Takashi Iwai
  4 siblings, 1 reply; 15+ messages in thread
From: Liam Girdwood @ 2014-02-13 19:15 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, alsa-devel, Benson Leung

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

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver.
  2014-02-13 19:15 [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Liam Girdwood
                   ` (3 preceding siblings ...)
  2014-02-13 19:15 ` [PATCH 5/5] ASoC: Intel: Add build support for Intel SST DSP core Liam Girdwood
@ 2014-02-14  8:29 ` Takashi Iwai
  2014-02-14 10:02   ` Liam Girdwood
  4 siblings, 1 reply; 15+ messages in thread
From: Takashi Iwai @ 2014-02-14  8:29 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, Mark Brown, Benson Leung

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
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader.
  2014-02-13 19:15 ` [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader Liam Girdwood
@ 2014-02-14  8:55   ` Takashi Iwai
  2014-02-14  9:38     ` Liam Girdwood
  0 siblings, 1 reply; 15+ messages in thread
From: Takashi Iwai @ 2014-02-14  8:55 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, Mark Brown, Benson Leung

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
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 5/5] ASoC: Intel: Add build support for Intel SST DSP core.
  2014-02-13 19:15 ` [PATCH 5/5] ASoC: Intel: Add build support for Intel SST DSP core Liam Girdwood
@ 2014-02-14  8:56   ` Takashi Iwai
  2014-02-14  9:40     ` Liam Girdwood
  0 siblings, 1 reply; 15+ messages in thread
From: Takashi Iwai @ 2014-02-14  8:56 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, Mark Brown, Benson Leung

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
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader.
  2014-02-14  8:55   ` Takashi Iwai
@ 2014-02-14  9:38     ` Liam Girdwood
  2014-02-14 16:11       ` Liam Girdwood
  0 siblings, 1 reply; 15+ messages in thread
From: Liam Girdwood @ 2014-02-14  9:38 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Mark Brown, Benson Leung

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 5/5] ASoC: Intel: Add build support for Intel SST DSP core.
  2014-02-14  8:56   ` Takashi Iwai
@ 2014-02-14  9:40     ` Liam Girdwood
  0 siblings, 0 replies; 15+ messages in thread
From: Liam Girdwood @ 2014-02-14  9:40 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Mark Brown, Benson Leung

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver.
  2014-02-14  8:29 ` [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Takashi Iwai
@ 2014-02-14 10:02   ` Liam Girdwood
  0 siblings, 0 replies; 15+ messages in thread
From: Liam Girdwood @ 2014-02-14 10:02 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Mark Brown, Benson Leung

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/5] ASoC: Intel: Add common SST driver loader on ACPI systems
  2014-02-13 19:15 ` [PATCH 2/5] ASoC: Intel: Add common SST driver loader on ACPI systems Liam Girdwood
@ 2014-02-14 10:30   ` Jarkko Nikula
  0 siblings, 0 replies; 15+ messages in thread
From: Jarkko Nikula @ 2014-02-14 10:30 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, Mark Brown, Benson Leung

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/

-- 
Jarkko

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader.
  2014-02-14  9:38     ` Liam Girdwood
@ 2014-02-14 16:11       ` Liam Girdwood
  2014-02-14 17:06         ` Takashi Iwai
  0 siblings, 1 reply; 15+ messages in thread
From: Liam Girdwood @ 2014-02-14 16:11 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Mark Brown, Benson Leung

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.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader.
  2014-02-14 16:11       ` Liam Girdwood
@ 2014-02-14 17:06         ` Takashi Iwai
  2014-02-14 17:40           ` Liam Girdwood
  0 siblings, 1 reply; 15+ messages in thread
From: Takashi Iwai @ 2014-02-14 17:06 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, Mark Brown, Benson Leung

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader.
  2014-02-14 17:06         ` Takashi Iwai
@ 2014-02-14 17:40           ` Liam Girdwood
  0 siblings, 0 replies; 15+ messages in thread
From: Liam Girdwood @ 2014-02-14 17:40 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Mark Brown, Benson Leung

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 

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2014-02-14 17:40 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-13 19:15 [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Liam Girdwood
2014-02-13 19:15 ` [PATCH 2/5] ASoC: Intel: Add common SST driver loader on ACPI systems Liam Girdwood
2014-02-14 10:30   ` Jarkko Nikula
2014-02-13 19:15 ` [PATCH 3/5] ASoC: Intel: Add Intel SST audio DSP Firmware loader Liam Girdwood
2014-02-14  8:55   ` Takashi Iwai
2014-02-14  9:38     ` Liam Girdwood
2014-02-14 16:11       ` Liam Girdwood
2014-02-14 17:06         ` Takashi Iwai
2014-02-14 17:40           ` Liam Girdwood
2014-02-13 19:15 ` [PATCH 4/5] ASoC: Intel: Add trace support for generic SST IPC messages Liam Girdwood
2014-02-13 19:15 ` [PATCH 5/5] ASoC: Intel: Add build support for Intel SST DSP core Liam Girdwood
2014-02-14  8:56   ` Takashi Iwai
2014-02-14  9:40     ` Liam Girdwood
2014-02-14  8:29 ` [PATCH 1/5] ASoC: Intel: Add Intel SST audio DSP low level shim driver Takashi Iwai
2014-02-14 10:02   ` Liam Girdwood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.