brintos

brintos / linux-shallow public Read only

0
0
Text · 8.0 KiB · 7af6a34 Raw
351 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * AMD Yellow Carp ACP PCI Driver4 *5 * Copyright 2021 Advanced Micro Devices, Inc.6 */7 8#include <linux/pci.h>9#include <linux/module.h>10#include <linux/io.h>11#include <linux/delay.h>12#include <linux/platform_device.h>13#include <linux/interrupt.h>14#include <sound/pcm_params.h>15#include <linux/pm_runtime.h>16 17#include "acp6x.h"18 19struct acp6x_dev_data {20	void __iomem *acp6x_base;21	struct resource *res;22	bool acp6x_audio_mode;23	struct platform_device *pdev[ACP6x_DEVS];24};25 26static int acp6x_power_on(void __iomem *acp_base)27{28	u32 val;29	int timeout;30 31	val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);32 33	if (!val)34		return val;35 36	if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)37		acp6x_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);38	timeout = 0;39	while (++timeout < 500) {40		val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);41		if (!val)42			return 0;43		udelay(1);44	}45	return -ETIMEDOUT;46}47 48static int acp6x_reset(void __iomem *acp_base)49{50	u32 val;51	int timeout;52 53	acp6x_writel(1, acp_base + ACP_SOFT_RESET);54	timeout = 0;55	while (++timeout < 500) {56		val = acp6x_readl(acp_base + ACP_SOFT_RESET);57		if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)58			break;59		cpu_relax();60	}61	acp6x_writel(0, acp_base + ACP_SOFT_RESET);62	timeout = 0;63	while (++timeout < 500) {64		val = acp6x_readl(acp_base + ACP_SOFT_RESET);65		if (!val)66			return 0;67		cpu_relax();68	}69	return -ETIMEDOUT;70}71 72static void acp6x_enable_interrupts(void __iomem *acp_base)73{74	acp6x_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);75}76 77static void acp6x_disable_interrupts(void __iomem *acp_base)78{79	acp6x_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +80		     ACP_EXTERNAL_INTR_STAT);81	acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_CNTL);82	acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);83}84 85static int acp6x_init(void __iomem *acp_base)86{87	int ret;88 89	/* power on */90	ret = acp6x_power_on(acp_base);91	if (ret) {92		pr_err("ACP power on failed\n");93		return ret;94	}95	acp6x_writel(0x01, acp_base + ACP_CONTROL);96	/* Reset */97	ret = acp6x_reset(acp_base);98	if (ret) {99		pr_err("ACP reset failed\n");100		return ret;101	}102	acp6x_writel(0x03, acp_base + ACP_CLKMUX_SEL);103	acp6x_enable_interrupts(acp_base);104	return 0;105}106 107static int acp6x_deinit(void __iomem *acp_base)108{109	int ret;110 111	acp6x_disable_interrupts(acp_base);112	/* Reset */113	ret = acp6x_reset(acp_base);114	if (ret) {115		pr_err("ACP reset failed\n");116		return ret;117	}118	acp6x_writel(0x00, acp_base + ACP_CLKMUX_SEL);119	acp6x_writel(0x00, acp_base + ACP_CONTROL);120	return 0;121}122 123static irqreturn_t acp6x_irq_handler(int irq, void *dev_id)124{125	struct acp6x_dev_data *adata;126	struct pdm_dev_data *yc_pdm_data;127	u32 val;128 129	adata = dev_id;130	if (!adata)131		return IRQ_NONE;132 133	val = acp6x_readl(adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);134	if (val & BIT(PDM_DMA_STAT)) {135		yc_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);136		acp6x_writel(BIT(PDM_DMA_STAT), adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);137		if (yc_pdm_data->capture_stream)138			snd_pcm_period_elapsed(yc_pdm_data->capture_stream);139		return IRQ_HANDLED;140	}141	return IRQ_NONE;142}143 144static int snd_acp6x_probe(struct pci_dev *pci,145			   const struct pci_device_id *pci_id)146{147	struct acp6x_dev_data *adata;148	struct platform_device_info pdevinfo[ACP6x_DEVS];149	int index = 0;150	int val = 0x00;151	u32 addr;152	unsigned int irqflags, flag;153	int ret;154 155	irqflags = IRQF_SHARED;156 157	/* Return if acp config flag is defined */158	flag = snd_amd_acp_find_config(pci);159	if (flag)160		return -ENODEV;161 162	/* Yellow Carp device check */163	switch (pci->revision) {164	case 0x60:165	case 0x6f:166		break;167	default:168		dev_dbg(&pci->dev, "acp6x pci device not found\n");169		return -ENODEV;170	}171	if (pci_enable_device(pci)) {172		dev_err(&pci->dev, "pci_enable_device failed\n");173		return -ENODEV;174	}175 176	ret = pci_request_regions(pci, "AMD ACP3x audio");177	if (ret < 0) {178		dev_err(&pci->dev, "pci_request_regions failed\n");179		goto disable_pci;180	}181 182	adata = devm_kzalloc(&pci->dev, sizeof(struct acp6x_dev_data),183			     GFP_KERNEL);184	if (!adata) {185		ret = -ENOMEM;186		goto release_regions;187	}188 189	addr = pci_resource_start(pci, 0);190	adata->acp6x_base = devm_ioremap(&pci->dev, addr,191					 pci_resource_len(pci, 0));192	if (!adata->acp6x_base) {193		ret = -ENOMEM;194		goto release_regions;195	}196	pci_set_master(pci);197	pci_set_drvdata(pci, adata);198	ret = acp6x_init(adata->acp6x_base);199	if (ret)200		goto release_regions;201	val = acp6x_readl(adata->acp6x_base + ACP_PIN_CONFIG);202	switch (val) {203	case ACP_CONFIG_0:204	case ACP_CONFIG_1:205	case ACP_CONFIG_2:206	case ACP_CONFIG_3:207	case ACP_CONFIG_9:208	case ACP_CONFIG_15:209		dev_info(&pci->dev, "Audio Mode %d\n", val);210		break;211	default:212		adata->res = devm_kzalloc(&pci->dev,213					  sizeof(struct resource),214					  GFP_KERNEL);215		if (!adata->res) {216			ret = -ENOMEM;217			goto de_init;218		}219 220		adata->res->name = "acp_iomem";221		adata->res->flags = IORESOURCE_MEM;222		adata->res->start = addr;223		adata->res->end = addr + (ACP6x_REG_END - ACP6x_REG_START);224 225		adata->acp6x_audio_mode = ACP6x_PDM_MODE;226 227		memset(&pdevinfo, 0, sizeof(pdevinfo));228		pdevinfo[0].name = "acp_yc_pdm_dma";229		pdevinfo[0].id = 0;230		pdevinfo[0].parent = &pci->dev;231		pdevinfo[0].num_res = 1;232		pdevinfo[0].res = adata->res;233 234		pdevinfo[1].name = "dmic-codec";235		pdevinfo[1].id = 0;236		pdevinfo[1].parent = &pci->dev;237 238		pdevinfo[2].name = "acp_yc_mach";239		pdevinfo[2].id = 0;240		pdevinfo[2].parent = &pci->dev;241 242		for (index = 0; index < ACP6x_DEVS; index++) {243			adata->pdev[index] =244				platform_device_register_full(&pdevinfo[index]);245			if (IS_ERR(adata->pdev[index])) {246				dev_err(&pci->dev, "cannot register %s device\n",247					pdevinfo[index].name);248				ret = PTR_ERR(adata->pdev[index]);249				goto unregister_devs;250			}251		}252		break;253	}254	ret = devm_request_irq(&pci->dev, pci->irq, acp6x_irq_handler,255			       irqflags, "ACP_PCI_IRQ", adata);256	if (ret) {257		dev_err(&pci->dev, "ACP PCI IRQ request failed\n");258		goto unregister_devs;259	}260	pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);261	pm_runtime_use_autosuspend(&pci->dev);262	pm_runtime_put_noidle(&pci->dev);263	pm_runtime_allow(&pci->dev);264 265	return 0;266unregister_devs:267	for (--index; index >= 0; index--)268		platform_device_unregister(adata->pdev[index]);269de_init:270	if (acp6x_deinit(adata->acp6x_base))271		dev_err(&pci->dev, "ACP de-init failed\n");272release_regions:273	pci_release_regions(pci);274disable_pci:275	pci_disable_device(pci);276 277	return ret;278}279 280static int __maybe_unused snd_acp6x_suspend(struct device *dev)281{282	struct acp6x_dev_data *adata;283	int ret;284 285	adata = dev_get_drvdata(dev);286	ret = acp6x_deinit(adata->acp6x_base);287	if (ret)288		dev_err(dev, "ACP de-init failed\n");289	return ret;290}291 292static int __maybe_unused snd_acp6x_resume(struct device *dev)293{294	struct acp6x_dev_data *adata;295	int ret;296 297	adata = dev_get_drvdata(dev);298	ret = acp6x_init(adata->acp6x_base);299	if (ret)300		dev_err(dev, "ACP init failed\n");301	return ret;302}303 304static const struct dev_pm_ops acp6x_pm = {305	SET_RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)306	SET_SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)307};308 309static void snd_acp6x_remove(struct pci_dev *pci)310{311	struct acp6x_dev_data *adata;312	int ret, index;313 314	adata = pci_get_drvdata(pci);315	if (adata->acp6x_audio_mode == ACP6x_PDM_MODE) {316		for (index = 0; index < ACP6x_DEVS; index++)317			platform_device_unregister(adata->pdev[index]);318	}319	ret = acp6x_deinit(adata->acp6x_base);320	if (ret)321		dev_err(&pci->dev, "ACP de-init failed\n");322	pm_runtime_forbid(&pci->dev);323	pm_runtime_get_noresume(&pci->dev);324	pci_release_regions(pci);325	pci_disable_device(pci);326}327 328static const struct pci_device_id snd_acp6x_ids[] = {329	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),330	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,331	.class_mask = 0xffffff },332	{ 0, },333};334MODULE_DEVICE_TABLE(pci, snd_acp6x_ids);335 336static struct pci_driver yc_acp6x_driver  = {337	.name = KBUILD_MODNAME,338	.id_table = snd_acp6x_ids,339	.probe = snd_acp6x_probe,340	.remove = snd_acp6x_remove,341	.driver = {342		.pm = &acp6x_pm,343	}344};345 346module_pci_driver(yc_acp6x_driver);347 348MODULE_AUTHOR("Vijendar.Mukunda@amd.com");349MODULE_DESCRIPTION("AMD ACP Yellow Carp PCI driver");350MODULE_LICENSE("GPL v2");351