18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * skl-sst.c - HDA DSP library functions for SKL platform
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014-15, Intel Corporation.
68c2ecf20Sopenharmony_ci * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
78c2ecf20Sopenharmony_ci *	Jeeja KP <jeeja.kp@intel.com>
88c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/uuid.h>
168c2ecf20Sopenharmony_ci#include "../common/sst-dsp.h"
178c2ecf20Sopenharmony_ci#include "../common/sst-dsp-priv.h"
188c2ecf20Sopenharmony_ci#include "../common/sst-ipc.h"
198c2ecf20Sopenharmony_ci#include "skl.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define SKL_BASEFW_TIMEOUT	300
228c2ecf20Sopenharmony_ci#define SKL_INIT_TIMEOUT	1000
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Intel HD Audio SRAM Window 0*/
258c2ecf20Sopenharmony_ci#define SKL_ADSP_SRAM0_BASE	0x8000
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* Firmware status window */
288c2ecf20Sopenharmony_ci#define SKL_ADSP_FW_STATUS	SKL_ADSP_SRAM0_BASE
298c2ecf20Sopenharmony_ci#define SKL_ADSP_ERROR_CODE	(SKL_ADSP_FW_STATUS + 0x4)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define SKL_NUM_MODULES		1
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	u32 cur_sts;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return (cur_sts == status);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int skl_transfer_firmware(struct sst_dsp *ctx,
438c2ecf20Sopenharmony_ci		const void *basefw, u32 base_fw_size)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	int ret = 0;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size,
488c2ecf20Sopenharmony_ci								true);
498c2ecf20Sopenharmony_ci	if (ret < 0)
508c2ecf20Sopenharmony_ci		return ret;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	ret = sst_dsp_register_poll(ctx,
538c2ecf20Sopenharmony_ci			SKL_ADSP_FW_STATUS,
548c2ecf20Sopenharmony_ci			SKL_FW_STS_MASK,
558c2ecf20Sopenharmony_ci			SKL_FW_RFW_START,
568c2ecf20Sopenharmony_ci			SKL_BASEFW_TIMEOUT,
578c2ecf20Sopenharmony_ci			"Firmware boot");
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	ctx->cl_dev.ops.cl_stop_dma(ctx);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return ret;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int skl_load_base_firmware(struct sst_dsp *ctx)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	int ret = 0, i;
698c2ecf20Sopenharmony_ci	struct skl_dev *skl = ctx->thread_context;
708c2ecf20Sopenharmony_ci	struct firmware stripped_fw;
718c2ecf20Sopenharmony_ci	u32 reg;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	skl->boot_complete = false;
748c2ecf20Sopenharmony_ci	init_waitqueue_head(&skl->boot_wait);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (ctx->fw == NULL) {
778c2ecf20Sopenharmony_ci		ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
788c2ecf20Sopenharmony_ci		if (ret < 0) {
798c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "Request firmware failed %d\n", ret);
808c2ecf20Sopenharmony_ci			return -EIO;
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* prase uuids on first boot */
858c2ecf20Sopenharmony_ci	if (skl->is_first_boot) {
868c2ecf20Sopenharmony_ci		ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0);
878c2ecf20Sopenharmony_ci		if (ret < 0) {
888c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "UUID parsing err: %d\n", ret);
898c2ecf20Sopenharmony_ci			release_firmware(ctx->fw);
908c2ecf20Sopenharmony_ci			skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
918c2ecf20Sopenharmony_ci			return ret;
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* check for extended manifest */
968c2ecf20Sopenharmony_ci	stripped_fw.data = ctx->fw->data;
978c2ecf20Sopenharmony_ci	stripped_fw.size = ctx->fw->size;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	skl_dsp_strip_extended_manifest(&stripped_fw);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret = skl_dsp_boot(ctx);
1028c2ecf20Sopenharmony_ci	if (ret < 0) {
1038c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
1048c2ecf20Sopenharmony_ci		goto skl_load_base_firmware_failed;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ret = skl_cldma_prepare(ctx);
1088c2ecf20Sopenharmony_ci	if (ret < 0) {
1098c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "CL dma prepare failed : %d\n", ret);
1108c2ecf20Sopenharmony_ci		goto skl_load_base_firmware_failed;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* enable Interrupt */
1148c2ecf20Sopenharmony_ci	skl_ipc_int_enable(ctx);
1158c2ecf20Sopenharmony_ci	skl_ipc_op_int_enable(ctx);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* check ROM Status */
1188c2ecf20Sopenharmony_ci	for (i = SKL_INIT_TIMEOUT; i > 0; --i) {
1198c2ecf20Sopenharmony_ci		if (skl_check_fw_status(ctx, SKL_FW_INIT)) {
1208c2ecf20Sopenharmony_ci			dev_dbg(ctx->dev,
1218c2ecf20Sopenharmony_ci				"ROM loaded, we can continue with FW loading\n");
1228c2ecf20Sopenharmony_ci			break;
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci		mdelay(1);
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	if (!i) {
1278c2ecf20Sopenharmony_ci		reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS);
1288c2ecf20Sopenharmony_ci		dev_err(ctx->dev,
1298c2ecf20Sopenharmony_ci			"Timeout waiting for ROM init done, reg:0x%x\n", reg);
1308c2ecf20Sopenharmony_ci		ret = -EIO;
1318c2ecf20Sopenharmony_ci		goto transfer_firmware_failed;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size);
1358c2ecf20Sopenharmony_ci	if (ret < 0) {
1368c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
1378c2ecf20Sopenharmony_ci		goto transfer_firmware_failed;
1388c2ecf20Sopenharmony_ci	} else {
1398c2ecf20Sopenharmony_ci		ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
1408c2ecf20Sopenharmony_ci					msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
1418c2ecf20Sopenharmony_ci		if (ret == 0) {
1428c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n");
1438c2ecf20Sopenharmony_ci			ret = -EIO;
1448c2ecf20Sopenharmony_ci			goto transfer_firmware_failed;
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
1488c2ecf20Sopenharmony_ci		skl->fw_loaded = true;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci	return 0;
1518c2ecf20Sopenharmony_citransfer_firmware_failed:
1528c2ecf20Sopenharmony_ci	ctx->cl_dev.ops.cl_cleanup_controller(ctx);
1538c2ecf20Sopenharmony_ciskl_load_base_firmware_failed:
1548c2ecf20Sopenharmony_ci	skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
1558c2ecf20Sopenharmony_ci	release_firmware(ctx->fw);
1568c2ecf20Sopenharmony_ci	ctx->fw = NULL;
1578c2ecf20Sopenharmony_ci	return ret;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	int ret;
1638c2ecf20Sopenharmony_ci	struct skl_ipc_dxstate_info dx;
1648c2ecf20Sopenharmony_ci	struct skl_dev *skl = ctx->thread_context;
1658c2ecf20Sopenharmony_ci	unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* If core0 is being turned on, we need to load the FW */
1688c2ecf20Sopenharmony_ci	if (core_id == SKL_DSP_CORE0_ID) {
1698c2ecf20Sopenharmony_ci		ret = skl_load_base_firmware(ctx);
1708c2ecf20Sopenharmony_ci		if (ret < 0) {
1718c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "unable to load firmware\n");
1728c2ecf20Sopenharmony_ci			return ret;
1738c2ecf20Sopenharmony_ci		}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		/* load libs as they are also lost on D3 */
1768c2ecf20Sopenharmony_ci		if (skl->lib_count > 1) {
1778c2ecf20Sopenharmony_ci			ret = ctx->fw_ops.load_library(ctx, skl->lib_info,
1788c2ecf20Sopenharmony_ci							skl->lib_count);
1798c2ecf20Sopenharmony_ci			if (ret < 0) {
1808c2ecf20Sopenharmony_ci				dev_err(ctx->dev, "reload libs failed: %d\n",
1818c2ecf20Sopenharmony_ci						ret);
1828c2ecf20Sopenharmony_ci				return ret;
1838c2ecf20Sopenharmony_ci			}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		}
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/*
1898c2ecf20Sopenharmony_ci	 * If any core other than core 0 is being moved to D0, enable the
1908c2ecf20Sopenharmony_ci	 * core and send the set dx IPC for the core.
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	if (core_id != SKL_DSP_CORE0_ID) {
1938c2ecf20Sopenharmony_ci		ret = skl_dsp_enable_core(ctx, core_mask);
1948c2ecf20Sopenharmony_ci		if (ret < 0)
1958c2ecf20Sopenharmony_ci			return ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		dx.core_mask = core_mask;
1988c2ecf20Sopenharmony_ci		dx.dx_mask = core_mask;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
2018c2ecf20Sopenharmony_ci					SKL_BASE_FW_MODULE_ID, &dx);
2028c2ecf20Sopenharmony_ci		if (ret < 0) {
2038c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n",
2048c2ecf20Sopenharmony_ci					core_id);
2058c2ecf20Sopenharmony_ci			skl_dsp_disable_core(ctx, core_mask);
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	skl->cores.state[core_id] = SKL_DSP_RUNNING;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return 0;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	int ret;
2178c2ecf20Sopenharmony_ci	struct skl_ipc_dxstate_info dx;
2188c2ecf20Sopenharmony_ci	struct skl_dev *skl = ctx->thread_context;
2198c2ecf20Sopenharmony_ci	unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	dx.core_mask = core_mask;
2228c2ecf20Sopenharmony_ci	dx.dx_mask = SKL_IPC_D3_MASK;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
2258c2ecf20Sopenharmony_ci	if (ret < 0)
2268c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (core_id == SKL_DSP_CORE0_ID) {
2298c2ecf20Sopenharmony_ci		/* disable Interrupt */
2308c2ecf20Sopenharmony_ci		ctx->cl_dev.ops.cl_cleanup_controller(ctx);
2318c2ecf20Sopenharmony_ci		skl_cldma_int_disable(ctx);
2328c2ecf20Sopenharmony_ci		skl_ipc_op_int_disable(ctx);
2338c2ecf20Sopenharmony_ci		skl_ipc_int_disable(ctx);
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	ret = skl_dsp_disable_core(ctx, core_mask);
2378c2ecf20Sopenharmony_ci	if (ret < 0)
2388c2ecf20Sopenharmony_ci		return ret;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	skl->cores.state[core_id] = SKL_DSP_RESET;
2418c2ecf20Sopenharmony_ci	return ret;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic unsigned int skl_get_errorcode(struct sst_dsp *ctx)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	 return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/*
2508c2ecf20Sopenharmony_ci * since get/set_module are called from DAPM context,
2518c2ecf20Sopenharmony_ci * we don't need lock for usage count
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_cistatic int skl_get_module(struct sst_dsp *ctx, u16 mod_id)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct skl_module_table *module;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	list_for_each_entry(module, &ctx->module_list, list) {
2588c2ecf20Sopenharmony_ci		if (module->mod_info->mod_id == mod_id)
2598c2ecf20Sopenharmony_ci			return ++module->usage_cnt;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return -EINVAL;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int skl_put_module(struct sst_dsp *ctx, u16 mod_id)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct skl_module_table *module;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	list_for_each_entry(module, &ctx->module_list, list) {
2708c2ecf20Sopenharmony_ci		if (module->mod_info->mod_id == mod_id)
2718c2ecf20Sopenharmony_ci			return --module->usage_cnt;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return -EINVAL;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx,
2788c2ecf20Sopenharmony_ci						char *mod_name, int mod_id)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	const struct firmware *fw;
2818c2ecf20Sopenharmony_ci	struct skl_module_table *skl_module;
2828c2ecf20Sopenharmony_ci	unsigned int size;
2838c2ecf20Sopenharmony_ci	int ret;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	ret = request_firmware(&fw, mod_name, ctx->dev);
2868c2ecf20Sopenharmony_ci	if (ret < 0) {
2878c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Request Module %s failed :%d\n",
2888c2ecf20Sopenharmony_ci							mod_name, ret);
2898c2ecf20Sopenharmony_ci		return NULL;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL);
2938c2ecf20Sopenharmony_ci	if (skl_module == NULL) {
2948c2ecf20Sopenharmony_ci		release_firmware(fw);
2958c2ecf20Sopenharmony_ci		return NULL;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	size = sizeof(*skl_module->mod_info);
2998c2ecf20Sopenharmony_ci	skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
3008c2ecf20Sopenharmony_ci	if (skl_module->mod_info == NULL) {
3018c2ecf20Sopenharmony_ci		release_firmware(fw);
3028c2ecf20Sopenharmony_ci		return NULL;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	skl_module->mod_info->mod_id = mod_id;
3068c2ecf20Sopenharmony_ci	skl_module->mod_info->fw = fw;
3078c2ecf20Sopenharmony_ci	list_add(&skl_module->list, &ctx->module_list);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return skl_module;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/* get a module from it's unique ID */
3138c2ecf20Sopenharmony_cistatic struct skl_module_table *skl_module_get_from_id(
3148c2ecf20Sopenharmony_ci			struct sst_dsp *ctx, u16 mod_id)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct skl_module_table *module;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (list_empty(&ctx->module_list)) {
3198c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Module list is empty\n");
3208c2ecf20Sopenharmony_ci		return NULL;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	list_for_each_entry(module, &ctx->module_list, list) {
3248c2ecf20Sopenharmony_ci		if (module->mod_info->mod_id == mod_id)
3258c2ecf20Sopenharmony_ci			return module;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return NULL;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic int skl_transfer_module(struct sst_dsp *ctx, const void *data,
3328c2ecf20Sopenharmony_ci			u32 size, u16 mod_id, u8 table_id, bool is_module)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	int ret, bytes_left, curr_pos;
3358c2ecf20Sopenharmony_ci	struct skl_dev *skl = ctx->thread_context;
3368c2ecf20Sopenharmony_ci	skl->mod_load_complete = false;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, data, size, false);
3398c2ecf20Sopenharmony_ci	if (bytes_left < 0)
3408c2ecf20Sopenharmony_ci		return bytes_left;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* check is_module flag to load module or library */
3438c2ecf20Sopenharmony_ci	if (is_module)
3448c2ecf20Sopenharmony_ci		ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, &mod_id);
3458c2ecf20Sopenharmony_ci	else
3468c2ecf20Sopenharmony_ci		ret = skl_sst_ipc_load_library(&skl->ipc, 0, table_id, false);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (ret < 0) {
3498c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Failed to Load %s with err %d\n",
3508c2ecf20Sopenharmony_ci				is_module ? "module" : "lib", ret);
3518c2ecf20Sopenharmony_ci		goto out;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/*
3558c2ecf20Sopenharmony_ci	 * if bytes_left > 0 then wait for BDL complete interrupt and
3568c2ecf20Sopenharmony_ci	 * copy the next chunk till bytes_left is 0. if bytes_left is
3578c2ecf20Sopenharmony_ci	 * zero, then wait for load module IPC reply
3588c2ecf20Sopenharmony_ci	 */
3598c2ecf20Sopenharmony_ci	while (bytes_left > 0) {
3608c2ecf20Sopenharmony_ci		curr_pos = size - bytes_left;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		ret = skl_cldma_wait_interruptible(ctx);
3638c2ecf20Sopenharmony_ci		if (ret < 0)
3648c2ecf20Sopenharmony_ci			goto out;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx,
3678c2ecf20Sopenharmony_ci							data + curr_pos,
3688c2ecf20Sopenharmony_ci							bytes_left, false);
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	ret = wait_event_timeout(skl->mod_load_wait, skl->mod_load_complete,
3728c2ecf20Sopenharmony_ci				msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
3738c2ecf20Sopenharmony_ci	if (ret == 0 || !skl->mod_load_status) {
3748c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Module Load failed\n");
3758c2ecf20Sopenharmony_ci		ret = -EIO;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ciout:
3798c2ecf20Sopenharmony_ci	ctx->cl_dev.ops.cl_stop_dma(ctx);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return ret;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int
3858c2ecf20Sopenharmony_ciskl_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct skl_dev *skl = ctx->thread_context;
3888c2ecf20Sopenharmony_ci	struct firmware stripped_fw;
3898c2ecf20Sopenharmony_ci	int ret, i;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* library indices start from 1 to N. 0 represents base FW */
3928c2ecf20Sopenharmony_ci	for (i = 1; i < lib_count; i++) {
3938c2ecf20Sopenharmony_ci		ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_fw,
3948c2ecf20Sopenharmony_ci					SKL_ADSP_FW_BIN_HDR_OFFSET, i);
3958c2ecf20Sopenharmony_ci		if (ret < 0)
3968c2ecf20Sopenharmony_ci			goto load_library_failed;
3978c2ecf20Sopenharmony_ci		ret = skl_transfer_module(ctx, stripped_fw.data,
3988c2ecf20Sopenharmony_ci				stripped_fw.size, 0, i, false);
3998c2ecf20Sopenharmony_ci		if (ret < 0)
4008c2ecf20Sopenharmony_ci			goto load_library_failed;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ciload_library_failed:
4068c2ecf20Sopenharmony_ci	skl_release_library(linfo, lib_count);
4078c2ecf20Sopenharmony_ci	return ret;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct skl_module_table *module_entry = NULL;
4138c2ecf20Sopenharmony_ci	int ret = 0;
4148c2ecf20Sopenharmony_ci	char mod_name[64]; /* guid str = 32 chars + 4 hyphens */
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	snprintf(mod_name, sizeof(mod_name), "intel/dsp_fw_%pUL.bin", guid);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	module_entry = skl_module_get_from_id(ctx, mod_id);
4198c2ecf20Sopenharmony_ci	if (module_entry == NULL) {
4208c2ecf20Sopenharmony_ci		module_entry = skl_fill_module_table(ctx, mod_name, mod_id);
4218c2ecf20Sopenharmony_ci		if (module_entry == NULL) {
4228c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "Failed to Load module\n");
4238c2ecf20Sopenharmony_ci			return -EINVAL;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (!module_entry->usage_cnt) {
4288c2ecf20Sopenharmony_ci		ret = skl_transfer_module(ctx, module_entry->mod_info->fw->data,
4298c2ecf20Sopenharmony_ci				module_entry->mod_info->fw->size,
4308c2ecf20Sopenharmony_ci				mod_id, 0, true);
4318c2ecf20Sopenharmony_ci		if (ret < 0) {
4328c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "Failed to Load module\n");
4338c2ecf20Sopenharmony_ci			return ret;
4348c2ecf20Sopenharmony_ci		}
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	ret = skl_get_module(ctx, mod_id);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return ret;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	int usage_cnt;
4458c2ecf20Sopenharmony_ci	struct skl_dev *skl = ctx->thread_context;
4468c2ecf20Sopenharmony_ci	int ret = 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	usage_cnt = skl_put_module(ctx, mod_id);
4498c2ecf20Sopenharmony_ci	if (usage_cnt < 0) {
4508c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt);
4518c2ecf20Sopenharmony_ci		return -EIO;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* if module is used by others return, no need to unload */
4558c2ecf20Sopenharmony_ci	if (usage_cnt > 0)
4568c2ecf20Sopenharmony_ci		return 0;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	ret = skl_ipc_unload_modules(&skl->ipc,
4598c2ecf20Sopenharmony_ci			SKL_NUM_MODULES, &mod_id);
4608c2ecf20Sopenharmony_ci	if (ret < 0) {
4618c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Failed to UnLoad module\n");
4628c2ecf20Sopenharmony_ci		skl_get_module(ctx, mod_id);
4638c2ecf20Sopenharmony_ci		return ret;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return ret;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_civoid skl_clear_module_cnt(struct sst_dsp *ctx)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct skl_module_table *module;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (list_empty(&ctx->module_list))
4748c2ecf20Sopenharmony_ci		return;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	list_for_each_entry(module, &ctx->module_list, list) {
4778c2ecf20Sopenharmony_ci		module->usage_cnt = 0;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_clear_module_cnt);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic void skl_clear_module_table(struct sst_dsp *ctx)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct skl_module_table *module, *tmp;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (list_empty(&ctx->module_list))
4878c2ecf20Sopenharmony_ci		return;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	list_for_each_entry_safe(module, tmp, &ctx->module_list, list) {
4908c2ecf20Sopenharmony_ci		list_del(&module->list);
4918c2ecf20Sopenharmony_ci		release_firmware(module->mod_info->fw);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic const struct skl_dsp_fw_ops skl_fw_ops = {
4968c2ecf20Sopenharmony_ci	.set_state_D0 = skl_set_dsp_D0,
4978c2ecf20Sopenharmony_ci	.set_state_D3 = skl_set_dsp_D3,
4988c2ecf20Sopenharmony_ci	.load_fw = skl_load_base_firmware,
4998c2ecf20Sopenharmony_ci	.get_fw_errcode = skl_get_errorcode,
5008c2ecf20Sopenharmony_ci	.load_library = skl_load_library,
5018c2ecf20Sopenharmony_ci	.load_mod = skl_load_module,
5028c2ecf20Sopenharmony_ci	.unload_mod = skl_unload_module,
5038c2ecf20Sopenharmony_ci};
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic struct sst_ops skl_ops = {
5068c2ecf20Sopenharmony_ci	.irq_handler = skl_dsp_sst_interrupt,
5078c2ecf20Sopenharmony_ci	.write = sst_shim32_write,
5088c2ecf20Sopenharmony_ci	.read = sst_shim32_read,
5098c2ecf20Sopenharmony_ci	.free = skl_dsp_free,
5108c2ecf20Sopenharmony_ci};
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic struct sst_dsp_device skl_dev = {
5138c2ecf20Sopenharmony_ci	.thread = skl_dsp_irq_thread_handler,
5148c2ecf20Sopenharmony_ci	.ops = &skl_ops,
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ciint skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
5188c2ecf20Sopenharmony_ci		const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
5198c2ecf20Sopenharmony_ci		struct skl_dev **dsp)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct skl_dev *skl;
5228c2ecf20Sopenharmony_ci	struct sst_dsp *sst;
5238c2ecf20Sopenharmony_ci	int ret;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev);
5268c2ecf20Sopenharmony_ci	if (ret < 0) {
5278c2ecf20Sopenharmony_ci		dev_err(dev, "%s: no device\n", __func__);
5288c2ecf20Sopenharmony_ci		return ret;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	skl = *dsp;
5328c2ecf20Sopenharmony_ci	sst = skl->dsp;
5338c2ecf20Sopenharmony_ci	sst->addr.lpe = mmio_base;
5348c2ecf20Sopenharmony_ci	sst->addr.shim = mmio_base;
5358c2ecf20Sopenharmony_ci	sst->addr.sram0_base = SKL_ADSP_SRAM0_BASE;
5368c2ecf20Sopenharmony_ci	sst->addr.sram1_base = SKL_ADSP_SRAM1_BASE;
5378c2ecf20Sopenharmony_ci	sst->addr.w0_stat_sz = SKL_ADSP_W0_STAT_SZ;
5388c2ecf20Sopenharmony_ci	sst->addr.w0_up_sz = SKL_ADSP_W0_UP_SZ;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
5418c2ecf20Sopenharmony_ci			SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	ret = skl_ipc_init(dev, skl);
5448c2ecf20Sopenharmony_ci	if (ret) {
5458c2ecf20Sopenharmony_ci		skl_dsp_free(sst);
5468c2ecf20Sopenharmony_ci		return ret;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	sst->fw_ops = skl_fw_ops;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	return skl_dsp_acquire_irq(sst);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_dsp_init);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ciint skl_sst_init_fw(struct device *dev, struct skl_dev *skl)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	int ret;
5588c2ecf20Sopenharmony_ci	struct sst_dsp *sst = skl->dsp;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	ret = sst->fw_ops.load_fw(sst);
5618c2ecf20Sopenharmony_ci	if (ret < 0) {
5628c2ecf20Sopenharmony_ci		dev_err(dev, "Load base fw failed : %d\n", ret);
5638c2ecf20Sopenharmony_ci		return ret;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	skl_dsp_init_core_state(sst);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (skl->lib_count > 1) {
5698c2ecf20Sopenharmony_ci		ret = sst->fw_ops.load_library(sst, skl->lib_info,
5708c2ecf20Sopenharmony_ci						skl->lib_count);
5718c2ecf20Sopenharmony_ci		if (ret < 0) {
5728c2ecf20Sopenharmony_ci			dev_err(dev, "Load Library failed : %x\n", ret);
5738c2ecf20Sopenharmony_ci			return ret;
5748c2ecf20Sopenharmony_ci		}
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci	skl->is_first_boot = false;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return 0;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_init_fw);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_civoid skl_sst_dsp_cleanup(struct device *dev, struct skl_dev *skl)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (skl->dsp->fw)
5868c2ecf20Sopenharmony_ci		release_firmware(skl->dsp->fw);
5878c2ecf20Sopenharmony_ci	skl_clear_module_table(skl->dsp);
5888c2ecf20Sopenharmony_ci	skl_freeup_uuid_list(skl);
5898c2ecf20Sopenharmony_ci	skl_ipc_free(&skl->ipc);
5908c2ecf20Sopenharmony_ci	skl->dsp->ops->free(skl->dsp);
5918c2ecf20Sopenharmony_ci	if (skl->boot_complete) {
5928c2ecf20Sopenharmony_ci		skl->dsp->cl_dev.ops.cl_cleanup_controller(skl->dsp);
5938c2ecf20Sopenharmony_ci		skl_cldma_int_disable(skl->dsp);
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
5998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Skylake IPC driver");
600