diff options
| author | Mark Brown <broonie@kernel.org> | 2015-04-12 14:48:33 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2015-04-12 14:48:33 -0400 |
| commit | aab0bb17ef2440ef5f68dcde5f1a6b32dcdfa9fb (patch) | |
| tree | 6e14e997a1424e189d47ec04f4b1e1b8e6d85491 /sound/soc/intel/common | |
| parent | 77b62fa5d23988155132cf7fee44f2c209e3dc4c (diff) | |
| parent | a5e5e12bd4ed5cd1123ace4300b5c07230fbf21e (diff) | |
Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
Diffstat (limited to 'sound/soc/intel/common')
| -rw-r--r-- | sound/soc/intel/common/Makefile | 7 | ||||
| -rw-r--r-- | sound/soc/intel/common/sst-acpi.c | 286 | ||||
| -rw-r--r-- | sound/soc/intel/common/sst-dsp-priv.h | 373 | ||||
| -rw-r--r-- | sound/soc/intel/common/sst-dsp.c | 420 | ||||
| -rw-r--r-- | sound/soc/intel/common/sst-dsp.h | 285 | ||||
| -rw-r--r-- | sound/soc/intel/common/sst-firmware.c | 1205 | ||||
| -rw-r--r-- | sound/soc/intel/common/sst-ipc.c | 294 | ||||
| -rw-r--r-- | sound/soc/intel/common/sst-ipc.h | 91 |
8 files changed, 2961 insertions, 0 deletions
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile new file mode 100644 index 000000000000..f24154ca4e98 --- /dev/null +++ b/sound/soc/intel/common/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o | ||
| 2 | snd-soc-sst-acpi-objs := sst-acpi.o | ||
| 3 | snd-soc-sst-ipc-objs := sst-ipc.o | ||
| 4 | |||
| 5 | obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o | ||
| 6 | obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o | ||
| 7 | |||
diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c new file mode 100644 index 000000000000..42f293f9c6e2 --- /dev/null +++ b/sound/soc/intel/common/sst-acpi.c | |||
| @@ -0,0 +1,286 @@ | |||
| 1 | /* | ||
| 2 | * Intel SST loader on ACPI systems | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013, Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License version | ||
| 8 | * 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/acpi.h> | ||
| 18 | #include <linux/device.h> | ||
| 19 | #include <linux/firmware.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | |||
| 23 | #include "sst-dsp.h" | ||
| 24 | |||
| 25 | #define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 | ||
| 26 | #define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 | ||
| 27 | #define SST_LPT_DSP_DMA_SIZE (1024 - 1) | ||
| 28 | |||
| 29 | /* Descriptor for SST ASoC machine driver */ | ||
| 30 | struct sst_acpi_mach { | ||
| 31 | /* ACPI ID for the matching machine driver. Audio codec for instance */ | ||
| 32 | const u8 id[ACPI_ID_LEN]; | ||
| 33 | /* machine driver name */ | ||
| 34 | const char *drv_name; | ||
| 35 | /* firmware file name */ | ||
| 36 | const char *fw_filename; | ||
| 37 | }; | ||
| 38 | |||
| 39 | /* Descriptor for setting up SST platform data */ | ||
| 40 | struct sst_acpi_desc { | ||
| 41 | const char *drv_name; | ||
| 42 | struct sst_acpi_mach *machines; | ||
| 43 | /* Platform resource indexes. Must set to -1 if not used */ | ||
| 44 | int resindex_lpe_base; | ||
| 45 | int resindex_pcicfg_base; | ||
| 46 | int resindex_fw_base; | ||
| 47 | int irqindex_host_ipc; | ||
| 48 | int resindex_dma_base; | ||
| 49 | /* Unique number identifying the SST core on platform */ | ||
| 50 | int sst_id; | ||
| 51 | /* DMA only valid when resindex_dma_base != -1*/ | ||
| 52 | int dma_engine; | ||
| 53 | int dma_size; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct sst_acpi_priv { | ||
| 57 | struct platform_device *pdev_mach; | ||
| 58 | struct platform_device *pdev_pcm; | ||
| 59 | struct sst_pdata sst_pdata; | ||
| 60 | struct sst_acpi_desc *desc; | ||
| 61 | struct sst_acpi_mach *mach; | ||
| 62 | }; | ||
| 63 | |||
| 64 | static void sst_acpi_fw_cb(const struct firmware *fw, void *context) | ||
| 65 | { | ||
| 66 | struct platform_device *pdev = context; | ||
| 67 | struct device *dev = &pdev->dev; | ||
| 68 | struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); | ||
| 69 | struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; | ||
| 70 | struct sst_acpi_desc *desc = sst_acpi->desc; | ||
| 71 | struct sst_acpi_mach *mach = sst_acpi->mach; | ||
| 72 | |||
| 73 | sst_pdata->fw = fw; | ||
| 74 | if (!fw) { | ||
| 75 | dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename); | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* register PCM and DAI driver */ | ||
| 80 | sst_acpi->pdev_pcm = | ||
| 81 | platform_device_register_data(dev, desc->drv_name, -1, | ||
| 82 | sst_pdata, sizeof(*sst_pdata)); | ||
| 83 | if (IS_ERR(sst_acpi->pdev_pcm)) { | ||
| 84 | dev_err(dev, "Cannot register device %s. Error %d\n", | ||
| 85 | desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm)); | ||
| 86 | } | ||
| 87 | |||
| 88 | return; | ||
| 89 | } | ||
| 90 | |||
| 91 | static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, | ||
| 92 | void *context, void **ret) | ||
| 93 | { | ||
| 94 | *(bool *)context = true; | ||
| 95 | return AE_OK; | ||
| 96 | } | ||
| 97 | |||
| 98 | static struct sst_acpi_mach *sst_acpi_find_machine( | ||
| 99 | struct sst_acpi_mach *machines) | ||
| 100 | { | ||
| 101 | struct sst_acpi_mach *mach; | ||
| 102 | bool found = false; | ||
| 103 | |||
| 104 | for (mach = machines; mach->id[0]; mach++) | ||
| 105 | if (ACPI_SUCCESS(acpi_get_devices(mach->id, | ||
| 106 | sst_acpi_mach_match, | ||
| 107 | &found, NULL)) && found) | ||
| 108 | return mach; | ||
| 109 | |||
| 110 | return NULL; | ||
| 111 | } | ||
| 112 | |||
| 113 | static int sst_acpi_probe(struct platform_device *pdev) | ||
| 114 | { | ||
| 115 | const struct acpi_device_id *id; | ||
| 116 | struct device *dev = &pdev->dev; | ||
| 117 | struct sst_acpi_priv *sst_acpi; | ||
| 118 | struct sst_pdata *sst_pdata; | ||
| 119 | struct sst_acpi_mach *mach; | ||
| 120 | struct sst_acpi_desc *desc; | ||
| 121 | struct resource *mmio; | ||
| 122 | int ret = 0; | ||
| 123 | |||
| 124 | sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL); | ||
| 125 | if (sst_acpi == NULL) | ||
| 126 | return -ENOMEM; | ||
| 127 | |||
| 128 | id = acpi_match_device(dev->driver->acpi_match_table, dev); | ||
| 129 | if (!id) | ||
| 130 | return -ENODEV; | ||
| 131 | |||
| 132 | desc = (struct sst_acpi_desc *)id->driver_data; | ||
| 133 | mach = sst_acpi_find_machine(desc->machines); | ||
| 134 | if (mach == NULL) { | ||
| 135 | dev_err(dev, "No matching ASoC machine driver found\n"); | ||
| 136 | return -ENODEV; | ||
| 137 | } | ||
| 138 | |||
| 139 | sst_pdata = &sst_acpi->sst_pdata; | ||
| 140 | sst_pdata->id = desc->sst_id; | ||
| 141 | sst_pdata->dma_dev = dev; | ||
| 142 | sst_acpi->desc = desc; | ||
| 143 | sst_acpi->mach = mach; | ||
| 144 | |||
| 145 | sst_pdata->resindex_dma_base = desc->resindex_dma_base; | ||
| 146 | if (desc->resindex_dma_base >= 0) { | ||
| 147 | sst_pdata->dma_engine = desc->dma_engine; | ||
| 148 | sst_pdata->dma_base = desc->resindex_dma_base; | ||
| 149 | sst_pdata->dma_size = desc->dma_size; | ||
| 150 | } | ||
| 151 | |||
| 152 | if (desc->irqindex_host_ipc >= 0) | ||
| 153 | sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc); | ||
| 154 | |||
| 155 | if (desc->resindex_lpe_base >= 0) { | ||
| 156 | mmio = platform_get_resource(pdev, IORESOURCE_MEM, | ||
| 157 | desc->resindex_lpe_base); | ||
| 158 | if (mmio) { | ||
| 159 | sst_pdata->lpe_base = mmio->start; | ||
| 160 | sst_pdata->lpe_size = resource_size(mmio); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | if (desc->resindex_pcicfg_base >= 0) { | ||
| 165 | mmio = platform_get_resource(pdev, IORESOURCE_MEM, | ||
| 166 | desc->resindex_pcicfg_base); | ||
| 167 | if (mmio) { | ||
| 168 | sst_pdata->pcicfg_base = mmio->start; | ||
| 169 | sst_pdata->pcicfg_size = resource_size(mmio); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | if (desc->resindex_fw_base >= 0) { | ||
| 174 | mmio = platform_get_resource(pdev, IORESOURCE_MEM, | ||
| 175 | desc->resindex_fw_base); | ||
| 176 | if (mmio) { | ||
| 177 | sst_pdata->fw_base = mmio->start; | ||
| 178 | sst_pdata->fw_size = resource_size(mmio); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | platform_set_drvdata(pdev, sst_acpi); | ||
| 183 | |||
| 184 | /* register machine driver */ | ||
| 185 | sst_acpi->pdev_mach = | ||
| 186 | platform_device_register_data(dev, mach->drv_name, -1, | ||
| 187 | sst_pdata, sizeof(*sst_pdata)); | ||
| 188 | if (IS_ERR(sst_acpi->pdev_mach)) | ||
| 189 | return PTR_ERR(sst_acpi->pdev_mach); | ||
| 190 | |||
| 191 | /* continue SST probing after firmware is loaded */ | ||
| 192 | ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename, | ||
| 193 | dev, GFP_KERNEL, pdev, sst_acpi_fw_cb); | ||
| 194 | if (ret) | ||
| 195 | platform_device_unregister(sst_acpi->pdev_mach); | ||
| 196 | |||
| 197 | return ret; | ||
| 198 | } | ||
| 199 | |||
| 200 | static int sst_acpi_remove(struct platform_device *pdev) | ||
| 201 | { | ||
| 202 | struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); | ||
| 203 | struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; | ||
| 204 | |||
| 205 | platform_device_unregister(sst_acpi->pdev_mach); | ||
| 206 | if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm)) | ||
| 207 | platform_device_unregister(sst_acpi->pdev_pcm); | ||
| 208 | release_firmware(sst_pdata->fw); | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | static struct sst_acpi_mach haswell_machines[] = { | ||
| 214 | { "INT33CA", "haswell-audio", "intel/IntcSST1.bin" }, | ||
| 215 | {} | ||
| 216 | }; | ||
| 217 | |||
| 218 | static struct sst_acpi_desc sst_acpi_haswell_desc = { | ||
| 219 | .drv_name = "haswell-pcm-audio", | ||
| 220 | .machines = haswell_machines, | ||
| 221 | .resindex_lpe_base = 0, | ||
| 222 | .resindex_pcicfg_base = 1, | ||
| 223 | .resindex_fw_base = -1, | ||
| 224 | .irqindex_host_ipc = 0, | ||
| 225 | .sst_id = SST_DEV_ID_LYNX_POINT, | ||
| 226 | .dma_engine = SST_DMA_TYPE_DW, | ||
| 227 | .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET, | ||
| 228 | .dma_size = SST_LPT_DSP_DMA_SIZE, | ||
| 229 | }; | ||
| 230 | |||
| 231 | static struct sst_acpi_mach broadwell_machines[] = { | ||
| 232 | { "INT343A", "broadwell-audio", "intel/IntcSST2.bin" }, | ||
| 233 | {} | ||
| 234 | }; | ||
| 235 | |||
| 236 | static struct sst_acpi_desc sst_acpi_broadwell_desc = { | ||
| 237 | .drv_name = "haswell-pcm-audio", | ||
| 238 | .machines = broadwell_machines, | ||
| 239 | .resindex_lpe_base = 0, | ||
| 240 | .resindex_pcicfg_base = 1, | ||
| 241 | .resindex_fw_base = -1, | ||
| 242 | .irqindex_host_ipc = 0, | ||
| 243 | .sst_id = SST_DEV_ID_WILDCAT_POINT, | ||
| 244 | .dma_engine = SST_DMA_TYPE_DW, | ||
| 245 | .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET, | ||
| 246 | .dma_size = SST_LPT_DSP_DMA_SIZE, | ||
| 247 | }; | ||
| 248 | |||
| 249 | static struct sst_acpi_mach baytrail_machines[] = { | ||
| 250 | { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, | ||
| 251 | { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, | ||
| 252 | {} | ||
| 253 | }; | ||
| 254 | |||
| 255 | static struct sst_acpi_desc sst_acpi_baytrail_desc = { | ||
| 256 | .drv_name = "baytrail-pcm-audio", | ||
| 257 | .machines = baytrail_machines, | ||
| 258 | .resindex_lpe_base = 0, | ||
| 259 | .resindex_pcicfg_base = 1, | ||
| 260 | .resindex_fw_base = 2, | ||
| 261 | .irqindex_host_ipc = 5, | ||
| 262 | .sst_id = SST_DEV_ID_BYT, | ||
| 263 | .resindex_dma_base = -1, | ||
| 264 | }; | ||
| 265 | |||
| 266 | static struct acpi_device_id sst_acpi_match[] = { | ||
| 267 | { "INT33C8", (unsigned long)&sst_acpi_haswell_desc }, | ||
| 268 | { "INT3438", (unsigned long)&sst_acpi_broadwell_desc }, | ||
| 269 | { "80860F28", (unsigned long)&sst_acpi_baytrail_desc }, | ||
| 270 | { } | ||
| 271 | }; | ||
| 272 | MODULE_DEVICE_TABLE(acpi, sst_acpi_match); | ||
| 273 | |||
| 274 | static struct platform_driver sst_acpi_driver = { | ||
| 275 | .probe = sst_acpi_probe, | ||
| 276 | .remove = sst_acpi_remove, | ||
| 277 | .driver = { | ||
| 278 | .name = "sst-acpi", | ||
| 279 | .acpi_match_table = ACPI_PTR(sst_acpi_match), | ||
| 280 | }, | ||
| 281 | }; | ||
| 282 | module_platform_driver(sst_acpi_driver); | ||
| 283 | |||
| 284 | MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>"); | ||
| 285 | MODULE_DESCRIPTION("Intel SST loader on ACPI systems"); | ||
| 286 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h new file mode 100644 index 000000000000..396d54510350 --- /dev/null +++ b/sound/soc/intel/common/sst-dsp-priv.h | |||
| @@ -0,0 +1,373 @@ | |||
| 1 | /* | ||
| 2 | * Intel Smart Sound Technology | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013, Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License version | ||
| 8 | * 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef __SOUND_SOC_SST_DSP_PRIV_H | ||
| 18 | #define __SOUND_SOC_SST_DSP_PRIV_H | ||
| 19 | |||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/types.h> | ||
| 22 | #include <linux/interrupt.h> | ||
| 23 | #include <linux/firmware.h> | ||
| 24 | |||
| 25 | struct sst_mem_block; | ||
| 26 | struct sst_module; | ||
| 27 | struct sst_fw; | ||
| 28 | |||
| 29 | /* do we need to remove or keep */ | ||
| 30 | #define DSP_DRAM_ADDR_OFFSET 0x400000 | ||
| 31 | |||
| 32 | /* | ||
| 33 | * DSP Operations exported by platform Audio DSP driver. | ||
| 34 | */ | ||
| 35 | struct sst_ops { | ||
| 36 | /* DSP core boot / reset */ | ||
| 37 | void (*boot)(struct sst_dsp *); | ||
| 38 | void (*reset)(struct sst_dsp *); | ||
| 39 | int (*wake)(struct sst_dsp *); | ||
| 40 | void (*sleep)(struct sst_dsp *); | ||
| 41 | void (*stall)(struct sst_dsp *); | ||
| 42 | |||
| 43 | /* Shim IO */ | ||
| 44 | void (*write)(void __iomem *addr, u32 offset, u32 value); | ||
| 45 | u32 (*read)(void __iomem *addr, u32 offset); | ||
| 46 | void (*write64)(void __iomem *addr, u32 offset, u64 value); | ||
| 47 | u64 (*read64)(void __iomem *addr, u32 offset); | ||
| 48 | |||
| 49 | /* DSP I/DRAM IO */ | ||
| 50 | void (*ram_read)(struct sst_dsp *sst, void *dest, void __iomem *src, | ||
| 51 | size_t bytes); | ||
| 52 | void (*ram_write)(struct sst_dsp *sst, void __iomem *dest, void *src, | ||
| 53 | size_t bytes); | ||
| 54 | |||
| 55 | void (*dump)(struct sst_dsp *); | ||
| 56 | |||
| 57 | /* IRQ handlers */ | ||
| 58 | irqreturn_t (*irq_handler)(int irq, void *context); | ||
| 59 | |||
| 60 | /* SST init and free */ | ||
| 61 | int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata); | ||
| 62 | void (*free)(struct sst_dsp *sst); | ||
| 63 | |||
| 64 | /* FW module parser/loader */ | ||
| 65 | int (*parse_fw)(struct sst_fw *sst_fw); | ||
| 66 | }; | ||
| 67 | |||
| 68 | /* | ||
| 69 | * Audio DSP memory offsets and addresses. | ||
| 70 | */ | ||
| 71 | struct sst_addr { | ||
| 72 | u32 lpe_base; | ||
| 73 | u32 shim_offset; | ||
| 74 | u32 iram_offset; | ||
| 75 | u32 dram_offset; | ||
| 76 | u32 dsp_iram_offset; | ||
| 77 | u32 dsp_dram_offset; | ||
| 78 | void __iomem *lpe; | ||
| 79 | void __iomem *shim; | ||
| 80 | void __iomem *pci_cfg; | ||
| 81 | void __iomem *fw_ext; | ||
| 82 | }; | ||
| 83 | |||
| 84 | /* | ||
| 85 | * Audio DSP Mailbox configuration. | ||
| 86 | */ | ||
| 87 | struct sst_mailbox { | ||
| 88 | void __iomem *in_base; | ||
| 89 | void __iomem *out_base; | ||
| 90 | size_t in_size; | ||
| 91 | size_t out_size; | ||
| 92 | }; | ||
| 93 | |||
| 94 | /* | ||
| 95 | * Audio DSP memory block types. | ||
| 96 | */ | ||
| 97 | enum sst_mem_type { | ||
| 98 | SST_MEM_IRAM = 0, | ||
| 99 | SST_MEM_DRAM = 1, | ||
| 100 | SST_MEM_ANY = 2, | ||
| 101 | SST_MEM_CACHE= 3, | ||
| 102 | }; | ||
| 103 | |||
| 104 | /* | ||
| 105 | * Audio DSP Generic Firmware File. | ||
| 106 | * | ||
| 107 | * SST Firmware files can consist of 1..N modules. This generic structure is | ||
| 108 | * used to manage each firmware file and it's modules regardless of SST firmware | ||
| 109 | * type. A SST driver may load multiple FW files. | ||
| 110 | */ | ||
| 111 | struct sst_fw { | ||
| 112 | struct sst_dsp *dsp; | ||
| 113 | |||
| 114 | /* base addresses of FW file data */ | ||
| 115 | dma_addr_t dmable_fw_paddr; /* physical address of fw data */ | ||
| 116 | void *dma_buf; /* virtual address of fw data */ | ||
| 117 | u32 size; /* size of fw data */ | ||
| 118 | |||
| 119 | /* lists */ | ||
| 120 | struct list_head list; /* DSP list of FW */ | ||
| 121 | struct list_head module_list; /* FW list of modules */ | ||
| 122 | |||
| 123 | void *private; /* core doesn't touch this */ | ||
| 124 | }; | ||
| 125 | |||
| 126 | /* | ||
| 127 | * Audio DSP Generic Module Template. | ||
| 128 | * | ||
| 129 | * Used to define and register a new FW module. This data is extracted from | ||
| 130 | * FW module header information. | ||
| 131 | */ | ||
| 132 | struct sst_module_template { | ||
| 133 | u32 id; | ||
| 134 | u32 entry; /* entry point */ | ||
| 135 | u32 scratch_size; | ||
| 136 | u32 persistent_size; | ||
| 137 | }; | ||
| 138 | |||
| 139 | /* | ||
| 140 | * Block Allocator - Used to allocate blocks of DSP memory. | ||
| 141 | */ | ||
| 142 | struct sst_block_allocator { | ||
| 143 | u32 id; | ||
| 144 | u32 offset; | ||
| 145 | int size; | ||
| 146 | enum sst_mem_type type; | ||
| 147 | }; | ||
| 148 | |||
| 149 | /* | ||
| 150 | * Runtime Module Instance - A module object can be instanciated multiple | ||
| 151 | * times within the DSP FW. | ||
| 152 | */ | ||
| 153 | struct sst_module_runtime { | ||
| 154 | struct sst_dsp *dsp; | ||
| 155 | int id; | ||
| 156 | struct sst_module *module; /* parent module we belong too */ | ||
| 157 | |||
| 158 | u32 persistent_offset; /* private memory offset */ | ||
| 159 | void *private; | ||
| 160 | |||
| 161 | struct list_head list; | ||
| 162 | struct list_head block_list; /* list of blocks used */ | ||
| 163 | }; | ||
| 164 | |||
| 165 | /* | ||
| 166 | * Runtime Module Context - The runtime context must be manually stored by the | ||
| 167 | * driver prior to enter S3 and restored after leaving S3. This should really be | ||
| 168 | * part of the memory context saved by the enter D3 message IPC ??? | ||
| 169 | */ | ||
| 170 | struct sst_module_runtime_context { | ||
| 171 | dma_addr_t dma_buffer; | ||
| 172 | u32 *buffer; | ||
| 173 | }; | ||
| 174 | |||
| 175 | /* | ||
| 176 | * Audio DSP Module State | ||
| 177 | */ | ||
| 178 | enum sst_module_state { | ||
| 179 | SST_MODULE_STATE_UNLOADED = 0, /* default state */ | ||
| 180 | SST_MODULE_STATE_LOADED, | ||
| 181 | SST_MODULE_STATE_INITIALIZED, /* and inactive */ | ||
| 182 | SST_MODULE_STATE_ACTIVE, | ||
| 183 | }; | ||
| 184 | |||
| 185 | /* | ||
| 186 | * Audio DSP Generic Module. | ||
| 187 | * | ||
| 188 | * Each Firmware file can consist of 1..N modules. A module can span multiple | ||
| 189 | * ADSP memory blocks. The simplest FW will be a file with 1 module. A module | ||
| 190 | * can be instanciated multiple times in the DSP. | ||
| 191 | */ | ||
| 192 | struct sst_module { | ||
| 193 | struct sst_dsp *dsp; | ||
| 194 | struct sst_fw *sst_fw; /* parent FW we belong too */ | ||
| 195 | |||
| 196 | /* module configuration */ | ||
| 197 | u32 id; | ||
| 198 | u32 entry; /* module entry point */ | ||
| 199 | s32 offset; /* module offset in firmware file */ | ||
| 200 | u32 size; /* module size */ | ||
| 201 | u32 scratch_size; /* global scratch memory required */ | ||
| 202 | u32 persistent_size; /* private memory required */ | ||
| 203 | enum sst_mem_type type; /* destination memory type */ | ||
| 204 | u32 data_offset; /* offset in ADSP memory space */ | ||
| 205 | void *data; /* module data */ | ||
| 206 | |||
| 207 | /* runtime */ | ||
| 208 | u32 usage_count; /* can be unloaded if count == 0 */ | ||
| 209 | void *private; /* core doesn't touch this */ | ||
| 210 | |||
| 211 | /* lists */ | ||
| 212 | struct list_head block_list; /* Module list of blocks in use */ | ||
| 213 | struct list_head list; /* DSP list of modules */ | ||
| 214 | struct list_head list_fw; /* FW list of modules */ | ||
| 215 | struct list_head runtime_list; /* list of runtime module objects*/ | ||
| 216 | |||
| 217 | /* state */ | ||
| 218 | enum sst_module_state state; | ||
| 219 | }; | ||
| 220 | |||
| 221 | /* | ||
| 222 | * SST Memory Block operations. | ||
| 223 | */ | ||
| 224 | struct sst_block_ops { | ||
| 225 | int (*enable)(struct sst_mem_block *block); | ||
| 226 | int (*disable)(struct sst_mem_block *block); | ||
| 227 | }; | ||
| 228 | |||
| 229 | /* | ||
| 230 | * SST Generic Memory Block. | ||
| 231 | * | ||
| 232 | * SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be | ||
| 233 | * power gated. | ||
| 234 | */ | ||
| 235 | struct sst_mem_block { | ||
| 236 | struct sst_dsp *dsp; | ||
| 237 | struct sst_module *module; /* module that uses this block */ | ||
| 238 | |||
| 239 | /* block config */ | ||
| 240 | u32 offset; /* offset from base */ | ||
| 241 | u32 size; /* block size */ | ||
| 242 | u32 index; /* block index 0..N */ | ||
| 243 | enum sst_mem_type type; /* block memory type IRAM/DRAM */ | ||
| 244 | struct sst_block_ops *ops; /* block operations, if any */ | ||
| 245 | |||
| 246 | /* block status */ | ||
| 247 | u32 bytes_used; /* bytes in use by modules */ | ||
| 248 | void *private; /* generic core does not touch this */ | ||
| 249 | int users; /* number of modules using this block */ | ||
| 250 | |||
| 251 | /* block lists */ | ||
| 252 | struct list_head module_list; /* Module list of blocks */ | ||
| 253 | struct list_head list; /* Map list of free/used blocks */ | ||
| 254 | }; | ||
| 255 | |||
| 256 | /* | ||
| 257 | * Generic SST Shim Interface. | ||
| 258 | */ | ||
| 259 | struct sst_dsp { | ||
| 260 | |||
| 261 | /* runtime */ | ||
| 262 | struct sst_dsp_device *sst_dev; | ||
| 263 | spinlock_t spinlock; /* IPC locking */ | ||
| 264 | struct mutex mutex; /* DSP FW lock */ | ||
| 265 | struct device *dev; | ||
| 266 | struct device *dma_dev; | ||
| 267 | void *thread_context; | ||
| 268 | int irq; | ||
| 269 | u32 id; | ||
| 270 | |||
| 271 | /* list of free and used ADSP memory blocks */ | ||
| 272 | struct list_head used_block_list; | ||
| 273 | struct list_head free_block_list; | ||
| 274 | |||
| 275 | /* operations */ | ||
| 276 | struct sst_ops *ops; | ||
| 277 | |||
| 278 | /* debug FS */ | ||
| 279 | struct dentry *debugfs_root; | ||
| 280 | |||
| 281 | /* base addresses */ | ||
| 282 | struct sst_addr addr; | ||
| 283 | |||
| 284 | /* mailbox */ | ||
| 285 | struct sst_mailbox mailbox; | ||
| 286 | |||
| 287 | /* SST FW files loaded and their modules */ | ||
| 288 | struct list_head module_list; | ||
| 289 | struct list_head fw_list; | ||
| 290 | |||
| 291 | /* scratch buffer */ | ||
| 292 | struct list_head scratch_block_list; | ||
| 293 | u32 scratch_offset; | ||
| 294 | u32 scratch_size; | ||
| 295 | |||
| 296 | /* platform data */ | ||
| 297 | struct sst_pdata *pdata; | ||
| 298 | |||
| 299 | /* DMA FW loading */ | ||
| 300 | struct sst_dma *dma; | ||
| 301 | bool fw_use_dma; | ||
| 302 | }; | ||
| 303 | |||
| 304 | /* Size optimised DRAM/IRAM memcpy */ | ||
| 305 | static inline void sst_dsp_write(struct sst_dsp *sst, void *src, | ||
| 306 | u32 dest_offset, size_t bytes) | ||
| 307 | { | ||
| 308 | sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes); | ||
| 309 | } | ||
| 310 | |||
| 311 | static inline void sst_dsp_read(struct sst_dsp *sst, void *dest, | ||
| 312 | u32 src_offset, size_t bytes) | ||
| 313 | { | ||
| 314 | sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes); | ||
| 315 | } | ||
| 316 | |||
| 317 | static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst) | ||
| 318 | { | ||
| 319 | return sst->thread_context; | ||
| 320 | } | ||
| 321 | |||
| 322 | /* Create/Free FW files - can contain multiple modules */ | ||
| 323 | struct sst_fw *sst_fw_new(struct sst_dsp *dsp, | ||
| 324 | const struct firmware *fw, void *private); | ||
| 325 | void sst_fw_free(struct sst_fw *sst_fw); | ||
| 326 | void sst_fw_free_all(struct sst_dsp *dsp); | ||
| 327 | int sst_fw_reload(struct sst_fw *sst_fw); | ||
| 328 | void sst_fw_unload(struct sst_fw *sst_fw); | ||
| 329 | |||
| 330 | /* Create/Free firmware modules */ | ||
| 331 | struct sst_module *sst_module_new(struct sst_fw *sst_fw, | ||
| 332 | struct sst_module_template *template, void *private); | ||
| 333 | void sst_module_free(struct sst_module *module); | ||
| 334 | struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id); | ||
| 335 | int sst_module_alloc_blocks(struct sst_module *module); | ||
| 336 | int sst_module_free_blocks(struct sst_module *module); | ||
| 337 | |||
| 338 | /* Create/Free firmware module runtime instances */ | ||
| 339 | struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module, | ||
| 340 | int id, void *private); | ||
| 341 | void sst_module_runtime_free(struct sst_module_runtime *runtime); | ||
| 342 | struct sst_module_runtime *sst_module_runtime_get_from_id( | ||
| 343 | struct sst_module *module, u32 id); | ||
| 344 | int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime, | ||
| 345 | int offset); | ||
| 346 | int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime); | ||
| 347 | int sst_module_runtime_save(struct sst_module_runtime *runtime, | ||
| 348 | struct sst_module_runtime_context *context); | ||
| 349 | int sst_module_runtime_restore(struct sst_module_runtime *runtime, | ||
| 350 | struct sst_module_runtime_context *context); | ||
| 351 | |||
| 352 | /* generic block allocation */ | ||
| 353 | int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba, | ||
| 354 | struct list_head *block_list); | ||
| 355 | int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list); | ||
| 356 | |||
| 357 | /* scratch allocation */ | ||
| 358 | int sst_block_alloc_scratch(struct sst_dsp *dsp); | ||
| 359 | void sst_block_free_scratch(struct sst_dsp *dsp); | ||
| 360 | |||
| 361 | /* Register the DSPs memory blocks - would be nice to read from ACPI */ | ||
| 362 | struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, | ||
| 363 | u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, | ||
| 364 | void *private); | ||
| 365 | void sst_mem_block_unregister_all(struct sst_dsp *dsp); | ||
| 366 | |||
| 367 | /* Create/Free DMA resources */ | ||
| 368 | int sst_dma_new(struct sst_dsp *sst); | ||
| 369 | void sst_dma_free(struct sst_dma *dma); | ||
| 370 | |||
| 371 | u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, | ||
| 372 | enum sst_mem_type type); | ||
| 373 | #endif | ||
diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c new file mode 100644 index 000000000000..64e94212d2d2 --- /dev/null +++ b/sound/soc/intel/common/sst-dsp.c | |||
| @@ -0,0 +1,420 @@ | |||
| 1 | /* | ||
| 2 | * Intel Smart Sound Technology (SST) DSP Core Driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013, Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License version | ||
| 8 | * 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/export.h> | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/io.h> | ||
| 23 | |||
| 24 | #include "sst-dsp.h" | ||
| 25 | #include "sst-dsp-priv.h" | ||
| 26 | |||
| 27 | #define CREATE_TRACE_POINTS | ||
| 28 | #include <trace/events/intel-sst.h> | ||
| 29 | |||
| 30 | /* Internal generic low-level SST IO functions - can be overidden */ | ||
| 31 | void sst_shim32_write(void __iomem *addr, u32 offset, u32 value) | ||
| 32 | { | ||
| 33 | writel(value, addr + offset); | ||
| 34 | } | ||
| 35 | EXPORT_SYMBOL_GPL(sst_shim32_write); | ||
| 36 | |||
| 37 | u32 sst_shim32_read(void __iomem *addr, u32 offset) | ||
| 38 | { | ||
| 39 | return readl(addr + offset); | ||
| 40 | } | ||
| 41 | EXPORT_SYMBOL_GPL(sst_shim32_read); | ||
| 42 | |||
| 43 | void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value) | ||
| 44 | { | ||
| 45 | memcpy_toio(addr + offset, &value, sizeof(value)); | ||
| 46 | } | ||
| 47 | EXPORT_SYMBOL_GPL(sst_shim32_write64); | ||
| 48 | |||
| 49 | u64 sst_shim32_read64(void __iomem *addr, u32 offset) | ||
| 50 | { | ||
| 51 | u64 val; | ||
| 52 | |||
| 53 | memcpy_fromio(&val, addr + offset, sizeof(val)); | ||
| 54 | return val; | ||
| 55 | } | ||
| 56 | EXPORT_SYMBOL_GPL(sst_shim32_read64); | ||
| 57 | |||
| 58 | static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest, | ||
| 59 | u32 *src, size_t bytes) | ||
| 60 | { | ||
| 61 | int i, words = bytes >> 2; | ||
| 62 | |||
| 63 | for (i = 0; i < words; i++) | ||
| 64 | writel(src[i], dest + i); | ||
| 65 | } | ||
| 66 | |||
| 67 | static inline void _sst_memcpy_fromio_32(u32 *dest, | ||
| 68 | const volatile __iomem u32 *src, size_t bytes) | ||
| 69 | { | ||
| 70 | int i, words = bytes >> 2; | ||
| 71 | |||
| 72 | for (i = 0; i < words; i++) | ||
| 73 | dest[i] = readl(src + i); | ||
| 74 | } | ||
| 75 | |||
| 76 | void sst_memcpy_toio_32(struct sst_dsp *sst, | ||
| 77 | void __iomem *dest, void *src, size_t bytes) | ||
| 78 | { | ||
| 79 | _sst_memcpy_toio_32(dest, src, bytes); | ||
| 80 | } | ||
| 81 | EXPORT_SYMBOL_GPL(sst_memcpy_toio_32); | ||
| 82 | |||
| 83 | void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest, | ||
| 84 | void __iomem *src, size_t bytes) | ||
| 85 | { | ||
| 86 | _sst_memcpy_fromio_32(dest, src, bytes); | ||
| 87 | } | ||
| 88 | EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32); | ||
| 89 | |||
| 90 | /* Public API */ | ||
| 91 | void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) | ||
| 92 | { | ||
| 93 | unsigned long flags; | ||
| 94 | |||
| 95 | spin_lock_irqsave(&sst->spinlock, flags); | ||
| 96 | sst->ops->write(sst->addr.shim, offset, value); | ||
| 97 | spin_unlock_irqrestore(&sst->spinlock, flags); | ||
| 98 | } | ||
| 99 | EXPORT_SYMBOL_GPL(sst_dsp_shim_write); | ||
| 100 | |||
| 101 | u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset) | ||
| 102 | { | ||
| 103 | unsigned long flags; | ||
| 104 | u32 val; | ||
| 105 | |||
| 106 | spin_lock_irqsave(&sst->spinlock, flags); | ||
| 107 | val = sst->ops->read(sst->addr.shim, offset); | ||
| 108 | spin_unlock_irqrestore(&sst->spinlock, flags); | ||
| 109 | |||
| 110 | return val; | ||
| 111 | } | ||
| 112 | EXPORT_SYMBOL_GPL(sst_dsp_shim_read); | ||
| 113 | |||
| 114 | void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value) | ||
| 115 | { | ||
| 116 | unsigned long flags; | ||
| 117 | |||
| 118 | spin_lock_irqsave(&sst->spinlock, flags); | ||
| 119 | sst->ops->write64(sst->addr.shim, offset, value); | ||
| 120 | spin_unlock_irqrestore(&sst->spinlock, flags); | ||
| 121 | } | ||
| 122 | EXPORT_SYMBOL_GPL(sst_dsp_shim_write64); | ||
| 123 | |||
| 124 | u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset) | ||
| 125 | { | ||
| 126 | unsigned long flags; | ||
| 127 | u64 val; | ||
| 128 | |||
| 129 | spin_lock_irqsave(&sst->spinlock, flags); | ||
| 130 | val = sst->ops->read64(sst->addr.shim, offset); | ||
| 131 | spin_unlock_irqrestore(&sst->spinlock, flags); | ||
| 132 | |||
| 133 | return val; | ||
| 134 | } | ||
| 135 | EXPORT_SYMBOL_GPL(sst_dsp_shim_read64); | ||
| 136 | |||
| 137 | void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) | ||
| 138 | { | ||
| 139 | sst->ops->write(sst->addr.shim, offset, value); | ||
| 140 | } | ||
| 141 | EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked); | ||
| 142 | |||
| 143 | u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) | ||
| 144 | { | ||
| 145 | return sst->ops->read(sst->addr.shim, offset); | ||
| 146 | } | ||
| 147 | EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked); | ||
| 148 | |||
| 149 | void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) | ||
| 150 | { | ||
| 151 | sst->ops->write64(sst->addr.shim, offset, value); | ||
| 152 | } | ||
| 153 | EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked); | ||
| 154 | |||
| 155 | u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) | ||
| 156 | { | ||
| 157 | return sst->ops->read64(sst->addr.shim, offset); | ||
| 158 | } | ||
| 159 | EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked); | ||
| 160 | |||
| 161 | int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, | ||
| 162 | u32 mask, u32 value) | ||
| 163 | { | ||
| 164 | bool change; | ||
| 165 | unsigned int old, new; | ||
| 166 | u32 ret; | ||
| 167 | |||
| 168 | ret = sst_dsp_shim_read_unlocked(sst, offset); | ||
| 169 | |||
| 170 | old = ret; | ||
| 171 | new = (old & (~mask)) | (value & mask); | ||
| 172 | |||
| 173 | change = (old != new); | ||
| 174 | if (change) | ||
| 175 | sst_dsp_shim_write_unlocked(sst, offset, new); | ||
| 176 | |||
| 177 | return change; | ||
| 178 | } | ||
| 179 | EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked); | ||
| 180 | |||
| 181 | int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, | ||
| 182 | u64 mask, u64 value) | ||
| 183 | { | ||
| 184 | bool change; | ||
| 185 | u64 old, new; | ||
| 186 | |||
| 187 | old = sst_dsp_shim_read64_unlocked(sst, offset); | ||
| 188 | |||
| 189 | new = (old & (~mask)) | (value & mask); | ||
| 190 | |||
| 191 | change = (old != new); | ||
| 192 | if (change) | ||
| 193 | sst_dsp_shim_write64_unlocked(sst, offset, new); | ||
| 194 | |||
| 195 | return change; | ||
| 196 | } | ||
| 197 | EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked); | ||
| 198 | |||
| 199 | int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, | ||
| 200 | u32 mask, u32 value) | ||
| 201 | { | ||
| 202 | unsigned long flags; | ||
| 203 | bool change; | ||
| 204 | |||
| 205 | spin_lock_irqsave(&sst->spinlock, flags); | ||
| 206 | change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value); | ||
| 207 | spin_unlock_irqrestore(&sst->spinlock, flags); | ||
| 208 | return change; | ||
| 209 | } | ||
| 210 | EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits); | ||
| 211 | |||
| 212 | int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, | ||
| 213 | u64 mask, u64 value) | ||
| 214 | { | ||
| 215 | unsigned long flags; | ||
| 216 | bool change; | ||
| 217 | |||
| 218 | spin_lock_irqsave(&sst->spinlock, flags); | ||
| 219 | change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value); | ||
| 220 | spin_unlock_irqrestore(&sst->spinlock, flags); | ||
| 221 | return change; | ||
| 222 | } | ||
| 223 | EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); | ||
| 224 | |||
| 225 | void sst_dsp_dump(struct sst_dsp *sst) | ||
| 226 | { | ||
| 227 | if (sst->ops->dump) | ||
| 228 | sst->ops->dump(sst); | ||
| 229 | } | ||
| 230 | EXPORT_SYMBOL_GPL(sst_dsp_dump); | ||
| 231 | |||
| 232 | void sst_dsp_reset(struct sst_dsp *sst) | ||
| 233 | { | ||
| 234 | if (sst->ops->reset) | ||
| 235 | sst->ops->reset(sst); | ||
| 236 | } | ||
| 237 | EXPORT_SYMBOL_GPL(sst_dsp_reset); | ||
| 238 | |||
| 239 | int sst_dsp_boot(struct sst_dsp *sst) | ||
| 240 | { | ||
| 241 | if (sst->ops->boot) | ||
| 242 | sst->ops->boot(sst); | ||
| 243 | |||
| 244 | return 0; | ||
| 245 | } | ||
| 246 | EXPORT_SYMBOL_GPL(sst_dsp_boot); | ||
| 247 | |||
| 248 | int sst_dsp_wake(struct sst_dsp *sst) | ||
| 249 | { | ||
| 250 | if (sst->ops->wake) | ||
| 251 | return sst->ops->wake(sst); | ||
| 252 | |||
| 253 | return 0; | ||
| 254 | } | ||
| 255 | EXPORT_SYMBOL_GPL(sst_dsp_wake); | ||
| 256 | |||
| 257 | void sst_dsp_sleep(struct sst_dsp *sst) | ||
| 258 | { | ||
| 259 | if (sst->ops->sleep) | ||
| 260 | sst->ops->sleep(sst); | ||
| 261 | } | ||
| 262 | EXPORT_SYMBOL_GPL(sst_dsp_sleep); | ||
| 263 | |||
| 264 | void sst_dsp_stall(struct sst_dsp *sst) | ||
| 265 | { | ||
| 266 | if (sst->ops->stall) | ||
| 267 | sst->ops->stall(sst); | ||
| 268 | } | ||
| 269 | EXPORT_SYMBOL_GPL(sst_dsp_stall); | ||
| 270 | |||
| 271 | void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) | ||
| 272 | { | ||
| 273 | sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY); | ||
| 274 | trace_sst_ipc_msg_tx(msg); | ||
| 275 | } | ||
| 276 | EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx); | ||
| 277 | |||
| 278 | u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp) | ||
| 279 | { | ||
| 280 | u32 msg; | ||
| 281 | |||
| 282 | msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); | ||
| 283 | trace_sst_ipc_msg_rx(msg); | ||
| 284 | |||
| 285 | return msg; | ||
| 286 | } | ||
| 287 | EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx); | ||
| 288 | |||
| 289 | int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size, | ||
| 290 | u32 outbox_offset, size_t outbox_size) | ||
| 291 | { | ||
| 292 | sst->mailbox.in_base = sst->addr.lpe + inbox_offset; | ||
| 293 | sst->mailbox.out_base = sst->addr.lpe + outbox_offset; | ||
| 294 | sst->mailbox.in_size = inbox_size; | ||
| 295 | sst->mailbox.out_size = outbox_size; | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init); | ||
| 299 | |||
| 300 | void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes) | ||
| 301 | { | ||
| 302 | u32 i; | ||
| 303 | |||
| 304 | trace_sst_ipc_outbox_write(bytes); | ||
| 305 | |||
| 306 | memcpy_toio(sst->mailbox.out_base, message, bytes); | ||
| 307 | |||
| 308 | for (i = 0; i < bytes; i += 4) | ||
| 309 | trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i)); | ||
| 310 | } | ||
| 311 | EXPORT_SYMBOL_GPL(sst_dsp_outbox_write); | ||
| 312 | |||
| 313 | void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes) | ||
| 314 | { | ||
| 315 | u32 i; | ||
| 316 | |||
| 317 | trace_sst_ipc_outbox_read(bytes); | ||
| 318 | |||
| 319 | memcpy_fromio(message, sst->mailbox.out_base, bytes); | ||
| 320 | |||
| 321 | for (i = 0; i < bytes; i += 4) | ||
| 322 | trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i)); | ||
| 323 | } | ||
| 324 | EXPORT_SYMBOL_GPL(sst_dsp_outbox_read); | ||
| 325 | |||
| 326 | void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes) | ||
| 327 | { | ||
| 328 | u32 i; | ||
| 329 | |||
| 330 | trace_sst_ipc_inbox_write(bytes); | ||
| 331 | |||
| 332 | memcpy_toio(sst->mailbox.in_base, message, bytes); | ||
| 333 | |||
| 334 | for (i = 0; i < bytes; i += 4) | ||
| 335 | trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i)); | ||
| 336 | } | ||
| 337 | EXPORT_SYMBOL_GPL(sst_dsp_inbox_write); | ||
| 338 | |||
| 339 | void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) | ||
| 340 | { | ||
| 341 | u32 i; | ||
| 342 | |||
| 343 | trace_sst_ipc_inbox_read(bytes); | ||
| 344 | |||
| 345 | memcpy_fromio(message, sst->mailbox.in_base, bytes); | ||
| 346 | |||
| 347 | for (i = 0; i < bytes; i += 4) | ||
| 348 | trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i)); | ||
| 349 | } | ||
| 350 | EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); | ||
| 351 | |||
| 352 | struct sst_dsp *sst_dsp_new(struct device *dev, | ||
| 353 | struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) | ||
| 354 | { | ||
| 355 | struct sst_dsp *sst; | ||
| 356 | int err; | ||
| 357 | |||
| 358 | dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id); | ||
| 359 | |||
| 360 | sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); | ||
| 361 | if (sst == NULL) | ||
| 362 | return NULL; | ||
| 363 | |||
| 364 | spin_lock_init(&sst->spinlock); | ||
| 365 | mutex_init(&sst->mutex); | ||
| 366 | sst->dev = dev; | ||
| 367 | sst->dma_dev = pdata->dma_dev; | ||
| 368 | sst->thread_context = sst_dev->thread_context; | ||
| 369 | sst->sst_dev = sst_dev; | ||
| 370 | sst->id = pdata->id; | ||
| 371 | sst->irq = pdata->irq; | ||
| 372 | sst->ops = sst_dev->ops; | ||
| 373 | sst->pdata = pdata; | ||
| 374 | INIT_LIST_HEAD(&sst->used_block_list); | ||
| 375 | INIT_LIST_HEAD(&sst->free_block_list); | ||
| 376 | INIT_LIST_HEAD(&sst->module_list); | ||
| 377 | INIT_LIST_HEAD(&sst->fw_list); | ||
| 378 | INIT_LIST_HEAD(&sst->scratch_block_list); | ||
| 379 | |||
| 380 | /* Initialise SST Audio DSP */ | ||
| 381 | if (sst->ops->init) { | ||
| 382 | err = sst->ops->init(sst, pdata); | ||
| 383 | if (err < 0) | ||
| 384 | return NULL; | ||
| 385 | } | ||
| 386 | |||
| 387 | /* Register the ISR */ | ||
| 388 | err = request_threaded_irq(sst->irq, sst->ops->irq_handler, | ||
| 389 | sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); | ||
| 390 | if (err) | ||
| 391 | goto irq_err; | ||
| 392 | |||
| 393 | err = sst_dma_new(sst); | ||
| 394 | if (err) | ||
| 395 | dev_warn(dev, "sst_dma_new failed %d\n", err); | ||
| 396 | |||
| 397 | return sst; | ||
| 398 | |||
| 399 | irq_err: | ||
| 400 | if (sst->ops->free) | ||
| 401 | sst->ops->free(sst); | ||
| 402 | |||
| 403 | return NULL; | ||
| 404 | } | ||
| 405 | EXPORT_SYMBOL_GPL(sst_dsp_new); | ||
| 406 | |||
| 407 | void sst_dsp_free(struct sst_dsp *sst) | ||
| 408 | { | ||
| 409 | free_irq(sst->irq, sst); | ||
| 410 | if (sst->ops->free) | ||
| 411 | sst->ops->free(sst); | ||
| 412 | |||
| 413 | sst_dma_free(sst->dma); | ||
| 414 | } | ||
| 415 | EXPORT_SYMBOL_GPL(sst_dsp_free); | ||
| 416 | |||
| 417 | /* Module information */ | ||
| 418 | MODULE_AUTHOR("Liam Girdwood"); | ||
| 419 | MODULE_DESCRIPTION("Intel SST Core"); | ||
| 420 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h new file mode 100644 index 000000000000..96aeb2556ad4 --- /dev/null +++ b/sound/soc/intel/common/sst-dsp.h | |||
| @@ -0,0 +1,285 @@ | |||
| 1 | /* | ||
| 2 | * Intel Smart Sound Technology (SST) Core | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013, Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License version | ||
| 8 | * 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef __SOUND_SOC_SST_DSP_H | ||
| 18 | #define __SOUND_SOC_SST_DSP_H | ||
| 19 | |||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/types.h> | ||
| 22 | #include <linux/interrupt.h> | ||
| 23 | |||
| 24 | /* SST Device IDs */ | ||
| 25 | #define SST_DEV_ID_LYNX_POINT 0x33C8 | ||
| 26 | #define SST_DEV_ID_WILDCAT_POINT 0x3438 | ||
| 27 | #define SST_DEV_ID_BYT 0x0F28 | ||
| 28 | |||
| 29 | /* Supported SST DMA Devices */ | ||
| 30 | #define SST_DMA_TYPE_DW 1 | ||
| 31 | |||
| 32 | /* autosuspend delay 5s*/ | ||
| 33 | #define SST_RUNTIME_SUSPEND_DELAY (5 * 1000) | ||
| 34 | |||
| 35 | /* SST Shim register map | ||
| 36 | * The register naming can differ between products. Some products also | ||
| 37 | * contain extra functionality. | ||
| 38 | */ | ||
| 39 | #define SST_CSR 0x00 | ||
| 40 | #define SST_PISR 0x08 | ||
| 41 | #define SST_PIMR 0x10 | ||
| 42 | #define SST_ISRX 0x18 | ||
| 43 | #define SST_ISRD 0x20 | ||
| 44 | #define SST_IMRX 0x28 | ||
| 45 | #define SST_IMRD 0x30 | ||
| 46 | #define SST_IPCX 0x38 /* IPC IA -> SST */ | ||
| 47 | #define SST_IPCD 0x40 /* IPC SST -> IA */ | ||
| 48 | #define SST_ISRSC 0x48 | ||
| 49 | #define SST_ISRLPESC 0x50 | ||
| 50 | #define SST_IMRSC 0x58 | ||
| 51 | #define SST_IMRLPESC 0x60 | ||
| 52 | #define SST_IPCSC 0x68 | ||
| 53 | #define SST_IPCLPESC 0x70 | ||
| 54 | #define SST_CLKCTL 0x78 | ||
| 55 | #define SST_CSR2 0x80 | ||
| 56 | #define SST_LTRC 0xE0 | ||
| 57 | #define SST_HMDC 0xE8 | ||
| 58 | |||
| 59 | #define SST_SHIM_BEGIN SST_CSR | ||
| 60 | #define SST_SHIM_END SST_HDMC | ||
| 61 | |||
| 62 | #define SST_DBGO 0xF0 | ||
| 63 | |||
| 64 | #define SST_SHIM_SIZE 0x100 | ||
| 65 | #define SST_PWMCTRL 0x1000 | ||
| 66 | |||
| 67 | /* SST Shim Register bits | ||
| 68 | * The register bit naming can differ between products. Some products also | ||
| 69 | * contain extra functionality. | ||
| 70 | */ | ||
| 71 | |||
| 72 | /* CSR / CS */ | ||
| 73 | #define SST_CSR_RST (0x1 << 1) | ||
| 74 | #define SST_CSR_SBCS0 (0x1 << 2) | ||
| 75 | #define SST_CSR_SBCS1 (0x1 << 3) | ||
| 76 | #define SST_CSR_DCS(x) (x << 4) | ||
| 77 | #define SST_CSR_DCS_MASK (0x7 << 4) | ||
| 78 | #define SST_CSR_STALL (0x1 << 10) | ||
| 79 | #define SST_CSR_S0IOCS (0x1 << 21) | ||
| 80 | #define SST_CSR_S1IOCS (0x1 << 23) | ||
| 81 | #define SST_CSR_LPCS (0x1 << 31) | ||
| 82 | #define SST_CSR_24MHZ_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS) | ||
| 83 | #define SST_CSR_24MHZ_NO_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1) | ||
| 84 | #define SST_BYT_CSR_RST (0x1 << 0) | ||
| 85 | #define SST_BYT_CSR_VECTOR_SEL (0x1 << 1) | ||
| 86 | #define SST_BYT_CSR_STALL (0x1 << 2) | ||
| 87 | #define SST_BYT_CSR_PWAITMODE (0x1 << 3) | ||
| 88 | |||
| 89 | /* ISRX / ISC */ | ||
| 90 | #define SST_ISRX_BUSY (0x1 << 1) | ||
| 91 | #define SST_ISRX_DONE (0x1 << 0) | ||
| 92 | #define SST_BYT_ISRX_REQUEST (0x1 << 1) | ||
| 93 | |||
| 94 | /* ISRD / ISD */ | ||
| 95 | #define SST_ISRD_BUSY (0x1 << 1) | ||
| 96 | #define SST_ISRD_DONE (0x1 << 0) | ||
| 97 | |||
| 98 | /* IMRX / IMC */ | ||
| 99 | #define SST_IMRX_BUSY (0x1 << 1) | ||
| 100 | #define SST_IMRX_DONE (0x1 << 0) | ||
| 101 | #define SST_BYT_IMRX_REQUEST (0x1 << 1) | ||
| 102 | |||
| 103 | /* IMRD / IMD */ | ||
| 104 | #define SST_IMRD_DONE (0x1 << 0) | ||
| 105 | #define SST_IMRD_BUSY (0x1 << 1) | ||
| 106 | #define SST_IMRD_SSP0 (0x1 << 16) | ||
| 107 | #define SST_IMRD_DMAC0 (0x1 << 21) | ||
| 108 | #define SST_IMRD_DMAC1 (0x1 << 22) | ||
| 109 | #define SST_IMRD_DMAC (SST_IMRD_DMAC0 | SST_IMRD_DMAC1) | ||
| 110 | |||
| 111 | /* IPCX / IPCC */ | ||
| 112 | #define SST_IPCX_DONE (0x1 << 30) | ||
| 113 | #define SST_IPCX_BUSY (0x1 << 31) | ||
| 114 | #define SST_BYT_IPCX_DONE ((u64)0x1 << 62) | ||
| 115 | #define SST_BYT_IPCX_BUSY ((u64)0x1 << 63) | ||
| 116 | |||
| 117 | /* IPCD */ | ||
| 118 | #define SST_IPCD_DONE (0x1 << 30) | ||
| 119 | #define SST_IPCD_BUSY (0x1 << 31) | ||
| 120 | #define SST_BYT_IPCD_DONE ((u64)0x1 << 62) | ||
| 121 | #define SST_BYT_IPCD_BUSY ((u64)0x1 << 63) | ||
| 122 | |||
| 123 | /* CLKCTL */ | ||
| 124 | #define SST_CLKCTL_SMOS(x) (x << 24) | ||
| 125 | #define SST_CLKCTL_MASK (3 << 24) | ||
| 126 | #define SST_CLKCTL_DCPLCG (1 << 18) | ||
| 127 | #define SST_CLKCTL_SCOE1 (1 << 17) | ||
| 128 | #define SST_CLKCTL_SCOE0 (1 << 16) | ||
| 129 | |||
| 130 | /* CSR2 / CS2 */ | ||
| 131 | #define SST_CSR2_SDFD_SSP0 (1 << 1) | ||
| 132 | #define SST_CSR2_SDFD_SSP1 (1 << 2) | ||
| 133 | |||
| 134 | /* LTRC */ | ||
| 135 | #define SST_LTRC_VAL(x) (x << 0) | ||
| 136 | |||
| 137 | /* HMDC */ | ||
| 138 | #define SST_HMDC_HDDA0(x) (x << 0) | ||
| 139 | #define SST_HMDC_HDDA1(x) (x << 7) | ||
| 140 | #define SST_HMDC_HDDA_E0_CH0 1 | ||
| 141 | #define SST_HMDC_HDDA_E0_CH1 2 | ||
| 142 | #define SST_HMDC_HDDA_E0_CH2 4 | ||
| 143 | #define SST_HMDC_HDDA_E0_CH3 8 | ||
| 144 | #define SST_HMDC_HDDA_E1_CH0 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0) | ||
| 145 | #define SST_HMDC_HDDA_E1_CH1 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1) | ||
| 146 | #define SST_HMDC_HDDA_E1_CH2 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2) | ||
| 147 | #define SST_HMDC_HDDA_E1_CH3 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3) | ||
| 148 | #define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \ | ||
| 149 | SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3) | ||
| 150 | #define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \ | ||
| 151 | SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3) | ||
| 152 | |||
| 153 | |||
| 154 | /* SST Vendor Defined Registers and bits */ | ||
| 155 | #define SST_VDRTCTL0 0xa0 | ||
| 156 | #define SST_VDRTCTL1 0xa4 | ||
| 157 | #define SST_VDRTCTL2 0xa8 | ||
| 158 | #define SST_VDRTCTL3 0xaC | ||
| 159 | |||
| 160 | /* VDRTCTL0 */ | ||
| 161 | #define SST_VDRTCL0_D3PGD (1 << 0) | ||
| 162 | #define SST_VDRTCL0_D3SRAMPGD (1 << 1) | ||
| 163 | #define SST_VDRTCL0_DSRAMPGE_SHIFT 12 | ||
| 164 | #define SST_VDRTCL0_DSRAMPGE_MASK (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT) | ||
| 165 | #define SST_VDRTCL0_ISRAMPGE_SHIFT 2 | ||
| 166 | #define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT) | ||
| 167 | |||
| 168 | /* VDRTCTL2 */ | ||
| 169 | #define SST_VDRTCL2_DCLCGE (1 << 1) | ||
| 170 | #define SST_VDRTCL2_DTCGE (1 << 10) | ||
| 171 | #define SST_VDRTCL2_APLLSE_MASK (1 << 31) | ||
| 172 | |||
| 173 | /* PMCS */ | ||
| 174 | #define SST_PMCS 0x84 | ||
| 175 | #define SST_PMCS_PS_MASK 0x3 | ||
| 176 | |||
| 177 | struct sst_dsp; | ||
| 178 | |||
| 179 | /* | ||
| 180 | * SST Device. | ||
| 181 | * | ||
| 182 | * This structure is populated by the SST core driver. | ||
| 183 | */ | ||
| 184 | struct sst_dsp_device { | ||
| 185 | /* Mandatory fields */ | ||
| 186 | struct sst_ops *ops; | ||
| 187 | irqreturn_t (*thread)(int irq, void *context); | ||
| 188 | void *thread_context; | ||
| 189 | }; | ||
| 190 | |||
| 191 | /* | ||
| 192 | * SST Platform Data. | ||
| 193 | */ | ||
| 194 | struct sst_pdata { | ||
| 195 | /* ACPI data */ | ||
| 196 | u32 lpe_base; | ||
| 197 | u32 lpe_size; | ||
| 198 | u32 pcicfg_base; | ||
| 199 | u32 pcicfg_size; | ||
| 200 | u32 fw_base; | ||
| 201 | u32 fw_size; | ||
| 202 | int irq; | ||
| 203 | |||
| 204 | /* Firmware */ | ||
| 205 | const struct firmware *fw; | ||
| 206 | |||
| 207 | /* DMA */ | ||
| 208 | int resindex_dma_base; /* other fields invalid if equals to -1 */ | ||
| 209 | u32 dma_base; | ||
| 210 | u32 dma_size; | ||
| 211 | int dma_engine; | ||
| 212 | struct device *dma_dev; | ||
| 213 | |||
| 214 | /* DSP */ | ||
| 215 | u32 id; | ||
| 216 | void *dsp; | ||
| 217 | }; | ||
| 218 | |||
| 219 | /* Initialization */ | ||
| 220 | struct sst_dsp *sst_dsp_new(struct device *dev, | ||
| 221 | struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); | ||
| 222 | void sst_dsp_free(struct sst_dsp *sst); | ||
| 223 | |||
| 224 | /* SHIM Read / Write */ | ||
| 225 | void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); | ||
| 226 | u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset); | ||
| 227 | int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, | ||
| 228 | u32 mask, u32 value); | ||
| 229 | void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value); | ||
| 230 | u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset); | ||
| 231 | int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, | ||
| 232 | u64 mask, u64 value); | ||
| 233 | |||
| 234 | /* SHIM Read / Write Unlocked for callers already holding sst lock */ | ||
| 235 | void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value); | ||
| 236 | u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset); | ||
| 237 | int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, | ||
| 238 | u32 mask, u32 value); | ||
| 239 | void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value); | ||
| 240 | u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset); | ||
| 241 | int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, | ||
| 242 | u64 mask, u64 value); | ||
| 243 | |||
| 244 | /* Internal generic low-level SST IO functions - can be overidden */ | ||
| 245 | void sst_shim32_write(void __iomem *addr, u32 offset, u32 value); | ||
| 246 | u32 sst_shim32_read(void __iomem *addr, u32 offset); | ||
| 247 | void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value); | ||
| 248 | u64 sst_shim32_read64(void __iomem *addr, u32 offset); | ||
| 249 | void sst_memcpy_toio_32(struct sst_dsp *sst, | ||
| 250 | void __iomem *dest, void *src, size_t bytes); | ||
| 251 | void sst_memcpy_fromio_32(struct sst_dsp *sst, | ||
| 252 | void *dest, void __iomem *src, size_t bytes); | ||
| 253 | |||
| 254 | /* DSP reset & boot */ | ||
| 255 | void sst_dsp_reset(struct sst_dsp *sst); | ||
| 256 | int sst_dsp_boot(struct sst_dsp *sst); | ||
| 257 | int sst_dsp_wake(struct sst_dsp *sst); | ||
| 258 | void sst_dsp_sleep(struct sst_dsp *sst); | ||
| 259 | void sst_dsp_stall(struct sst_dsp *sst); | ||
| 260 | |||
| 261 | /* DMA */ | ||
| 262 | int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id); | ||
| 263 | void sst_dsp_dma_put_channel(struct sst_dsp *dsp); | ||
| 264 | int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr, | ||
| 265 | dma_addr_t src_addr, size_t size); | ||
| 266 | int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr, | ||
| 267 | dma_addr_t src_addr, size_t size); | ||
| 268 | |||
| 269 | /* Msg IO */ | ||
| 270 | void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); | ||
| 271 | u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp); | ||
| 272 | |||
| 273 | /* Mailbox management */ | ||
| 274 | int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset, | ||
| 275 | size_t inbox_size, u32 outbox_offset, size_t outbox_size); | ||
| 276 | void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes); | ||
| 277 | void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes); | ||
| 278 | void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes); | ||
| 279 | void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes); | ||
| 280 | void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes); | ||
| 281 | |||
| 282 | /* Debug */ | ||
| 283 | void sst_dsp_dump(struct sst_dsp *sst); | ||
| 284 | |||
| 285 | #endif | ||
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c new file mode 100644 index 000000000000..ebcca6dc48d1 --- /dev/null +++ b/sound/soc/intel/common/sst-firmware.c | |||
| @@ -0,0 +1,1205 @@ | |||
| 1 | /* | ||
| 2 | * Intel SST Firmware Loader | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013, Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License version | ||
| 8 | * 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/sched.h> | ||
| 20 | #include <linux/firmware.h> | ||
| 21 | #include <linux/export.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/dma-mapping.h> | ||
| 24 | #include <linux/dmaengine.h> | ||
| 25 | #include <linux/pci.h> | ||
| 26 | #include <linux/acpi.h> | ||
| 27 | |||
| 28 | /* supported DMA engine drivers */ | ||
| 29 | #include <linux/platform_data/dma-dw.h> | ||
| 30 | #include <linux/dma/dw.h> | ||
| 31 | |||
| 32 | #include <asm/page.h> | ||
| 33 | #include <asm/pgtable.h> | ||
| 34 | |||
| 35 | #include "sst-dsp.h" | ||
| 36 | #include "sst-dsp-priv.h" | ||
| 37 | |||
| 38 | #define SST_DMA_RESOURCES 2 | ||
| 39 | #define SST_DSP_DMA_MAX_BURST 0x3 | ||
| 40 | #define SST_HSW_BLOCK_ANY 0xffffffff | ||
| 41 | |||
| 42 | #define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000 | ||
| 43 | |||
| 44 | struct sst_dma { | ||
| 45 | struct sst_dsp *sst; | ||
| 46 | |||
| 47 | struct dw_dma_chip *chip; | ||
| 48 | |||
| 49 | struct dma_async_tx_descriptor *desc; | ||
| 50 | struct dma_chan *ch; | ||
| 51 | }; | ||
| 52 | |||
| 53 | static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) | ||
| 54 | { | ||
| 55 | /* __iowrite32_copy use 32bit size values so divide by 4 */ | ||
| 56 | __iowrite32_copy((void *)dest, src, bytes/4); | ||
| 57 | } | ||
| 58 | |||
| 59 | static void sst_dma_transfer_complete(void *arg) | ||
| 60 | { | ||
| 61 | struct sst_dsp *sst = (struct sst_dsp *)arg; | ||
| 62 | |||
| 63 | dev_dbg(sst->dev, "DMA: callback\n"); | ||
| 64 | } | ||
| 65 | |||
| 66 | static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr, | ||
| 67 | dma_addr_t src_addr, size_t size) | ||
| 68 | { | ||
| 69 | struct dma_async_tx_descriptor *desc; | ||
| 70 | struct sst_dma *dma = sst->dma; | ||
| 71 | |||
| 72 | if (dma->ch == NULL) { | ||
| 73 | dev_err(sst->dev, "error: no DMA channel\n"); | ||
| 74 | return -ENODEV; | ||
| 75 | } | ||
| 76 | |||
| 77 | dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n", | ||
| 78 | (unsigned long)src_addr, (unsigned long)dest_addr, size); | ||
| 79 | |||
| 80 | desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr, | ||
| 81 | src_addr, size, DMA_CTRL_ACK); | ||
| 82 | if (!desc){ | ||
| 83 | dev_err(sst->dev, "error: dma prep memcpy failed\n"); | ||
| 84 | return -EINVAL; | ||
| 85 | } | ||
| 86 | |||
| 87 | desc->callback = sst_dma_transfer_complete; | ||
| 88 | desc->callback_param = sst; | ||
| 89 | |||
| 90 | desc->tx_submit(desc); | ||
| 91 | dma_wait_for_async_tx(desc); | ||
| 92 | |||
| 93 | return 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* copy to DSP */ | ||
| 97 | int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr, | ||
| 98 | dma_addr_t src_addr, size_t size) | ||
| 99 | { | ||
| 100 | return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP, | ||
| 101 | src_addr, size); | ||
| 102 | } | ||
| 103 | EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto); | ||
| 104 | |||
| 105 | /* copy from DSP */ | ||
| 106 | int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr, | ||
| 107 | dma_addr_t src_addr, size_t size) | ||
| 108 | { | ||
| 109 | return sst_dsp_dma_copy(sst, dest_addr, | ||
| 110 | src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size); | ||
| 111 | } | ||
| 112 | EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom); | ||
| 113 | |||
| 114 | /* remove module from memory - callers hold locks */ | ||
| 115 | static void block_list_remove(struct sst_dsp *dsp, | ||
| 116 | struct list_head *block_list) | ||
| 117 | { | ||
| 118 | struct sst_mem_block *block, *tmp; | ||
| 119 | int err; | ||
| 120 | |||
| 121 | /* disable each block */ | ||
| 122 | list_for_each_entry(block, block_list, module_list) { | ||
| 123 | |||
| 124 | if (block->ops && block->ops->disable) { | ||
| 125 | err = block->ops->disable(block); | ||
| 126 | if (err < 0) | ||
| 127 | dev_err(dsp->dev, | ||
| 128 | "error: cant disable block %d:%d\n", | ||
| 129 | block->type, block->index); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /* mark each block as free */ | ||
| 134 | list_for_each_entry_safe(block, tmp, block_list, module_list) { | ||
| 135 | list_del(&block->module_list); | ||
| 136 | list_move(&block->list, &dsp->free_block_list); | ||
| 137 | dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n", | ||
| 138 | block->type, block->index, block->offset); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | /* prepare the memory block to receive data from host - callers hold locks */ | ||
| 143 | static int block_list_prepare(struct sst_dsp *dsp, | ||
| 144 | struct list_head *block_list) | ||
| 145 | { | ||
| 146 | struct sst_mem_block *block; | ||
| 147 | int ret = 0; | ||
| 148 | |||
| 149 | /* enable each block so that's it'e ready for data */ | ||
| 150 | list_for_each_entry(block, block_list, module_list) { | ||
| 151 | |||
| 152 | if (block->ops && block->ops->enable && !block->users) { | ||
| 153 | ret = block->ops->enable(block); | ||
| 154 | if (ret < 0) { | ||
| 155 | dev_err(dsp->dev, | ||
| 156 | "error: cant disable block %d:%d\n", | ||
| 157 | block->type, block->index); | ||
| 158 | goto err; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | return ret; | ||
| 163 | |||
| 164 | err: | ||
| 165 | list_for_each_entry(block, block_list, module_list) { | ||
| 166 | if (block->ops && block->ops->disable) | ||
| 167 | block->ops->disable(block); | ||
| 168 | } | ||
| 169 | return ret; | ||
| 170 | } | ||
| 171 | |||
| 172 | static struct dw_dma_platform_data dw_pdata = { | ||
| 173 | .is_private = 1, | ||
| 174 | .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, | ||
| 175 | .chan_priority = CHAN_PRIORITY_ASCENDING, | ||
| 176 | }; | ||
| 177 | |||
| 178 | static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, | ||
| 179 | int irq) | ||
| 180 | { | ||
| 181 | struct dw_dma_chip *chip; | ||
| 182 | int err; | ||
| 183 | |||
| 184 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); | ||
| 185 | if (!chip) | ||
| 186 | return ERR_PTR(-ENOMEM); | ||
| 187 | |||
| 188 | chip->irq = irq; | ||
| 189 | chip->regs = devm_ioremap_resource(dev, mem); | ||
| 190 | if (IS_ERR(chip->regs)) | ||
| 191 | return ERR_CAST(chip->regs); | ||
| 192 | |||
| 193 | err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31)); | ||
| 194 | if (err) | ||
| 195 | return ERR_PTR(err); | ||
| 196 | |||
| 197 | chip->dev = dev; | ||
| 198 | err = dw_dma_probe(chip, &dw_pdata); | ||
| 199 | if (err) | ||
| 200 | return ERR_PTR(err); | ||
| 201 | |||
| 202 | return chip; | ||
| 203 | } | ||
| 204 | |||
| 205 | static void dw_remove(struct dw_dma_chip *chip) | ||
| 206 | { | ||
| 207 | dw_dma_remove(chip); | ||
| 208 | } | ||
| 209 | |||
| 210 | static bool dma_chan_filter(struct dma_chan *chan, void *param) | ||
| 211 | { | ||
| 212 | struct sst_dsp *dsp = (struct sst_dsp *)param; | ||
| 213 | |||
| 214 | return chan->device->dev == dsp->dma_dev; | ||
| 215 | } | ||
| 216 | |||
| 217 | int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) | ||
| 218 | { | ||
| 219 | struct sst_dma *dma = dsp->dma; | ||
| 220 | struct dma_slave_config slave; | ||
| 221 | dma_cap_mask_t mask; | ||
| 222 | int ret; | ||
| 223 | |||
| 224 | dma_cap_zero(mask); | ||
| 225 | dma_cap_set(DMA_SLAVE, mask); | ||
| 226 | dma_cap_set(DMA_MEMCPY, mask); | ||
| 227 | |||
| 228 | dma->ch = dma_request_channel(mask, dma_chan_filter, dsp); | ||
| 229 | if (dma->ch == NULL) { | ||
| 230 | dev_err(dsp->dev, "error: DMA request channel failed\n"); | ||
| 231 | return -EIO; | ||
| 232 | } | ||
| 233 | |||
| 234 | memset(&slave, 0, sizeof(slave)); | ||
| 235 | slave.direction = DMA_MEM_TO_DEV; | ||
| 236 | slave.src_addr_width = | ||
| 237 | slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
| 238 | slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST; | ||
| 239 | |||
| 240 | ret = dmaengine_slave_config(dma->ch, &slave); | ||
| 241 | if (ret) { | ||
| 242 | dev_err(dsp->dev, "error: unable to set DMA slave config %d\n", | ||
| 243 | ret); | ||
| 244 | dma_release_channel(dma->ch); | ||
| 245 | dma->ch = NULL; | ||
| 246 | } | ||
| 247 | |||
| 248 | return ret; | ||
| 249 | } | ||
| 250 | EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel); | ||
| 251 | |||
| 252 | void sst_dsp_dma_put_channel(struct sst_dsp *dsp) | ||
| 253 | { | ||
| 254 | struct sst_dma *dma = dsp->dma; | ||
| 255 | |||
| 256 | if (!dma->ch) | ||
| 257 | return; | ||
| 258 | |||
| 259 | dma_release_channel(dma->ch); | ||
| 260 | dma->ch = NULL; | ||
| 261 | } | ||
| 262 | EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel); | ||
| 263 | |||
| 264 | int sst_dma_new(struct sst_dsp *sst) | ||
| 265 | { | ||
| 266 | struct sst_pdata *sst_pdata = sst->pdata; | ||
| 267 | struct sst_dma *dma; | ||
| 268 | struct resource mem; | ||
| 269 | const char *dma_dev_name; | ||
| 270 | int ret = 0; | ||
| 271 | |||
| 272 | if (sst->pdata->resindex_dma_base == -1) | ||
| 273 | /* DMA is not used, return and squelsh error messages */ | ||
| 274 | return 0; | ||
| 275 | |||
| 276 | /* configure the correct platform data for whatever DMA engine | ||
| 277 | * is attached to the ADSP IP. */ | ||
| 278 | switch (sst->pdata->dma_engine) { | ||
| 279 | case SST_DMA_TYPE_DW: | ||
| 280 | dma_dev_name = "dw_dmac"; | ||
| 281 | break; | ||
| 282 | default: | ||
| 283 | dev_err(sst->dev, "error: invalid DMA engine %d\n", | ||
| 284 | sst->pdata->dma_engine); | ||
| 285 | return -EINVAL; | ||
| 286 | } | ||
| 287 | |||
| 288 | dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL); | ||
| 289 | if (!dma) | ||
| 290 | return -ENOMEM; | ||
| 291 | |||
| 292 | dma->sst = sst; | ||
| 293 | |||
| 294 | memset(&mem, 0, sizeof(mem)); | ||
| 295 | |||
| 296 | mem.start = sst->addr.lpe_base + sst_pdata->dma_base; | ||
| 297 | mem.end = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1; | ||
| 298 | mem.flags = IORESOURCE_MEM; | ||
| 299 | |||
| 300 | /* now register DMA engine device */ | ||
| 301 | dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq); | ||
| 302 | if (IS_ERR(dma->chip)) { | ||
| 303 | dev_err(sst->dev, "error: DMA device register failed\n"); | ||
| 304 | ret = PTR_ERR(dma->chip); | ||
| 305 | goto err_dma_dev; | ||
| 306 | } | ||
| 307 | |||
| 308 | sst->dma = dma; | ||
| 309 | sst->fw_use_dma = true; | ||
| 310 | return 0; | ||
| 311 | |||
| 312 | err_dma_dev: | ||
| 313 | devm_kfree(sst->dev, dma); | ||
| 314 | return ret; | ||
| 315 | } | ||
| 316 | EXPORT_SYMBOL(sst_dma_new); | ||
| 317 | |||
| 318 | void sst_dma_free(struct sst_dma *dma) | ||
| 319 | { | ||
| 320 | |||
| 321 | if (dma == NULL) | ||
| 322 | return; | ||
| 323 | |||
| 324 | if (dma->ch) | ||
| 325 | dma_release_channel(dma->ch); | ||
| 326 | |||
| 327 | if (dma->chip) | ||
| 328 | dw_remove(dma->chip); | ||
| 329 | |||
| 330 | } | ||
| 331 | EXPORT_SYMBOL(sst_dma_free); | ||
| 332 | |||
| 333 | /* create new generic firmware object */ | ||
| 334 | struct sst_fw *sst_fw_new(struct sst_dsp *dsp, | ||
| 335 | const struct firmware *fw, void *private) | ||
| 336 | { | ||
| 337 | struct sst_fw *sst_fw; | ||
| 338 | int err; | ||
| 339 | |||
| 340 | if (!dsp->ops->parse_fw) | ||
| 341 | return NULL; | ||
| 342 | |||
| 343 | sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL); | ||
| 344 | if (sst_fw == NULL) | ||
| 345 | return NULL; | ||
| 346 | |||
| 347 | sst_fw->dsp = dsp; | ||
| 348 | sst_fw->private = private; | ||
| 349 | sst_fw->size = fw->size; | ||
| 350 | |||
| 351 | /* allocate DMA buffer to store FW data */ | ||
| 352 | sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size, | ||
| 353 | &sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL); | ||
| 354 | if (!sst_fw->dma_buf) { | ||
| 355 | dev_err(dsp->dev, "error: DMA alloc failed\n"); | ||
| 356 | kfree(sst_fw); | ||
| 357 | return NULL; | ||
| 358 | } | ||
| 359 | |||
| 360 | /* copy FW data to DMA-able memory */ | ||
| 361 | memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size); | ||
| 362 | |||
| 363 | if (dsp->fw_use_dma) { | ||
| 364 | err = sst_dsp_dma_get_channel(dsp, 0); | ||
| 365 | if (err < 0) | ||
| 366 | goto chan_err; | ||
| 367 | } | ||
| 368 | |||
| 369 | /* call core specific FW paser to load FW data into DSP */ | ||
| 370 | err = dsp->ops->parse_fw(sst_fw); | ||
| 371 | if (err < 0) { | ||
| 372 | dev_err(dsp->dev, "error: parse fw failed %d\n", err); | ||
| 373 | goto parse_err; | ||
| 374 | } | ||
| 375 | |||
| 376 | if (dsp->fw_use_dma) | ||
| 377 | sst_dsp_dma_put_channel(dsp); | ||
| 378 | |||
| 379 | mutex_lock(&dsp->mutex); | ||
| 380 | list_add(&sst_fw->list, &dsp->fw_list); | ||
| 381 | mutex_unlock(&dsp->mutex); | ||
| 382 | |||
| 383 | return sst_fw; | ||
| 384 | |||
| 385 | parse_err: | ||
| 386 | if (dsp->fw_use_dma) | ||
| 387 | sst_dsp_dma_put_channel(dsp); | ||
| 388 | chan_err: | ||
| 389 | dma_free_coherent(dsp->dma_dev, sst_fw->size, | ||
| 390 | sst_fw->dma_buf, | ||
| 391 | sst_fw->dmable_fw_paddr); | ||
| 392 | sst_fw->dma_buf = NULL; | ||
| 393 | kfree(sst_fw); | ||
| 394 | return NULL; | ||
| 395 | } | ||
| 396 | EXPORT_SYMBOL_GPL(sst_fw_new); | ||
| 397 | |||
| 398 | int sst_fw_reload(struct sst_fw *sst_fw) | ||
| 399 | { | ||
| 400 | struct sst_dsp *dsp = sst_fw->dsp; | ||
| 401 | int ret; | ||
| 402 | |||
| 403 | dev_dbg(dsp->dev, "reloading firmware\n"); | ||
| 404 | |||
| 405 | /* call core specific FW paser to load FW data into DSP */ | ||
| 406 | ret = dsp->ops->parse_fw(sst_fw); | ||
| 407 | if (ret < 0) | ||
| 408 | dev_err(dsp->dev, "error: parse fw failed %d\n", ret); | ||
| 409 | |||
| 410 | return ret; | ||
| 411 | } | ||
| 412 | EXPORT_SYMBOL_GPL(sst_fw_reload); | ||
| 413 | |||
| 414 | void sst_fw_unload(struct sst_fw *sst_fw) | ||
| 415 | { | ||
| 416 | struct sst_dsp *dsp = sst_fw->dsp; | ||
| 417 | struct sst_module *module, *mtmp; | ||
| 418 | struct sst_module_runtime *runtime, *rtmp; | ||
| 419 | |||
| 420 | dev_dbg(dsp->dev, "unloading firmware\n"); | ||
| 421 | |||
| 422 | mutex_lock(&dsp->mutex); | ||
| 423 | |||
| 424 | /* check module by module */ | ||
| 425 | list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) { | ||
| 426 | if (module->sst_fw == sst_fw) { | ||
| 427 | |||
| 428 | /* remove runtime modules */ | ||
| 429 | list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) { | ||
| 430 | |||
| 431 | block_list_remove(dsp, &runtime->block_list); | ||
| 432 | list_del(&runtime->list); | ||
| 433 | kfree(runtime); | ||
| 434 | } | ||
| 435 | |||
| 436 | /* now remove the module */ | ||
| 437 | block_list_remove(dsp, &module->block_list); | ||
| 438 | list_del(&module->list); | ||
| 439 | kfree(module); | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | /* remove all scratch blocks */ | ||
| 444 | block_list_remove(dsp, &dsp->scratch_block_list); | ||
| 445 | |||
| 446 | mutex_unlock(&dsp->mutex); | ||
| 447 | } | ||
| 448 | EXPORT_SYMBOL_GPL(sst_fw_unload); | ||
| 449 | |||
| 450 | /* free single firmware object */ | ||
| 451 | void sst_fw_free(struct sst_fw *sst_fw) | ||
| 452 | { | ||
| 453 | struct sst_dsp *dsp = sst_fw->dsp; | ||
| 454 | |||
| 455 | mutex_lock(&dsp->mutex); | ||
| 456 | list_del(&sst_fw->list); | ||
| 457 | mutex_unlock(&dsp->mutex); | ||
| 458 | |||
| 459 | if (sst_fw->dma_buf) | ||
| 460 | dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf, | ||
| 461 | sst_fw->dmable_fw_paddr); | ||
| 462 | kfree(sst_fw); | ||
| 463 | } | ||
| 464 | EXPORT_SYMBOL_GPL(sst_fw_free); | ||
| 465 | |||
| 466 | /* free all firmware objects */ | ||
| 467 | void sst_fw_free_all(struct sst_dsp *dsp) | ||
| 468 | { | ||
| 469 | struct sst_fw *sst_fw, *t; | ||
| 470 | |||
| 471 | mutex_lock(&dsp->mutex); | ||
| 472 | list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) { | ||
| 473 | |||
| 474 | list_del(&sst_fw->list); | ||
| 475 | dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf, | ||
| 476 | sst_fw->dmable_fw_paddr); | ||
| 477 | kfree(sst_fw); | ||
| 478 | } | ||
| 479 | mutex_unlock(&dsp->mutex); | ||
| 480 | } | ||
| 481 | EXPORT_SYMBOL_GPL(sst_fw_free_all); | ||
| 482 | |||
| 483 | /* create a new SST generic module from FW template */ | ||
| 484 | struct sst_module *sst_module_new(struct sst_fw *sst_fw, | ||
| 485 | struct sst_module_template *template, void *private) | ||
| 486 | { | ||
| 487 | struct sst_dsp *dsp = sst_fw->dsp; | ||
| 488 | struct sst_module *sst_module; | ||
| 489 | |||
| 490 | sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL); | ||
| 491 | if (sst_module == NULL) | ||
| 492 | return NULL; | ||
| 493 | |||
| 494 | sst_module->id = template->id; | ||
| 495 | sst_module->dsp = dsp; | ||
| 496 | sst_module->sst_fw = sst_fw; | ||
| 497 | sst_module->scratch_size = template->scratch_size; | ||
| 498 | sst_module->persistent_size = template->persistent_size; | ||
| 499 | sst_module->entry = template->entry; | ||
| 500 | sst_module->state = SST_MODULE_STATE_UNLOADED; | ||
| 501 | |||
| 502 | INIT_LIST_HEAD(&sst_module->block_list); | ||
| 503 | INIT_LIST_HEAD(&sst_module->runtime_list); | ||
| 504 | |||
| 505 | mutex_lock(&dsp->mutex); | ||
| 506 | list_add(&sst_module->list, &dsp->module_list); | ||
| 507 | mutex_unlock(&dsp->mutex); | ||
| 508 | |||
| 509 | return sst_module; | ||
| 510 | } | ||
| 511 | EXPORT_SYMBOL_GPL(sst_module_new); | ||
| 512 | |||
| 513 | /* free firmware module and remove from available list */ | ||
| 514 | void sst_module_free(struct sst_module *sst_module) | ||
| 515 | { | ||
| 516 | struct sst_dsp *dsp = sst_module->dsp; | ||
| 517 | |||
| 518 | mutex_lock(&dsp->mutex); | ||
| 519 | list_del(&sst_module->list); | ||
| 520 | mutex_unlock(&dsp->mutex); | ||
| 521 | |||
| 522 | kfree(sst_module); | ||
| 523 | } | ||
| 524 | EXPORT_SYMBOL_GPL(sst_module_free); | ||
| 525 | |||
| 526 | struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module, | ||
| 527 | int id, void *private) | ||
| 528 | { | ||
| 529 | struct sst_dsp *dsp = module->dsp; | ||
| 530 | struct sst_module_runtime *runtime; | ||
| 531 | |||
| 532 | runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); | ||
| 533 | if (runtime == NULL) | ||
| 534 | return NULL; | ||
| 535 | |||
| 536 | runtime->id = id; | ||
| 537 | runtime->dsp = dsp; | ||
| 538 | runtime->module = module; | ||
| 539 | INIT_LIST_HEAD(&runtime->block_list); | ||
| 540 | |||
| 541 | mutex_lock(&dsp->mutex); | ||
| 542 | list_add(&runtime->list, &module->runtime_list); | ||
| 543 | mutex_unlock(&dsp->mutex); | ||
| 544 | |||
| 545 | return runtime; | ||
| 546 | } | ||
| 547 | EXPORT_SYMBOL_GPL(sst_module_runtime_new); | ||
| 548 | |||
| 549 | void sst_module_runtime_free(struct sst_module_runtime *runtime) | ||
| 550 | { | ||
| 551 | struct sst_dsp *dsp = runtime->dsp; | ||
| 552 | |||
| 553 | mutex_lock(&dsp->mutex); | ||
| 554 | list_del(&runtime->list); | ||
| 555 | mutex_unlock(&dsp->mutex); | ||
| 556 | |||
| 557 | kfree(runtime); | ||
| 558 | } | ||
| 559 | EXPORT_SYMBOL_GPL(sst_module_runtime_free); | ||
| 560 | |||
| 561 | static struct sst_mem_block *find_block(struct sst_dsp *dsp, | ||
| 562 | struct sst_block_allocator *ba) | ||
| 563 | { | ||
| 564 | struct sst_mem_block *block; | ||
| 565 | |||
| 566 | list_for_each_entry(block, &dsp->free_block_list, list) { | ||
| 567 | if (block->type == ba->type && block->offset == ba->offset) | ||
| 568 | return block; | ||
| 569 | } | ||
| 570 | |||
| 571 | return NULL; | ||
| 572 | } | ||
| 573 | |||
| 574 | /* Block allocator must be on block boundary */ | ||
| 575 | static int block_alloc_contiguous(struct sst_dsp *dsp, | ||
| 576 | struct sst_block_allocator *ba, struct list_head *block_list) | ||
| 577 | { | ||
| 578 | struct list_head tmp = LIST_HEAD_INIT(tmp); | ||
| 579 | struct sst_mem_block *block; | ||
| 580 | u32 block_start = SST_HSW_BLOCK_ANY; | ||
| 581 | int size = ba->size, offset = ba->offset; | ||
| 582 | |||
| 583 | while (ba->size > 0) { | ||
| 584 | |||
| 585 | block = find_block(dsp, ba); | ||
| 586 | if (!block) { | ||
| 587 | list_splice(&tmp, &dsp->free_block_list); | ||
| 588 | |||
| 589 | ba->size = size; | ||
| 590 | ba->offset = offset; | ||
| 591 | return -ENOMEM; | ||
| 592 | } | ||
| 593 | |||
| 594 | list_move_tail(&block->list, &tmp); | ||
| 595 | ba->offset += block->size; | ||
| 596 | ba->size -= block->size; | ||
| 597 | } | ||
| 598 | ba->size = size; | ||
| 599 | ba->offset = offset; | ||
| 600 | |||
| 601 | list_for_each_entry(block, &tmp, list) { | ||
| 602 | |||
| 603 | if (block->offset < block_start) | ||
| 604 | block_start = block->offset; | ||
| 605 | |||
| 606 | list_add(&block->module_list, block_list); | ||
| 607 | |||
| 608 | dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", | ||
| 609 | block->type, block->index, block->offset); | ||
| 610 | } | ||
| 611 | |||
| 612 | list_splice(&tmp, &dsp->used_block_list); | ||
| 613 | return 0; | ||
| 614 | } | ||
| 615 | |||
| 616 | /* allocate first free DSP blocks for data - callers hold locks */ | ||
| 617 | static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba, | ||
| 618 | struct list_head *block_list) | ||
| 619 | { | ||
| 620 | struct sst_mem_block *block, *tmp; | ||
| 621 | int ret = 0; | ||
| 622 | |||
| 623 | if (ba->size == 0) | ||
| 624 | return 0; | ||
| 625 | |||
| 626 | /* find first free whole blocks that can hold module */ | ||
| 627 | list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { | ||
| 628 | |||
| 629 | /* ignore blocks with wrong type */ | ||
| 630 | if (block->type != ba->type) | ||
| 631 | continue; | ||
| 632 | |||
| 633 | if (ba->size > block->size) | ||
| 634 | continue; | ||
| 635 | |||
| 636 | ba->offset = block->offset; | ||
| 637 | block->bytes_used = ba->size % block->size; | ||
| 638 | list_add(&block->module_list, block_list); | ||
| 639 | list_move(&block->list, &dsp->used_block_list); | ||
| 640 | dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", | ||
| 641 | block->type, block->index, block->offset); | ||
| 642 | return 0; | ||
| 643 | } | ||
| 644 | |||
| 645 | /* then find free multiple blocks that can hold module */ | ||
| 646 | list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { | ||
| 647 | |||
| 648 | /* ignore blocks with wrong type */ | ||
| 649 | if (block->type != ba->type) | ||
| 650 | continue; | ||
| 651 | |||
| 652 | /* do we span > 1 blocks */ | ||
| 653 | if (ba->size > block->size) { | ||
| 654 | |||
| 655 | /* align ba to block boundary */ | ||
| 656 | ba->offset = block->offset; | ||
| 657 | |||
| 658 | ret = block_alloc_contiguous(dsp, ba, block_list); | ||
| 659 | if (ret == 0) | ||
| 660 | return ret; | ||
| 661 | |||
| 662 | } | ||
| 663 | } | ||
| 664 | |||
| 665 | /* not enough free block space */ | ||
| 666 | return -ENOMEM; | ||
| 667 | } | ||
| 668 | |||
| 669 | int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba, | ||
| 670 | struct list_head *block_list) | ||
| 671 | { | ||
| 672 | int ret; | ||
| 673 | |||
| 674 | dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n", | ||
| 675 | ba->size, ba->offset, ba->type); | ||
| 676 | |||
| 677 | mutex_lock(&dsp->mutex); | ||
| 678 | |||
| 679 | ret = block_alloc(dsp, ba, block_list); | ||
| 680 | if (ret < 0) { | ||
| 681 | dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret); | ||
| 682 | goto out; | ||
| 683 | } | ||
| 684 | |||
| 685 | /* prepare DSP blocks for module usage */ | ||
| 686 | ret = block_list_prepare(dsp, block_list); | ||
| 687 | if (ret < 0) | ||
| 688 | dev_err(dsp->dev, "error: prepare failed\n"); | ||
| 689 | |||
| 690 | out: | ||
| 691 | mutex_unlock(&dsp->mutex); | ||
| 692 | return ret; | ||
| 693 | } | ||
| 694 | EXPORT_SYMBOL_GPL(sst_alloc_blocks); | ||
| 695 | |||
| 696 | int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list) | ||
| 697 | { | ||
| 698 | mutex_lock(&dsp->mutex); | ||
| 699 | block_list_remove(dsp, block_list); | ||
| 700 | mutex_unlock(&dsp->mutex); | ||
| 701 | return 0; | ||
| 702 | } | ||
| 703 | EXPORT_SYMBOL_GPL(sst_free_blocks); | ||
| 704 | |||
| 705 | /* allocate memory blocks for static module addresses - callers hold locks */ | ||
| 706 | static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba, | ||
| 707 | struct list_head *block_list) | ||
| 708 | { | ||
| 709 | struct sst_mem_block *block, *tmp; | ||
| 710 | struct sst_block_allocator ba_tmp = *ba; | ||
| 711 | u32 end = ba->offset + ba->size, block_end; | ||
| 712 | int err; | ||
| 713 | |||
| 714 | /* only IRAM/DRAM blocks are managed */ | ||
| 715 | if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM) | ||
| 716 | return 0; | ||
| 717 | |||
| 718 | /* are blocks already attached to this module */ | ||
| 719 | list_for_each_entry_safe(block, tmp, block_list, module_list) { | ||
| 720 | |||
| 721 | /* ignore blocks with wrong type */ | ||
| 722 | if (block->type != ba->type) | ||
| 723 | continue; | ||
| 724 | |||
| 725 | block_end = block->offset + block->size; | ||
| 726 | |||
| 727 | /* find block that holds section */ | ||
| 728 | if (ba->offset >= block->offset && end <= block_end) | ||
| 729 | return 0; | ||
| 730 | |||
| 731 | /* does block span more than 1 section */ | ||
| 732 | if (ba->offset >= block->offset && ba->offset < block_end) { | ||
| 733 | |||
| 734 | /* align ba to block boundary */ | ||
| 735 | ba_tmp.size -= block_end - ba->offset; | ||
| 736 | ba_tmp.offset = block_end; | ||
| 737 | err = block_alloc_contiguous(dsp, &ba_tmp, block_list); | ||
| 738 | if (err < 0) | ||
| 739 | return -ENOMEM; | ||
| 740 | |||
| 741 | /* module already owns blocks */ | ||
| 742 | return 0; | ||
| 743 | } | ||
| 744 | } | ||
| 745 | |||
| 746 | /* find first free blocks that can hold section in free list */ | ||
| 747 | list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { | ||
| 748 | block_end = block->offset + block->size; | ||
| 749 | |||
| 750 | /* ignore blocks with wrong type */ | ||
| 751 | if (block->type != ba->type) | ||
| 752 | continue; | ||
| 753 | |||
| 754 | /* find block that holds section */ | ||
| 755 | if (ba->offset >= block->offset && end <= block_end) { | ||
| 756 | |||
| 757 | /* add block */ | ||
| 758 | list_move(&block->list, &dsp->used_block_list); | ||
| 759 | list_add(&block->module_list, block_list); | ||
| 760 | dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", | ||
| 761 | block->type, block->index, block->offset); | ||
| 762 | return 0; | ||
| 763 | } | ||
| 764 | |||
| 765 | /* does block span more than 1 section */ | ||
| 766 | if (ba->offset >= block->offset && ba->offset < block_end) { | ||
| 767 | |||
| 768 | /* add block */ | ||
| 769 | list_move(&block->list, &dsp->used_block_list); | ||
| 770 | list_add(&block->module_list, block_list); | ||
| 771 | /* align ba to block boundary */ | ||
| 772 | ba_tmp.size -= block_end - ba->offset; | ||
| 773 | ba_tmp.offset = block_end; | ||
| 774 | |||
| 775 | err = block_alloc_contiguous(dsp, &ba_tmp, block_list); | ||
| 776 | if (err < 0) | ||
| 777 | return -ENOMEM; | ||
| 778 | |||
| 779 | return 0; | ||
| 780 | } | ||
| 781 | } | ||
| 782 | |||
| 783 | return -ENOMEM; | ||
| 784 | } | ||
| 785 | |||
| 786 | /* Load fixed module data into DSP memory blocks */ | ||
| 787 | int sst_module_alloc_blocks(struct sst_module *module) | ||
| 788 | { | ||
| 789 | struct sst_dsp *dsp = module->dsp; | ||
| 790 | struct sst_fw *sst_fw = module->sst_fw; | ||
| 791 | struct sst_block_allocator ba; | ||
| 792 | int ret; | ||
| 793 | |||
| 794 | memset(&ba, 0, sizeof(ba)); | ||
| 795 | ba.size = module->size; | ||
| 796 | ba.type = module->type; | ||
| 797 | ba.offset = module->offset; | ||
| 798 | |||
| 799 | dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n", | ||
| 800 | ba.size, ba.offset, ba.type); | ||
| 801 | |||
| 802 | mutex_lock(&dsp->mutex); | ||
| 803 | |||
| 804 | /* alloc blocks that includes this section */ | ||
| 805 | ret = block_alloc_fixed(dsp, &ba, &module->block_list); | ||
| 806 | if (ret < 0) { | ||
| 807 | dev_err(dsp->dev, | ||
| 808 | "error: no free blocks for section at offset 0x%x size 0x%x\n", | ||
| 809 | module->offset, module->size); | ||
| 810 | mutex_unlock(&dsp->mutex); | ||
| 811 | return -ENOMEM; | ||
| 812 | } | ||
| 813 | |||
| 814 | /* prepare DSP blocks for module copy */ | ||
| 815 | ret = block_list_prepare(dsp, &module->block_list); | ||
| 816 | if (ret < 0) { | ||
| 817 | dev_err(dsp->dev, "error: fw module prepare failed\n"); | ||
| 818 | goto err; | ||
| 819 | } | ||
| 820 | |||
| 821 | /* copy partial module data to blocks */ | ||
| 822 | if (dsp->fw_use_dma) { | ||
| 823 | ret = sst_dsp_dma_copyto(dsp, | ||
| 824 | dsp->addr.lpe_base + module->offset, | ||
| 825 | sst_fw->dmable_fw_paddr + module->data_offset, | ||
| 826 | module->size); | ||
| 827 | if (ret < 0) { | ||
| 828 | dev_err(dsp->dev, "error: module copy failed\n"); | ||
| 829 | goto err; | ||
| 830 | } | ||
| 831 | } else | ||
| 832 | sst_memcpy32(dsp->addr.lpe + module->offset, module->data, | ||
| 833 | module->size); | ||
| 834 | |||
| 835 | mutex_unlock(&dsp->mutex); | ||
| 836 | return ret; | ||
| 837 | |||
| 838 | err: | ||
| 839 | block_list_remove(dsp, &module->block_list); | ||
| 840 | mutex_unlock(&dsp->mutex); | ||
| 841 | return ret; | ||
| 842 | } | ||
| 843 | EXPORT_SYMBOL_GPL(sst_module_alloc_blocks); | ||
| 844 | |||
| 845 | /* Unload entire module from DSP memory */ | ||
| 846 | int sst_module_free_blocks(struct sst_module *module) | ||
| 847 | { | ||
| 848 | struct sst_dsp *dsp = module->dsp; | ||
| 849 | |||
| 850 | mutex_lock(&dsp->mutex); | ||
| 851 | block_list_remove(dsp, &module->block_list); | ||
| 852 | mutex_unlock(&dsp->mutex); | ||
| 853 | return 0; | ||
| 854 | } | ||
| 855 | EXPORT_SYMBOL_GPL(sst_module_free_blocks); | ||
| 856 | |||
| 857 | int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime, | ||
| 858 | int offset) | ||
| 859 | { | ||
| 860 | struct sst_dsp *dsp = runtime->dsp; | ||
| 861 | struct sst_module *module = runtime->module; | ||
| 862 | struct sst_block_allocator ba; | ||
| 863 | int ret; | ||
| 864 | |||
| 865 | if (module->persistent_size == 0) | ||
| 866 | return 0; | ||
| 867 | |||
| 868 | memset(&ba, 0, sizeof(ba)); | ||
| 869 | ba.size = module->persistent_size; | ||
| 870 | ba.type = SST_MEM_DRAM; | ||
| 871 | |||
| 872 | mutex_lock(&dsp->mutex); | ||
| 873 | |||
| 874 | /* do we need to allocate at a fixed address ? */ | ||
| 875 | if (offset != 0) { | ||
| 876 | |||
| 877 | ba.offset = offset; | ||
| 878 | |||
| 879 | dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n", | ||
| 880 | ba.size, ba.type, ba.offset); | ||
| 881 | |||
| 882 | /* alloc blocks that includes this section */ | ||
| 883 | ret = block_alloc_fixed(dsp, &ba, &runtime->block_list); | ||
| 884 | |||
| 885 | } else { | ||
| 886 | dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n", | ||
| 887 | ba.size, ba.type); | ||
| 888 | |||
| 889 | /* alloc blocks that includes this section */ | ||
| 890 | ret = block_alloc(dsp, &ba, &runtime->block_list); | ||
| 891 | } | ||
| 892 | if (ret < 0) { | ||
| 893 | dev_err(dsp->dev, | ||
| 894 | "error: no free blocks for runtime module size 0x%x\n", | ||
| 895 | module->persistent_size); | ||
| 896 | mutex_unlock(&dsp->mutex); | ||
| 897 | return -ENOMEM; | ||
| 898 | } | ||
| 899 | runtime->persistent_offset = ba.offset; | ||
| 900 | |||
| 901 | /* prepare DSP blocks for module copy */ | ||
| 902 | ret = block_list_prepare(dsp, &runtime->block_list); | ||
| 903 | if (ret < 0) { | ||
| 904 | dev_err(dsp->dev, "error: runtime block prepare failed\n"); | ||
| 905 | goto err; | ||
| 906 | } | ||
| 907 | |||
| 908 | mutex_unlock(&dsp->mutex); | ||
| 909 | return ret; | ||
| 910 | |||
| 911 | err: | ||
| 912 | block_list_remove(dsp, &module->block_list); | ||
| 913 | mutex_unlock(&dsp->mutex); | ||
| 914 | return ret; | ||
| 915 | } | ||
| 916 | EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks); | ||
| 917 | |||
| 918 | int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime) | ||
| 919 | { | ||
| 920 | struct sst_dsp *dsp = runtime->dsp; | ||
| 921 | |||
| 922 | mutex_lock(&dsp->mutex); | ||
| 923 | block_list_remove(dsp, &runtime->block_list); | ||
| 924 | mutex_unlock(&dsp->mutex); | ||
| 925 | return 0; | ||
| 926 | } | ||
| 927 | EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks); | ||
| 928 | |||
| 929 | int sst_module_runtime_save(struct sst_module_runtime *runtime, | ||
| 930 | struct sst_module_runtime_context *context) | ||
| 931 | { | ||
| 932 | struct sst_dsp *dsp = runtime->dsp; | ||
| 933 | struct sst_module *module = runtime->module; | ||
| 934 | int ret = 0; | ||
| 935 | |||
| 936 | dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n", | ||
| 937 | runtime->id, runtime->persistent_offset, | ||
| 938 | module->persistent_size); | ||
| 939 | |||
| 940 | context->buffer = dma_alloc_coherent(dsp->dma_dev, | ||
| 941 | module->persistent_size, | ||
| 942 | &context->dma_buffer, GFP_DMA | GFP_KERNEL); | ||
| 943 | if (!context->buffer) { | ||
| 944 | dev_err(dsp->dev, "error: DMA context alloc failed\n"); | ||
| 945 | return -ENOMEM; | ||
| 946 | } | ||
| 947 | |||
| 948 | mutex_lock(&dsp->mutex); | ||
| 949 | |||
| 950 | if (dsp->fw_use_dma) { | ||
| 951 | |||
| 952 | ret = sst_dsp_dma_get_channel(dsp, 0); | ||
| 953 | if (ret < 0) | ||
| 954 | goto err; | ||
| 955 | |||
| 956 | ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer, | ||
| 957 | dsp->addr.lpe_base + runtime->persistent_offset, | ||
| 958 | module->persistent_size); | ||
| 959 | sst_dsp_dma_put_channel(dsp); | ||
| 960 | if (ret < 0) { | ||
| 961 | dev_err(dsp->dev, "error: context copy failed\n"); | ||
| 962 | goto err; | ||
| 963 | } | ||
| 964 | } else | ||
| 965 | sst_memcpy32(context->buffer, dsp->addr.lpe + | ||
| 966 | runtime->persistent_offset, | ||
| 967 | module->persistent_size); | ||
| 968 | |||
| 969 | err: | ||
| 970 | mutex_unlock(&dsp->mutex); | ||
| 971 | return ret; | ||
| 972 | } | ||
| 973 | EXPORT_SYMBOL_GPL(sst_module_runtime_save); | ||
| 974 | |||
| 975 | int sst_module_runtime_restore(struct sst_module_runtime *runtime, | ||
| 976 | struct sst_module_runtime_context *context) | ||
| 977 | { | ||
| 978 | struct sst_dsp *dsp = runtime->dsp; | ||
| 979 | struct sst_module *module = runtime->module; | ||
| 980 | int ret = 0; | ||
| 981 | |||
| 982 | dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n", | ||
| 983 | runtime->id, runtime->persistent_offset, | ||
| 984 | module->persistent_size); | ||
| 985 | |||
| 986 | mutex_lock(&dsp->mutex); | ||
| 987 | |||
| 988 | if (!context->buffer) { | ||
| 989 | dev_info(dsp->dev, "no context buffer need to restore!\n"); | ||
| 990 | goto err; | ||
| 991 | } | ||
| 992 | |||
| 993 | if (dsp->fw_use_dma) { | ||
| 994 | |||
| 995 | ret = sst_dsp_dma_get_channel(dsp, 0); | ||
| 996 | if (ret < 0) | ||
| 997 | goto err; | ||
| 998 | |||
| 999 | ret = sst_dsp_dma_copyto(dsp, | ||
| 1000 | dsp->addr.lpe_base + runtime->persistent_offset, | ||
| 1001 | context->dma_buffer, module->persistent_size); | ||
| 1002 | sst_dsp_dma_put_channel(dsp); | ||
| 1003 | if (ret < 0) { | ||
| 1004 | dev_err(dsp->dev, "error: module copy failed\n"); | ||
| 1005 | goto err; | ||
| 1006 | } | ||
| 1007 | } else | ||
| 1008 | sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset, | ||
| 1009 | context->buffer, module->persistent_size); | ||
| 1010 | |||
| 1011 | dma_free_coherent(dsp->dma_dev, module->persistent_size, | ||
| 1012 | context->buffer, context->dma_buffer); | ||
| 1013 | context->buffer = NULL; | ||
| 1014 | |||
| 1015 | err: | ||
| 1016 | mutex_unlock(&dsp->mutex); | ||
| 1017 | return ret; | ||
| 1018 | } | ||
| 1019 | EXPORT_SYMBOL_GPL(sst_module_runtime_restore); | ||
| 1020 | |||
| 1021 | /* register a DSP memory block for use with FW based modules */ | ||
| 1022 | struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, | ||
| 1023 | u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, | ||
| 1024 | void *private) | ||
| 1025 | { | ||
| 1026 | struct sst_mem_block *block; | ||
| 1027 | |||
| 1028 | block = kzalloc(sizeof(*block), GFP_KERNEL); | ||
| 1029 | if (block == NULL) | ||
| 1030 | return NULL; | ||
| 1031 | |||
| 1032 | block->offset = offset; | ||
| 1033 | block->size = size; | ||
| 1034 | block->index = index; | ||
| 1035 | block->type = type; | ||
| 1036 | block->dsp = dsp; | ||
| 1037 | block->private = private; | ||
| 1038 | block->ops = ops; | ||
| 1039 | |||
| 1040 | mutex_lock(&dsp->mutex); | ||
| 1041 | list_add(&block->list, &dsp->free_block_list); | ||
| 1042 | mutex_unlock(&dsp->mutex); | ||
| 1043 | |||
| 1044 | return block; | ||
| 1045 | } | ||
| 1046 | EXPORT_SYMBOL_GPL(sst_mem_block_register); | ||
| 1047 | |||
| 1048 | /* unregister all DSP memory blocks */ | ||
| 1049 | void sst_mem_block_unregister_all(struct sst_dsp *dsp) | ||
| 1050 | { | ||
| 1051 | struct sst_mem_block *block, *tmp; | ||
| 1052 | |||
| 1053 | mutex_lock(&dsp->mutex); | ||
| 1054 | |||
| 1055 | /* unregister used blocks */ | ||
| 1056 | list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) { | ||
| 1057 | list_del(&block->list); | ||
| 1058 | kfree(block); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | /* unregister free blocks */ | ||
| 1062 | list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { | ||
| 1063 | list_del(&block->list); | ||
| 1064 | kfree(block); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | mutex_unlock(&dsp->mutex); | ||
| 1068 | } | ||
| 1069 | EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all); | ||
| 1070 | |||
| 1071 | /* allocate scratch buffer blocks */ | ||
| 1072 | int sst_block_alloc_scratch(struct sst_dsp *dsp) | ||
| 1073 | { | ||
| 1074 | struct sst_module *module; | ||
| 1075 | struct sst_block_allocator ba; | ||
| 1076 | int ret; | ||
| 1077 | |||
| 1078 | mutex_lock(&dsp->mutex); | ||
| 1079 | |||
| 1080 | /* calculate required scratch size */ | ||
| 1081 | dsp->scratch_size = 0; | ||
| 1082 | list_for_each_entry(module, &dsp->module_list, list) { | ||
| 1083 | dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n", | ||
| 1084 | module->id, module->scratch_size); | ||
| 1085 | if (dsp->scratch_size < module->scratch_size) | ||
| 1086 | dsp->scratch_size = module->scratch_size; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n", | ||
| 1090 | dsp->scratch_size); | ||
| 1091 | |||
| 1092 | if (dsp->scratch_size == 0) { | ||
| 1093 | dev_info(dsp->dev, "no modules need scratch buffer\n"); | ||
| 1094 | mutex_unlock(&dsp->mutex); | ||
| 1095 | return 0; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | /* allocate blocks for module scratch buffers */ | ||
| 1099 | dev_dbg(dsp->dev, "allocating scratch blocks\n"); | ||
| 1100 | |||
| 1101 | ba.size = dsp->scratch_size; | ||
| 1102 | ba.type = SST_MEM_DRAM; | ||
| 1103 | |||
| 1104 | /* do we need to allocate at fixed offset */ | ||
| 1105 | if (dsp->scratch_offset != 0) { | ||
| 1106 | |||
| 1107 | dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n", | ||
| 1108 | ba.size, ba.type, ba.offset); | ||
| 1109 | |||
| 1110 | ba.offset = dsp->scratch_offset; | ||
| 1111 | |||
| 1112 | /* alloc blocks that includes this section */ | ||
| 1113 | ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list); | ||
| 1114 | |||
| 1115 | } else { | ||
| 1116 | dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n", | ||
| 1117 | ba.size, ba.type); | ||
| 1118 | |||
| 1119 | ba.offset = 0; | ||
| 1120 | ret = block_alloc(dsp, &ba, &dsp->scratch_block_list); | ||
| 1121 | } | ||
| 1122 | if (ret < 0) { | ||
| 1123 | dev_err(dsp->dev, "error: can't alloc scratch blocks\n"); | ||
| 1124 | mutex_unlock(&dsp->mutex); | ||
| 1125 | return ret; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | ret = block_list_prepare(dsp, &dsp->scratch_block_list); | ||
| 1129 | if (ret < 0) { | ||
| 1130 | dev_err(dsp->dev, "error: scratch block prepare failed\n"); | ||
| 1131 | mutex_unlock(&dsp->mutex); | ||
| 1132 | return ret; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | /* assign the same offset of scratch to each module */ | ||
| 1136 | dsp->scratch_offset = ba.offset; | ||
| 1137 | mutex_unlock(&dsp->mutex); | ||
| 1138 | return dsp->scratch_size; | ||
| 1139 | } | ||
| 1140 | EXPORT_SYMBOL_GPL(sst_block_alloc_scratch); | ||
| 1141 | |||
| 1142 | /* free all scratch blocks */ | ||
| 1143 | void sst_block_free_scratch(struct sst_dsp *dsp) | ||
| 1144 | { | ||
| 1145 | mutex_lock(&dsp->mutex); | ||
| 1146 | block_list_remove(dsp, &dsp->scratch_block_list); | ||
| 1147 | mutex_unlock(&dsp->mutex); | ||
| 1148 | } | ||
| 1149 | EXPORT_SYMBOL_GPL(sst_block_free_scratch); | ||
| 1150 | |||
| 1151 | /* get a module from it's unique ID */ | ||
| 1152 | struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id) | ||
| 1153 | { | ||
| 1154 | struct sst_module *module; | ||
| 1155 | |||
| 1156 | mutex_lock(&dsp->mutex); | ||
| 1157 | |||
| 1158 | list_for_each_entry(module, &dsp->module_list, list) { | ||
| 1159 | if (module->id == id) { | ||
| 1160 | mutex_unlock(&dsp->mutex); | ||
| 1161 | return module; | ||
| 1162 | } | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | mutex_unlock(&dsp->mutex); | ||
| 1166 | return NULL; | ||
| 1167 | } | ||
| 1168 | EXPORT_SYMBOL_GPL(sst_module_get_from_id); | ||
| 1169 | |||
| 1170 | struct sst_module_runtime *sst_module_runtime_get_from_id( | ||
| 1171 | struct sst_module *module, u32 id) | ||
| 1172 | { | ||
| 1173 | struct sst_module_runtime *runtime; | ||
| 1174 | struct sst_dsp *dsp = module->dsp; | ||
| 1175 | |||
| 1176 | mutex_lock(&dsp->mutex); | ||
| 1177 | |||
| 1178 | list_for_each_entry(runtime, &module->runtime_list, list) { | ||
| 1179 | if (runtime->id == id) { | ||
| 1180 | mutex_unlock(&dsp->mutex); | ||
| 1181 | return runtime; | ||
| 1182 | } | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | mutex_unlock(&dsp->mutex); | ||
| 1186 | return NULL; | ||
| 1187 | } | ||
| 1188 | EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id); | ||
| 1189 | |||
| 1190 | /* returns block address in DSP address space */ | ||
| 1191 | u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, | ||
| 1192 | enum sst_mem_type type) | ||
| 1193 | { | ||
| 1194 | switch (type) { | ||
| 1195 | case SST_MEM_IRAM: | ||
| 1196 | return offset - dsp->addr.iram_offset + | ||
| 1197 | dsp->addr.dsp_iram_offset; | ||
| 1198 | case SST_MEM_DRAM: | ||
| 1199 | return offset - dsp->addr.dram_offset + | ||
| 1200 | dsp->addr.dsp_dram_offset; | ||
| 1201 | default: | ||
| 1202 | return 0; | ||
| 1203 | } | ||
| 1204 | } | ||
| 1205 | EXPORT_SYMBOL_GPL(sst_dsp_get_offset); | ||
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c new file mode 100644 index 000000000000..4b62a553823c --- /dev/null +++ b/sound/soc/intel/common/sst-ipc.c | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | /* | ||
| 2 | * Intel SST generic IPC Support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015, Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License version | ||
| 8 | * 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/types.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/list.h> | ||
| 20 | #include <linux/wait.h> | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/spinlock.h> | ||
| 23 | #include <linux/device.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/workqueue.h> | ||
| 26 | #include <linux/sched.h> | ||
| 27 | #include <linux/delay.h> | ||
| 28 | #include <linux/platform_device.h> | ||
| 29 | #include <linux/kthread.h> | ||
| 30 | #include <sound/asound.h> | ||
| 31 | |||
| 32 | #include "sst-dsp.h" | ||
| 33 | #include "sst-dsp-priv.h" | ||
| 34 | #include "sst-ipc.h" | ||
| 35 | |||
| 36 | /* IPC message timeout (msecs) */ | ||
| 37 | #define IPC_TIMEOUT_MSECS 300 | ||
| 38 | |||
| 39 | #define IPC_EMPTY_LIST_SIZE 8 | ||
| 40 | |||
| 41 | /* locks held by caller */ | ||
| 42 | static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc) | ||
| 43 | { | ||
| 44 | struct ipc_message *msg = NULL; | ||
| 45 | |||
| 46 | if (!list_empty(&ipc->empty_list)) { | ||
| 47 | msg = list_first_entry(&ipc->empty_list, struct ipc_message, | ||
| 48 | list); | ||
| 49 | list_del(&msg->list); | ||
| 50 | } | ||
| 51 | |||
| 52 | return msg; | ||
| 53 | } | ||
| 54 | |||
| 55 | static int tx_wait_done(struct sst_generic_ipc *ipc, | ||
| 56 | struct ipc_message *msg, void *rx_data) | ||
| 57 | { | ||
| 58 | unsigned long flags; | ||
| 59 | int ret; | ||
| 60 | |||
| 61 | /* wait for DSP completion (in all cases atm inc pending) */ | ||
| 62 | ret = wait_event_timeout(msg->waitq, msg->complete, | ||
| 63 | msecs_to_jiffies(IPC_TIMEOUT_MSECS)); | ||
| 64 | |||
| 65 | spin_lock_irqsave(&ipc->dsp->spinlock, flags); | ||
| 66 | if (ret == 0) { | ||
| 67 | if (ipc->ops.shim_dbg != NULL) | ||
| 68 | ipc->ops.shim_dbg(ipc, "message timeout"); | ||
| 69 | |||
| 70 | list_del(&msg->list); | ||
| 71 | ret = -ETIMEDOUT; | ||
| 72 | } else { | ||
| 73 | |||
| 74 | /* copy the data returned from DSP */ | ||
| 75 | if (msg->rx_size) | ||
| 76 | memcpy(rx_data, msg->rx_data, msg->rx_size); | ||
| 77 | ret = msg->errno; | ||
| 78 | } | ||
| 79 | |||
| 80 | list_add_tail(&msg->list, &ipc->empty_list); | ||
| 81 | spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); | ||
| 82 | return ret; | ||
| 83 | } | ||
| 84 | |||
| 85 | static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, | ||
| 86 | void *tx_data, size_t tx_bytes, void *rx_data, | ||
| 87 | size_t rx_bytes, int wait) | ||
| 88 | { | ||
| 89 | struct ipc_message *msg; | ||
| 90 | unsigned long flags; | ||
| 91 | |||
| 92 | spin_lock_irqsave(&ipc->dsp->spinlock, flags); | ||
| 93 | |||
| 94 | msg = msg_get_empty(ipc); | ||
| 95 | if (msg == NULL) { | ||
| 96 | spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); | ||
| 97 | return -EBUSY; | ||
| 98 | } | ||
| 99 | |||
| 100 | msg->header = header; | ||
| 101 | msg->tx_size = tx_bytes; | ||
| 102 | msg->rx_size = rx_bytes; | ||
| 103 | msg->wait = wait; | ||
| 104 | msg->errno = 0; | ||
| 105 | msg->pending = false; | ||
| 106 | msg->complete = false; | ||
| 107 | |||
| 108 | if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL)) | ||
| 109 | ipc->ops.tx_data_copy(msg, tx_data, tx_bytes); | ||
| 110 | |||
| 111 | list_add_tail(&msg->list, &ipc->tx_list); | ||
| 112 | spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); | ||
| 113 | |||
| 114 | queue_kthread_work(&ipc->kworker, &ipc->kwork); | ||
| 115 | |||
| 116 | if (wait) | ||
| 117 | return tx_wait_done(ipc, msg, rx_data); | ||
| 118 | else | ||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | static int msg_empty_list_init(struct sst_generic_ipc *ipc) | ||
| 123 | { | ||
| 124 | int i; | ||
| 125 | |||
| 126 | ipc->msg = kzalloc(sizeof(struct ipc_message) * | ||
| 127 | IPC_EMPTY_LIST_SIZE, GFP_KERNEL); | ||
| 128 | if (ipc->msg == NULL) | ||
| 129 | return -ENOMEM; | ||
| 130 | |||
| 131 | for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { | ||
| 132 | init_waitqueue_head(&ipc->msg[i].waitq); | ||
| 133 | list_add(&ipc->msg[i].list, &ipc->empty_list); | ||
| 134 | } | ||
| 135 | |||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | static void ipc_tx_msgs(struct kthread_work *work) | ||
| 140 | { | ||
| 141 | struct sst_generic_ipc *ipc = | ||
| 142 | container_of(work, struct sst_generic_ipc, kwork); | ||
| 143 | struct ipc_message *msg; | ||
| 144 | unsigned long flags; | ||
| 145 | u64 ipcx; | ||
| 146 | |||
| 147 | spin_lock_irqsave(&ipc->dsp->spinlock, flags); | ||
| 148 | |||
| 149 | if (list_empty(&ipc->tx_list) || ipc->pending) { | ||
| 150 | spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); | ||
| 151 | return; | ||
| 152 | } | ||
| 153 | |||
| 154 | /* if the DSP is busy, we will TX messages after IRQ. | ||
| 155 | * also postpone if we are in the middle of procesing completion irq*/ | ||
| 156 | ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX); | ||
| 157 | if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) { | ||
| 158 | spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); | ||
| 159 | return; | ||
| 160 | } | ||
| 161 | |||
| 162 | msg = list_first_entry(&ipc->tx_list, struct ipc_message, list); | ||
| 163 | list_move(&msg->list, &ipc->rx_list); | ||
| 164 | |||
| 165 | if (ipc->ops.tx_msg != NULL) | ||
| 166 | ipc->ops.tx_msg(ipc, msg); | ||
| 167 | |||
| 168 | spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); | ||
| 169 | } | ||
| 170 | |||
| 171 | int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, | ||
| 172 | void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) | ||
| 173 | { | ||
| 174 | return ipc_tx_message(ipc, header, tx_data, tx_bytes, | ||
| 175 | rx_data, rx_bytes, 1); | ||
| 176 | } | ||
| 177 | EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait); | ||
| 178 | |||
| 179 | int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, | ||
| 180 | void *tx_data, size_t tx_bytes) | ||
| 181 | { | ||
| 182 | return ipc_tx_message(ipc, header, tx_data, tx_bytes, | ||
| 183 | NULL, 0, 0); | ||
| 184 | } | ||
| 185 | EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait); | ||
| 186 | |||
| 187 | struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, | ||
| 188 | u64 header) | ||
| 189 | { | ||
| 190 | struct ipc_message *msg; | ||
| 191 | u64 mask; | ||
| 192 | |||
| 193 | if (ipc->ops.reply_msg_match != NULL) | ||
| 194 | header = ipc->ops.reply_msg_match(header, &mask); | ||
| 195 | |||
| 196 | if (list_empty(&ipc->rx_list)) { | ||
| 197 | dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n", | ||
| 198 | header); | ||
| 199 | return NULL; | ||
| 200 | } | ||
| 201 | |||
| 202 | list_for_each_entry(msg, &ipc->rx_list, list) { | ||
| 203 | if ((msg->header & mask) == header) | ||
| 204 | return msg; | ||
| 205 | } | ||
| 206 | |||
| 207 | return NULL; | ||
| 208 | } | ||
| 209 | EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg); | ||
| 210 | |||
| 211 | /* locks held by caller */ | ||
| 212 | void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, | ||
| 213 | struct ipc_message *msg) | ||
| 214 | { | ||
| 215 | msg->complete = true; | ||
| 216 | |||
| 217 | if (!msg->wait) | ||
| 218 | list_add_tail(&msg->list, &ipc->empty_list); | ||
| 219 | else | ||
| 220 | wake_up(&msg->waitq); | ||
| 221 | } | ||
| 222 | EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete); | ||
| 223 | |||
| 224 | void sst_ipc_drop_all(struct sst_generic_ipc *ipc) | ||
| 225 | { | ||
| 226 | struct ipc_message *msg, *tmp; | ||
| 227 | unsigned long flags; | ||
| 228 | int tx_drop_cnt = 0, rx_drop_cnt = 0; | ||
| 229 | |||
| 230 | /* drop all TX and Rx messages before we stall + reset DSP */ | ||
| 231 | spin_lock_irqsave(&ipc->dsp->spinlock, flags); | ||
| 232 | |||
| 233 | list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { | ||
| 234 | list_move(&msg->list, &ipc->empty_list); | ||
| 235 | tx_drop_cnt++; | ||
| 236 | } | ||
| 237 | |||
| 238 | list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) { | ||
| 239 | list_move(&msg->list, &ipc->empty_list); | ||
| 240 | rx_drop_cnt++; | ||
| 241 | } | ||
| 242 | |||
| 243 | spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); | ||
| 244 | |||
| 245 | if (tx_drop_cnt || rx_drop_cnt) | ||
| 246 | dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n", | ||
| 247 | tx_drop_cnt, rx_drop_cnt); | ||
| 248 | } | ||
| 249 | EXPORT_SYMBOL_GPL(sst_ipc_drop_all); | ||
| 250 | |||
| 251 | int sst_ipc_init(struct sst_generic_ipc *ipc) | ||
| 252 | { | ||
| 253 | int ret; | ||
| 254 | |||
| 255 | INIT_LIST_HEAD(&ipc->tx_list); | ||
| 256 | INIT_LIST_HEAD(&ipc->rx_list); | ||
| 257 | INIT_LIST_HEAD(&ipc->empty_list); | ||
| 258 | init_waitqueue_head(&ipc->wait_txq); | ||
| 259 | |||
| 260 | ret = msg_empty_list_init(ipc); | ||
| 261 | if (ret < 0) | ||
| 262 | return -ENOMEM; | ||
| 263 | |||
| 264 | /* start the IPC message thread */ | ||
| 265 | init_kthread_worker(&ipc->kworker); | ||
| 266 | ipc->tx_thread = kthread_run(kthread_worker_fn, | ||
| 267 | &ipc->kworker, "%s", | ||
| 268 | dev_name(ipc->dev)); | ||
| 269 | if (IS_ERR(ipc->tx_thread)) { | ||
| 270 | dev_err(ipc->dev, "error: failed to create message TX task\n"); | ||
| 271 | ret = PTR_ERR(ipc->tx_thread); | ||
| 272 | kfree(ipc->msg); | ||
| 273 | return ret; | ||
| 274 | } | ||
| 275 | |||
| 276 | init_kthread_work(&ipc->kwork, ipc_tx_msgs); | ||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | EXPORT_SYMBOL_GPL(sst_ipc_init); | ||
| 280 | |||
| 281 | void sst_ipc_fini(struct sst_generic_ipc *ipc) | ||
| 282 | { | ||
| 283 | if (ipc->tx_thread) | ||
| 284 | kthread_stop(ipc->tx_thread); | ||
| 285 | |||
| 286 | if (ipc->msg) | ||
| 287 | kfree(ipc->msg); | ||
| 288 | } | ||
| 289 | EXPORT_SYMBOL_GPL(sst_ipc_fini); | ||
| 290 | |||
| 291 | /* Module information */ | ||
| 292 | MODULE_AUTHOR("Jin Yao"); | ||
| 293 | MODULE_DESCRIPTION("Intel SST IPC generic"); | ||
| 294 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h new file mode 100644 index 000000000000..125ea451a373 --- /dev/null +++ b/sound/soc/intel/common/sst-ipc.h | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /* | ||
| 2 | * Intel SST generic IPC Support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015, Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License version | ||
| 8 | * 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef __SST_GENERIC_IPC_H | ||
| 18 | #define __SST_GENERIC_IPC_H | ||
| 19 | |||
| 20 | #include <linux/types.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/wait.h> | ||
| 23 | #include <linux/list.h> | ||
| 24 | #include <linux/workqueue.h> | ||
| 25 | #include <linux/sched.h> | ||
| 26 | #include <linux/kthread.h> | ||
| 27 | |||
| 28 | #define IPC_MAX_MAILBOX_BYTES 256 | ||
| 29 | |||
| 30 | struct ipc_message { | ||
| 31 | struct list_head list; | ||
| 32 | u64 header; | ||
| 33 | |||
| 34 | /* direction wrt host CPU */ | ||
| 35 | char tx_data[IPC_MAX_MAILBOX_BYTES]; | ||
| 36 | size_t tx_size; | ||
| 37 | char rx_data[IPC_MAX_MAILBOX_BYTES]; | ||
| 38 | size_t rx_size; | ||
| 39 | |||
| 40 | wait_queue_head_t waitq; | ||
| 41 | bool pending; | ||
| 42 | bool complete; | ||
| 43 | bool wait; | ||
| 44 | int errno; | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct sst_generic_ipc; | ||
| 48 | |||
| 49 | struct sst_plat_ipc_ops { | ||
| 50 | void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *); | ||
| 51 | void (*shim_dbg)(struct sst_generic_ipc *, const char *); | ||
| 52 | void (*tx_data_copy)(struct ipc_message *, char *, size_t); | ||
| 53 | u64 (*reply_msg_match)(u64 header, u64 *mask); | ||
| 54 | }; | ||
| 55 | |||
| 56 | /* SST generic IPC data */ | ||
| 57 | struct sst_generic_ipc { | ||
| 58 | struct device *dev; | ||
| 59 | struct sst_dsp *dsp; | ||
| 60 | |||
| 61 | /* IPC messaging */ | ||
| 62 | struct list_head tx_list; | ||
| 63 | struct list_head rx_list; | ||
| 64 | struct list_head empty_list; | ||
| 65 | wait_queue_head_t wait_txq; | ||
| 66 | struct task_struct *tx_thread; | ||
| 67 | struct kthread_worker kworker; | ||
| 68 | struct kthread_work kwork; | ||
| 69 | bool pending; | ||
| 70 | struct ipc_message *msg; | ||
| 71 | |||
| 72 | struct sst_plat_ipc_ops ops; | ||
| 73 | }; | ||
| 74 | |||
| 75 | int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, | ||
| 76 | void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); | ||
| 77 | |||
| 78 | int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, | ||
| 79 | void *tx_data, size_t tx_bytes); | ||
| 80 | |||
| 81 | struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, | ||
| 82 | u64 header); | ||
| 83 | |||
| 84 | void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, | ||
| 85 | struct ipc_message *msg); | ||
| 86 | |||
| 87 | void sst_ipc_drop_all(struct sst_generic_ipc *ipc); | ||
| 88 | int sst_ipc_init(struct sst_generic_ipc *ipc); | ||
| 89 | void sst_ipc_fini(struct sst_generic_ipc *ipc); | ||
| 90 | |||
| 91 | #endif | ||
