Linux Audio

Check our new training course

Loading...
v6.13.7
  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
 32void 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
 40void 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 */
 54int 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 */
 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)
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	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
284static 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
304end_release:
305	release_firmware(fw);
306	return retval;
307
308}
309
310void 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 */
341static 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 */
366static 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
380void 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 */
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		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
442restore:
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
v6.2
  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_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
 33void 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
 41void 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 */
 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_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 */
 84int 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
108static 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 */
147static 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 */
173static 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 */
232static 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 */
260static 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
273void 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
285static 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
305end_release:
306	release_firmware(fw);
307	return retval;
308
309}
310
311void 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 */
342static 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 */
367static 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
381void 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 */
393int 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
443restore:
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