Merge branch 'topic/rt5670' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[deliverable/linux.git] / sound / soc / intel / sst / sst_loader.c
CommitLineData
9012c954
VK
1/*
2 * sst_dsp.c - Intel SST Driver for audio engine
3 *
4 * Copyright (C) 2008-14 Intel Corp
5 * Authors: Vinod Koul <vinod.koul@intel.com>
6 * Harsha Priya <priya.harsha@intel.com>
7 * Dharageswari R <dharageswari.r@intel.com>
8 * KP Jeeja <jeeja.kp@intel.com>
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21 *
22 * This file contains all dsp controlling functions like firmware download,
23 * setting/resetting dsp cores, etc
24 */
25#include <linux/pci.h>
26#include <linux/delay.h>
27#include <linux/fs.h>
28#include <linux/sched.h>
29#include <linux/firmware.h>
30#include <linux/dmaengine.h>
31#include <linux/pm_runtime.h>
32#include <linux/pm_qos.h>
33#include <sound/core.h>
34#include <sound/pcm.h>
35#include <sound/soc.h>
36#include <sound/compress_driver.h>
37#include <asm/platform_sst_audio.h>
38#include "../sst-mfld-platform.h"
39#include "sst.h"
40#include "../sst-dsp.h"
41
b3baaa47 42static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
9012c954 43{
b3baaa47
VK
44 /* __iowrite32_copy uses 32-bit count values so divide by 4 for
45 * right count in words
46 */
47 __iowrite32_copy(dst, src, count/4);
9012c954
VK
48}
49
50/**
51 * intel_sst_reset_dsp_mrfld - Resetting SST DSP
52 *
53 * This resets DSP in case of MRFLD platfroms
54 */
55int 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_merrifield - Start the SST DSP processor
80 *
81 * This starts the DSP in MERRIFIELD platfroms
82 */
83int 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
107static 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 */
146static 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 */
172static 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 */
231static 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 */
259static 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 == true)
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
272void 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 if (!list_empty(&sst_drv_ctx->memcpy_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
286static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
287 const struct firmware *fw)
288{
289 int retval = 0;
290
291 sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
292 if (!sst->fw_in_mem) {
293 retval = -ENOMEM;
294 goto end_release;
295 }
296 dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
297 dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
298 memcpy(sst->fw_in_mem, fw->data, fw->size);
299 retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
300 if (retval) {
301 dev_err(sst->dev, "Failed to parse fw\n");
302 kfree(sst->fw_in_mem);
303 sst->fw_in_mem = NULL;
304 }
305
306end_release:
307 release_firmware(fw);
308 return retval;
309
310}
311
312void sst_firmware_load_cb(const struct firmware *fw, void *context)
313{
314 struct intel_sst_drv *ctx = context;
315
316 dev_dbg(ctx->dev, "Enter\n");
317
318 if (fw == NULL) {
319 dev_err(ctx->dev, "request fw failed\n");
320 return;
321 }
322
323 mutex_lock(&ctx->sst_lock);
324
325 if (ctx->sst_state != SST_RESET ||
326 ctx->fw_in_mem != NULL) {
4e3461d3 327 release_firmware(fw);
9012c954
VK
328 mutex_unlock(&ctx->sst_lock);
329 return;
330 }
331
332 dev_dbg(ctx->dev, "Request Fw completed\n");
333 sst_cache_and_parse_fw(ctx, fw);
334 mutex_unlock(&ctx->sst_lock);
335}
336
337/*
338 * sst_request_fw - requests audio fw from kernel and saves a copy
339 *
340 * This function requests the SST FW from the kernel, parses it and
341 * saves a copy in the driver context
342 */
343static int sst_request_fw(struct intel_sst_drv *sst)
344{
345 int retval = 0;
9012c954
VK
346 const struct firmware *fw;
347
5794b7ec 348 retval = request_firmware(&fw, sst->firmware_name, sst->dev);
9012c954
VK
349 if (fw == NULL) {
350 dev_err(sst->dev, "fw is returning as null\n");
351 return -EINVAL;
352 }
353 if (retval) {
354 dev_err(sst->dev, "request fw failed %d\n", retval);
355 return retval;
356 }
357 mutex_lock(&sst->sst_lock);
358 retval = sst_cache_and_parse_fw(sst, fw);
359 mutex_unlock(&sst->sst_lock);
360
361 return retval;
362}
363
364/*
365 * Writing the DDR physical base to DCCM offset
366 * so that FW can use it to setup TLB
367 */
368static void sst_dccm_config_write(void __iomem *dram_base,
369 unsigned int ddr_base)
370{
371 void __iomem *addr;
372 u32 bss_reset = 0;
373
374 addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
375 memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
376 bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
377 addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
378 memcpy32_toio(addr, &bss_reset, sizeof(u32));
379
380}
381
382void sst_post_download_mrfld(struct intel_sst_drv *ctx)
383{
384 sst_dccm_config_write(ctx->dram, ctx->ddr_base);
385 dev_dbg(ctx->dev, "config written to DCCM\n");
386}
387
388/**
389 * sst_load_fw - function to load FW into DSP
390 * Transfers the FW to DSP using dma/memcpy
391 */
392int 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 sst_drv_ctx->sst_state == SST_SHUTDOWN)
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 BUG_ON(!sst_drv_ctx->fw_in_mem);
411 block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
412 if (block == NULL)
413 return -ENOMEM;
414
415 /* Prevent C-states beyond C6 */
416 pm_qos_update_request(sst_drv_ctx->qos, 0);
417
418 sst_drv_ctx->sst_state = SST_FW_LOADING;
419
420 ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
421 if (ret_val)
422 goto restore;
423
424 sst_do_memcpy(&sst_drv_ctx->memcpy_list);
425
426 /* Write the DRAM/DCCM config before enabling FW */
427 if (sst_drv_ctx->ops->post_download)
428 sst_drv_ctx->ops->post_download(sst_drv_ctx);
429
430 /* bring sst out of reset */
431 ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
432 if (ret_val)
433 goto restore;
434
435 ret_val = sst_wait_timeout(sst_drv_ctx, block);
436 if (ret_val) {
437 dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
438 /* FW download failed due to timeout */
439 ret_val = -EBUSY;
440
441 }
442
443
444restore:
445 /* Re-enable Deeper C-states beyond C6 */
446 pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
447 sst_free_block(sst_drv_ctx, block);
448 dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
449
450 if (sst_drv_ctx->ops->restore_dsp_context)
451 sst_drv_ctx->ops->restore_dsp_context();
452 sst_drv_ctx->sst_state = SST_FW_RUNNING;
453 return ret_val;
454}
455
This page took 0.047428 seconds and 5 git commands to generate.