brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · a013a60 Raw
348 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// AMD ACP PCI Driver4//5//Copyright 2016 Advanced Micro Devices, Inc.6 7#include <linux/pci.h>8#include <linux/module.h>9#include <linux/io.h>10#include <linux/platform_device.h>11#include <linux/interrupt.h>12#include <linux/pm_runtime.h>13#include <linux/delay.h>14 15#include "acp3x.h"16 17struct acp3x_dev_data {18	void __iomem *acp3x_base;19	bool acp3x_audio_mode;20	struct resource *res;21	struct platform_device *pdev[ACP3x_DEVS];22	u32 pme_en;23};24 25static int acp3x_power_on(struct acp3x_dev_data *adata)26{27	void __iomem *acp3x_base = adata->acp3x_base;28	u32 val;29	int timeout;30 31	val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);32 33	if (val == 0)34		return val;35 36	if (!((val & ACP_PGFSM_STATUS_MASK) ==37				ACP_POWER_ON_IN_PROGRESS))38		rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,39			acp3x_base + mmACP_PGFSM_CONTROL);40	timeout = 0;41	while (++timeout < 500) {42		val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);43		if (!val) {44			/* ACP power On clears PME_EN.45			 * Restore the value to its prior state46			 */47			rv_writel(adata->pme_en, acp3x_base + mmACP_PME_EN);48			return 0;49		}50		udelay(1);51	}52	return -ETIMEDOUT;53}54 55static int acp3x_reset(void __iomem *acp3x_base)56{57	u32 val;58	int timeout;59 60	rv_writel(1, acp3x_base + mmACP_SOFT_RESET);61	timeout = 0;62	while (++timeout < 500) {63		val = rv_readl(acp3x_base + mmACP_SOFT_RESET);64		if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)65			break;66		cpu_relax();67	}68	rv_writel(0, acp3x_base + mmACP_SOFT_RESET);69	timeout = 0;70	while (++timeout < 500) {71		val = rv_readl(acp3x_base + mmACP_SOFT_RESET);72		if (!val)73			return 0;74		cpu_relax();75	}76	return -ETIMEDOUT;77}78 79static void acp3x_enable_interrupts(void __iomem *acp_base)80{81	rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);82}83 84static void acp3x_disable_interrupts(void __iomem *acp_base)85{86	rv_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +87		  mmACP_EXTERNAL_INTR_STAT);88	rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_CNTL);89	rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_ENB);90}91 92static int acp3x_init(struct acp3x_dev_data *adata)93{94	void __iomem *acp3x_base = adata->acp3x_base;95	int ret;96 97	/* power on */98	ret = acp3x_power_on(adata);99	if (ret) {100		pr_err("ACP3x power on failed\n");101		return ret;102	}103	/* Reset */104	ret = acp3x_reset(acp3x_base);105	if (ret) {106		pr_err("ACP3x reset failed\n");107		return ret;108	}109	acp3x_enable_interrupts(acp3x_base);110	return 0;111}112 113static int acp3x_deinit(void __iomem *acp3x_base)114{115	int ret;116 117	acp3x_disable_interrupts(acp3x_base);118	/* Reset */119	ret = acp3x_reset(acp3x_base);120	if (ret) {121		pr_err("ACP3x reset failed\n");122		return ret;123	}124	return 0;125}126 127static int snd_acp3x_probe(struct pci_dev *pci,128			   const struct pci_device_id *pci_id)129{130	struct acp3x_dev_data *adata;131	struct platform_device_info pdevinfo[ACP3x_DEVS];132	unsigned int irqflags;133	int ret, i;134	u32 addr, val;135 136	/* Raven device detection */137	if (pci->revision != 0x00)138		return -ENODEV;139 140	if (pci_enable_device(pci)) {141		dev_err(&pci->dev, "pci_enable_device failed\n");142		return -ENODEV;143	}144 145	ret = pci_request_regions(pci, "AMD ACP3x audio");146	if (ret < 0) {147		dev_err(&pci->dev, "pci_request_regions failed\n");148		goto disable_pci;149	}150 151	adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),152			     GFP_KERNEL);153	if (!adata) {154		ret = -ENOMEM;155		goto release_regions;156	}157 158	irqflags = IRQF_SHARED;159 160	addr = pci_resource_start(pci, 0);161	adata->acp3x_base = devm_ioremap(&pci->dev, addr,162					pci_resource_len(pci, 0));163	if (!adata->acp3x_base) {164		ret = -ENOMEM;165		goto release_regions;166	}167	pci_set_master(pci);168	pci_set_drvdata(pci, adata);169	/* Save ACP_PME_EN state */170	adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);171	ret = acp3x_init(adata);172	if (ret)173		goto release_regions;174 175	val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);176	switch (val) {177	case I2S_MODE:178		adata->res = devm_kzalloc(&pci->dev,179					  sizeof(struct resource) * 4,180					  GFP_KERNEL);181		if (!adata->res) {182			ret = -ENOMEM;183			goto de_init;184		}185 186		adata->res[0].name = "acp3x_i2s_iomem";187		adata->res[0].flags = IORESOURCE_MEM;188		adata->res[0].start = addr;189		adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);190 191		adata->res[1].name = "acp3x_i2s_sp";192		adata->res[1].flags = IORESOURCE_MEM;193		adata->res[1].start = addr + ACP3x_I2STDM_REG_START;194		adata->res[1].end = addr + ACP3x_I2STDM_REG_END;195 196		adata->res[2].name = "acp3x_i2s_bt";197		adata->res[2].flags = IORESOURCE_MEM;198		adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;199		adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;200 201		adata->res[3].name = "acp3x_i2s_irq";202		adata->res[3].flags = IORESOURCE_IRQ;203		adata->res[3].start = pci->irq;204		adata->res[3].end = adata->res[3].start;205 206		adata->acp3x_audio_mode = ACP3x_I2S_MODE;207 208		memset(&pdevinfo, 0, sizeof(pdevinfo));209		pdevinfo[0].name = "acp3x_rv_i2s_dma";210		pdevinfo[0].id = 0;211		pdevinfo[0].parent = &pci->dev;212		pdevinfo[0].num_res = 4;213		pdevinfo[0].res = &adata->res[0];214		pdevinfo[0].data = &irqflags;215		pdevinfo[0].size_data = sizeof(irqflags);216 217		pdevinfo[1].name = "acp3x_i2s_playcap";218		pdevinfo[1].id = 0;219		pdevinfo[1].parent = &pci->dev;220		pdevinfo[1].num_res = 1;221		pdevinfo[1].res = &adata->res[1];222 223		pdevinfo[2].name = "acp3x_i2s_playcap";224		pdevinfo[2].id = 1;225		pdevinfo[2].parent = &pci->dev;226		pdevinfo[2].num_res = 1;227		pdevinfo[2].res = &adata->res[1];228 229		pdevinfo[3].name = "acp3x_i2s_playcap";230		pdevinfo[3].id = 2;231		pdevinfo[3].parent = &pci->dev;232		pdevinfo[3].num_res = 1;233		pdevinfo[3].res = &adata->res[2];234		for (i = 0; i < ACP3x_DEVS; i++) {235			adata->pdev[i] =236				platform_device_register_full(&pdevinfo[i]);237			if (IS_ERR(adata->pdev[i])) {238				dev_err(&pci->dev, "cannot register %s device\n",239					pdevinfo[i].name);240				ret = PTR_ERR(adata->pdev[i]);241				goto unregister_devs;242			}243		}244		break;245	default:246		dev_info(&pci->dev, "ACP audio mode : %d\n", val);247		break;248	}249	pm_runtime_set_autosuspend_delay(&pci->dev, 2000);250	pm_runtime_use_autosuspend(&pci->dev);251	pm_runtime_put_noidle(&pci->dev);252	pm_runtime_allow(&pci->dev);253	return 0;254 255unregister_devs:256	if (val == I2S_MODE)257		for (i = 0; i < ACP3x_DEVS; i++)258			platform_device_unregister(adata->pdev[i]);259de_init:260	if (acp3x_deinit(adata->acp3x_base))261		dev_err(&pci->dev, "ACP de-init failed\n");262release_regions:263	pci_release_regions(pci);264disable_pci:265	pci_disable_device(pci);266 267	return ret;268}269 270static int snd_acp3x_suspend(struct device *dev)271{272	int ret;273	struct acp3x_dev_data *adata;274 275	adata = dev_get_drvdata(dev);276	ret = acp3x_deinit(adata->acp3x_base);277	if (ret)278		dev_err(dev, "ACP de-init failed\n");279	else280		dev_dbg(dev, "ACP de-initialized\n");281 282	return 0;283}284 285static int snd_acp3x_resume(struct device *dev)286{287	int ret;288	struct acp3x_dev_data *adata;289 290	adata = dev_get_drvdata(dev);291	ret = acp3x_init(adata);292	if (ret) {293		dev_err(dev, "ACP init failed\n");294		return ret;295	}296	return 0;297}298 299static const struct dev_pm_ops acp3x_pm = {300	.runtime_suspend = snd_acp3x_suspend,301	.runtime_resume =  snd_acp3x_resume,302	.resume =	snd_acp3x_resume,303};304 305static void snd_acp3x_remove(struct pci_dev *pci)306{307	struct acp3x_dev_data *adata;308	int i, ret;309 310	adata = pci_get_drvdata(pci);311	if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {312		for (i = 0; i < ACP3x_DEVS; i++)313			platform_device_unregister(adata->pdev[i]);314	}315	ret = acp3x_deinit(adata->acp3x_base);316	if (ret)317		dev_err(&pci->dev, "ACP de-init failed\n");318	pm_runtime_forbid(&pci->dev);319	pm_runtime_get_noresume(&pci->dev);320	pci_release_regions(pci);321	pci_disable_device(pci);322}323 324static const struct pci_device_id snd_acp3x_ids[] = {325	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),326	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,327	.class_mask = 0xffffff },328	{ 0, },329};330MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);331 332static struct pci_driver acp3x_driver  = {333	.name = KBUILD_MODNAME,334	.id_table = snd_acp3x_ids,335	.probe = snd_acp3x_probe,336	.remove = snd_acp3x_remove,337	.driver = {338		.pm = &acp3x_pm,339	}340};341 342module_pci_driver(acp3x_driver);343 344MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");345MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");346MODULE_DESCRIPTION("AMD ACP3x PCI driver");347MODULE_LICENSE("GPL v2");348