162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  sst.c - Intel SST Driver for audio engine
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2008-14	Intel Corp
662306a36Sopenharmony_ci *  Authors:	Vinod Koul <vinod.koul@intel.com>
762306a36Sopenharmony_ci *		Harsha Priya <priya.harsha@intel.com>
862306a36Sopenharmony_ci *		Dharageswari R <dharageswari.r@intel.com>
962306a36Sopenharmony_ci *		KP Jeeja <jeeja.kp@intel.com>
1062306a36Sopenharmony_ci *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/fs.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/io.h>
1862306a36Sopenharmony_ci#include <linux/firmware.h>
1962306a36Sopenharmony_ci#include <linux/pci.h>
2062306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2162306a36Sopenharmony_ci#include <linux/pm_qos.h>
2262306a36Sopenharmony_ci#include <linux/async.h>
2362306a36Sopenharmony_ci#include <linux/acpi.h>
2462306a36Sopenharmony_ci#include <linux/sysfs.h>
2562306a36Sopenharmony_ci#include <sound/core.h>
2662306a36Sopenharmony_ci#include <sound/soc.h>
2762306a36Sopenharmony_ci#include <asm/platform_sst_audio.h>
2862306a36Sopenharmony_ci#include "../sst-mfld-platform.h"
2962306a36Sopenharmony_ci#include "sst.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciMODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
3262306a36Sopenharmony_ciMODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
3362306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
3462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic inline bool sst_is_process_reply(u32 msg_id)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return ((msg_id & PROCESS_MSG) ? true : false);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic inline bool sst_validate_mailbox_size(unsigned int size)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	return ((size <= SST_MAILBOX_SIZE) ? true : false);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	union interrupt_reg_mrfld isr;
4962306a36Sopenharmony_ci	union ipc_header_mrfld header;
5062306a36Sopenharmony_ci	union sst_imr_reg_mrfld imr;
5162306a36Sopenharmony_ci	struct ipc_post *msg = NULL;
5262306a36Sopenharmony_ci	unsigned int size;
5362306a36Sopenharmony_ci	struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
5462306a36Sopenharmony_ci	irqreturn_t retval = IRQ_HANDLED;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Interrupt arrived, check src */
5762306a36Sopenharmony_ci	isr.full = sst_shim_read64(drv->shim, SST_ISRX);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (isr.part.done_interrupt) {
6062306a36Sopenharmony_ci		/* Clear done bit */
6162306a36Sopenharmony_ci		spin_lock(&drv->ipc_spin_lock);
6262306a36Sopenharmony_ci		header.full = sst_shim_read64(drv->shim,
6362306a36Sopenharmony_ci					drv->ipc_reg.ipcx);
6462306a36Sopenharmony_ci		header.p.header_high.part.done = 0;
6562306a36Sopenharmony_ci		sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		/* write 1 to clear status register */;
6862306a36Sopenharmony_ci		isr.part.done_interrupt = 1;
6962306a36Sopenharmony_ci		sst_shim_write64(drv->shim, SST_ISRX, isr.full);
7062306a36Sopenharmony_ci		spin_unlock(&drv->ipc_spin_lock);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		/* we can send more messages to DSP so trigger work */
7362306a36Sopenharmony_ci		queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
7462306a36Sopenharmony_ci		retval = IRQ_HANDLED;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (isr.part.busy_interrupt) {
7862306a36Sopenharmony_ci		/* message from dsp so copy that */
7962306a36Sopenharmony_ci		spin_lock(&drv->ipc_spin_lock);
8062306a36Sopenharmony_ci		imr.full = sst_shim_read64(drv->shim, SST_IMRX);
8162306a36Sopenharmony_ci		imr.part.busy_interrupt = 1;
8262306a36Sopenharmony_ci		sst_shim_write64(drv->shim, SST_IMRX, imr.full);
8362306a36Sopenharmony_ci		spin_unlock(&drv->ipc_spin_lock);
8462306a36Sopenharmony_ci		header.full =  sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
8762306a36Sopenharmony_ci			drv->ops->clear_interrupt(drv);
8862306a36Sopenharmony_ci			return IRQ_HANDLED;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		if (header.p.header_high.part.large) {
9262306a36Sopenharmony_ci			size = header.p.header_low_payload;
9362306a36Sopenharmony_ci			if (sst_validate_mailbox_size(size)) {
9462306a36Sopenharmony_ci				memcpy_fromio(msg->mailbox_data,
9562306a36Sopenharmony_ci					drv->mailbox + drv->mailbox_recv_offset, size);
9662306a36Sopenharmony_ci			} else {
9762306a36Sopenharmony_ci				dev_err(drv->dev,
9862306a36Sopenharmony_ci					"Mailbox not copied, payload size is: %u\n", size);
9962306a36Sopenharmony_ci				header.p.header_low_payload = 0;
10062306a36Sopenharmony_ci			}
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		msg->mrfld_header = header;
10462306a36Sopenharmony_ci		msg->is_process_reply =
10562306a36Sopenharmony_ci			sst_is_process_reply(header.p.header_high.part.msg_id);
10662306a36Sopenharmony_ci		spin_lock(&drv->rx_msg_lock);
10762306a36Sopenharmony_ci		list_add_tail(&msg->node, &drv->rx_list);
10862306a36Sopenharmony_ci		spin_unlock(&drv->rx_msg_lock);
10962306a36Sopenharmony_ci		drv->ops->clear_interrupt(drv);
11062306a36Sopenharmony_ci		retval = IRQ_WAKE_THREAD;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	return retval;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
11862306a36Sopenharmony_ci	struct ipc_post *__msg, *msg;
11962306a36Sopenharmony_ci	unsigned long irq_flags;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
12262306a36Sopenharmony_ci	if (list_empty(&drv->rx_list)) {
12362306a36Sopenharmony_ci		spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
12462306a36Sopenharmony_ci		return IRQ_HANDLED;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
12862306a36Sopenharmony_ci		list_del(&msg->node);
12962306a36Sopenharmony_ci		spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
13062306a36Sopenharmony_ci		if (msg->is_process_reply)
13162306a36Sopenharmony_ci			drv->ops->process_message(msg);
13262306a36Sopenharmony_ci		else
13362306a36Sopenharmony_ci			drv->ops->process_reply(drv, msg);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		if (msg->is_large)
13662306a36Sopenharmony_ci			kfree(msg->mailbox_data);
13762306a36Sopenharmony_ci		kfree(msg);
13862306a36Sopenharmony_ci		spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
14162306a36Sopenharmony_ci	return IRQ_HANDLED;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	int ret = 0;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
14962306a36Sopenharmony_ci			IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
15062306a36Sopenharmony_ci			true, true, false, true);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (ret < 0) {
15362306a36Sopenharmony_ci		dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
15462306a36Sopenharmony_ci		return -EIO;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic struct intel_sst_ops mrfld_ops = {
16262306a36Sopenharmony_ci	.interrupt = intel_sst_interrupt_mrfld,
16362306a36Sopenharmony_ci	.irq_thread = intel_sst_irq_thread_mrfld,
16462306a36Sopenharmony_ci	.clear_interrupt = intel_sst_clear_intr_mrfld,
16562306a36Sopenharmony_ci	.start = sst_start_mrfld,
16662306a36Sopenharmony_ci	.reset = intel_sst_reset_dsp_mrfld,
16762306a36Sopenharmony_ci	.post_message = sst_post_message_mrfld,
16862306a36Sopenharmony_ci	.process_reply = sst_process_reply_mrfld,
16962306a36Sopenharmony_ci	.save_dsp_context =  sst_save_dsp_context_v2,
17062306a36Sopenharmony_ci	.alloc_stream = sst_alloc_stream_mrfld,
17162306a36Sopenharmony_ci	.post_download = sst_post_download_mrfld,
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciint sst_driver_ops(struct intel_sst_drv *sst)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	switch (sst->dev_id) {
17862306a36Sopenharmony_ci	case PCI_DEVICE_ID_INTEL_SST_TNG:
17962306a36Sopenharmony_ci	case PCI_DEVICE_ID_INTEL_SST_BYT:
18062306a36Sopenharmony_ci	case PCI_DEVICE_ID_INTEL_SST_BSW:
18162306a36Sopenharmony_ci		sst->tstamp = SST_TIME_STAMP_MRFLD;
18262306a36Sopenharmony_ci		sst->ops = &mrfld_ops;
18362306a36Sopenharmony_ci		return 0;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	default:
18662306a36Sopenharmony_ci		dev_err(sst->dev,
18762306a36Sopenharmony_ci			"SST Driver capabilities missing for dev_id: %x",
18862306a36Sopenharmony_ci			sst->dev_id);
18962306a36Sopenharmony_ci		return -EINVAL;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_civoid sst_process_pending_msg(struct work_struct *work)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct intel_sst_drv *ctx = container_of(work,
19662306a36Sopenharmony_ci			struct intel_sst_drv, ipc_post_msg_wq);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	ctx->ops->post_message(ctx, NULL, false);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int sst_workqueue_init(struct intel_sst_drv *ctx)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->memcpy_list);
20462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->rx_list);
20562306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
20662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->block_list);
20762306a36Sopenharmony_ci	INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
20862306a36Sopenharmony_ci	init_waitqueue_head(&ctx->wait_queue);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ctx->post_msg_wq =
21162306a36Sopenharmony_ci		create_singlethread_workqueue("sst_post_msg_wq");
21262306a36Sopenharmony_ci	if (!ctx->post_msg_wq)
21362306a36Sopenharmony_ci		return -EBUSY;
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void sst_init_locks(struct intel_sst_drv *ctx)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	mutex_init(&ctx->sst_lock);
22062306a36Sopenharmony_ci	spin_lock_init(&ctx->rx_msg_lock);
22162306a36Sopenharmony_ci	spin_lock_init(&ctx->ipc_spin_lock);
22262306a36Sopenharmony_ci	spin_lock_init(&ctx->block_lock);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/*
22662306a36Sopenharmony_ci * Driver handles PCI IDs in ACPI - sst_acpi_probe() - and we are using only
22762306a36Sopenharmony_ci * device ID part. If real ACPI ID appears, the kstrtouint() returns error, so
22862306a36Sopenharmony_ci * we are fine with using unsigned short as dev_id type.
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_ciint sst_alloc_drv_context(struct intel_sst_drv **ctx,
23162306a36Sopenharmony_ci		struct device *dev, unsigned short dev_id)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	*ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
23462306a36Sopenharmony_ci	if (!(*ctx))
23562306a36Sopenharmony_ci		return -ENOMEM;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	(*ctx)->dev = dev;
23862306a36Sopenharmony_ci	(*ctx)->dev_id = dev_id;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return 0;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_alloc_drv_context);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic ssize_t firmware_version_show(struct device *dev,
24562306a36Sopenharmony_ci			    struct device_attribute *attr, char *buf)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 &&
25062306a36Sopenharmony_ci	    ctx->fw_version.minor == 0 && ctx->fw_version.build == 0)
25162306a36Sopenharmony_ci		return sysfs_emit(buf, "FW not yet loaded\n");
25262306a36Sopenharmony_ci	else
25362306a36Sopenharmony_ci		return sysfs_emit(buf, "v%02x.%02x.%02x.%02x\n",
25462306a36Sopenharmony_ci				  ctx->fw_version.type, ctx->fw_version.major,
25562306a36Sopenharmony_ci				  ctx->fw_version.minor, ctx->fw_version.build);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_version);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic const struct attribute *sst_fw_version_attrs[] = {
26262306a36Sopenharmony_ci	&dev_attr_firmware_version.attr,
26362306a36Sopenharmony_ci	NULL,
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic const struct attribute_group sst_fw_version_attr_group = {
26762306a36Sopenharmony_ci	.attrs = (struct attribute **)sst_fw_version_attrs,
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ciint sst_context_init(struct intel_sst_drv *ctx)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	int ret = 0, i;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!ctx->pdata)
27562306a36Sopenharmony_ci		return -EINVAL;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (!ctx->pdata->probe_data)
27862306a36Sopenharmony_ci		return -EINVAL;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ret = sst_driver_ops(ctx);
28362306a36Sopenharmony_ci	if (ret != 0)
28462306a36Sopenharmony_ci		return -EINVAL;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	sst_init_locks(ctx);
28762306a36Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_RESET);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* pvt_id 0 reserved for async messages */
29062306a36Sopenharmony_ci	ctx->pvt_id = 1;
29162306a36Sopenharmony_ci	ctx->stream_cnt = 0;
29262306a36Sopenharmony_ci	ctx->fw_in_mem = NULL;
29362306a36Sopenharmony_ci	/* we use memcpy, so set to 0 */
29462306a36Sopenharmony_ci	ctx->use_dma = 0;
29562306a36Sopenharmony_ci	ctx->use_lli = 0;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (sst_workqueue_init(ctx))
29862306a36Sopenharmony_ci		return -EINVAL;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
30162306a36Sopenharmony_ci	ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
30262306a36Sopenharmony_ci	ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	dev_info(ctx->dev, "Got drv data max stream %d\n",
30562306a36Sopenharmony_ci				ctx->info.max_streams);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	for (i = 1; i <= ctx->info.max_streams; i++) {
30862306a36Sopenharmony_ci		struct stream_info *stream = &ctx->streams[i];
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		memset(stream, 0, sizeof(*stream));
31162306a36Sopenharmony_ci		stream->pipe_id = PIPE_RSVD;
31262306a36Sopenharmony_ci		mutex_init(&stream->lock);
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Register the ISR */
31662306a36Sopenharmony_ci	ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
31762306a36Sopenharmony_ci					ctx->ops->irq_thread, 0, SST_DRV_NAME,
31862306a36Sopenharmony_ci					ctx);
31962306a36Sopenharmony_ci	if (ret)
32062306a36Sopenharmony_ci		goto do_free_mem;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* default intr are unmasked so set this as masked */
32562306a36Sopenharmony_ci	sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ctx->qos = devm_kzalloc(ctx->dev,
32862306a36Sopenharmony_ci		sizeof(struct pm_qos_request), GFP_KERNEL);
32962306a36Sopenharmony_ci	if (!ctx->qos) {
33062306a36Sopenharmony_ci		ret = -ENOMEM;
33162306a36Sopenharmony_ci		goto do_free_mem;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci	cpu_latency_qos_add_request(ctx->qos, PM_QOS_DEFAULT_VALUE);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
33662306a36Sopenharmony_ci	ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
33762306a36Sopenharmony_ci				      ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
33862306a36Sopenharmony_ci	if (ret) {
33962306a36Sopenharmony_ci		dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
34062306a36Sopenharmony_ci		goto do_free_mem;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = sysfs_create_group(&ctx->dev->kobj,
34462306a36Sopenharmony_ci				 &sst_fw_version_attr_group);
34562306a36Sopenharmony_ci	if (ret) {
34662306a36Sopenharmony_ci		dev_err(ctx->dev,
34762306a36Sopenharmony_ci			"Unable to create sysfs\n");
34862306a36Sopenharmony_ci		goto err_sysfs;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	sst_register(ctx->dev);
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_cierr_sysfs:
35462306a36Sopenharmony_ci	sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cido_free_mem:
35762306a36Sopenharmony_ci	destroy_workqueue(ctx->post_msg_wq);
35862306a36Sopenharmony_ci	return ret;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_context_init);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_civoid sst_context_cleanup(struct intel_sst_drv *ctx)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	pm_runtime_get_noresume(ctx->dev);
36562306a36Sopenharmony_ci	pm_runtime_disable(ctx->dev);
36662306a36Sopenharmony_ci	sst_unregister(ctx->dev);
36762306a36Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
36862306a36Sopenharmony_ci	sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
36962306a36Sopenharmony_ci	destroy_workqueue(ctx->post_msg_wq);
37062306a36Sopenharmony_ci	cpu_latency_qos_remove_request(ctx->qos);
37162306a36Sopenharmony_ci	kfree(ctx->fw_sg_list.src);
37262306a36Sopenharmony_ci	kfree(ctx->fw_sg_list.dst);
37362306a36Sopenharmony_ci	ctx->fw_sg_list.list_len = 0;
37462306a36Sopenharmony_ci	kfree(ctx->fw_in_mem);
37562306a36Sopenharmony_ci	ctx->fw_in_mem = NULL;
37662306a36Sopenharmony_ci	sst_memcpy_free_resources(ctx);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_context_cleanup);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_civoid sst_configure_runtime_pm(struct intel_sst_drv *ctx)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
38362306a36Sopenharmony_ci	pm_runtime_use_autosuspend(ctx->dev);
38462306a36Sopenharmony_ci	/*
38562306a36Sopenharmony_ci	 * For acpi devices, the actual physical device state is
38662306a36Sopenharmony_ci	 * initially active. So change the state to active before
38762306a36Sopenharmony_ci	 * enabling the pm
38862306a36Sopenharmony_ci	 */
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (!acpi_disabled)
39162306a36Sopenharmony_ci		pm_runtime_set_active(ctx->dev);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	pm_runtime_enable(ctx->dev);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (acpi_disabled)
39662306a36Sopenharmony_ci		pm_runtime_set_active(ctx->dev);
39762306a36Sopenharmony_ci	else
39862306a36Sopenharmony_ci		pm_runtime_put_noidle(ctx->dev);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int intel_sst_runtime_suspend(struct device *dev)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	int ret = 0;
40562306a36Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (ctx->sst_state == SST_RESET) {
40862306a36Sopenharmony_ci		dev_dbg(dev, "LPE is already in RESET state, No action\n");
40962306a36Sopenharmony_ci		return 0;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci	/* save fw context */
41262306a36Sopenharmony_ci	if (ctx->ops->save_dsp_context(ctx))
41362306a36Sopenharmony_ci		return -EBUSY;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* Move the SST state to Reset */
41662306a36Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_RESET);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	synchronize_irq(ctx->irq_num);
41962306a36Sopenharmony_ci	flush_workqueue(ctx->post_msg_wq);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	ctx->ops->reset(ctx);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return ret;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int intel_sst_suspend(struct device *dev)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
42962306a36Sopenharmony_ci	struct sst_fw_save *fw_save;
43062306a36Sopenharmony_ci	int i, ret;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* check first if we are already in SW reset */
43362306a36Sopenharmony_ci	if (ctx->sst_state == SST_RESET)
43462306a36Sopenharmony_ci		return 0;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/*
43762306a36Sopenharmony_ci	 * check if any stream is active and running
43862306a36Sopenharmony_ci	 * they should already by suspend by soc_suspend
43962306a36Sopenharmony_ci	 */
44062306a36Sopenharmony_ci	for (i = 1; i <= ctx->info.max_streams; i++) {
44162306a36Sopenharmony_ci		struct stream_info *stream = &ctx->streams[i];
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		if (stream->status == STREAM_RUNNING) {
44462306a36Sopenharmony_ci			dev_err(dev, "stream %d is running, can't suspend, abort\n", i);
44562306a36Sopenharmony_ci			return -EBUSY;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		if (ctx->pdata->streams_lost_on_suspend) {
44962306a36Sopenharmony_ci			stream->resume_status = stream->status;
45062306a36Sopenharmony_ci			stream->resume_prev = stream->prev;
45162306a36Sopenharmony_ci			if (stream->status != STREAM_UN_INIT)
45262306a36Sopenharmony_ci				sst_free_stream(ctx, i);
45362306a36Sopenharmony_ci		}
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci	synchronize_irq(ctx->irq_num);
45662306a36Sopenharmony_ci	flush_workqueue(ctx->post_msg_wq);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/* Move the SST state to Reset */
45962306a36Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_RESET);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* tell DSP we are suspending */
46262306a36Sopenharmony_ci	if (ctx->ops->save_dsp_context(ctx))
46362306a36Sopenharmony_ci		return -EBUSY;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* save the memories */
46662306a36Sopenharmony_ci	fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL);
46762306a36Sopenharmony_ci	if (!fw_save)
46862306a36Sopenharmony_ci		return -ENOMEM;
46962306a36Sopenharmony_ci	fw_save->iram = kvzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
47062306a36Sopenharmony_ci	if (!fw_save->iram) {
47162306a36Sopenharmony_ci		ret = -ENOMEM;
47262306a36Sopenharmony_ci		goto iram;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci	fw_save->dram = kvzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
47562306a36Sopenharmony_ci	if (!fw_save->dram) {
47662306a36Sopenharmony_ci		ret = -ENOMEM;
47762306a36Sopenharmony_ci		goto dram;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	fw_save->sram = kvzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
48062306a36Sopenharmony_ci	if (!fw_save->sram) {
48162306a36Sopenharmony_ci		ret = -ENOMEM;
48262306a36Sopenharmony_ci		goto sram;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	fw_save->ddr = kvzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
48662306a36Sopenharmony_ci	if (!fw_save->ddr) {
48762306a36Sopenharmony_ci		ret = -ENOMEM;
48862306a36Sopenharmony_ci		goto ddr;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base);
49262306a36Sopenharmony_ci	memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base);
49362306a36Sopenharmony_ci	memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE);
49462306a36Sopenharmony_ci	memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	ctx->fw_save = fw_save;
49762306a36Sopenharmony_ci	ctx->ops->reset(ctx);
49862306a36Sopenharmony_ci	return 0;
49962306a36Sopenharmony_ciddr:
50062306a36Sopenharmony_ci	kvfree(fw_save->sram);
50162306a36Sopenharmony_cisram:
50262306a36Sopenharmony_ci	kvfree(fw_save->dram);
50362306a36Sopenharmony_cidram:
50462306a36Sopenharmony_ci	kvfree(fw_save->iram);
50562306a36Sopenharmony_ciiram:
50662306a36Sopenharmony_ci	kfree(fw_save);
50762306a36Sopenharmony_ci	return ret;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int intel_sst_resume(struct device *dev)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
51362306a36Sopenharmony_ci	struct sst_fw_save *fw_save = ctx->fw_save;
51462306a36Sopenharmony_ci	struct sst_block *block;
51562306a36Sopenharmony_ci	int i, ret = 0;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (!fw_save)
51862306a36Sopenharmony_ci		return 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	sst_set_fw_state_locked(ctx, SST_FW_LOADING);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* we have to restore the memory saved */
52362306a36Sopenharmony_ci	ctx->ops->reset(ctx);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	ctx->fw_save = NULL;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base);
52862306a36Sopenharmony_ci	memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base);
52962306a36Sopenharmony_ci	memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE);
53062306a36Sopenharmony_ci	memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	kvfree(fw_save->sram);
53362306a36Sopenharmony_ci	kvfree(fw_save->dram);
53462306a36Sopenharmony_ci	kvfree(fw_save->iram);
53562306a36Sopenharmony_ci	kvfree(fw_save->ddr);
53662306a36Sopenharmony_ci	kfree(fw_save);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	block = sst_create_block(ctx, 0, FW_DWNL_ID);
53962306a36Sopenharmony_ci	if (block == NULL)
54062306a36Sopenharmony_ci		return -ENOMEM;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* start and wait for ack */
54462306a36Sopenharmony_ci	ctx->ops->start(ctx);
54562306a36Sopenharmony_ci	ret = sst_wait_timeout(ctx, block);
54662306a36Sopenharmony_ci	if (ret) {
54762306a36Sopenharmony_ci		dev_err(ctx->dev, "fw download failed %d\n", ret);
54862306a36Sopenharmony_ci		/* FW download failed due to timeout */
54962306a36Sopenharmony_ci		ret = -EBUSY;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	} else {
55262306a36Sopenharmony_ci		sst_set_fw_state_locked(ctx, SST_FW_RUNNING);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (ctx->pdata->streams_lost_on_suspend) {
55662306a36Sopenharmony_ci		for (i = 1; i <= ctx->info.max_streams; i++) {
55762306a36Sopenharmony_ci			struct stream_info *stream = &ctx->streams[i];
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci			if (stream->resume_status != STREAM_UN_INIT) {
56062306a36Sopenharmony_ci				dev_dbg(ctx->dev, "Re-allocing stream %d status %d prev %d\n",
56162306a36Sopenharmony_ci					i, stream->resume_status,
56262306a36Sopenharmony_ci					stream->resume_prev);
56362306a36Sopenharmony_ci				sst_realloc_stream(ctx, i);
56462306a36Sopenharmony_ci				stream->status = stream->resume_status;
56562306a36Sopenharmony_ci				stream->prev = stream->resume_prev;
56662306a36Sopenharmony_ci			}
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	sst_free_block(ctx, block);
57162306a36Sopenharmony_ci	return ret;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ciconst struct dev_pm_ops intel_sst_pm = {
57562306a36Sopenharmony_ci	.suspend = intel_sst_suspend,
57662306a36Sopenharmony_ci	.resume = intel_sst_resume,
57762306a36Sopenharmony_ci	.runtime_suspend = intel_sst_runtime_suspend,
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_sst_pm);
580