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