brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · 9feaaa2 Raw
557 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)2//3// This file is provided under a dual BSD/GPLv2 license.  When using or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2018 Intel Corporation7//8// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>9//	    Ranjani Sridharan <ranjani.sridharan@linux.intel.com>10//	    Rander Wang <rander.wang@intel.com>11//          Keyon Jie <yang.jie@linux.intel.com>12//13 14/*15 * Hardware interface for generic Intel audio DSP HDA IP16 */17 18#include <sound/hda_register.h>19#include <sound/sof/ipc4/header.h>20#include <trace/events/sof_intel.h>21#include "../ops.h"22#include "hda.h"23#include "telemetry.h"24 25EXPORT_TRACEPOINT_SYMBOL(sof_intel_ipc_firmware_initiated);26EXPORT_TRACEPOINT_SYMBOL(sof_intel_ipc_firmware_response);27EXPORT_TRACEPOINT_SYMBOL(sof_intel_hda_irq_ipc_check);28 29static void hda_dsp_ipc_host_done(struct snd_sof_dev *sdev)30{31	/*32	 * tell DSP cmd is done - clear busy33	 * interrupt and send reply msg to dsp34	 */35	snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,36				       HDA_DSP_REG_HIPCT,37				       HDA_DSP_REG_HIPCT_BUSY,38				       HDA_DSP_REG_HIPCT_BUSY);39 40	/* unmask BUSY interrupt */41	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,42				HDA_DSP_REG_HIPCCTL,43				HDA_DSP_REG_HIPCCTL_BUSY,44				HDA_DSP_REG_HIPCCTL_BUSY);45}46 47static void hda_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)48{49	/*50	 * set DONE bit - tell DSP we have received the reply msg51	 * from DSP, and processed it, don't send more reply to host52	 */53	snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,54				       HDA_DSP_REG_HIPCIE,55				       HDA_DSP_REG_HIPCIE_DONE,56				       HDA_DSP_REG_HIPCIE_DONE);57 58	/* unmask Done interrupt */59	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,60				HDA_DSP_REG_HIPCCTL,61				HDA_DSP_REG_HIPCCTL_DONE,62				HDA_DSP_REG_HIPCCTL_DONE);63}64 65int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)66{67	/* send IPC message to DSP */68	sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,69			  msg->msg_size);70	snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,71			  HDA_DSP_REG_HIPCI_BUSY);72 73	return 0;74}75EXPORT_SYMBOL_NS(hda_dsp_ipc_send_msg, SND_SOC_SOF_INTEL_HDA_COMMON);76 77static inline bool hda_dsp_ipc4_pm_msg(u32 primary)78{79	/* pm setting is only supported by module msg */80	if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG)81		return false;82 83	if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX ||84	    SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX)85		return true;86 87	return false;88}89 90void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,91				     struct snd_sof_ipc_msg *msg)92{93	struct sof_ipc4_msg *msg_data = msg->msg_data;94 95	/* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */96	if (hda_dsp_ipc4_pm_msg(msg_data->primary))97		return;98 99	mod_delayed_work(system_wq, &hdev->d0i3_work,100			 msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS));101}102EXPORT_SYMBOL_NS(hda_dsp_ipc4_schedule_d0i3_work, SND_SOC_SOF_INTEL_HDA_COMMON);103 104int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)105{106	struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;107	struct sof_ipc4_msg *msg_data = msg->msg_data;108 109	if (hda_ipc4_tx_is_busy(sdev)) {110		hdev->delayed_ipc_tx_msg = msg;111		return 0;112	}113 114	hdev->delayed_ipc_tx_msg = NULL;115 116	/* send the message via mailbox */117	if (msg_data->data_size)118		sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,119				  msg_data->data_size);120 121	snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE, msg_data->extension);122	snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,123			  msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);124 125	hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);126 127	return 0;128}129EXPORT_SYMBOL_NS(hda_dsp_ipc4_send_msg, SND_SOC_SOF_INTEL_HDA_COMMON);130 131void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)132{133	struct snd_sof_ipc_msg *msg = sdev->msg;134	struct sof_ipc_reply reply;135	struct sof_ipc_cmd_hdr *hdr;136 137	/*138	 * Sometimes, there is unexpected reply ipc arriving. The reply139	 * ipc belongs to none of the ipcs sent from driver.140	 * In this case, the driver must ignore the ipc.141	 */142	if (!msg) {143		dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");144		return;145	}146 147	hdr = msg->msg_data;148	if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||149	    hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {150		/*151		 * memory windows are powered off before sending IPC reply,152		 * so we can't read the mailbox for CTX_SAVE and PM_GATE153		 * replies.154		 */155		reply.error = 0;156		reply.hdr.cmd = SOF_IPC_GLB_REPLY;157		reply.hdr.size = sizeof(reply);158		memcpy(msg->reply_data, &reply, sizeof(reply));159 160		msg->reply_error = 0;161	} else {162		snd_sof_ipc_get_reply(sdev);163	}164}165EXPORT_SYMBOL_NS(hda_dsp_ipc_get_reply, SND_SOC_SOF_INTEL_HDA_COMMON);166 167irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context)168{169	struct sof_ipc4_msg notification_data = {{ 0 }};170	struct snd_sof_dev *sdev = context;171	bool ack_received = false;172	bool ipc_irq = false;173	u32 hipcie, hipct;174 175	hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);176	hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);177 178	if (hipcie & HDA_DSP_REG_HIPCIE_DONE) {179		/* DSP received the message */180		snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,181					HDA_DSP_REG_HIPCCTL_DONE, 0);182		hda_dsp_ipc_dsp_done(sdev);183 184		ipc_irq = true;185		ack_received = true;186	}187 188	if (hipct & HDA_DSP_REG_HIPCT_BUSY) {189		/* Message from DSP (reply or notification) */190		u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR,191					      HDA_DSP_REG_HIPCTE);192		u32 primary = hipct & HDA_DSP_REG_HIPCT_MSG_MASK;193		u32 extension = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK;194 195		/* mask BUSY interrupt */196		snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,197					HDA_DSP_REG_HIPCCTL_BUSY, 0);198 199		if (primary & SOF_IPC4_MSG_DIR_MASK) {200			/* Reply received */201			if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {202				struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;203 204				data->primary = primary;205				data->extension = extension;206 207				spin_lock_irq(&sdev->ipc_lock);208 209				snd_sof_ipc_get_reply(sdev);210				hda_dsp_ipc_host_done(sdev);211				snd_sof_ipc_reply(sdev, data->primary);212 213				spin_unlock_irq(&sdev->ipc_lock);214			} else {215				dev_dbg_ratelimited(sdev->dev,216						    "IPC reply before FW_READY: %#x|%#x\n",217						    primary, extension);218			}219		} else {220			/* Notification received */221 222			notification_data.primary = primary;223			notification_data.extension = extension;224			sdev->ipc->msg.rx_data = &notification_data;225			snd_sof_ipc_msgs_rx(sdev);226			sdev->ipc->msg.rx_data = NULL;227 228			/* Let DSP know that we have finished processing the message */229			hda_dsp_ipc_host_done(sdev);230		}231 232		ipc_irq = true;233	}234 235	if (!ipc_irq)236		/* This interrupt is not shared so no need to return IRQ_NONE. */237		dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");238 239	if (ack_received) {240		struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;241 242		if (hdev->delayed_ipc_tx_msg)243			hda_dsp_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg);244	}245 246	return IRQ_HANDLED;247}248EXPORT_SYMBOL_NS(hda_dsp_ipc4_irq_thread, SND_SOC_SOF_INTEL_HDA_COMMON);249 250/* IPC handler thread */251irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)252{253	struct snd_sof_dev *sdev = context;254	u32 hipci;255	u32 hipcie;256	u32 hipct;257	u32 hipcte;258	u32 msg;259	u32 msg_ext;260	bool ipc_irq = false;261 262	/* read IPC status */263	hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR,264				  HDA_DSP_REG_HIPCIE);265	hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);266	hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI);267	hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE);268 269	/* is this a reply message from the DSP */270	if (hipcie & HDA_DSP_REG_HIPCIE_DONE) {271		msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK;272		msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK;273 274		trace_sof_intel_ipc_firmware_response(sdev, msg, msg_ext);275 276		/* mask Done interrupt */277		snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,278					HDA_DSP_REG_HIPCCTL,279					HDA_DSP_REG_HIPCCTL_DONE, 0);280 281		/*282		 * Make sure the interrupt thread cannot be preempted between283		 * waking up the sender and re-enabling the interrupt. Also284		 * protect against a theoretical race with sof_ipc_tx_message():285		 * if the DSP is fast enough to receive an IPC message, reply to286		 * it, and the host interrupt processing calls this function on287		 * a different core from the one, where the sending is taking288		 * place, the message might not yet be marked as expecting a289		 * reply.290		 */291		if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {292			spin_lock_irq(&sdev->ipc_lock);293 294			/* handle immediate reply from DSP core */295			hda_dsp_ipc_get_reply(sdev);296			snd_sof_ipc_reply(sdev, msg);297 298			/* set the done bit */299			hda_dsp_ipc_dsp_done(sdev);300 301			spin_unlock_irq(&sdev->ipc_lock);302		} else {303			dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",304					    msg);305		}306 307		ipc_irq = true;308	}309 310	/* is this a new message from DSP */311	if (hipct & HDA_DSP_REG_HIPCT_BUSY) {312		msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK;313		msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK;314 315		trace_sof_intel_ipc_firmware_initiated(sdev, msg, msg_ext);316 317		/* mask BUSY interrupt */318		snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,319					HDA_DSP_REG_HIPCCTL,320					HDA_DSP_REG_HIPCCTL_BUSY, 0);321 322		/* handle messages from DSP */323		if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {324			struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;325			bool non_recoverable = true;326 327			/*328			 * This is a PANIC message!329			 *330			 * If it is arriving during firmware boot and it is not331			 * the last boot attempt then change the non_recoverable332			 * to false as the DSP might be able to boot in the next333			 * iteration(s)334			 */335			if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS &&336			    hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS)337				non_recoverable = false;338 339			snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext),340					  non_recoverable);341		} else {342			/* normal message - process normally */343			snd_sof_ipc_msgs_rx(sdev);344		}345 346		hda_dsp_ipc_host_done(sdev);347 348		ipc_irq = true;349	}350 351	if (!ipc_irq) {352		/*353		 * This interrupt is not shared so no need to return IRQ_NONE.354		 */355		dev_dbg_ratelimited(sdev->dev,356				    "nothing to do in IPC IRQ thread\n");357	}358 359	return IRQ_HANDLED;360}361EXPORT_SYMBOL_NS(hda_dsp_ipc_irq_thread, SND_SOC_SOF_INTEL_HDA_COMMON);362 363/* Check if an IPC IRQ occurred */364bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev)365{366	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;367	bool ret = false;368	u32 irq_status;369 370	if (sdev->dspless_mode_selected)371		return false;372 373	/* store status */374	irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);375	trace_sof_intel_hda_irq_ipc_check(sdev, irq_status);376 377	/* invalid message ? */378	if (irq_status == 0xffffffff)379		goto out;380 381	/* IPC message ? */382	if (irq_status & HDA_DSP_ADSPIS_IPC)383		ret = true;384 385	/* CLDMA message ? */386	if (irq_status & HDA_DSP_ADSPIS_CL_DMA) {387		hda->code_loading = 0;388		wake_up(&hda->waitq);389		ret = false;390	}391 392out:393	return ret;394}395EXPORT_SYMBOL_NS(hda_dsp_check_ipc_irq, SND_SOC_SOF_INTEL_HDA_COMMON);396 397int hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)398{399	return HDA_DSP_MBOX_UPLINK_OFFSET;400}401EXPORT_SYMBOL_NS(hda_dsp_ipc_get_mailbox_offset, SND_SOC_SOF_INTEL_HDA_COMMON);402 403int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)404{405	return SRAM_WINDOW_OFFSET(id);406}407EXPORT_SYMBOL_NS(hda_dsp_ipc_get_window_offset, SND_SOC_SOF_INTEL_HDA_COMMON);408 409int hda_ipc_msg_data(struct snd_sof_dev *sdev,410		     struct snd_sof_pcm_stream *sps,411		     void *p, size_t sz)412{413	if (!sps || !sdev->stream_box.size) {414		sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);415	} else {416		struct snd_pcm_substream *substream = sps->substream;417		struct hdac_stream *hstream = substream->runtime->private_data;418		struct sof_intel_hda_stream *hda_stream;419 420		hda_stream = container_of(hstream,421					  struct sof_intel_hda_stream,422					  hext_stream.hstream);423 424		/* The stream might already be closed */425		if (!hstream)426			return -ESTRPIPE;427 428		sof_mailbox_read(sdev, hda_stream->sof_intel_stream.posn_offset, p, sz);429	}430 431	return 0;432}433EXPORT_SYMBOL_NS(hda_ipc_msg_data, SND_SOC_SOF_INTEL_HDA_COMMON);434 435int hda_set_stream_data_offset(struct snd_sof_dev *sdev,436			       struct snd_sof_pcm_stream *sps,437			       size_t posn_offset)438{439	struct snd_pcm_substream *substream = sps->substream;440	struct hdac_stream *hstream = substream->runtime->private_data;441	struct sof_intel_hda_stream *hda_stream;442 443	hda_stream = container_of(hstream, struct sof_intel_hda_stream,444				  hext_stream.hstream);445 446	/* check for unaligned offset or overflow */447	if (posn_offset > sdev->stream_box.size ||448	    posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)449		return -EINVAL;450 451	hda_stream->sof_intel_stream.posn_offset = sdev->stream_box.offset + posn_offset;452 453	dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",454		substream->stream, hda_stream->sof_intel_stream.posn_offset);455 456	return 0;457}458EXPORT_SYMBOL_NS(hda_set_stream_data_offset, SND_SOC_SOF_INTEL_HDA_COMMON);459 460void hda_ipc4_dsp_dump(struct snd_sof_dev *sdev, u32 flags)461{462	char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;463 464	/* print ROM/FW status */465	hda_dsp_get_state(sdev, level);466 467	if (flags & SOF_DBG_DUMP_REGS)468		sof_ipc4_intel_dump_telemetry_state(sdev, flags);469	else470		hda_dsp_dump_ext_rom_status(sdev, level, flags);471}472EXPORT_SYMBOL_NS(hda_ipc4_dsp_dump, SND_SOC_SOF_INTEL_HDA_COMMON);473 474bool hda_check_ipc_irq(struct snd_sof_dev *sdev)475{476	const struct sof_intel_dsp_desc *chip;477 478	chip = get_chip_info(sdev->pdata);479	if (chip && chip->check_ipc_irq)480		return chip->check_ipc_irq(sdev);481 482	return false;483}484EXPORT_SYMBOL_NS(hda_check_ipc_irq, SND_SOC_SOF_INTEL_HDA_COMMON);485 486void hda_ipc_irq_dump(struct snd_sof_dev *sdev)487{488	u32 adspis;489	u32 intsts;490	u32 intctl;491	u32 ppsts;492	u8 rirbsts;493 494	/* read key IRQ stats and config registers */495	adspis = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);496	intsts = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);497	intctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL);498	ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS);499	rirbsts = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, AZX_REG_RIRBSTS);500 501	dev_err(sdev->dev, "hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n",502		intsts, intctl, rirbsts);503	dev_err(sdev->dev, "dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n", ppsts, adspis);504}505EXPORT_SYMBOL_NS(hda_ipc_irq_dump, SND_SOC_SOF_INTEL_HDA_COMMON);506 507void hda_ipc_dump(struct snd_sof_dev *sdev)508{509	u32 hipcie;510	u32 hipct;511	u32 hipcctl;512 513	hda_ipc_irq_dump(sdev);514 515	/* read IPC status */516	hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);517	hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);518	hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL);519 520	/* dump the IPC regs */521	/* TODO: parse the raw msg */522	dev_err(sdev->dev, "host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",523		hipcie, hipct, hipcctl);524}525EXPORT_SYMBOL_NS(hda_ipc_dump, SND_SOC_SOF_INTEL_HDA_COMMON);526 527void hda_ipc4_dump(struct snd_sof_dev *sdev)528{529	u32 hipci, hipcie, hipct, hipcte, hipcctl;530 531	hda_ipc_irq_dump(sdev);532 533	hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI);534	hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);535	hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);536	hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE);537	hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL);538 539	/* dump the IPC regs */540	/* TODO: parse the raw msg */541	dev_err(sdev->dev, "Host IPC initiator: %#x|%#x, target: %#x|%#x, ctl: %#x\n",542		hipci, hipcie, hipct, hipcte, hipcctl);543}544EXPORT_SYMBOL_NS(hda_ipc4_dump, SND_SOC_SOF_INTEL_HDA_COMMON);545 546bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev)547{548	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;549	const struct sof_intel_dsp_desc *chip = hda->desc;550	u32 val;551 552	val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->ipc_req);553 554	return !!(val & chip->ipc_req_mask);555}556EXPORT_SYMBOL_NS(hda_ipc4_tx_is_busy, SND_SOC_SOF_INTEL_HDA_COMMON);557