brintos

brintos / linux-shallow public Read only

0
0
Text · 8.0 KiB · af56ff0 Raw
343 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// AMD Vangogh ACP PCI Driver4//5// Copyright (C) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.6 7#include <linux/pci.h>8#include <linux/module.h>9#include <linux/io.h>10#include <linux/delay.h>11#include <linux/platform_device.h>12#include <linux/interrupt.h>13#include <linux/pm_runtime.h>14 15#include "acp5x.h"16#include "../mach-config.h"17 18struct acp5x_dev_data {19	void __iomem *acp5x_base;20	bool acp5x_audio_mode;21	struct resource *res;22	struct platform_device *pdev[ACP5x_DEVS];23};24 25static int acp5x_power_on(void __iomem *acp5x_base)26{27	u32 val;28	int timeout;29 30	val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);31 32	if (val == 0)33		return val;34 35	if ((val & ACP_PGFSM_STATUS_MASK) !=36				ACP_POWER_ON_IN_PROGRESS)37		acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,38			   acp5x_base + ACP_PGFSM_CONTROL);39	timeout = 0;40	while (++timeout < 500) {41		val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);42		if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON)43			return 0;44		udelay(1);45	}46	return -ETIMEDOUT;47}48 49static int acp5x_reset(void __iomem *acp5x_base)50{51	u32 val;52	int timeout;53 54	acp_writel(1, acp5x_base + ACP_SOFT_RESET);55	timeout = 0;56	while (++timeout < 500) {57		val = acp_readl(acp5x_base + ACP_SOFT_RESET);58		if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)59			break;60		cpu_relax();61	}62	acp_writel(0, acp5x_base + ACP_SOFT_RESET);63	timeout = 0;64	while (++timeout < 500) {65		val = acp_readl(acp5x_base + ACP_SOFT_RESET);66		if (!val)67			return 0;68		cpu_relax();69	}70	return -ETIMEDOUT;71}72 73static void acp5x_enable_interrupts(void __iomem *acp5x_base)74{75	acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);76}77 78static void acp5x_disable_interrupts(void __iomem *acp5x_base)79{80	acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base +81		   ACP_EXTERNAL_INTR_STAT);82	acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL);83	acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB);84}85 86static int acp5x_init(void __iomem *acp5x_base)87{88	int ret;89 90	/* power on */91	ret = acp5x_power_on(acp5x_base);92	if (ret) {93		pr_err("ACP5x power on failed\n");94		return ret;95	}96	acp_writel(0x01, acp5x_base + ACP_CONTROL);97	/* Reset */98	ret = acp5x_reset(acp5x_base);99	if (ret) {100		pr_err("ACP5x reset failed\n");101		return ret;102	}103	acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);104	acp5x_enable_interrupts(acp5x_base);105	return 0;106}107 108static int acp5x_deinit(void __iomem *acp5x_base)109{110	int ret;111 112	acp5x_disable_interrupts(acp5x_base);113	/* Reset */114	ret = acp5x_reset(acp5x_base);115	if (ret) {116		pr_err("ACP5x reset failed\n");117		return ret;118	}119	acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);120	acp_writel(0x00, acp5x_base + ACP_CONTROL);121	return 0;122}123 124static int snd_acp5x_probe(struct pci_dev *pci,125			   const struct pci_device_id *pci_id)126{127	struct acp5x_dev_data *adata;128	struct platform_device_info pdevinfo[ACP5x_DEVS];129	unsigned int irqflags, flag;130	int ret, i;131	u32 addr, val;132 133	/*134	 * Return if ACP config flag is defined, except when board135	 * supports SOF while it is not being enabled in kernel config.136	 */137	flag = snd_amd_acp_find_config(pci);138	if (flag != FLAG_AMD_LEGACY &&139	    (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH)))140		return -ENODEV;141 142	irqflags = IRQF_SHARED;143	if (pci->revision != 0x50)144		return -ENODEV;145 146	if (pci_enable_device(pci)) {147		dev_err(&pci->dev, "pci_enable_device failed\n");148		return -ENODEV;149	}150 151	ret = pci_request_regions(pci, "AMD ACP5x audio");152	if (ret < 0) {153		dev_err(&pci->dev, "pci_request_regions failed\n");154		goto disable_pci;155	}156 157	adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),158			     GFP_KERNEL);159	if (!adata) {160		ret = -ENOMEM;161		goto release_regions;162	}163	addr = pci_resource_start(pci, 0);164	adata->acp5x_base = devm_ioremap(&pci->dev, addr,165					 pci_resource_len(pci, 0));166	if (!adata->acp5x_base) {167		ret = -ENOMEM;168		goto release_regions;169	}170	pci_set_master(pci);171	pci_set_drvdata(pci, adata);172	ret = acp5x_init(adata->acp5x_base);173	if (ret)174		goto release_regions;175 176	val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);177	switch (val) {178	case I2S_MODE:179		adata->res = devm_kzalloc(&pci->dev,180					  sizeof(struct resource) * ACP5x_RES,181					  GFP_KERNEL);182		if (!adata->res) {183			ret = -ENOMEM;184			goto de_init;185		}186 187		adata->res[0].name = "acp5x_i2s_iomem";188		adata->res[0].flags = IORESOURCE_MEM;189		adata->res[0].start = addr;190		adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);191 192		adata->res[1].name = "acp5x_i2s_sp";193		adata->res[1].flags = IORESOURCE_MEM;194		adata->res[1].start = addr + ACP5x_I2STDM_REG_START;195		adata->res[1].end = addr + ACP5x_I2STDM_REG_END;196 197		adata->res[2].name = "acp5x_i2s_hs";198		adata->res[2].flags = IORESOURCE_MEM;199		adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;200		adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;201 202		adata->res[3].name = "acp5x_i2s_irq";203		adata->res[3].flags = IORESOURCE_IRQ;204		adata->res[3].start = pci->irq;205		adata->res[3].end = adata->res[3].start;206 207		adata->acp5x_audio_mode = ACP5x_I2S_MODE;208 209		memset(&pdevinfo, 0, sizeof(pdevinfo));210		pdevinfo[0].name = "acp5x_i2s_dma";211		pdevinfo[0].id = 0;212		pdevinfo[0].parent = &pci->dev;213		pdevinfo[0].num_res = 4;214		pdevinfo[0].res = &adata->res[0];215		pdevinfo[0].data = &irqflags;216		pdevinfo[0].size_data = sizeof(irqflags);217 218		pdevinfo[1].name = "acp5x_i2s_playcap";219		pdevinfo[1].id = 0;220		pdevinfo[1].parent = &pci->dev;221		pdevinfo[1].num_res = 1;222		pdevinfo[1].res = &adata->res[1];223 224		pdevinfo[2].name = "acp5x_i2s_playcap";225		pdevinfo[2].id = 1;226		pdevinfo[2].parent = &pci->dev;227		pdevinfo[2].num_res = 1;228		pdevinfo[2].res = &adata->res[2];229 230		pdevinfo[3].name = "acp5x_mach";231		pdevinfo[3].id = 0;232		pdevinfo[3].parent = &pci->dev;233		for (i = 0; i < ACP5x_DEVS; i++) {234			adata->pdev[i] =235				platform_device_register_full(&pdevinfo[i]);236			if (IS_ERR(adata->pdev[i])) {237				dev_err(&pci->dev, "cannot register %s device\n",238					pdevinfo[i].name);239				ret = PTR_ERR(adata->pdev[i]);240				goto unregister_devs;241			}242		}243		break;244	default:245		dev_info(&pci->dev, "ACP audio mode : %d\n", val);246	}247	pm_runtime_set_autosuspend_delay(&pci->dev, 2000);248	pm_runtime_use_autosuspend(&pci->dev);249	pm_runtime_put_noidle(&pci->dev);250	pm_runtime_allow(&pci->dev);251	return 0;252 253unregister_devs:254	for (--i; i >= 0; i--)255		platform_device_unregister(adata->pdev[i]);256de_init:257	if (acp5x_deinit(adata->acp5x_base))258		dev_err(&pci->dev, "ACP de-init failed\n");259release_regions:260	pci_release_regions(pci);261disable_pci:262	pci_disable_device(pci);263 264	return ret;265}266 267static int snd_acp5x_suspend(struct device *dev)268{269	int ret;270	struct acp5x_dev_data *adata;271 272	adata = dev_get_drvdata(dev);273	ret = acp5x_deinit(adata->acp5x_base);274	if (ret)275		dev_err(dev, "ACP de-init failed\n");276	else277		dev_dbg(dev, "ACP de-initialized\n");278 279	return ret;280}281 282static int snd_acp5x_resume(struct device *dev)283{284	int ret;285	struct acp5x_dev_data *adata;286 287	adata = dev_get_drvdata(dev);288	ret = acp5x_init(adata->acp5x_base);289	if (ret) {290		dev_err(dev, "ACP init failed\n");291		return ret;292	}293	return 0;294}295 296static const struct dev_pm_ops acp5x_pm = {297	RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL)298	SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)299};300 301static void snd_acp5x_remove(struct pci_dev *pci)302{303	struct acp5x_dev_data *adata;304	int i, ret;305 306	adata = pci_get_drvdata(pci);307	if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {308		for (i = 0; i < ACP5x_DEVS; i++)309			platform_device_unregister(adata->pdev[i]);310	}311	ret = acp5x_deinit(adata->acp5x_base);312	if (ret)313		dev_err(&pci->dev, "ACP de-init failed\n");314	pm_runtime_forbid(&pci->dev);315	pm_runtime_get_noresume(&pci->dev);316	pci_release_regions(pci);317	pci_disable_device(pci);318}319 320static const struct pci_device_id snd_acp5x_ids[] = {321	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),322	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,323	.class_mask = 0xffffff },324	{ 0, },325};326MODULE_DEVICE_TABLE(pci, snd_acp5x_ids);327 328static struct pci_driver acp5x_driver  = {329	.name = KBUILD_MODNAME,330	.id_table = snd_acp5x_ids,331	.probe = snd_acp5x_probe,332	.remove = snd_acp5x_remove,333	.driver = {334		.pm = pm_ptr(&acp5x_pm),335	}336};337 338module_pci_driver(acp5x_driver);339 340MODULE_AUTHOR("Vijendar.Mukunda@amd.com");341MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");342MODULE_LICENSE("GPL v2");343