~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/intel/atom/sst/sst_loader.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  *  sst_dsp.c - Intel SST Driver for audio engine
  4  *
  5  *  Copyright (C) 2008-14       Intel Corp
  6  *  Authors:    Vinod Koul <vinod.koul@intel.com>
  7  *              Harsha Priya <priya.harsha@intel.com>
  8  *              Dharageswari R <dharageswari.r@intel.com>
  9  *              KP Jeeja <jeeja.kp@intel.com>
 10  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 11  *
 12  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 13  *
 14  *  This file contains all dsp controlling functions like firmware download,
 15  * setting/resetting dsp cores, etc
 16  */
 17 #include <linux/pci.h>
 18 #include <linux/delay.h>
 19 #include <linux/fs.h>
 20 #include <linux/sched.h>
 21 #include <linux/firmware.h>
 22 #include <linux/dmaengine.h>
 23 #include <linux/pm_qos.h>
 24 #include <sound/core.h>
 25 #include <sound/pcm.h>
 26 #include <sound/soc.h>
 27 #include <sound/compress_driver.h>
 28 #include <asm/platform_sst_audio.h>
 29 #include "../sst-mfld-platform.h"
 30 #include "sst.h"
 31 
 32 void memcpy32_toio(void __iomem *dst, const void *src, int count)
 33 {
 34         /* __iowrite32_copy uses 32-bit count values so divide by 4 for
 35          * right count in words
 36          */
 37         __iowrite32_copy(dst, src, count / 4);
 38 }
 39 
 40 void memcpy32_fromio(void *dst, const void __iomem *src, int count)
 41 {
 42         /* __ioread32_copy uses 32-bit count values so divide by 4 for
 43          * right count in words
 44          */
 45         __ioread32_copy(dst, src, count / 4);
 46 }
 47 
 48 /**
 49  * intel_sst_reset_dsp_mrfld - Resetting SST DSP
 50  * @sst_drv_ctx: intel_sst_drv context pointer
 51  *
 52  * This resets DSP in case of MRFLD platfroms
 53  */
 54 int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
 55 {
 56         union config_status_reg_mrfld csr;
 57 
 58         dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
 59         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
 60 
 61         dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
 62 
 63         csr.full |= 0x7;
 64         sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
 65         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
 66 
 67         dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
 68 
 69         csr.full &= ~(0x1);
 70         sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
 71 
 72         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
 73         dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
 74         return 0;
 75 }
 76 
 77 /**
 78  * sst_start_mrfld - Start the SST DSP processor
 79  * @sst_drv_ctx: intel_sst_drv context pointer
 80  *
 81  * This starts the DSP in MERRIFIELD platfroms
 82  */
 83 int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
 84 {
 85         union config_status_reg_mrfld csr;
 86 
 87         dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
 88         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
 89         dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
 90 
 91         csr.full |= 0x7;
 92         sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
 93 
 94         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
 95         dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
 96 
 97         csr.part.xt_snoop = 1;
 98         csr.full &= ~(0x5);
 99         sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
100 
101         csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
102         dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
103                         csr.full);
104         return 0;
105 }
106 
107 static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
108                 struct fw_module_header **module, u32 *num_modules)
109 {
110         struct sst_fw_header *header;
111         const void *sst_fw_in_mem = ctx->fw_in_mem;
112 
113         dev_dbg(ctx->dev, "Enter\n");
114 
115         /* Read the header information from the data pointer */
116         header = (struct sst_fw_header *)sst_fw_in_mem;
117         dev_dbg(ctx->dev,
118                 "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
119                 header->signature, header->file_size, header->modules,
120                 header->file_format, sizeof(*header));
121 
122         /* verify FW */
123         if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
124                 (size != header->file_size + sizeof(*header))) {
125                 /* Invalid FW signature */
126                 dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
127                 return -EINVAL;
128         }
129         *num_modules = header->modules;
130         *module = (void *)sst_fw_in_mem + sizeof(*header);
131 
132         return 0;
133 }
134 
135 /*
136  * sst_fill_memcpy_list - Fill the memcpy list
137  *
138  * @memcpy_list: List to be filled
139  * @destn: Destination addr to be filled in the list
140  * @src: Source addr to be filled in the list
141  * @size: Size to be filled in the list
142  *
143  * Adds the node to the list after required fields
144  * are populated in the node
145  */
146 static int sst_fill_memcpy_list(struct list_head *memcpy_list,
147                         void *destn, const void *src, u32 size, bool is_io)
148 {
149         struct sst_memcpy_list *listnode;
150 
151         listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
152         if (listnode == NULL)
153                 return -ENOMEM;
154         listnode->dstn = destn;
155         listnode->src = src;
156         listnode->size = size;
157         listnode->is_io = is_io;
158         list_add_tail(&listnode->memcpylist, memcpy_list);
159 
160         return 0;
161 }
162 
163 /**
164  * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
165  *
166  * @sst_drv_ctx         : driver context
167  * @module              : FW module header
168  * @memcpy_list : Pointer to the list to be populated
169  * Create the memcpy list as the number of block to be copied
170  * returns error or 0 if module sizes are proper
171  */
172 static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
173                 struct fw_module_header *module, struct list_head *memcpy_list)
174 {
175         struct fw_block_info *block;
176         u32 count;
177         int ret_val = 0;
178         void __iomem *ram_iomem;
179 
180         dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
181                         module->signature, module->mod_size,
182                         module->blocks, module->type);
183         dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
184 
185         block = (void *)module + sizeof(*module);
186 
187         for (count = 0; count < module->blocks; count++) {
188                 if (block->size <= 0) {
189                         dev_err(sst_drv_ctx->dev, "block size invalid\n");
190                         return -EINVAL;
191                 }
192                 switch (block->type) {
193                 case SST_IRAM:
194                         ram_iomem = sst_drv_ctx->iram;
195                         break;
196                 case SST_DRAM:
197                         ram_iomem = sst_drv_ctx->dram;
198                         break;
199                 case SST_DDR:
200                         ram_iomem = sst_drv_ctx->ddr;
201                         break;
202                 case SST_CUSTOM_INFO:
203                         block = (void *)block + sizeof(*block) + block->size;
204                         continue;
205                 default:
206                         dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
207                                         block->type, count);
208                         return -EINVAL;
209                 }
210 
211                 ret_val = sst_fill_memcpy_list(memcpy_list,
212                                 ram_iomem + block->ram_offset,
213                                 (void *)block + sizeof(*block), block->size, 1);
214                 if (ret_val)
215                         return ret_val;
216 
217                 block = (void *)block + sizeof(*block) + block->size;
218         }
219         return 0;
220 }
221 
222 /**
223  * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
224  *
225  * @ctx                 : pointer to drv context
226  * @size                : size of the firmware
227  * @fw_list             : pointer to list_head to be populated
228  * This function parses the FW image and saves the parsed image in the list
229  * for memcpy
230  */
231 static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
232                                 struct list_head *fw_list)
233 {
234         struct fw_module_header *module;
235         u32 count, num_modules;
236         int ret_val;
237 
238         ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
239         if (ret_val)
240                 return ret_val;
241 
242         for (count = 0; count < num_modules; count++) {
243                 ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
244                 if (ret_val)
245                         return ret_val;
246                 module = (void *)module + sizeof(*module) + module->mod_size;
247         }
248 
249         return 0;
250 }
251 
252 /**
253  * sst_do_memcpy - function initiates the memcpy
254  *
255  * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
256  *
257  * Triggers the memcpy
258  */
259 static void sst_do_memcpy(struct list_head *memcpy_list)
260 {
261         struct sst_memcpy_list *listnode;
262 
263         list_for_each_entry(listnode, memcpy_list, memcpylist) {
264                 if (listnode->is_io)
265                         memcpy32_toio((void __iomem *)listnode->dstn,
266                                         listnode->src, listnode->size);
267                 else
268                         memcpy(listnode->dstn, listnode->src, listnode->size);
269         }
270 }
271 
272 void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
273 {
274         struct sst_memcpy_list *listnode, *tmplistnode;
275 
276         /* Free the list */
277         list_for_each_entry_safe(listnode, tmplistnode,
278                                  &sst_drv_ctx->memcpy_list, memcpylist) {
279                 list_del(&listnode->memcpylist);
280                 kfree(listnode);
281         }
282 }
283 
284 static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
285                 const struct firmware *fw)
286 {
287         int retval = 0;
288 
289         sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
290         if (!sst->fw_in_mem) {
291                 retval = -ENOMEM;
292                 goto end_release;
293         }
294         dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
295         dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
296         memcpy(sst->fw_in_mem, fw->data, fw->size);
297         retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
298         if (retval) {
299                 dev_err(sst->dev, "Failed to parse fw\n");
300                 kfree(sst->fw_in_mem);
301                 sst->fw_in_mem = NULL;
302         }
303 
304 end_release:
305         release_firmware(fw);
306         return retval;
307 
308 }
309 
310 void sst_firmware_load_cb(const struct firmware *fw, void *context)
311 {
312         struct intel_sst_drv *ctx = context;
313 
314         dev_dbg(ctx->dev, "Enter\n");
315 
316         if (fw == NULL) {
317                 dev_err(ctx->dev, "request fw failed\n");
318                 return;
319         }
320 
321         mutex_lock(&ctx->sst_lock);
322 
323         if (ctx->sst_state != SST_RESET ||
324                         ctx->fw_in_mem != NULL) {
325                 release_firmware(fw);
326                 mutex_unlock(&ctx->sst_lock);
327                 return;
328         }
329 
330         dev_dbg(ctx->dev, "Request Fw completed\n");
331         sst_cache_and_parse_fw(ctx, fw);
332         mutex_unlock(&ctx->sst_lock);
333 }
334 
335 /*
336  * sst_request_fw - requests audio fw from kernel and saves a copy
337  *
338  * This function requests the SST FW from the kernel, parses it and
339  * saves a copy in the driver context
340  */
341 static int sst_request_fw(struct intel_sst_drv *sst)
342 {
343         int retval = 0;
344         const struct firmware *fw;
345 
346         retval = request_firmware(&fw, sst->firmware_name, sst->dev);
347         if (retval) {
348                 dev_err(sst->dev, "request fw failed %d\n", retval);
349                 return retval;
350         }
351         if (fw == NULL) {
352                 dev_err(sst->dev, "fw is returning as null\n");
353                 return -EINVAL;
354         }
355         mutex_lock(&sst->sst_lock);
356         retval = sst_cache_and_parse_fw(sst, fw);
357         mutex_unlock(&sst->sst_lock);
358 
359         return retval;
360 }
361 
362 /*
363  * Writing the DDR physical base to DCCM offset
364  * so that FW can use it to setup TLB
365  */
366 static void sst_dccm_config_write(void __iomem *dram_base,
367                 unsigned int ddr_base)
368 {
369         void __iomem *addr;
370         u32 bss_reset = 0;
371 
372         addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
373         memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
374         bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
375         addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
376         memcpy32_toio(addr, &bss_reset, sizeof(u32));
377 
378 }
379 
380 void sst_post_download_mrfld(struct intel_sst_drv *ctx)
381 {
382         sst_dccm_config_write(ctx->dram, ctx->ddr_base);
383         dev_dbg(ctx->dev, "config written to DCCM\n");
384 }
385 
386 /**
387  * sst_load_fw - function to load FW into DSP
388  * @sst_drv_ctx: intel_sst_drv context pointer
389  *
390  * Transfers the FW to DSP using dma/memcpy
391  */
392 int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
393 {
394         int ret_val = 0;
395         struct sst_block *block;
396 
397         dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
398 
399         if (sst_drv_ctx->sst_state !=  SST_RESET)
400                 return -EAGAIN;
401 
402         if (!sst_drv_ctx->fw_in_mem) {
403                 dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
404                 ret_val = sst_request_fw(sst_drv_ctx);
405                 if (ret_val)
406                         return ret_val;
407         }
408 
409         block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
410         if (block == NULL)
411                 return -ENOMEM;
412 
413         /* Prevent C-states beyond C6 */
414         cpu_latency_qos_update_request(sst_drv_ctx->qos, 0);
415 
416         sst_drv_ctx->sst_state = SST_FW_LOADING;
417 
418         ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
419         if (ret_val)
420                 goto restore;
421 
422         sst_do_memcpy(&sst_drv_ctx->memcpy_list);
423 
424         /* Write the DRAM/DCCM config before enabling FW */
425         if (sst_drv_ctx->ops->post_download)
426                 sst_drv_ctx->ops->post_download(sst_drv_ctx);
427 
428         /* bring sst out of reset */
429         ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
430         if (ret_val)
431                 goto restore;
432 
433         ret_val = sst_wait_timeout(sst_drv_ctx, block);
434         if (ret_val) {
435                 dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
436                 /* FW download failed due to timeout */
437                 ret_val = -EBUSY;
438 
439         }
440 
441 
442 restore:
443         /* Re-enable Deeper C-states beyond C6 */
444         cpu_latency_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
445         sst_free_block(sst_drv_ctx, block);
446         dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
447 
448         if (sst_drv_ctx->ops->restore_dsp_context)
449                 sst_drv_ctx->ops->restore_dsp_context();
450         sst_drv_ctx->sst_state = SST_FW_RUNNING;
451         return ret_val;
452 }
453 
454 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php