diff options
author | Liam Girdwood <liam.r.girdwood@linux.intel.com> | 2018-07-09 11:09:54 +0100 |
---|---|---|
committer | Liam Girdwood <liam.r.girdwood@linux.intel.com> | 2018-07-09 11:09:54 +0100 |
commit | 9b685810e82dddf05915b50f5d38088a243fe13d (patch) | |
tree | bb6d3e036d7818595f386777297dde85a6b95b1d | |
parent | dcc8b997601eb210989f7cec96d6608f0ab32838 (diff) | |
download | asoc-topic/sof-upstream-core.tar.gz |
[FOR FIX] Rebase WiPtopic/sof-upstream-core
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
-rw-r--r-- | include/sound/sof.h | 10 | ||||
-rw-r--r-- | include/uapi/sound/sof-ipc.h | 4 | ||||
-rw-r--r-- | include/uapi/sound/sof-topology.h | 3 | ||||
-rw-r--r-- | sound/soc/sof/Kconfig | 30 | ||||
-rw-r--r-- | sound/soc/sof/Makefile | 20 | ||||
-rw-r--r-- | sound/soc/sof/control.c | 54 | ||||
-rw-r--r-- | sound/soc/sof/core.c | 76 | ||||
-rw-r--r-- | sound/soc/sof/debug.c | 12 | ||||
-rw-r--r-- | sound/soc/sof/ipc.c | 360 | ||||
-rw-r--r-- | sound/soc/sof/ops.c | 22 | ||||
-rw-r--r-- | sound/soc/sof/ops.h | 32 | ||||
-rw-r--r-- | sound/soc/sof/pcm.c | 35 | ||||
-rw-r--r-- | sound/soc/sof/sof-priv.h | 92 | ||||
-rw-r--r-- | sound/soc/sof/topology.c | 335 | ||||
-rw-r--r-- | sound/soc/sof/trace.c | 6 | ||||
-rw-r--r-- | sound/soc/sof/utils.c | 57 |
16 files changed, 708 insertions, 440 deletions
diff --git a/include/sound/sof.h b/include/sound/sof.h index 7767ec8ce188d6..e522e13e7a18d7 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,7 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/pci.h> +#include <sound/soc.h> #include <sound/soc-acpi.h> #include <uapi/sound/sof-ipc.h> @@ -73,4 +74,13 @@ struct sof_dev_desc { const char *nocodec_tplg_filename; }; +int sof_nocodec_setup(struct device *dev, + struct snd_sof_pdata *sof_pdata, + struct snd_soc_acpi_mach *mach, + const struct sof_dev_desc *desc, + struct snd_sof_dsp_ops *ops); + +int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, + struct snd_soc_dai_link *links, int link_num, + struct snd_soc_card *card); #endif diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 7bd9d64a9c50b1..e1df5c0a86a162 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -99,7 +99,7 @@ #define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) /* Get message component id */ -#define SOF_IPC_MESSAGE_ID(x) (x & 0xffff) +#define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff) /* maximum message size for mailbox Tx/Rx */ #define SOF_IPC_MSG_MAX_SIZE 128 @@ -209,7 +209,7 @@ enum sof_ipc_dai_type { /* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ struct sof_ipc_dai_ssp_params { uint16_t mode; // FIXME: do we need this? - uint16_t clk_id; // FIXME: do we need this? + uint16_t mclk_id; uint32_t mclk_rate; /* mclk frequency in Hz */ uint32_t fsync_rate; /* fsync frequency in Hz */ diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 1a4957b7fd049d..096aaed4333059 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -37,7 +37,6 @@ #define SOF_TKN_DAI_DMAC_CONFIG 153 #define SOF_TKN_DAI_TYPE 154 #define SOF_TKN_DAI_INDEX 155 -#define SOF_TKN_DAI_SAMPLE_BITS 156 /* scheduling */ #define SOF_TKN_SCHED_DEADLINE 200 @@ -68,6 +67,8 @@ #define SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE 500 #define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 #define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 +#define SOF_TKN_INTEL_SSP_MCLK_ID 503 +#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 504 /* DMIC */ #define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 0e2a262c4daa9a..20ff0389c180b4 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -7,11 +7,9 @@ config SND_SOC_SOF_ACPI config SND_SOC_SOF_PLATFORM tristate - config SND_SOC_SOF tristate "Sound Open Firmware Support" default m - depends on SND_DMA_SGBUF select SND_SOC_TOPOLOGY select SND_SOC_COMPRESS help @@ -19,27 +17,21 @@ config SND_SOC_SOF Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_NOCODEC - tristate "SOF nocodec mode Support" +config SND_SOC_SOF_DEBUG + bool "SOF debugging features" depends on SND_SOC_SOF help - This adds support for a dummy/nocodec machine driver fallback - option if no known codec is detected. This is typically only - enabled for developers or devices where the sound card is - controlled externally - Say Y if you need this nocodec fallback option + This option can be used to enable or disable individual SOF firmware + and driver debugging options. + Say Y if you are debugging SOF FW or drivers. If unsure select "N". -config SND_SOC_SOF_FORCE_NOCODEC_MODE - tristate "SOF force nocodec Mode" - depends on SND_SOC_SOF_NOCODEC +config SND_SOC_SOF_DEBUG_XRUN_STOP + bool "SOF stop on XRUN" + depends on SND_SOC_SOF_DEBUG help - This forces SOF to use dummy/nocodec as machine driver, even - though there is a codec detected on the real platform. This is - typically only enabled for developers for debug purposes, before - codec/machine driver is ready, or to exclude the impact of those - drivers - Say Y if you need this force nocodec mode option + This option forces PCMs to stop on any XRUN event. This is useful to + preserve any trace data ond pipeline status prior to the XRUN. + Say Y if you are debugging SOF FW pipeline XRUNs. If unsure select "N". -source "sound/soc/sof/intel/Kconfig" diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index ece93000a1974d..aad44d2f669756 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,22 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o compressed.o -snd-sof-spi-objs := hw-spi.o - -snd-sof-pci-objs := sof-pci-dev.o -snd-sof-acpi-objs := sof-acpi-dev.o -snd-sof-platform-objs := sof-platform-dev.o -snd-sof-nocodec-objs := nocodec.o +snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o debug.o topology.o\ + control.o trace.o obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o -obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o - -obj-$(CONFIG_SND_SOC_SOF_ACPI) += sof-acpi-dev.o -obj-$(CONFIG_SND_SOC_SOF_PCI) += sof-pci-dev.o -obj-$(CONFIG_SND_SOC_SOF_SPI) += sof-spi-dev.o -obj-$(SND_SOC_SOF_PLATFORM) += sof-platform-dev.o - -obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o - -obj-$(CONFIG_SND_SOC_SOF_INTEL) += intel/ diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 1c88596e70ba3a..e65340a8f704d9 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -20,57 +20,19 @@ #include <uapi/sound/sof-ipc.h> #include "sof-priv.h" -/* simple volume table TODO: to be replaced by coefficients from topology */ -/* -52 dB to +12 dB in 2 dB steps, 33 values */ -static const u32 volume_map[] = { - 165, - 207, - 261, - 328, - 414, - 521, - 655, - 825, - 1039, - 1308, - 1646, - 2072, - 2609, - 3285, - 4135, - 5206, - 6554, - 8250, - 10387, - 13076, - 16462, - 20724, - 26090, - 32846, - 41350, - 52057, - 65536, /* 0 dB for Qx.16 gain value */ - 82505, - 103868, - 130762, - 164619, - 207243, - 260904 -}; - -static inline u32 mixer_to_ipc(unsigned int value) +static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) { - if (value >= ARRAY_SIZE(volume_map)) - return volume_map[0]; + if (value >= size) + return volume_map[size - 1]; else return volume_map[value]; } -static inline u32 ipc_to_mixer(u32 value) +static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) { int i; - for (i = 0; i < ARRAY_SIZE(volume_map); i++) { + for (i = 0; i < size; i++) { if (volume_map[i] >= value) return i; } @@ -98,7 +60,8 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, /* read back each channel */ for (i = 0; i < channels; i++) ucontrol->value.integer.value[i] = - ipc_to_mixer(cdata->chanv[i].value); + ipc_to_mixer(cdata->chanv[i].value, + scontrol->volume_table, sm->max + 1); pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); @@ -120,7 +83,8 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, /* update each channel */ for (i = 0; i < channels; i++) { cdata->chanv[i].value = - mixer_to_ipc(ucontrol->value.integer.value[i]); + mixer_to_ipc(ucontrol->value.integer.value[i], + scontrol->volume_table, sm->max + 1); cdata->chanv[i].channel = i; } diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 3bbcc34a72bbdd..5b178b62972736 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -8,6 +8,7 @@ * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> */ +#include <linux/mm.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> @@ -33,7 +34,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm = NULL; list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (spcm->pcm.dai_id == rtd->dai_link->id) + if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) return spcm; } @@ -86,7 +87,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm = NULL; list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (spcm->pcm.pcm_id == pcm_id) + if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id) return spcm; } @@ -122,6 +123,11 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, return NULL; } +static inline unsigned int sof_get_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + /* * FW Panic/fault handling. */ @@ -145,62 +151,6 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_IDLE, "can't enter idle"}, }; -/* only need xtensa atm */ -static void sof_arch_dsp_oops(struct snd_sof_dev *sdev, void *oops) -{ - struct sof_ipc_dsp_oops_xtensa *xoops = oops; - - dev_err(sdev->dev, "error: DSP Firmware Oops\n"); - dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", - xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); - dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", - xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); - dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", - xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); - dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", - xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); - dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", - xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); -} - -static void sof_dsp_dump_stack(struct snd_sof_dev *sdev, void *oops, - u32 *stack, u32 stack_words) -{ - struct sof_ipc_dsp_oops_xtensa *xoops = oops; - u32 stack_ptr = xoops->stack; - int i; - - dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); - - for (i = 0; i <= stack_words - 4; i += 4) { - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", - stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], - stack[i + 3]); - } - - /* deal with any remaining words */ - switch (stack_words - i) { - case 0: - break; - case 1: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", - stack_ptr + stack_words - 1, stack[stack_words - 1]); - break; - case 2: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", - stack_ptr + stack_words - 2, stack[stack_words - 2], - stack[stack_words - 1]); - break; - case 3: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", - stack_ptr + stack_words - 3, stack[stack_words - 3], - stack[stack_words - 2], stack[stack_words - 1]); - break; - default: - break; - } -} - int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, u32 tracep_code, void *oops, void *stack, size_t stack_words) @@ -232,8 +182,8 @@ int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); out: - sof_arch_dsp_oops(sdev, oops); - sof_dsp_dump_stack(sdev, oops, stack, stack_words); + sof_oops(sdev, oops); + sof_stack(sdev, oops, stack, stack_words); return -EFAULT; } EXPORT_SYMBOL(snd_sof_get_status); @@ -407,6 +357,12 @@ static int sof_remove(struct platform_device *pdev) return 0; } +void snd_sof_shutdown(struct device *dev) +{ +} +EXPORT_SYMBOL(snd_sof_shutdown); + + static struct platform_driver sof_driver = { .driver = { .name = "sof-audio", diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 6f9abbd240d9a9..bee43dbd7e640a 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -35,7 +35,7 @@ static int sof_dfsentry_open(struct inode *inode, struct file *file) static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dfsentry_io *dfse = file->private_data; struct snd_sof_dev *sdev = dfse->sdev; int size; u32 *buf; @@ -86,7 +86,7 @@ int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, void __iomem *base, size_t size, const char *name) { - struct snd_sof_dfsentry *dfse; + struct snd_sof_dfsentry_io *dfse; if (!sdev) return -EINVAL; @@ -102,8 +102,7 @@ int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, dfse->dfsentry = debugfs_create_file(name, 0444, sdev->debugfs_root, dfse, &sof_dfs_fops); if (!dfse->dfsentry) { - dev_err(sdev->dev, "error: cannot create debugfs entry %s\n", - name); + dev_err(sdev->dev, "cannot create debugfs entry.\n"); kfree(dfse); return -ENODEV; } @@ -129,11 +128,12 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) for (i = 0; i < ops->debug_map_count; i++) { map = &ops->debug_map[i]; - err = snd_sof_debugfs_create_item(sdev, sdev->bar[map->bar] + + err = snd_sof_debugfs_create_item(sdev, + sdev->bar[map->bar] + map->offset, map->size, map->name); if (err < 0) - dev_err(sdev->dev, "error: cannot create debugfs for %s\n", + dev_err(sdev->dev, "cannot create debugfs for %s\n", map->name); } diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index bd646c23cf9fd2..a08529a111c7e3 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -6,6 +6,9 @@ * Copyright(c) 2017 Intel Corporation. All rights reserved. * * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> + * + * Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided + * by platform driver code. */ #include <linux/types.h> @@ -31,11 +34,21 @@ #include "sof-priv.h" #include "ops.h" -/* IPC message timeout (msecs) */ +/* + * IPC message default size and timeout (msecs). + * TODO: allow platforms to set size and timeout. + */ #define IPC_TIMEOUT_MSECS 300 #define IPC_EMPTY_LIST_SIZE 8 +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id); +static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); + +/* + * IPC message Tx/Rx message handling. + */ + /* SOF generic IPC data */ struct snd_sof_ipc { struct snd_sof_dev *sdev; @@ -59,6 +72,7 @@ static struct snd_sof_ipc_msg *msg_get_empty(struct snd_sof_ipc *ipc) { struct snd_sof_ipc_msg *msg = NULL; + /* get first empty message in the list */ if (!list_empty(&ipc->empty_list)) { msg = list_first_entry(&ipc->empty_list, struct snd_sof_ipc_msg, list); @@ -68,6 +82,7 @@ static struct snd_sof_ipc_msg *msg_get_empty(struct snd_sof_ipc *ipc) return msg; } +/* wait for IPC message reply */ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, void *reply_data) { @@ -111,6 +126,7 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, return ret; } +/* send IPC message from host to DSP */ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes) @@ -121,6 +137,7 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, spin_lock_irqsave(&sdev->ipc_lock, flags); + /* get an empty message */ msg = msg_get_empty(ipc); if (!msg) { spin_unlock_irqrestore(&sdev->ipc_lock, flags); @@ -132,9 +149,11 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, msg->reply_size = reply_bytes; msg->complete = false; + /* attach any data */ if (msg_bytes) memcpy(msg->msg_data, msg_data, msg_bytes); + /* add message to transmit list */ list_add_tail(&msg->list, &ipc->tx_list); /* schedule the messgae if not busy */ @@ -143,10 +162,12 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, spin_unlock_irqrestore(&sdev->ipc_lock, flags); + /* now wait for completion */ return tx_wait_done(ipc, msg, reply_data); } EXPORT_SYMBOL(sof_ipc_tx_message); +/* send next IPC message in list */ static void ipc_tx_next_msg(struct work_struct *work) { struct snd_sof_ipc *ipc = @@ -156,13 +177,15 @@ static void ipc_tx_next_msg(struct work_struct *work) spin_lock_irq(&sdev->ipc_lock); + /* send message if HW read and message in TX list */ if (list_empty(&ipc->tx_list)) goto out; + /* sned first message in TX list */ msg = list_first_entry(&ipc->tx_list, struct snd_sof_ipc_msg, list); list_move(&msg->list, &ipc->reply_list); - snd_sof_dsp_send_msg(sdev, msg); + dev_dbg(sdev->dev, "ipc: send 0x%x\n", msg->header); out: @@ -200,6 +223,7 @@ void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, wake_up(&msg->waitq); } +/* drop all IPC messages in preparation for DSP stall/reset */ void sof_ipc_drop_all(struct snd_sof_ipc *ipc) { struct snd_sof_dev *sdev = ipc->sdev; @@ -223,6 +247,7 @@ void sof_ipc_drop_all(struct snd_sof_ipc *ipc) } EXPORT_SYMBOL(sof_ipc_drop_all); +/* handle reply message from DSP */ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) { struct snd_sof_ipc_msg *msg; @@ -239,17 +264,105 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); -int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, - size_t dspbox_size, u32 hostbox, - size_t hostbox_size) +/* DSP firmware has sent host a message */ +static void ipc_msgs_rx(struct work_struct *work) { - sdev->dsp_box.offset = dspbox; - sdev->dsp_box.size = dspbox_size; - sdev->host_box.offset = hostbox; - sdev->host_box.size = hostbox_size; - return 0; + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, rx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr hdr; + u32 cmd, type; + int err = -EINVAL; + + /* read back header */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &hdr, sizeof(hdr)); + + cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + type = hdr.cmd & SOF_CMD_TYPE_MASK; + + /* check message type */ + switch (cmd) { + case SOF_IPC_GLB_REPLY: + dev_err(sdev->dev, "error: ipc reply unknown\n"); + break; + case SOF_IPC_FW_READY: + /* check for FW boot completion */ + if (!sdev->boot_complete) { + if (sdev->ops->fw_ready) + err = sdev->ops->fw_ready(sdev, cmd); + if (err < 0) { + dev_err(sdev->dev, "DSP firmware boot timeout %d\n", + err); + } else { + /* firmware boot completed OK */ + sdev->boot_complete = true; + dev_dbg(sdev->dev, "booting DSP firmware completed\n"); + wake_up(&sdev->boot_wait); + } + } + break; + case SOF_IPC_GLB_COMPOUND: + case SOF_IPC_GLB_TPLG_MSG: + case SOF_IPC_GLB_PM_MSG: + case SOF_IPC_GLB_COMP_MSG: + break; + case SOF_IPC_GLB_STREAM_MSG: + /* need to pass msg id into the function */ + ipc_stream_message(sdev, hdr.cmd); + break; + case SOF_IPC_GLB_TRACE_MSG: + ipc_trace_message(sdev, type); + break; + default: + dev_err(sdev->dev, "unknown DSP message 0x%x\n", cmd); + break; + } + + dev_dbg(sdev->dev, "ipc rx: 0x%x done\n", hdr.cmd); + + /* tell DSP we are done */ + snd_sof_dsp_cmd_done(sdev); } -EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); + +/* schedule work to transmit any IPC in queue */ +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->tx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_tx); + +/* schedule work to handle IPC from DSP */ +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); + +/* + * IPC trace mechanism. + */ + +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_dma_trace_posn posn; + + switch (msg_id) { + case SOF_IPC_TRACE_DMA_POSITION: + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + snd_sof_trace_update_pos(sdev, &posn); + break; + default: + dev_err(sdev->dev, "error: unhandled trace message %x\n", + msg_id); + break; + } +} + +/* + * IPC stream position. + */ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) { @@ -293,6 +406,7 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) snd_pcm_period_elapsed(spcm->stream[direction].substream); } +/* DSP notifies host of an XRUN within FW */ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) { struct sof_ipc_stream_posn posn; @@ -300,7 +414,7 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) u32 posn_offset; int direction; - /* check if we have stream box */ + /* check if we have stream MMIO on this platform */ if (sdev->stream_box.size == 0) { /* read back full message */ snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, @@ -330,12 +444,14 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", posn.host_posn, posn.xrun_comp_id, posn.xrun_size); - return; /* TODO: don't do anything yet until preload is working */ - +#if defined(CONFIG_SOC_SOF_DEBUG_XRUN_STOP) + /* stop PCM on XRUN - used for pipeline debug */ memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); snd_pcm_stop_xrun(spcm->stream[direction].substream); +#endif } +/* stream notifications from DSP FW */ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) { /* get msg cmd type and msd id */ @@ -356,148 +472,7 @@ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) } } -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) -{ - struct sof_ipc_dma_trace_posn posn; - - switch (msg_id) { - case SOF_IPC_TRACE_DMA_POSITION: - /* read back full message */ - snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, - sizeof(posn)); - snd_sof_trace_update_pos(sdev, &posn); - break; - default: - dev_err(sdev->dev, "error: unhandled trace message %x\n", - msg_id); - break; - } -} - -/* DSP firmware has sent host a message */ -static void ipc_msgs_rx(struct work_struct *work) -{ - struct snd_sof_ipc *ipc = - container_of(work, struct snd_sof_ipc, rx_kwork); - struct snd_sof_dev *sdev = ipc->sdev; - struct sof_ipc_hdr hdr; - u32 cmd, type; - int err = -EINVAL; - - /* read back header */ - snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &hdr, sizeof(hdr)); - - cmd = hdr.cmd & SOF_GLB_TYPE_MASK; - type = hdr.cmd & SOF_CMD_TYPE_MASK; - - switch (cmd) { - case SOF_IPC_GLB_REPLY: - dev_err(sdev->dev, "error: ipc reply unknown\n"); - break; - case SOF_IPC_FW_READY: - /* check for FW boot completion */ - if (!sdev->boot_complete) { - if (sdev->ops->fw_ready) - err = sdev->ops->fw_ready(sdev, cmd); - if (err < 0) { - dev_err(sdev->dev, "DSP firmware boot timeout %d\n", - err); - } else { - /* firmware boot completed OK */ - sdev->boot_complete = true; - dev_dbg(sdev->dev, "booting DSP firmware completed\n"); - wake_up(&sdev->boot_wait); - } - } - break; - case SOF_IPC_GLB_COMPOUND: - case SOF_IPC_GLB_TPLG_MSG: - case SOF_IPC_GLB_PM_MSG: - case SOF_IPC_GLB_COMP_MSG: - break; - case SOF_IPC_GLB_STREAM_MSG: - /* need to pass msg id into the function */ - ipc_stream_message(sdev, hdr.cmd); - break; - case SOF_IPC_GLB_TRACE_MSG: - ipc_trace_message(sdev, type); - break; - default: - dev_err(sdev->dev, "unknown DSP message 0x%x\n", cmd); - break; - } - - dev_dbg(sdev->dev, "ipc rx: 0x%x done\n", hdr.cmd); - - snd_sof_dsp_cmd_done(sdev); -} - -void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev) -{ - schedule_work(&sdev->ipc->tx_kwork); -} -EXPORT_SYMBOL(snd_sof_ipc_msgs_tx); - -void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) -{ - schedule_work(&sdev->ipc->rx_kwork); -} -EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); - -struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc *ipc; - struct snd_sof_ipc_msg *msg; - int i; - - ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); - if (!ipc) - return NULL; - - INIT_LIST_HEAD(&ipc->tx_list); - INIT_LIST_HEAD(&ipc->reply_list); - INIT_LIST_HEAD(&ipc->empty_list); - init_waitqueue_head(&ipc->wait_txq); - INIT_WORK(&ipc->tx_kwork, ipc_tx_next_msg); - INIT_WORK(&ipc->rx_kwork, ipc_msgs_rx); - ipc->sdev = sdev; - - /* pre-allocate messages */ - dev_dbg(sdev->dev, "pre-allocate %d IPC messages\n", - IPC_EMPTY_LIST_SIZE); - msg = devm_kzalloc(sdev->dev, sizeof(struct snd_sof_ipc_msg) * - IPC_EMPTY_LIST_SIZE, GFP_KERNEL); - if (!msg) - return NULL; - - /* pre-allocate message data */ - for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { - msg->msg_data = devm_kzalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); - if (!msg->msg_data) - return NULL; - - msg->reply_data = devm_kzalloc(sdev->dev, PAGE_SIZE, - GFP_KERNEL); - if (!msg->reply_data) - return NULL; - - init_waitqueue_head(&msg->waitq); - list_add(&msg->list, &ipc->empty_list); - msg++; - } - - return ipc; -} -EXPORT_SYMBOL(snd_sof_ipc_init); - -void snd_sof_ipc_free(struct snd_sof_dev *sdev) -{ - /* TODO: send IPC to prepare DSP for shutdown */ - cancel_work_sync(&sdev->ipc->tx_kwork); - cancel_work_sync(&sdev->ipc->rx_kwork); -} -EXPORT_SYMBOL(snd_sof_ipc_free); - +/* get stream position IPC - use faster MMIO method if available on platform */ int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int direction, struct sof_ipc_stream_posn *posn) @@ -524,6 +499,10 @@ int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(snd_sof_ipc_stream_posn); +/* + * IPC get()/set() for kcontrols. + */ + int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, struct snd_sof_control *scontrol, u32 ipc_cmd, enum sof_ipc_ctrl_type ctrl_type, @@ -607,3 +586,72 @@ int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, return 0; } EXPORT_SYMBOL(snd_sof_ipc_get_comp_data); + +/* + * IPC layer enumeration. + */ + +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size) +{ + sdev->dsp_box.offset = dspbox; + sdev->dsp_box.size = dspbox_size; + sdev->host_box.offset = hostbox; + sdev->host_box.size = hostbox_size; + return 0; +} +EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); + +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc *ipc; + struct snd_sof_ipc_msg *msg; + int i; + + ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return NULL; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->reply_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + INIT_WORK(&ipc->tx_kwork, ipc_tx_next_msg); + INIT_WORK(&ipc->rx_kwork, ipc_msgs_rx); + ipc->sdev = sdev; + + /* pre-allocate messages */ + dev_dbg(sdev->dev, "pre-allocate %d IPC messages\n", + IPC_EMPTY_LIST_SIZE); + msg = devm_kzalloc(sdev->dev, sizeof(struct snd_sof_ipc_msg) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (!msg) + return NULL; + + /* pre-allocate message data */ + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + msg->msg_data = devm_kzalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); + if (!msg->msg_data) + return NULL; + + msg->reply_data = devm_kzalloc(sdev->dev, PAGE_SIZE, + GFP_KERNEL); + if (!msg->reply_data) + return NULL; + + init_waitqueue_head(&msg->waitq); + list_add(&msg->list, &ipc->empty_list); + msg++; + } + + return ipc; +} +EXPORT_SYMBOL(snd_sof_ipc_init); + +void snd_sof_ipc_free(struct snd_sof_dev *sdev) +{ + cancel_work_sync(&sdev->ipc->tx_kwork); + cancel_work_sync(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_free); diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 9ac69e83a584a3..0668f64e1ef672 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -23,7 +23,7 @@ int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, { bool change; unsigned int old, new; - u32 ret; + u32 ret = ~0; /* explicit init to remove uninitialized use warnings */ pci_read_config_dword(sdev->pci, offset, &ret); dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", @@ -188,3 +188,23 @@ int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, return ret; } EXPORT_SYMBOL(snd_sof_dsp_register_poll); + +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) +{ + dev_err(sdev->dev, "error : DSP panic!\n"); + + /* check if DSP is not ready and did not set the dsp_oops_offset. + * if the dsp_oops_offset is not set, set it from the panic message. + * Also add a check to memory window setting with panic message. + */ + if (!sdev->dsp_oops_offset) + sdev->dsp_oops_offset = offset; + else + dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", + sdev->dsp_oops_offset, offset); + + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_trace_notify_for_error(sdev); + snd_sof_dsp_cmd_done(sdev); +} +EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index e49ac67becfae3..828a49f7bb2c60 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -220,8 +220,9 @@ static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) } /* host PCM ops */ -static inline int snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream) +static inline int +snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) { if (sdev->ops && sdev->ops->pcm_open) return sdev->ops->pcm_open(sdev, substream); @@ -229,9 +230,10 @@ static inline int snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, return 0; } - /* disconnect pcm substream to a host stream */ -static inline int snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream) +/* disconnect pcm substream to a host stream */ +static inline int +snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) { if (sdev->ops && sdev->ops->pcm_close) return sdev->ops->pcm_close(sdev, substream); @@ -239,10 +241,11 @@ static inline int snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, return 0; } - /* host stream hw params */ -static inline int snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +/* host stream hw params */ +static inline int +snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { if (sdev->ops && sdev->ops->pcm_hw_params) return sdev->ops->pcm_hw_params(sdev, substream, params); @@ -250,6 +253,17 @@ static inline int snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, return 0; } +/* host stream trigger */ +static inline int +snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + if (sdev->ops && sdev->ops->pcm_trigger) + return sdev->ops->pcm_trigger(sdev, substream, cmd); + else + return 0; +} + int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 63c99165ae193b..23c3a88ca592b9 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -243,6 +243,8 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return -EINVAL; } + snd_sof_pcm_platform_trigger(sdev, substream, cmd); + /* send IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, sizeof(stream), &reply, sizeof(reply)); @@ -302,10 +304,10 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) /* set any runtime constraints based on topology */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - caps->period_size_min); + le32_to_cpu(caps->period_size_min)); snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - caps->period_size_min); + le32_to_cpu(caps->period_size_min)); /* set runtime config */ runtime->hw.info = SNDRV_PCM_INFO_MMAP | @@ -314,12 +316,12 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; - runtime->hw.formats = caps->formats; - runtime->hw.period_bytes_min = caps->period_size_min; - runtime->hw.period_bytes_max = caps->period_size_max; - runtime->hw.periods_min = caps->periods_min; - runtime->hw.periods_max = caps->periods_max; - runtime->hw.buffer_bytes_max = caps->buffer_size_max; + runtime->hw.formats = le64_to_cpu(caps->formats); + runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min); + runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max); + runtime->hw.periods_min = le32_to_cpu(caps->periods_min); + runtime->hw.periods_max = le32_to_cpu(caps->periods_max); + runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max); dev_dbg(sdev->dev, "period min %zd max %zd bytes\n", runtime->hw.period_bytes_min, @@ -413,8 +415,8 @@ static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->parent, - caps->buffer_size_min, - caps->buffer_size_max); + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); if (ret) { dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", caps->buffer_size_min, caps->buffer_size_max, @@ -446,8 +448,8 @@ capture: ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->parent, - caps->buffer_size_min, - caps->buffer_size_max); + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); if (ret) { dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", caps->buffer_size_min, caps->buffer_size_max, @@ -525,7 +527,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, channels->max = 2; snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -535,13 +537,13 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, switch (dai->comp_dai.config.frame_fmt) { case SOF_IPC_FRAME_S16_LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S16_LE); break; case SOF_IPC_FRAME_S24_4LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S24_LE); break; case SOF_IPC_FRAME_S32_LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S32_LE); break; default: dev_err(sdev->dev, "No available DAI format!\n"); @@ -629,7 +631,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->probe = sof_pcm_probe; pd->remove = sof_pcm_remove; pd->ops = &sof_pcm_ops; - pd->compr_ops = &sof_compressed_ops; pd->pcm_new = sof_pcm_new; pd->pcm_free = sof_pcm_free; pd->ignore_machine = plat_data->machine->drv_name; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 8fe495182acfa1..da2daf7ad82ba0 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -57,7 +57,14 @@ struct snd_sof_ipc; struct snd_sof_debugfs_map; struct snd_soc_tplg_ops; struct snd_soc_component; +struct sof_intel_hda_dev; +struct snd_sof_pdata; +/* + * SOF DSP HW abstraction operations. + * Used to abstract DSP HW architecture and any IO busses between host CPU + * and DSP device(s). + */ struct snd_sof_dsp_ops { /* probe and remove */ int (*remove)(struct snd_sof_dev *sof_dev); @@ -95,9 +102,9 @@ struct snd_sof_dsp_ops { /* mailbox */ void (*mailbox_read)(struct snd_sof_dev *sof_dev, u32 offset, - void __iomem *addr, size_t bytes); + void *addr, size_t bytes); void (*mailbox_write)(struct snd_sof_dev *sof_dev, u32 offset, - void __iomem *addr, size_t bytes); + void *addr, size_t bytes); /* ipc */ int (*send_msg)(struct snd_sof_dev *sof_dev, @@ -124,6 +131,10 @@ struct snd_sof_dsp_ops { struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); + /* host stream trigger */ + int (*pcm_trigger)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd); + /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, const struct firmware *fw); @@ -141,21 +152,36 @@ struct snd_sof_dsp_ops { int num_drv; }; -struct snd_sof_pdata; +/* DSP architecture specific callbacks for oops and stack dumps */ +struct sof_arch_ops { + void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); + void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, + u32 *stack, u32 stack_words); +}; +/* DSP device HW descriptor mapping between bus ID and ops */ struct sof_ops_table { const struct sof_dev_desc *desc; struct snd_sof_dsp_ops *ops; struct platform_device *(*new_data)(struct snd_sof_pdata *pdata); }; -struct snd_sof_dfsentry { +/* FS entry for debug files that can expose DSP memories, registers */ +struct snd_sof_dfsentry_io { + struct dentry *dfsentry; + size_t size; + void __iomem *buf; + struct snd_sof_dev *sdev; +}; + +struct snd_sof_dfsentry_buf { struct dentry *dfsentry; size_t size; void *buf; struct snd_sof_dev *sdev; }; +/* Debug mapping for any DSP memory or registers that can used for debug */ struct snd_sof_debugfs_map { const char *name; u32 bar; @@ -163,11 +189,28 @@ struct snd_sof_debugfs_map { u32 size; }; +/* mailbox descriptor, used for host <-> DSP IPC */ struct snd_sof_mailbox { u32 offset; size_t size; }; +/* IPC message descriptor for host <-> DSP IO */ +struct snd_sof_ipc_msg { + struct list_head list; + + /* message data */ + u32 header; + void *msg_data; + void *reply_data; + size_t msg_size; + size_t reply_size; + + wait_queue_head_t waitq; + bool complete; +}; + +/* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; struct snd_dma_buffer page_table; @@ -175,6 +218,7 @@ struct snd_sof_pcm_stream { struct snd_pcm_substream *substream; }; +/* ASLA SOF PCM device */ struct snd_sof_pcm { struct snd_sof_dev *sdev; struct snd_soc_tplg_pcm pcm; @@ -184,6 +228,7 @@ struct snd_sof_pcm { struct list_head list; /* list in sdev pcm list */ }; +/* ALSA SOF Kcontrol device */ struct snd_sof_control { struct snd_sof_dev *sdev; int comp_id; @@ -198,6 +243,7 @@ struct snd_sof_control { struct list_head list; /* list in sdev control list */ }; +/* ASoC SOF DAPM widget */ struct snd_sof_widget { struct snd_sof_dev *sdev; int comp_id; @@ -209,9 +255,10 @@ struct snd_sof_widget { struct mutex mutex; struct list_head list; /* list in sdev widget list */ - void *private; /* core does not touch this */ + void *private; /* core does not touch this */ }; +/* ASoC DAI device */ struct snd_sof_dai { struct snd_sof_dev *sdev; const char *name; @@ -221,22 +268,6 @@ struct snd_sof_dai { struct list_head list; /* list in sdev dai list */ }; -struct snd_sof_ipc_msg { - struct list_head list; - - /* message data */ - u32 header; - void *msg_data; - void *reply_data; - size_t msg_size; - size_t reply_size; - - wait_queue_head_t waitq; - bool complete; -}; - -struct sof_intel_hda_dev; - /* * SOF Device Level. */ @@ -258,6 +289,7 @@ struct snd_sof_dev { struct snd_sof_pdata *pdata; const struct snd_sof_dsp_ops *ops; struct sof_intel_hda_dev *hda; /* for HDA based DSP HW */ + const struct sof_arch_ops *arch_ops; /* IPC */ struct snd_sof_ipc *ipc; @@ -371,6 +403,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, int *direction); struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, unsigned int pcm_id); +void sof_ipc_drop_all(struct snd_sof_ipc *ipc); /* * Stream IPC @@ -438,4 +471,21 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/* + * DSP Architectures. + */ +static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, + u32 stack_words) +{ + if (sdev->arch_ops->dsp_stack) + sdev->arch_ops->dsp_stack(sdev, oops, stack, stack_words); +} + +static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) +{ + if (sdev->arch_ops->dsp_oops) + sdev->arch_ops->dsp_oops(sdev, oops); +} + +extern const struct sof_arch_ops sof_xtensa_arch_ops; #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 03b054b2492612..9d5b575760f9c5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -20,15 +20,166 @@ #include <linux/string.h> #include <sound/soc-topology.h> #include <sound/soc.h> +#include <uapi/sound/tlv.h> +#include <sound/tlv.h> #include <uapi/sound/sof-ipc.h> #include <uapi/sound/sof-topology.h> #include "sof-priv.h" #define COMP_ID_UNASSIGNED 0xffffffff +/* Constants used in the computation of linear volume gain from dB gain */ +/* 20th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_TWENTIETH_ROOT_OF_TEN 73533 +/* 40th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_FORTIETH_ROOT_OF_TEN 69419 +/* Volume fractional word length */ +#define VOLUME_FWL 16 +/* 0.5 dB step value in topology TLV */ +#define VOL_HALF_DB_STEP 50 + +/* TLV data items */ +#define TLV_ITEMS 3 +#define TLV_MIN 0 +#define TLV_STEP 1 +#define TLV_MUTE 2 + +static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) +{ + /* we only support dB scale TLV type at the moment */ + if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) + return -EINVAL; -/* - * Supported DAI types and lookup, add new ones to end of list . + /* min value in topology tlv data is multiplied by 100 */ + tlv[TLV_MIN] = (int)p[SNDRV_CTL_TLVO_DB_SCALE_MIN] / 100; + + /* volume steps */ + tlv[TLV_STEP] = (int)(p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MASK); + + /* mute ON/OFF */ + if ((p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MUTE) == 0) + tlv[TLV_MUTE] = 0; + else + tlv[TLV_MUTE] = 1; + + return 0; +} + +/* Function to truncate an unsigned 64-bit number + * by x bits and return 32-bit unsigned number + * This function also takes care of rounding while truncating + */ +static inline u32 vol_shift_64(u64 i, u32 x) +{ + /* do not truncate more than 32 bits */ + if (x > 32) + x = 32; + + if (x == 0) + return (u32)i; + + return (u32)(((i >> (x - 1)) + 1) >> 1); +} + +/* Function to compute a ^ exp where, + * a is a fractional number represented by a fixed-point integer + * with a fractional world length of "fwl" + * exp is an integer + * fwl is the fractional word length + * Return value is a fractional number represented by a fixed-point + * integer with a fractional word length of "fwl" + */ +static u32 vol_pow32(u32 a, int exp, u32 fwl) +{ + int i, iter; + u32 power = 1 << fwl; + u64 numerator; + + /* if exponent is 0, return 1 */ + if (exp == 0) + return power; + + /* determine the number of iterations based on the exponent */ + if (exp < 0) + iter = exp * -1; + else + iter = exp; + + /* mutiply a "iter" times to compute power */ + for (i = 0; i < iter; i++) { + /* Product of 2 Qx.fwl fixed-point numbers yields a Q2*x.2*fwl + * Truncate product back to fwl fractional bits with rounding + */ + power = vol_shift_64((u64)power * a, fwl); + } + + if (exp > 0) { + /* if exp is positive, return the result */ + return power; + } + + /* if exp is negative, return the multiplicative inverse */ + numerator = (u64)1 << (fwl << 1); + do_div(numerator, power); + + return (u32)numerator; +} + +/* Function to calculate volume gain from TLV data + * This function can only handle gain steps that are multiples of 0.5 dB + */ +static u32 vol_compute_gain(u32 value, int *tlv) +{ + int dB_gain; + u32 linear_gain; + int f_step; + + /* mute volume */ + if (value == 0 && tlv[TLV_MUTE]) + return 0; + + /* compute dB gain from tlv + * tlv_step in topology is multiplied by 100 + */ + dB_gain = tlv[TLV_MIN] + (value * tlv[TLV_STEP]) / 100; + + /* compute linear gain + * represented by fixed-point int with VOLUME_FWL fractional bits + */ + linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN, dB_gain, VOLUME_FWL); + + /* extract the fractional part of volume step */ + f_step = tlv[TLV_STEP] - (tlv[TLV_STEP] / 100); + + /* if volume step is an odd multiple of 0.5 dB */ + if (f_step == VOL_HALF_DB_STEP && (value & 1)) + linear_gain = vol_shift_64((u64)linear_gain * + VOL_FORTIETH_ROOT_OF_TEN, + VOLUME_FWL); + + return linear_gain; +} + +/* Set up volume table for kcontrols from tlv data + * "size" specifies the number of entries in the table */ +static int set_up_volume_table(struct snd_sof_control *scontrol, + int tlv[TLV_ITEMS], int size) +{ + int j; + + /* init the volume table */ + scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); + if (!scontrol->volume_table) + return -ENOMEM; + + /* populate the volume table */ + for (j = 0; j < size ; j++) + scontrol->volume_table[j] = vol_compute_gain(j, tlv); + + return 0; +} struct sof_dai_types { const char *name; @@ -69,7 +220,7 @@ static const struct sof_frame_types sof_frames[] = { {"float", SOF_IPC_FRAME_FLOAT}, }; -static enum sof_ipc_dai_type find_format(const char *name) +static enum sof_ipc_frame find_format(const char *name) { int i; @@ -97,19 +248,20 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct sof_ipc_ctrl_data *cdata; /* validate topology data */ - if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) + if (le32_to_cpu(mc->num_channels) >= SND_SOC_TPLG_MAX_CHAN) return -EINVAL; /* init the volume get/put data */ scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - sizeof(struct sof_ipc_ctrl_value_chan) * mc->num_channels; + sizeof(struct sof_ipc_ctrl_value_chan) * + le32_to_cpu(mc->num_channels); scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); cdata = scontrol->control_data; if (!scontrol->control_data) return -ENOMEM; scontrol->comp_id = sdev->next_comp_id; - scontrol->num_channels = mc->num_channels; + scontrol->num_channels = le32_to_cpu(mc->num_channels); dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); @@ -135,7 +287,7 @@ static int get_token_u32(void *elem, void *object, u32 offset, u32 size) struct snd_soc_tplg_vendor_value_elem *velem = elem; u32 *val = object + offset; - *val = velem->value; + *val = le32_to_cpu(velem->value); return 0; } @@ -190,11 +342,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, type), 0}, }; -static const struct sof_topology_token dai_ssp_link_tokens[] = { - {SOF_TKN_DAI_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, -}; - /* scheduling */ static const struct sof_topology_token sched_tokens[] = { {SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -265,6 +412,12 @@ static const struct sof_topology_token ssp_tokens[] = { {SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, offsetof(struct sof_ipc_dai_ssp_params, fs_keep_active), 0}, + {SOF_TKN_INTEL_SSP_MCLK_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, + {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, }; /* DMIC */ @@ -348,7 +501,7 @@ static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, int i, j; /* parse element by element */ - for (i = 0; i < array->num_elems; i++) { + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { elem = &array->uuid[i]; /* search for token */ @@ -358,7 +511,7 @@ static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, continue; /* match token id */ - if (tokens[j].token != elem->token) + if (tokens[j].token != le32_to_cpu(elem->token)) continue; /* matched - now load token */ @@ -378,7 +531,7 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, int i, j; /* parse element by element */ - for (i = 0; i < array->num_elems; i++) { + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { elem = &array->string[i]; /* search for token */ @@ -388,7 +541,7 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, continue; /* match token id */ - if (tokens[j].token != elem->token) + if (tokens[j].token != le32_to_cpu(elem->token)) continue; /* matched - now load token */ @@ -412,7 +565,7 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, u32 *index = NULL; /* parse element by element */ - for (i = 0; i < array->num_elems; i++) { + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { elem = &array->value[i]; /* search for token */ @@ -423,7 +576,7 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, continue; /* match token id */ - if (tokens[j].token != elem->token) + if (tokens[j].token != le32_to_cpu(elem->token)) continue; /* pdm config array index */ @@ -436,7 +589,8 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, /* inc number of pdm array index */ if (index) - ++(*index); + (*index)++; + /* fallthrough */ case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: @@ -478,7 +632,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, int asize; while (priv_size > 0) { - asize = array->size; + asize = le32_to_cpu(array->size); /* validate asize */ if (asize < 0) { /* FIXME: A zero-size array makes no sense */ @@ -496,7 +650,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, } /* call correct parser depending on type */ - switch (array->type) { + switch (le32_to_cpu(array->type)) { case SND_SOC_TPLG_TUPLE_TYPE_UUID: sof_parse_uuid_tokens(scomp, object, tokens, count, array); @@ -555,7 +709,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, scontrol->sdev = sdev; mutex_init(&scontrol->mutex); - switch (hdr->ops.info) { + switch (le32_to_cpu(hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: @@ -678,16 +832,16 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &comp_dai, dai_tokens, ARRAY_SIZE(dai_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dai tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } ret = sof_parse_tokens(scomp, &comp_dai.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dai.cfg tokens failed %d\n", private->size); @@ -733,7 +887,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &buffer, buffer_tokens, ARRAY_SIZE(buffer_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse buffer tokens failed %d\n", private->size); @@ -774,7 +928,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &host, pcm_tokens, ARRAY_SIZE(pcm_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse host tokens failed %d\n", private->size); @@ -783,10 +937,10 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &host.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -835,7 +989,7 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, ret = sof_parse_tokens(scomp, &pipeline, sched_tokens, ARRAY_SIZE(sched_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n", private->size); @@ -875,7 +1029,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &mixer.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n", private->size); @@ -900,14 +1054,41 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_volume volume; - int ret; + struct snd_soc_dapm_widget *widget = swidget->widget; + const struct snd_kcontrol_new *kc = NULL; + struct soc_mixer_control *sm; + struct snd_sof_control *scontrol; + const unsigned int *p; + int ret, tlv[TLV_ITEMS]; - if (tw->num_kcontrols != 1) { + if (le32_to_cpu(tw->num_kcontrols) != 1) { dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", tw->num_kcontrols); return -EINVAL; } + /* set up volume gain tables for kcontrol */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + + /* get volume control */ + scontrol = sm->dobj.private; + + /* get topology tlv data */ + p = kc->tlv.p; + + /* extract tlv data */ + if (get_tlv_data(p, tlv) < 0) { + dev_err(sdev->dev, "error: invalid TLV data\n"); + return -EINVAL; + } + + /* set up volume table */ + if (set_up_volume_table(scontrol, tlv, sm->max + 1) < 0) { + dev_err(sdev->dev, "error: setting up volume table\n"); + return -ENOMEM; + } + /* configure dai IPC message */ memset(&volume, 0, sizeof(volume)); volume.comp.hdr.size = sizeof(volume); @@ -918,7 +1099,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &volume, volume_tokens, ARRAY_SIZE(volume_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse volume tokens failed %d\n", private->size); @@ -926,10 +1107,10 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, } ret = sof_parse_tokens(scomp, &volume.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -964,7 +1145,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &src, src_tokens, ARRAY_SIZE(src_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse src tokens failed %d\n", private->size); @@ -973,10 +1154,10 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &src.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1013,19 +1194,19 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &tone, tone_tokens, ARRAY_SIZE(tone_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse tone tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } ret = sof_parse_tokens(scomp, &tone.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1076,7 +1257,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, memset(&reply, 0, sizeof(reply)); dev_dbg(sdev->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", - swidget->comp_id, index, tw->id, tw->name, + swidget->comp_id, index, swidget->id, tw->name, tw->sname ? tw->sname : "none"); /* handle any special case widgets */ @@ -1139,7 +1320,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_effect: default: dev_warn(sdev->dev, "warning: widget type %d name %s not handled\n", - tw->id, tw->name); + swidget->id, tw->name); break; } @@ -1147,7 +1328,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, if (ret < 0 || reply.rhdr.error < 0) { dev_err(sdev->dev, "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n", - tw->shift, tw->id, tw->name, + tw->shift, swidget->id, tw->name, tw->sname ? tw->sname : "none", reply.rhdr.error); return ret; } @@ -1302,39 +1483,29 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, memset(&config->ssp, 0, sizeof(struct sof_ipc_dai_ssp_params)); config->hdr.size = size; - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, &config->ssp, dai_ssp_link_tokens, - ARRAY_SIZE(dai_ssp_link_tokens), - private->array, private->size); - if (ret != 0) { - dev_err(sdev->dev, "error: parse ssp link tokens failed %d\n", - private->size); - return ret; - } - - ret = sof_parse_tokens(scomp, config, ssp_tokens, + ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens, ARRAY_SIZE(ssp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse ssp tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } - config->ssp.mclk_rate = hw_config->mclk_rate; - config->ssp.bclk_rate = hw_config->bclk_rate; - config->ssp.fsync_rate = hw_config->fsync_rate; - config->ssp.tdm_slots = hw_config->tdm_slots; - config->ssp.tdm_slot_width = hw_config->tdm_slot_width; + config->ssp.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->ssp.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->ssp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->ssp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->ssp.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); config->ssp.mclk_direction = hw_config->mclk_direction; - config->ssp.rx_slots = hw_config->rx_slots; - config->ssp.tx_slots = hw_config->tx_slots; + config->ssp.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->ssp.tx_slots = le32_to_cpu(hw_config->tx_slots); - dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d\n", + dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d\n", config->id, config->format, config->ssp.mclk_rate, config->ssp.bclk_rate, config->ssp.fsync_rate, config->ssp.sample_valid_bits, - config->ssp.tdm_slot_width, config->ssp.tdm_slots); + config->ssp.tdm_slot_width, config->ssp.tdm_slots, config->ssp.mclk_id); /* send message to DSP */ ret = sof_ipc_tx_message(sdev->ipc, @@ -1366,10 +1537,10 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, /* get DMIC tokens */ ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens, ARRAY_SIZE(dmic_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dmic tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1398,10 +1569,10 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, /* get DMIC PDM tokens */ ret = sof_parse_tokens(scomp, &ipc_config->dmic.pdm[0], dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dmic pdm tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); kfree(ipc_config); return ret; } @@ -1474,10 +1645,10 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index, /* get any bespoke DAI tokens */ ret = sof_parse_tokens(scomp, config, hda_tokens, ARRAY_SIZE(hda_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse hda tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1516,14 +1687,14 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, return 0; /* only support 1 config atm */ - if (cfg->num_hw_configs != 1) { + if (le32_to_cpu(cfg->num_hw_configs) != 1) { dev_err(sdev->dev, "error: unexpected DAI config count %d\n", - cfg->num_hw_configs); + le32_to_cpu(cfg->num_hw_configs)); return -EINVAL; } /* check we have some tokens - we need at least DAI type */ - if (private->size == 0) { + if (le32_to_cpu(private->size) == 0) { dev_err(sdev->dev, "error: expected tokens for DAI, none found\n"); return -EINVAL; } @@ -1533,10 +1704,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, /* get any common DAI tokens */ ret = sof_parse_tokens(scomp, &config, dai_link_tokens, ARRAY_SIZE(dai_link_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse link tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1544,8 +1715,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, hw_config = &cfg->hw_config[0]; config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; - config.id = hw_config->id; - config.format = hw_config->fmt; + config.id = le32_to_cpu(hw_config->id); + config.format = le32_to_cpu(hw_config->fmt); /* now load DAI specific data and send IPC - type comes from token */ switch (config.type) { @@ -1703,7 +1874,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, static int sof_route_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - /* TODO: unload routes when yopology is changed */ + /* TODO: unload routes when topology is changed */ return 0; } diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 5991967d8099d6..a27edb9f4a7ea9 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -68,7 +68,7 @@ out: static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dfsentry_buf *dfse = file->private_data; struct snd_sof_dev *sdev = dfse->sdev; unsigned long rem; loff_t lpos = *ppos; @@ -118,7 +118,7 @@ static const struct file_operations sof_dfs_trace_fops = { static int trace_debugfs_create(struct snd_sof_dev *sdev) { - struct snd_sof_dfsentry *dfse; + struct snd_sof_dfsentry_buf *dfse; if (!sdev) return -EINVAL; @@ -131,7 +131,7 @@ static int trace_debugfs_create(struct snd_sof_dev *sdev) dfse->size = sdev->dmatb.bytes; dfse->sdev = sdev; - dfse->dfsentry = debugfs_create_file("trace", 0644, sdev->debugfs_root, + dfse->dfsentry = debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, &sof_dfs_trace_fops); if (!dfse->dfsentry) { dev_err(sdev->dev, diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c new file mode 100644 index 00000000000000..9636ed48d1644c --- /dev/null +++ b/sound/soc/sof/utils.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Keyon Jie <yang.jie@linux.intel.com> + */ + +#include <linux/device.h> +#include <sound/soc.h> +#include <sound/sof.h> +#include "sof-priv.h" + +int sof_bes_setup(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops, + struct snd_soc_dai_link *links, int link_num, + struct snd_soc_card *card) +{ + char name[32]; + int i; + + if (!ops || !links || !card) { + dev_err(sdev->dev, "error: no link, card or ops for BEs\n"); + return -EINVAL; + } + + /* set up BE dai_links */ + for (i = 0; i < link_num; i++) { + snprintf(name, 32, "NoCodec-%d", i); + links[i].name = kmemdup(name, sizeof(name), GFP_KERNEL); + if (!links[i].name) + goto no_mem; + + links[i].id = i; + links[i].no_pcm = 1; + links[i].cpu_dai_name = ops->dai_drv->drv[i].name; + links[i].platform_name = "sof-audio"; + links[i].codec_dai_name = "snd-soc-dummy-dai"; + links[i].codec_name = "snd-soc-dummy"; + links[i].dpcm_playback = 1; + links[i].dpcm_capture = 1; + } + + card->dai_link = links; + card->num_links = link_num; + + return 0; +no_mem: + /* free allocated memories and return error */ + for (; i > 0; i--) + kfree(links[i - 1].name); + + return -ENOMEM; +} +EXPORT_SYMBOL(sof_bes_setup); + |