18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  sst.c - Intel SST Driver for audio engine
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2008-14	Intel Corp
68c2ecf20Sopenharmony_ci *  Authors:	Vinod Koul <vinod.koul@intel.com>
78c2ecf20Sopenharmony_ci *		Harsha Priya <priya.harsha@intel.com>
88c2ecf20Sopenharmony_ci *		Dharageswari R <dharageswari.r@intel.com>
98c2ecf20Sopenharmony_ci *		KP Jeeja <jeeja.kp@intel.com>
108c2ecf20Sopenharmony_ci *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/fs.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci#include <linux/firmware.h>
198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
208c2ecf20Sopenharmony_ci#include <linux/pm_qos.h>
218c2ecf20Sopenharmony_ci#include <linux/async.h>
228c2ecf20Sopenharmony_ci#include <linux/acpi.h>
238c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
248c2ecf20Sopenharmony_ci#include <sound/core.h>
258c2ecf20Sopenharmony_ci#include <sound/soc.h>
268c2ecf20Sopenharmony_ci#include <asm/platform_sst_audio.h>
278c2ecf20Sopenharmony_ci#include "../sst-mfld-platform.h"
288c2ecf20Sopenharmony_ci#include "sst.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic inline bool sst_is_process_reply(u32 msg_id)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	return ((msg_id & PROCESS_MSG) ? true : false);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic inline bool sst_validate_mailbox_size(unsigned int size)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return ((size <= SST_MAILBOX_SIZE) ? true : false);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	union interrupt_reg_mrfld isr;
488c2ecf20Sopenharmony_ci	union ipc_header_mrfld header;
498c2ecf20Sopenharmony_ci	union sst_imr_reg_mrfld imr;
508c2ecf20Sopenharmony_ci	struct ipc_post *msg = NULL;
518c2ecf20Sopenharmony_ci	unsigned int size;
528c2ecf20Sopenharmony_ci	struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
538c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_HANDLED;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* Interrupt arrived, check src */
568c2ecf20Sopenharmony_ci	isr.full = sst_shim_read64(drv->shim, SST_ISRX);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (isr.part.done_interrupt) {
598c2ecf20Sopenharmony_ci		/* Clear done bit */
608c2ecf20Sopenharmony_ci		spin_lock(&drv->ipc_spin_lock);
618c2ecf20Sopenharmony_ci		header.full = sst_shim_read64(drv->shim,
628c2ecf20Sopenharmony_ci					drv->ipc_reg.ipcx);
638c2ecf20Sopenharmony_ci		header.p.header_high.part.done = 0;
648c2ecf20Sopenharmony_ci		sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		/* write 1 to clear status register */;
678c2ecf20Sopenharmony_ci		isr.part.done_interrupt = 1;
688c2ecf20Sopenharmony_ci		sst_shim_write64(drv->shim, SST_ISRX, isr.full);
698c2ecf20Sopenharmony_ci		spin_unlock(&drv->ipc_spin_lock);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		/* we can send more messages to DSP so trigger work */
728c2ecf20Sopenharmony_ci		queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
738c2ecf20Sopenharmony_ci		retval = IRQ_HANDLED;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (isr.part.busy_interrupt) {
778c2ecf20Sopenharmony_ci		/* message from dsp so copy that */
788c2ecf20Sopenharmony_ci		spin_lock(&drv->ipc_spin_lock);
798c2ecf20Sopenharmony_ci		imr.full = sst_shim_read64(drv->shim, SST_IMRX);
808c2ecf20Sopenharmony_ci		imr.part.busy_interrupt = 1;
818c2ecf20Sopenharmony_ci		sst_shim_write64(drv->shim, SST_IMRX, imr.full);
828c2ecf20Sopenharmony_ci		spin_unlock(&drv->ipc_spin_lock);
838c2ecf20Sopenharmony_ci		header.full =  sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
868c2ecf20Sopenharmony_ci			drv->ops->clear_interrupt(drv);
878c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
888c2ecf20Sopenharmony_ci		}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		if (header.p.header_high.part.large) {
918c2ecf20Sopenharmony_ci			size = header.p.header_low_payload;
928c2ecf20Sopenharmony_ci			if (sst_validate_mailbox_size(size)) {
938c2ecf20Sopenharmony_ci				memcpy_fromio(msg->mailbox_data,
948c2ecf20Sopenharmony_ci					drv->mailbox + drv->mailbox_recv_offset, size);
958c2ecf20Sopenharmony_ci			} else {
968c2ecf20Sopenharmony_ci				dev_err(drv->dev,
978c2ecf20Sopenharmony_ci					"Mailbox not copied, payload size is: %u\n", size);
988c2ecf20Sopenharmony_ci				header.p.header_low_payload = 0;
998c2ecf20Sopenharmony_ci			}
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		msg->mrfld_header = header;
1038c2ecf20Sopenharmony_ci		msg->is_process_reply =
1048c2ecf20Sopenharmony_ci			sst_is_process_reply(header.p.header_high.part.msg_id);
1058c2ecf20Sopenharmony_ci		spin_lock(&drv->rx_msg_lock);
1068c2ecf20Sopenharmony_ci		list_add_tail(&msg->node, &drv->rx_list);
1078c2ecf20Sopenharmony_ci		spin_unlock(&drv->rx_msg_lock);
1088c2ecf20Sopenharmony_ci		drv->ops->clear_interrupt(drv);
1098c2ecf20Sopenharmony_ci		retval = IRQ_WAKE_THREAD;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	return retval;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
1178c2ecf20Sopenharmony_ci	struct ipc_post *__msg, *msg = NULL;
1188c2ecf20Sopenharmony_ci	unsigned long irq_flags;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
1218c2ecf20Sopenharmony_ci	if (list_empty(&drv->rx_list)) {
1228c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
1238c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
1278c2ecf20Sopenharmony_ci		list_del(&msg->node);
1288c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
1298c2ecf20Sopenharmony_ci		if (msg->is_process_reply)
1308c2ecf20Sopenharmony_ci			drv->ops->process_message(msg);
1318c2ecf20Sopenharmony_ci		else
1328c2ecf20Sopenharmony_ci			drv->ops->process_reply(drv, msg);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		if (msg->is_large)
1358c2ecf20Sopenharmony_ci			kfree(msg->mailbox_data);
1368c2ecf20Sopenharmony_ci		kfree(msg);
1378c2ecf20Sopenharmony_ci		spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
1408c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	int ret = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
1488c2ecf20Sopenharmony_ci			IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
1498c2ecf20Sopenharmony_ci			true, true, false, true);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (ret < 0) {
1528c2ecf20Sopenharmony_ci		dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
1538c2ecf20Sopenharmony_ci		return -EIO;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct intel_sst_ops mrfld_ops = {
1618c2ecf20Sopenharmony_ci	.interrupt = intel_sst_interrupt_mrfld,
1628c2ecf20Sopenharmony_ci	.irq_thread = intel_sst_irq_thread_mrfld,
1638c2ecf20Sopenharmony_ci	.clear_interrupt = intel_sst_clear_intr_mrfld,
1648c2ecf20Sopenharmony_ci	.start = sst_start_mrfld,
1658c2ecf20Sopenharmony_ci	.reset = intel_sst_reset_dsp_mrfld,
1668c2ecf20Sopenharmony_ci	.post_message = sst_post_message_mrfld,
1678c2ecf20Sopenharmony_ci	.process_reply = sst_process_reply_mrfld,
1688c2ecf20Sopenharmony_ci	.save_dsp_context =  sst_save_dsp_context_v2,
1698c2ecf20Sopenharmony_ci	.alloc_stream = sst_alloc_stream_mrfld,
1708c2ecf20Sopenharmony_ci	.post_download = sst_post_download_mrfld,
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciint sst_driver_ops(struct intel_sst_drv *sst)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	switch (sst->dev_id) {
1778c2ecf20Sopenharmony_ci	case SST_MRFLD_PCI_ID:
1788c2ecf20Sopenharmony_ci	case SST_BYT_ACPI_ID:
1798c2ecf20Sopenharmony_ci	case SST_CHV_ACPI_ID:
1808c2ecf20Sopenharmony_ci		sst->tstamp = SST_TIME_STAMP_MRFLD;
1818c2ecf20Sopenharmony_ci		sst->ops = &mrfld_ops;
1828c2ecf20Sopenharmony_ci		return 0;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	default:
1858c2ecf20Sopenharmony_ci		dev_err(sst->dev,
1868c2ecf20Sopenharmony_ci			"SST Driver capabilities missing for dev_id: %x",
1878c2ecf20Sopenharmony_ci			sst->dev_id);
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	};
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_civoid sst_process_pending_msg(struct work_struct *work)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct intel_sst_drv *ctx = container_of(work,
1958c2ecf20Sopenharmony_ci			struct intel_sst_drv, ipc_post_msg_wq);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ctx->ops->post_message(ctx, NULL, false);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int sst_workqueue_init(struct intel_sst_drv *ctx)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->memcpy_list);
2038c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->rx_list);
2048c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
2058c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->block_list);
2068c2ecf20Sopenharmony_ci	INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
2078c2ecf20Sopenharmony_ci	init_waitqueue_head(&ctx->wait_queue);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	ctx->post_msg_wq =
2108c2ecf20Sopenharmony_ci		create_singlethread_workqueue("sst_post_msg_wq");
2118c2ecf20Sopenharmony_ci	if (!ctx->post_msg_wq)
2128c2ecf20Sopenharmony_ci		return -EBUSY;
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void sst_init_locks(struct intel_sst_drv *ctx)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	mutex_init(&ctx->sst_lock);
2198c2ecf20Sopenharmony_ci	spin_lock_init(&ctx->rx_msg_lock);
2208c2ecf20Sopenharmony_ci	spin_lock_init(&ctx->ipc_spin_lock);
2218c2ecf20Sopenharmony_ci	spin_lock_init(&ctx->block_lock);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ciint sst_alloc_drv_context(struct intel_sst_drv **ctx,
2258c2ecf20Sopenharmony_ci		struct device *dev, unsigned int dev_id)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	*ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
2288c2ecf20Sopenharmony_ci	if (!(*ctx))
2298c2ecf20Sopenharmony_ci		return -ENOMEM;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	(*ctx)->dev = dev;
2328c2ecf20Sopenharmony_ci	(*ctx)->dev_id = dev_id;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_alloc_drv_context);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic ssize_t firmware_version_show(struct device *dev,
2398c2ecf20Sopenharmony_ci			    struct device_attribute *attr, char *buf)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 &&
2448c2ecf20Sopenharmony_ci	    ctx->fw_version.minor == 0 && ctx->fw_version.build == 0)
2458c2ecf20Sopenharmony_ci		return sprintf(buf, "FW not yet loaded\n");
2468c2ecf20Sopenharmony_ci	else
2478c2ecf20Sopenharmony_ci		return sprintf(buf, "v%02x.%02x.%02x.%02x\n",
2488c2ecf20Sopenharmony_ci			       ctx->fw_version.type, ctx->fw_version.major,
2498c2ecf20Sopenharmony_ci			       ctx->fw_version.minor, ctx->fw_version.build);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_version);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic const struct attribute *sst_fw_version_attrs[] = {
2568c2ecf20Sopenharmony_ci	&dev_attr_firmware_version.attr,
2578c2ecf20Sopenharmony_ci	NULL,
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic const struct attribute_group sst_fw_version_attr_group = {
2618c2ecf20Sopenharmony_ci	.attrs = (struct attribute **)sst_fw_version_attrs,
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciint sst_context_init(struct intel_sst_drv *ctx)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	int ret = 0, i;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (!ctx->pdata)
2698c2ecf20Sopenharmony_ci		return -EINVAL;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (!ctx->pdata->probe_data)
2728c2ecf20Sopenharmony_ci		return -EINVAL;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	ret = sst_driver_ops(ctx);
2778c2ecf20Sopenharmony_ci	if (ret != 0)
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	sst_init_locks(ctx);
2818c2ecf20Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_RESET);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* pvt_id 0 reserved for async messages */
2848c2ecf20Sopenharmony_ci	ctx->pvt_id = 1;
2858c2ecf20Sopenharmony_ci	ctx->stream_cnt = 0;
2868c2ecf20Sopenharmony_ci	ctx->fw_in_mem = NULL;
2878c2ecf20Sopenharmony_ci	/* we use memcpy, so set to 0 */
2888c2ecf20Sopenharmony_ci	ctx->use_dma = 0;
2898c2ecf20Sopenharmony_ci	ctx->use_lli = 0;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (sst_workqueue_init(ctx))
2928c2ecf20Sopenharmony_ci		return -EINVAL;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
2958c2ecf20Sopenharmony_ci	ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
2968c2ecf20Sopenharmony_ci	ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	dev_info(ctx->dev, "Got drv data max stream %d\n",
2998c2ecf20Sopenharmony_ci				ctx->info.max_streams);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	for (i = 1; i <= ctx->info.max_streams; i++) {
3028c2ecf20Sopenharmony_ci		struct stream_info *stream = &ctx->streams[i];
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		memset(stream, 0, sizeof(*stream));
3058c2ecf20Sopenharmony_ci		stream->pipe_id = PIPE_RSVD;
3068c2ecf20Sopenharmony_ci		mutex_init(&stream->lock);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Register the ISR */
3108c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
3118c2ecf20Sopenharmony_ci					ctx->ops->irq_thread, 0, SST_DRV_NAME,
3128c2ecf20Sopenharmony_ci					ctx);
3138c2ecf20Sopenharmony_ci	if (ret)
3148c2ecf20Sopenharmony_ci		goto do_free_mem;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* default intr are unmasked so set this as masked */
3198c2ecf20Sopenharmony_ci	sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ctx->qos = devm_kzalloc(ctx->dev,
3228c2ecf20Sopenharmony_ci		sizeof(struct pm_qos_request), GFP_KERNEL);
3238c2ecf20Sopenharmony_ci	if (!ctx->qos) {
3248c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3258c2ecf20Sopenharmony_ci		goto do_free_mem;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	cpu_latency_qos_add_request(ctx->qos, PM_QOS_DEFAULT_VALUE);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
3308c2ecf20Sopenharmony_ci	ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
3318c2ecf20Sopenharmony_ci				      ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
3328c2ecf20Sopenharmony_ci	if (ret) {
3338c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
3348c2ecf20Sopenharmony_ci		goto do_free_mem;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	ret = sysfs_create_group(&ctx->dev->kobj,
3388c2ecf20Sopenharmony_ci				 &sst_fw_version_attr_group);
3398c2ecf20Sopenharmony_ci	if (ret) {
3408c2ecf20Sopenharmony_ci		dev_err(ctx->dev,
3418c2ecf20Sopenharmony_ci			"Unable to create sysfs\n");
3428c2ecf20Sopenharmony_ci		goto err_sysfs;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	sst_register(ctx->dev);
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_cierr_sysfs:
3488c2ecf20Sopenharmony_ci	sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cido_free_mem:
3518c2ecf20Sopenharmony_ci	destroy_workqueue(ctx->post_msg_wq);
3528c2ecf20Sopenharmony_ci	return ret;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_context_init);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_civoid sst_context_cleanup(struct intel_sst_drv *ctx)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(ctx->dev);
3598c2ecf20Sopenharmony_ci	pm_runtime_disable(ctx->dev);
3608c2ecf20Sopenharmony_ci	sst_unregister(ctx->dev);
3618c2ecf20Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
3628c2ecf20Sopenharmony_ci	sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
3638c2ecf20Sopenharmony_ci	flush_scheduled_work();
3648c2ecf20Sopenharmony_ci	destroy_workqueue(ctx->post_msg_wq);
3658c2ecf20Sopenharmony_ci	cpu_latency_qos_remove_request(ctx->qos);
3668c2ecf20Sopenharmony_ci	kfree(ctx->fw_sg_list.src);
3678c2ecf20Sopenharmony_ci	kfree(ctx->fw_sg_list.dst);
3688c2ecf20Sopenharmony_ci	ctx->fw_sg_list.list_len = 0;
3698c2ecf20Sopenharmony_ci	kfree(ctx->fw_in_mem);
3708c2ecf20Sopenharmony_ci	ctx->fw_in_mem = NULL;
3718c2ecf20Sopenharmony_ci	sst_memcpy_free_resources(ctx);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_context_cleanup);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_civoid sst_configure_runtime_pm(struct intel_sst_drv *ctx)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
3788c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(ctx->dev);
3798c2ecf20Sopenharmony_ci	/*
3808c2ecf20Sopenharmony_ci	 * For acpi devices, the actual physical device state is
3818c2ecf20Sopenharmony_ci	 * initially active. So change the state to active before
3828c2ecf20Sopenharmony_ci	 * enabling the pm
3838c2ecf20Sopenharmony_ci	 */
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (!acpi_disabled)
3868c2ecf20Sopenharmony_ci		pm_runtime_set_active(ctx->dev);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	pm_runtime_enable(ctx->dev);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (acpi_disabled)
3918c2ecf20Sopenharmony_ci		pm_runtime_set_active(ctx->dev);
3928c2ecf20Sopenharmony_ci	else
3938c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(ctx->dev);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic int intel_sst_runtime_suspend(struct device *dev)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	int ret = 0;
4008c2ecf20Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (ctx->sst_state == SST_RESET) {
4038c2ecf20Sopenharmony_ci		dev_dbg(dev, "LPE is already in RESET state, No action\n");
4048c2ecf20Sopenharmony_ci		return 0;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci	/* save fw context */
4078c2ecf20Sopenharmony_ci	if (ctx->ops->save_dsp_context(ctx))
4088c2ecf20Sopenharmony_ci		return -EBUSY;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Move the SST state to Reset */
4118c2ecf20Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_RESET);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	synchronize_irq(ctx->irq_num);
4148c2ecf20Sopenharmony_ci	flush_workqueue(ctx->post_msg_wq);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	ctx->ops->reset(ctx);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return ret;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int intel_sst_suspend(struct device *dev)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
4248c2ecf20Sopenharmony_ci	struct sst_fw_save *fw_save;
4258c2ecf20Sopenharmony_ci	int i, ret;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* check first if we are already in SW reset */
4288c2ecf20Sopenharmony_ci	if (ctx->sst_state == SST_RESET)
4298c2ecf20Sopenharmony_ci		return 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/*
4328c2ecf20Sopenharmony_ci	 * check if any stream is active and running
4338c2ecf20Sopenharmony_ci	 * they should already by suspend by soc_suspend
4348c2ecf20Sopenharmony_ci	 */
4358c2ecf20Sopenharmony_ci	for (i = 1; i <= ctx->info.max_streams; i++) {
4368c2ecf20Sopenharmony_ci		struct stream_info *stream = &ctx->streams[i];
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		if (stream->status == STREAM_RUNNING) {
4398c2ecf20Sopenharmony_ci			dev_err(dev, "stream %d is running, can't suspend, abort\n", i);
4408c2ecf20Sopenharmony_ci			return -EBUSY;
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		if (ctx->pdata->streams_lost_on_suspend) {
4448c2ecf20Sopenharmony_ci			stream->resume_status = stream->status;
4458c2ecf20Sopenharmony_ci			stream->resume_prev = stream->prev;
4468c2ecf20Sopenharmony_ci			if (stream->status != STREAM_UN_INIT)
4478c2ecf20Sopenharmony_ci				sst_free_stream(ctx, i);
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	synchronize_irq(ctx->irq_num);
4518c2ecf20Sopenharmony_ci	flush_workqueue(ctx->post_msg_wq);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Move the SST state to Reset */
4548c2ecf20Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_RESET);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* tell DSP we are suspending */
4578c2ecf20Sopenharmony_ci	if (ctx->ops->save_dsp_context(ctx))
4588c2ecf20Sopenharmony_ci		return -EBUSY;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* save the memories */
4618c2ecf20Sopenharmony_ci	fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL);
4628c2ecf20Sopenharmony_ci	if (!fw_save)
4638c2ecf20Sopenharmony_ci		return -ENOMEM;
4648c2ecf20Sopenharmony_ci	fw_save->iram = kvzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
4658c2ecf20Sopenharmony_ci	if (!fw_save->iram) {
4668c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4678c2ecf20Sopenharmony_ci		goto iram;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci	fw_save->dram = kvzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
4708c2ecf20Sopenharmony_ci	if (!fw_save->dram) {
4718c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4728c2ecf20Sopenharmony_ci		goto dram;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	fw_save->sram = kvzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
4758c2ecf20Sopenharmony_ci	if (!fw_save->sram) {
4768c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4778c2ecf20Sopenharmony_ci		goto sram;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	fw_save->ddr = kvzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
4818c2ecf20Sopenharmony_ci	if (!fw_save->ddr) {
4828c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4838c2ecf20Sopenharmony_ci		goto ddr;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base);
4878c2ecf20Sopenharmony_ci	memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base);
4888c2ecf20Sopenharmony_ci	memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE);
4898c2ecf20Sopenharmony_ci	memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ctx->fw_save = fw_save;
4928c2ecf20Sopenharmony_ci	ctx->ops->reset(ctx);
4938c2ecf20Sopenharmony_ci	return 0;
4948c2ecf20Sopenharmony_ciddr:
4958c2ecf20Sopenharmony_ci	kvfree(fw_save->sram);
4968c2ecf20Sopenharmony_cisram:
4978c2ecf20Sopenharmony_ci	kvfree(fw_save->dram);
4988c2ecf20Sopenharmony_cidram:
4998c2ecf20Sopenharmony_ci	kvfree(fw_save->iram);
5008c2ecf20Sopenharmony_ciiram:
5018c2ecf20Sopenharmony_ci	kfree(fw_save);
5028c2ecf20Sopenharmony_ci	return ret;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic int intel_sst_resume(struct device *dev)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
5088c2ecf20Sopenharmony_ci	struct sst_fw_save *fw_save = ctx->fw_save;
5098c2ecf20Sopenharmony_ci	struct sst_block *block;
5108c2ecf20Sopenharmony_ci	int i, ret = 0;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (!fw_save)
5138c2ecf20Sopenharmony_ci		return 0;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_FW_LOADING);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* we have to restore the memory saved */
5188c2ecf20Sopenharmony_ci	ctx->ops->reset(ctx);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	ctx->fw_save = NULL;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base);
5238c2ecf20Sopenharmony_ci	memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base);
5248c2ecf20Sopenharmony_ci	memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE);
5258c2ecf20Sopenharmony_ci	memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	kvfree(fw_save->sram);
5288c2ecf20Sopenharmony_ci	kvfree(fw_save->dram);
5298c2ecf20Sopenharmony_ci	kvfree(fw_save->iram);
5308c2ecf20Sopenharmony_ci	kvfree(fw_save->ddr);
5318c2ecf20Sopenharmony_ci	kfree(fw_save);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	block = sst_create_block(ctx, 0, FW_DWNL_ID);
5348c2ecf20Sopenharmony_ci	if (block == NULL)
5358c2ecf20Sopenharmony_ci		return -ENOMEM;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/* start and wait for ack */
5398c2ecf20Sopenharmony_ci	ctx->ops->start(ctx);
5408c2ecf20Sopenharmony_ci	ret = sst_wait_timeout(ctx, block);
5418c2ecf20Sopenharmony_ci	if (ret) {
5428c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "fw download failed %d\n", ret);
5438c2ecf20Sopenharmony_ci		/* FW download failed due to timeout */
5448c2ecf20Sopenharmony_ci		ret = -EBUSY;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	} else {
5478c2ecf20Sopenharmony_ci		sst_set_fw_state_locked(ctx, SST_FW_RUNNING);
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (ctx->pdata->streams_lost_on_suspend) {
5518c2ecf20Sopenharmony_ci		for (i = 1; i <= ctx->info.max_streams; i++) {
5528c2ecf20Sopenharmony_ci			struct stream_info *stream = &ctx->streams[i];
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci			if (stream->resume_status != STREAM_UN_INIT) {
5558c2ecf20Sopenharmony_ci				dev_dbg(ctx->dev, "Re-allocing stream %d status %d prev %d\n",
5568c2ecf20Sopenharmony_ci					i, stream->resume_status,
5578c2ecf20Sopenharmony_ci					stream->resume_prev);
5588c2ecf20Sopenharmony_ci				sst_realloc_stream(ctx, i);
5598c2ecf20Sopenharmony_ci				stream->status = stream->resume_status;
5608c2ecf20Sopenharmony_ci				stream->prev = stream->resume_prev;
5618c2ecf20Sopenharmony_ci			}
5628c2ecf20Sopenharmony_ci		}
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	sst_free_block(ctx, block);
5668c2ecf20Sopenharmony_ci	return ret;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ciconst struct dev_pm_ops intel_sst_pm = {
5708c2ecf20Sopenharmony_ci	.suspend = intel_sst_suspend,
5718c2ecf20Sopenharmony_ci	.resume = intel_sst_resume,
5728c2ecf20Sopenharmony_ci	.runtime_suspend = intel_sst_runtime_suspend,
5738c2ecf20Sopenharmony_ci};
5748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_sst_pm);
575