brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · 3505ac3 Raw
422 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-2021 Intel Corporation7//8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>9//10 11/*12 * Hardware interface for audio DSP on Atom devices13 */14 15#include <linux/module.h>16#include <sound/sof.h>17#include <sound/sof/xtensa.h>18#include <sound/soc-acpi.h>19#include <sound/soc-acpi-intel-match.h>20#include <sound/intel-dsp-config.h>21#include "../ops.h"22#include "shim.h"23#include "atom.h"24#include "../sof-acpi-dev.h"25#include "../sof-audio.h"26#include "../../intel/common/soc-intel-quirks.h"27 28static void atom_host_done(struct snd_sof_dev *sdev);29static void atom_dsp_done(struct snd_sof_dev *sdev);30 31/*32 * Debug33 */34 35static void atom_get_registers(struct snd_sof_dev *sdev,36			       struct sof_ipc_dsp_oops_xtensa *xoops,37			       struct sof_ipc_panic_info *panic_info,38			       u32 *stack, size_t stack_words)39{40	u32 offset = sdev->dsp_oops_offset;41 42	/* first read regsisters */43	sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));44 45	/* note: variable AR register array is not read */46 47	/* then get panic info */48	if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {49		dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",50			xoops->arch_hdr.totalsize);51		return;52	}53	offset += xoops->arch_hdr.totalsize;54	sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));55 56	/* then get the stack */57	offset += sizeof(*panic_info);58	sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));59}60 61void atom_dump(struct snd_sof_dev *sdev, u32 flags)62{63	struct sof_ipc_dsp_oops_xtensa xoops;64	struct sof_ipc_panic_info panic_info;65	u32 stack[STACK_DUMP_SIZE];66	u64 status, panic, imrd, imrx;67 68	/* now try generic SOF status messages */69	status = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);70	panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);71	atom_get_registers(sdev, &xoops, &panic_info, stack,72			   STACK_DUMP_SIZE);73	sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,74				 &panic_info, stack, STACK_DUMP_SIZE);75 76	/* provide some context for firmware debug */77	imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX);78	imrd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRD);79	dev_err(sdev->dev,80		"error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",81		(panic & SHIM_IPCX_BUSY) ? "yes" : "no",82		(panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);83	dev_err(sdev->dev,84		"error: mask host: pending %s complete %s raw 0x%llx\n",85		(imrx & SHIM_IMRX_BUSY) ? "yes" : "no",86		(imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);87	dev_err(sdev->dev,88		"error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",89		(status & SHIM_IPCD_BUSY) ? "yes" : "no",90		(status & SHIM_IPCD_DONE) ? "yes" : "no", status);91	dev_err(sdev->dev,92		"error: mask DSP: pending %s complete %s raw 0x%llx\n",93		(imrd & SHIM_IMRD_BUSY) ? "yes" : "no",94		(imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);95 96}97EXPORT_SYMBOL_NS(atom_dump, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);98 99/*100 * IPC Doorbell IRQ handler and thread.101 */102 103irqreturn_t atom_irq_handler(int irq, void *context)104{105	struct snd_sof_dev *sdev = context;106	u64 ipcx, ipcd;107	int ret = IRQ_NONE;108 109	ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);110	ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);111 112	if (ipcx & SHIM_BYT_IPCX_DONE) {113 114		/* reply message from DSP, Mask Done interrupt first */115		snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,116						   SHIM_IMRX,117						   SHIM_IMRX_DONE,118						   SHIM_IMRX_DONE);119		ret = IRQ_WAKE_THREAD;120	}121 122	if (ipcd & SHIM_BYT_IPCD_BUSY) {123 124		/* new message from DSP, Mask Busy interrupt first */125		snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,126						   SHIM_IMRX,127						   SHIM_IMRX_BUSY,128						   SHIM_IMRX_BUSY);129		ret = IRQ_WAKE_THREAD;130	}131 132	return ret;133}134EXPORT_SYMBOL_NS(atom_irq_handler, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);135 136irqreturn_t atom_irq_thread(int irq, void *context)137{138	struct snd_sof_dev *sdev = context;139	u64 ipcx, ipcd;140 141	ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);142	ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);143 144	/* reply message from DSP */145	if (ipcx & SHIM_BYT_IPCX_DONE) {146 147		spin_lock_irq(&sdev->ipc_lock);148 149		/*150		 * handle immediate reply from DSP core. If the msg is151		 * found, set done bit in cmd_done which is called at the152		 * end of message processing function, else set it here153		 * because the done bit can't be set in cmd_done function154		 * which is triggered by msg155		 */156		snd_sof_ipc_process_reply(sdev, ipcx);157 158		atom_dsp_done(sdev);159 160		spin_unlock_irq(&sdev->ipc_lock);161	}162 163	/* new message from DSP */164	if (ipcd & SHIM_BYT_IPCD_BUSY) {165 166		/* Handle messages from DSP Core */167		if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {168			snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET,169					  true);170		} else {171			snd_sof_ipc_msgs_rx(sdev);172		}173 174		atom_host_done(sdev);175	}176 177	return IRQ_HANDLED;178}179EXPORT_SYMBOL_NS(atom_irq_thread, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);180 181int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)182{183	/* unmask and prepare to receive Done interrupt */184	snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,185					   SHIM_IMRX_DONE, 0);186 187	/* send the message */188	sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,189			  msg->msg_size);190	snd_sof_dsp_write64(sdev, DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);191 192	return 0;193}194EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);195 196int atom_get_mailbox_offset(struct snd_sof_dev *sdev)197{198	return MBOX_OFFSET;199}200EXPORT_SYMBOL_NS(atom_get_mailbox_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);201 202int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id)203{204	return MBOX_OFFSET;205}206EXPORT_SYMBOL_NS(atom_get_window_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);207 208static void atom_host_done(struct snd_sof_dev *sdev)209{210	/* clear BUSY bit and set DONE bit - accept new messages */211	snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCD,212					   SHIM_BYT_IPCD_BUSY |213					   SHIM_BYT_IPCD_DONE,214					   SHIM_BYT_IPCD_DONE);215 216	/* unmask and prepare to receive next new message */217	snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,218					   SHIM_IMRX_BUSY, 0);219}220 221static void atom_dsp_done(struct snd_sof_dev *sdev)222{223	/* clear DONE bit - tell DSP we have completed */224	snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCX,225					   SHIM_BYT_IPCX_DONE, 0);226}227 228/*229 * DSP control.230 */231 232int atom_run(struct snd_sof_dev *sdev)233{234	int tries = 10;235 236	/* release stall and wait to unstall */237	snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,238				  SHIM_BYT_CSR_STALL, 0x0);239	while (tries--) {240		if (!(snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_CSR) &241		      SHIM_BYT_CSR_PWAITMODE))242			break;243		msleep(100);244	}245	if (tries < 0)246		return -ENODEV;247 248	/* return init core mask */249	return 1;250}251EXPORT_SYMBOL_NS(atom_run, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);252 253int atom_reset(struct snd_sof_dev *sdev)254{255	/* put DSP into reset, set reset vector and stall */256	snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,257				  SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |258				  SHIM_BYT_CSR_STALL,259				  SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |260				  SHIM_BYT_CSR_STALL);261 262	usleep_range(10, 15);263 264	/* take DSP out of reset and keep stalled for FW loading */265	snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,266				  SHIM_BYT_CSR_RST, 0);267 268	return 0;269}270EXPORT_SYMBOL_NS(atom_reset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);271 272static const char *fixup_tplg_name(struct snd_sof_dev *sdev,273				   const char *sof_tplg_filename,274				   const char *ssp_str)275{276	const char *tplg_filename = NULL;277	const char *split_ext;278	char *filename, *tmp;279 280	filename = kstrdup(sof_tplg_filename, GFP_KERNEL);281	if (!filename)282		return NULL;283 284	/* this assumes a .tplg extension */285	tmp = filename;286	split_ext = strsep(&tmp, ".");287	if (split_ext)288		tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,289					       "%s-%s.tplg",290					       split_ext, ssp_str);291	kfree(filename);292 293	return tplg_filename;294}295 296struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev)297{298	struct snd_sof_pdata *sof_pdata = sdev->pdata;299	const struct sof_dev_desc *desc = sof_pdata->desc;300	struct snd_soc_acpi_mach *mach;301	struct platform_device *pdev;302	const char *tplg_filename;303 304	mach = snd_soc_acpi_find_machine(desc->machines);305	if (!mach) {306		dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");307		return NULL;308	}309 310	pdev = to_platform_device(sdev->dev);311	if (soc_intel_is_byt_cr(pdev)) {312		dev_dbg(sdev->dev,313			"BYT-CR detected, SSP0 used instead of SSP2\n");314 315		tplg_filename = fixup_tplg_name(sdev,316						mach->sof_tplg_filename,317						"ssp0");318	} else {319		tplg_filename = mach->sof_tplg_filename;320	}321 322	if (!tplg_filename) {323		dev_dbg(sdev->dev,324			"error: no topology filename\n");325		return NULL;326	}327 328	sof_pdata->tplg_filename = tplg_filename;329	mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;330 331	return mach;332}333EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);334 335/* Atom DAIs */336struct snd_soc_dai_driver atom_dai[] = {337{338	.name = "ssp0-port",339	.playback = {340		.channels_min = 1,341		.channels_max = 8,342	},343	.capture = {344		.channels_min = 1,345		.channels_max = 8,346	},347},348{349	.name = "ssp1-port",350	.playback = {351		.channels_min = 1,352		.channels_max = 8,353	},354	.capture = {355		.channels_min = 1,356		.channels_max = 8,357	},358},359{360	.name = "ssp2-port",361	.playback = {362		.channels_min = 1,363		.channels_max = 8,364	},365	.capture = {366		.channels_min = 1,367		.channels_max = 8,368	}369},370{371	.name = "ssp3-port",372	.playback = {373		.channels_min = 1,374		.channels_max = 8,375	},376	.capture = {377		.channels_min = 1,378		.channels_max = 8,379	},380},381{382	.name = "ssp4-port",383	.playback = {384		.channels_min = 1,385		.channels_max = 8,386	},387	.capture = {388		.channels_min = 1,389		.channels_max = 8,390	},391},392{393	.name = "ssp5-port",394	.playback = {395		.channels_min = 1,396		.channels_max = 8,397	},398	.capture = {399		.channels_min = 1,400		.channels_max = 8,401	},402},403};404EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);405 406void atom_set_mach_params(struct snd_soc_acpi_mach *mach,407			  struct snd_sof_dev *sdev)408{409	struct snd_sof_pdata *pdata = sdev->pdata;410	const struct sof_dev_desc *desc = pdata->desc;411	struct snd_soc_acpi_mach_params *mach_params;412 413	mach_params = &mach->mach_params;414	mach_params->platform = dev_name(sdev->dev);415	mach_params->num_dai_drivers = desc->ops->num_drv;416	mach_params->dai_drivers = desc->ops->drv;417}418EXPORT_SYMBOL_NS(atom_set_mach_params, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);419 420MODULE_LICENSE("Dual BSD/GPL");421MODULE_DESCRIPTION("SOF support for Atom platforms");422