162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license.  When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
962306a36Sopenharmony_ci//	    Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
1062306a36Sopenharmony_ci//	    Rander Wang <rander.wang@intel.com>
1162306a36Sopenharmony_ci//          Keyon Jie <yang.jie@linux.intel.com>
1262306a36Sopenharmony_ci//
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * Hardware interface for audio DSP on Cannonlake.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <sound/sof/ext_manifest4.h>
1962306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h>
2062306a36Sopenharmony_ci#include <trace/events/sof_intel.h>
2162306a36Sopenharmony_ci#include "../ipc4-priv.h"
2262306a36Sopenharmony_ci#include "../ops.h"
2362306a36Sopenharmony_ci#include "hda.h"
2462306a36Sopenharmony_ci#include "hda-ipc.h"
2562306a36Sopenharmony_ci#include "../sof-audio.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = {
2862306a36Sopenharmony_ci	{"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
2962306a36Sopenharmony_ci	{"pp", HDA_DSP_PP_BAR,  0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS},
3062306a36Sopenharmony_ci	{"dsp", HDA_DSP_BAR,  0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic void cnl_ipc_host_done(struct snd_sof_dev *sdev);
3462306a36Sopenharmony_cistatic void cnl_ipc_dsp_done(struct snd_sof_dev *sdev);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciirqreturn_t cnl_ipc4_irq_thread(int irq, void *context)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct sof_ipc4_msg notification_data = {{ 0 }};
3962306a36Sopenharmony_ci	struct snd_sof_dev *sdev = context;
4062306a36Sopenharmony_ci	bool ack_received = false;
4162306a36Sopenharmony_ci	bool ipc_irq = false;
4262306a36Sopenharmony_ci	u32 hipcida, hipctdr;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
4562306a36Sopenharmony_ci	hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
4662306a36Sopenharmony_ci	if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) {
4762306a36Sopenharmony_ci		/* DSP received the message */
4862306a36Sopenharmony_ci		snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
4962306a36Sopenharmony_ci					CNL_DSP_REG_HIPCCTL,
5062306a36Sopenharmony_ci					CNL_DSP_REG_HIPCCTL_DONE, 0);
5162306a36Sopenharmony_ci		cnl_ipc_dsp_done(sdev);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		ipc_irq = true;
5462306a36Sopenharmony_ci		ack_received = true;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) {
5862306a36Sopenharmony_ci		/* Message from DSP (reply or notification) */
5962306a36Sopenharmony_ci		u32 hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
6062306a36Sopenharmony_ci					       CNL_DSP_REG_HIPCTDD);
6162306a36Sopenharmony_ci		u32 primary = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK;
6262306a36Sopenharmony_ci		u32 extension = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		if (primary & SOF_IPC4_MSG_DIR_MASK) {
6562306a36Sopenharmony_ci			/* Reply received */
6662306a36Sopenharmony_ci			if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
6762306a36Sopenharmony_ci				struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci				data->primary = primary;
7062306a36Sopenharmony_ci				data->extension = extension;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci				spin_lock_irq(&sdev->ipc_lock);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci				snd_sof_ipc_get_reply(sdev);
7562306a36Sopenharmony_ci				cnl_ipc_host_done(sdev);
7662306a36Sopenharmony_ci				snd_sof_ipc_reply(sdev, data->primary);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci				spin_unlock_irq(&sdev->ipc_lock);
7962306a36Sopenharmony_ci			} else {
8062306a36Sopenharmony_ci				dev_dbg_ratelimited(sdev->dev,
8162306a36Sopenharmony_ci						    "IPC reply before FW_READY: %#x|%#x\n",
8262306a36Sopenharmony_ci						    primary, extension);
8362306a36Sopenharmony_ci			}
8462306a36Sopenharmony_ci		} else {
8562306a36Sopenharmony_ci			/* Notification received */
8662306a36Sopenharmony_ci			notification_data.primary = primary;
8762306a36Sopenharmony_ci			notification_data.extension = extension;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci			sdev->ipc->msg.rx_data = &notification_data;
9062306a36Sopenharmony_ci			snd_sof_ipc_msgs_rx(sdev);
9162306a36Sopenharmony_ci			sdev->ipc->msg.rx_data = NULL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci			/* Let DSP know that we have finished processing the message */
9462306a36Sopenharmony_ci			cnl_ipc_host_done(sdev);
9562306a36Sopenharmony_ci		}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		ipc_irq = true;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (!ipc_irq)
10162306a36Sopenharmony_ci		/* This interrupt is not shared so no need to return IRQ_NONE. */
10262306a36Sopenharmony_ci		dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (ack_received) {
10562306a36Sopenharmony_ci		struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		if (hdev->delayed_ipc_tx_msg)
10862306a36Sopenharmony_ci			cnl_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg);
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return IRQ_HANDLED;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciirqreturn_t cnl_ipc_irq_thread(int irq, void *context)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct snd_sof_dev *sdev = context;
11762306a36Sopenharmony_ci	u32 hipci;
11862306a36Sopenharmony_ci	u32 hipcida;
11962306a36Sopenharmony_ci	u32 hipctdr;
12062306a36Sopenharmony_ci	u32 hipctdd;
12162306a36Sopenharmony_ci	u32 msg;
12262306a36Sopenharmony_ci	u32 msg_ext;
12362306a36Sopenharmony_ci	bool ipc_irq = false;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
12662306a36Sopenharmony_ci	hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
12762306a36Sopenharmony_ci	hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDD);
12862306a36Sopenharmony_ci	hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* reply message from DSP */
13162306a36Sopenharmony_ci	if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) {
13262306a36Sopenharmony_ci		msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK;
13362306a36Sopenharmony_ci		msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		trace_sof_intel_ipc_firmware_response(sdev, msg, msg_ext);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		/* mask Done interrupt */
13862306a36Sopenharmony_ci		snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
13962306a36Sopenharmony_ci					CNL_DSP_REG_HIPCCTL,
14062306a36Sopenharmony_ci					CNL_DSP_REG_HIPCCTL_DONE, 0);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
14362306a36Sopenharmony_ci			spin_lock_irq(&sdev->ipc_lock);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci			/* handle immediate reply from DSP core */
14662306a36Sopenharmony_ci			hda_dsp_ipc_get_reply(sdev);
14762306a36Sopenharmony_ci			snd_sof_ipc_reply(sdev, msg);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci			cnl_ipc_dsp_done(sdev);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci			spin_unlock_irq(&sdev->ipc_lock);
15262306a36Sopenharmony_ci		} else {
15362306a36Sopenharmony_ci			dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",
15462306a36Sopenharmony_ci					    msg);
15562306a36Sopenharmony_ci		}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		ipc_irq = true;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* new message from DSP */
16162306a36Sopenharmony_ci	if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) {
16262306a36Sopenharmony_ci		msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK;
16362306a36Sopenharmony_ci		msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		trace_sof_intel_ipc_firmware_initiated(sdev, msg, msg_ext);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		/* handle messages from DSP */
16862306a36Sopenharmony_ci		if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
16962306a36Sopenharmony_ci			struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
17062306a36Sopenharmony_ci			bool non_recoverable = true;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci			/*
17362306a36Sopenharmony_ci			 * This is a PANIC message!
17462306a36Sopenharmony_ci			 *
17562306a36Sopenharmony_ci			 * If it is arriving during firmware boot and it is not
17662306a36Sopenharmony_ci			 * the last boot attempt then change the non_recoverable
17762306a36Sopenharmony_ci			 * to false as the DSP might be able to boot in the next
17862306a36Sopenharmony_ci			 * iteration(s)
17962306a36Sopenharmony_ci			 */
18062306a36Sopenharmony_ci			if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS &&
18162306a36Sopenharmony_ci			    hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS)
18262306a36Sopenharmony_ci				non_recoverable = false;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci			snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext),
18562306a36Sopenharmony_ci					  non_recoverable);
18662306a36Sopenharmony_ci		} else {
18762306a36Sopenharmony_ci			snd_sof_ipc_msgs_rx(sdev);
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		cnl_ipc_host_done(sdev);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		ipc_irq = true;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (!ipc_irq) {
19662306a36Sopenharmony_ci		/*
19762306a36Sopenharmony_ci		 * This interrupt is not shared so no need to return IRQ_NONE.
19862306a36Sopenharmony_ci		 */
19962306a36Sopenharmony_ci		dev_dbg_ratelimited(sdev->dev,
20062306a36Sopenharmony_ci				    "nothing to do in IPC IRQ thread\n");
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return IRQ_HANDLED;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic void cnl_ipc_host_done(struct snd_sof_dev *sdev)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	/*
20962306a36Sopenharmony_ci	 * clear busy interrupt to tell dsp controller this
21062306a36Sopenharmony_ci	 * interrupt has been accepted, not trigger it again
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci	snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,
21362306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCTDR,
21462306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCTDR_BUSY,
21562306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCTDR_BUSY);
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * set done bit to ack dsp the msg has been
21862306a36Sopenharmony_ci	 * processed and send reply msg to dsp
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,
22162306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCTDA,
22262306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCTDA_DONE,
22362306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCTDA_DONE);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void cnl_ipc_dsp_done(struct snd_sof_dev *sdev)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	/*
22962306a36Sopenharmony_ci	 * set DONE bit - tell DSP we have received the reply msg
23062306a36Sopenharmony_ci	 * from DSP, and processed it, don't send more reply to host
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,
23362306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCIDA,
23462306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCIDA_DONE,
23562306a36Sopenharmony_ci				       CNL_DSP_REG_HIPCIDA_DONE);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* unmask Done interrupt */
23862306a36Sopenharmony_ci	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
23962306a36Sopenharmony_ci				CNL_DSP_REG_HIPCCTL,
24062306a36Sopenharmony_ci				CNL_DSP_REG_HIPCCTL_DONE,
24162306a36Sopenharmony_ci				CNL_DSP_REG_HIPCCTL_DONE);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg,
24562306a36Sopenharmony_ci				     u32 *dr, u32 *dd)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct sof_ipc_pm_gate *pm_gate = msg->msg_data;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (pm_gate->hdr.cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
25062306a36Sopenharmony_ci		/* send the compact message via the primary register */
25162306a36Sopenharmony_ci		*dr = HDA_IPC_MSG_COMPACT | HDA_IPC_PM_GATE;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		/* send payload via the extended data register */
25462306a36Sopenharmony_ci		*dd = pm_gate->flags;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		return true;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return false;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ciint cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
26562306a36Sopenharmony_ci	struct sof_ipc4_msg *msg_data = msg->msg_data;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (hda_ipc4_tx_is_busy(sdev)) {
26862306a36Sopenharmony_ci		hdev->delayed_ipc_tx_msg = msg;
26962306a36Sopenharmony_ci		return 0;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	hdev->delayed_ipc_tx_msg = NULL;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* send the message via mailbox */
27562306a36Sopenharmony_ci	if (msg_data->data_size)
27662306a36Sopenharmony_ci		sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
27762306a36Sopenharmony_ci				  msg_data->data_size);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, msg_data->extension);
28062306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
28162306a36Sopenharmony_ci			  msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciint cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
29162306a36Sopenharmony_ci	struct sof_ipc_cmd_hdr *hdr;
29262306a36Sopenharmony_ci	u32 dr = 0;
29362306a36Sopenharmony_ci	u32 dd = 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/*
29662306a36Sopenharmony_ci	 * Currently the only compact IPC supported is the PM_GATE
29762306a36Sopenharmony_ci	 * IPC which is used for transitioning the DSP between the
29862306a36Sopenharmony_ci	 * D0I0 and D0I3 states. And these are sent only during the
29962306a36Sopenharmony_ci	 * set_power_state() op. Therefore, there will never be a case
30062306a36Sopenharmony_ci	 * that a compact IPC results in the DSP exiting D0I3 without
30162306a36Sopenharmony_ci	 * the host and FW being in sync.
30262306a36Sopenharmony_ci	 */
30362306a36Sopenharmony_ci	if (cnl_compact_ipc_compress(msg, &dr, &dd)) {
30462306a36Sopenharmony_ci		/* send the message via IPC registers */
30562306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD,
30662306a36Sopenharmony_ci				  dd);
30762306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
30862306a36Sopenharmony_ci				  CNL_DSP_REG_HIPCIDR_BUSY | dr);
30962306a36Sopenharmony_ci		return 0;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* send the message via mailbox */
31362306a36Sopenharmony_ci	sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
31462306a36Sopenharmony_ci			  msg->msg_size);
31562306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
31662306a36Sopenharmony_ci			  CNL_DSP_REG_HIPCIDR_BUSY);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	hdr = msg->msg_data;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/*
32162306a36Sopenharmony_ci	 * Use mod_delayed_work() to schedule the delayed work
32262306a36Sopenharmony_ci	 * to avoid scheduling multiple workqueue items when
32362306a36Sopenharmony_ci	 * IPCs are sent at a high-rate. mod_delayed_work()
32462306a36Sopenharmony_ci	 * modifies the timer if the work is pending.
32562306a36Sopenharmony_ci	 * Also, a new delayed work should not be queued after the
32662306a36Sopenharmony_ci	 * CTX_SAVE IPC, which is sent before the DSP enters D3.
32762306a36Sopenharmony_ci	 */
32862306a36Sopenharmony_ci	if (hdr->cmd != (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE))
32962306a36Sopenharmony_ci		mod_delayed_work(system_wq, &hdev->d0i3_work,
33062306a36Sopenharmony_ci				 msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS));
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return 0;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_civoid cnl_ipc_dump(struct snd_sof_dev *sdev)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	u32 hipcctl;
33862306a36Sopenharmony_ci	u32 hipcida;
33962306a36Sopenharmony_ci	u32 hipctdr;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	hda_ipc_irq_dump(sdev);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* read IPC status */
34462306a36Sopenharmony_ci	hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
34562306a36Sopenharmony_ci	hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCCTL);
34662306a36Sopenharmony_ci	hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* dump the IPC regs */
34962306a36Sopenharmony_ci	/* TODO: parse the raw msg */
35062306a36Sopenharmony_ci	dev_err(sdev->dev,
35162306a36Sopenharmony_ci		"error: host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",
35262306a36Sopenharmony_ci		hipcida, hipctdr, hipcctl);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_civoid cnl_ipc4_dump(struct snd_sof_dev *sdev)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	hda_ipc_irq_dump(sdev);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	hipcidr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR);
36262306a36Sopenharmony_ci	hipcidd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD);
36362306a36Sopenharmony_ci	hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
36462306a36Sopenharmony_ci	hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
36562306a36Sopenharmony_ci	hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDD);
36662306a36Sopenharmony_ci	hipctda = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDA);
36762306a36Sopenharmony_ci	hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCCTL);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* dump the IPC regs */
37062306a36Sopenharmony_ci	/* TODO: parse the raw msg */
37162306a36Sopenharmony_ci	dev_err(sdev->dev,
37262306a36Sopenharmony_ci		"Host IPC initiator: %#x|%#x|%#x, target: %#x|%#x|%#x, ctl: %#x\n",
37362306a36Sopenharmony_ci		hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci/* cannonlake ops */
37762306a36Sopenharmony_cistruct snd_sof_dsp_ops sof_cnl_ops;
37862306a36Sopenharmony_ciEXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ciint sof_cnl_ops_init(struct snd_sof_dev *sdev)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	/* common defaults */
38362306a36Sopenharmony_ci	memcpy(&sof_cnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* probe/remove/shutdown */
38662306a36Sopenharmony_ci	sof_cnl_ops.shutdown	= hda_dsp_shutdown;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* ipc */
38962306a36Sopenharmony_ci	if (sdev->pdata->ipc_type == SOF_IPC) {
39062306a36Sopenharmony_ci		/* doorbell */
39162306a36Sopenharmony_ci		sof_cnl_ops.irq_thread	= cnl_ipc_irq_thread;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* ipc */
39462306a36Sopenharmony_ci		sof_cnl_ops.send_msg	= cnl_ipc_send_msg;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		/* debug */
39762306a36Sopenharmony_ci		sof_cnl_ops.ipc_dump	= cnl_ipc_dump;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
40362306a36Sopenharmony_ci		struct sof_ipc4_fw_data *ipc4_data;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
40662306a36Sopenharmony_ci		if (!sdev->private)
40762306a36Sopenharmony_ci			return -ENOMEM;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		ipc4_data = sdev->private;
41062306a36Sopenharmony_ci		ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		/* External library loading support */
41562306a36Sopenharmony_ci		ipc4_data->load_library = hda_dsp_ipc4_load_library;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		/* doorbell */
41862306a36Sopenharmony_ci		sof_cnl_ops.irq_thread	= cnl_ipc4_irq_thread;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		/* ipc */
42162306a36Sopenharmony_ci		sof_cnl_ops.send_msg	= cnl_ipc4_send_msg;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		/* debug */
42462306a36Sopenharmony_ci		sof_cnl_ops.ipc_dump	= cnl_ipc4_dump;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* set DAI driver ops */
43062306a36Sopenharmony_ci	hda_set_dai_drv_ops(sdev, &sof_cnl_ops);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* debug */
43362306a36Sopenharmony_ci	sof_cnl_ops.debug_map	= cnl_dsp_debugfs;
43462306a36Sopenharmony_ci	sof_cnl_ops.debug_map_count	= ARRAY_SIZE(cnl_dsp_debugfs);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/* pre/post fw run */
43762306a36Sopenharmony_ci	sof_cnl_ops.post_fw_run = hda_dsp_post_fw_run;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* firmware run */
44062306a36Sopenharmony_ci	sof_cnl_ops.run = hda_dsp_cl_boot_firmware;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* dsp core get/put */
44362306a36Sopenharmony_ci	sof_cnl_ops.core_get = hda_dsp_core_get;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci};
44762306a36Sopenharmony_ciEXPORT_SYMBOL_NS(sof_cnl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ciconst struct sof_intel_dsp_desc cnl_chip_info = {
45062306a36Sopenharmony_ci	/* Cannonlake */
45162306a36Sopenharmony_ci	.cores_num = 4,
45262306a36Sopenharmony_ci	.init_core_mask = 1,
45362306a36Sopenharmony_ci	.host_managed_cores_mask = GENMASK(3, 0),
45462306a36Sopenharmony_ci	.ipc_req = CNL_DSP_REG_HIPCIDR,
45562306a36Sopenharmony_ci	.ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
45662306a36Sopenharmony_ci	.ipc_ack = CNL_DSP_REG_HIPCIDA,
45762306a36Sopenharmony_ci	.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
45862306a36Sopenharmony_ci	.ipc_ctl = CNL_DSP_REG_HIPCCTL,
45962306a36Sopenharmony_ci	.rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
46062306a36Sopenharmony_ci	.rom_init_timeout	= 300,
46162306a36Sopenharmony_ci	.ssp_count = CNL_SSP_COUNT,
46262306a36Sopenharmony_ci	.ssp_base_offset = CNL_SSP_BASE_OFFSET,
46362306a36Sopenharmony_ci	.sdw_shim_base = SDW_SHIM_BASE,
46462306a36Sopenharmony_ci	.sdw_alh_base = SDW_ALH_BASE,
46562306a36Sopenharmony_ci	.d0i3_offset = SOF_HDA_VS_D0I3C,
46662306a36Sopenharmony_ci	.read_sdw_lcount =  hda_sdw_check_lcount_common,
46762306a36Sopenharmony_ci	.enable_sdw_irq	= hda_common_enable_sdw_irq,
46862306a36Sopenharmony_ci	.check_sdw_irq	= hda_common_check_sdw_irq,
46962306a36Sopenharmony_ci	.check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
47062306a36Sopenharmony_ci	.check_ipc_irq	= hda_dsp_check_ipc_irq,
47162306a36Sopenharmony_ci	.cl_init = cl_dsp_init,
47262306a36Sopenharmony_ci	.power_down_dsp = hda_power_down_dsp,
47362306a36Sopenharmony_ci	.disable_interrupts = hda_dsp_disable_interrupts,
47462306a36Sopenharmony_ci	.hw_ip_version = SOF_INTEL_CAVS_1_8,
47562306a36Sopenharmony_ci};
47662306a36Sopenharmony_ciEXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/*
47962306a36Sopenharmony_ci * JasperLake is technically derived from IceLake, and should be in
48062306a36Sopenharmony_ci * described in icl.c. However since JasperLake was designed with
48162306a36Sopenharmony_ci * two cores, it cannot support the IceLake-specific power-up sequences
48262306a36Sopenharmony_ci * which rely on core3. To simplify, JasperLake uses the CannonLake ops and
48362306a36Sopenharmony_ci * is described in cnl.c
48462306a36Sopenharmony_ci */
48562306a36Sopenharmony_ciconst struct sof_intel_dsp_desc jsl_chip_info = {
48662306a36Sopenharmony_ci	/* Jasperlake */
48762306a36Sopenharmony_ci	.cores_num = 2,
48862306a36Sopenharmony_ci	.init_core_mask = 1,
48962306a36Sopenharmony_ci	.host_managed_cores_mask = GENMASK(1, 0),
49062306a36Sopenharmony_ci	.ipc_req = CNL_DSP_REG_HIPCIDR,
49162306a36Sopenharmony_ci	.ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
49262306a36Sopenharmony_ci	.ipc_ack = CNL_DSP_REG_HIPCIDA,
49362306a36Sopenharmony_ci	.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
49462306a36Sopenharmony_ci	.ipc_ctl = CNL_DSP_REG_HIPCCTL,
49562306a36Sopenharmony_ci	.rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
49662306a36Sopenharmony_ci	.rom_init_timeout	= 300,
49762306a36Sopenharmony_ci	.ssp_count = ICL_SSP_COUNT,
49862306a36Sopenharmony_ci	.ssp_base_offset = CNL_SSP_BASE_OFFSET,
49962306a36Sopenharmony_ci	.sdw_shim_base = SDW_SHIM_BASE,
50062306a36Sopenharmony_ci	.sdw_alh_base = SDW_ALH_BASE,
50162306a36Sopenharmony_ci	.d0i3_offset = SOF_HDA_VS_D0I3C,
50262306a36Sopenharmony_ci	.read_sdw_lcount =  hda_sdw_check_lcount_common,
50362306a36Sopenharmony_ci	.enable_sdw_irq	= hda_common_enable_sdw_irq,
50462306a36Sopenharmony_ci	.check_sdw_irq	= hda_common_check_sdw_irq,
50562306a36Sopenharmony_ci	.check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
50662306a36Sopenharmony_ci	.check_ipc_irq	= hda_dsp_check_ipc_irq,
50762306a36Sopenharmony_ci	.cl_init = cl_dsp_init,
50862306a36Sopenharmony_ci	.power_down_dsp = hda_power_down_dsp,
50962306a36Sopenharmony_ci	.disable_interrupts = hda_dsp_disable_interrupts,
51062306a36Sopenharmony_ci	.hw_ip_version = SOF_INTEL_CAVS_2_0,
51162306a36Sopenharmony_ci};
51262306a36Sopenharmony_ciEXPORT_SYMBOL_NS(jsl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
513