brintos

brintos / linux-shallow public Read only

0
0
Text · 11.8 KiB · bf4ba6b Raw
454 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  sst_dsp.c - Intel SST Driver for audio engine4 *5 *  Copyright (C) 2008-14	Intel Corp6 *  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, etc16 */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 for35	 * right count in words36	 */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 for43	 * right count in words44	 */45	__ioread32_copy(dst, src, count / 4);46}47 48/**49 * intel_sst_reset_dsp_mrfld - Resetting SST DSP50 * @sst_drv_ctx: intel_sst_drv context pointer51 *52 * This resets DSP in case of MRFLD platfroms53 */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 processor79 * @sst_drv_ctx: intel_sst_drv context pointer80 *81 * This starts the DSP in MERRIFIELD platfroms82 */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 list137 *138 * @memcpy_list: List to be filled139 * @destn: Destination addr to be filled in the list140 * @src: Source addr to be filled in the list141 * @size: Size to be filled in the list142 *143 * Adds the node to the list after required fields144 * are populated in the node145 */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 list165 *166 * @sst_drv_ctx		: driver context167 * @module		: FW module header168 * @memcpy_list	: Pointer to the list to be populated169 * Create the memcpy list as the number of block to be copied170 * returns error or 0 if module sizes are proper171 */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 memcpy224 *225 * @ctx			: pointer to drv context226 * @size		: size of the firmware227 * @fw_list		: pointer to list_head to be populated228 * This function parses the FW image and saves the parsed image in the list229 * for memcpy230 */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 memcpy254 *255 * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated256 *257 * Triggers the memcpy258 */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		else268			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 copy337 *338 * This function requests the SST FW from the kernel, parses it and339 * saves a copy in the driver context340 */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 offset364 * so that FW can use it to setup TLB365 */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 DSP388 * @sst_drv_ctx: intel_sst_drv context pointer389 *390 * Transfers the FW to DSP using dma/memcpy391 */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 454