Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/intel/atom/sst/sst_loader.c
26516 views
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_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
455