162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMD Pink Sardine ACP PCI Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2022 Advanced Micro Devices, Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/pci.h> 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/acpi.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <sound/pcm_params.h> 1762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1862306a36Sopenharmony_ci#include <linux/iopoll.h> 1962306a36Sopenharmony_ci#include <linux/soundwire/sdw_amd.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "acp63.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int acp63_power_on(void __iomem *acp_base) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci u32 val; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci val = readl(acp_base + ACP_PGFSM_STATUS); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (!val) 3062306a36Sopenharmony_ci return val; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS) 3362306a36Sopenharmony_ci writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int acp63_reset(void __iomem *acp_base) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci u32 val; 4162306a36Sopenharmony_ci int ret; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci writel(1, acp_base + ACP_SOFT_RESET); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, 4662306a36Sopenharmony_ci val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK, 4762306a36Sopenharmony_ci DELAY_US, ACP_TIMEOUT); 4862306a36Sopenharmony_ci if (ret) 4962306a36Sopenharmony_ci return ret; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci writel(0, acp_base + ACP_SOFT_RESET); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void acp63_enable_interrupts(void __iomem *acp_base) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci writel(1, acp_base + ACP_EXTERNAL_INTR_ENB); 5962306a36Sopenharmony_ci writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void acp63_disable_interrupts(void __iomem *acp_base) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT); 6562306a36Sopenharmony_ci writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL); 6662306a36Sopenharmony_ci writel(0, acp_base + ACP_EXTERNAL_INTR_ENB); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int acp63_init(void __iomem *acp_base, struct device *dev) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci int ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = acp63_power_on(acp_base); 7462306a36Sopenharmony_ci if (ret) { 7562306a36Sopenharmony_ci dev_err(dev, "ACP power on failed\n"); 7662306a36Sopenharmony_ci return ret; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci writel(0x01, acp_base + ACP_CONTROL); 7962306a36Sopenharmony_ci ret = acp63_reset(acp_base); 8062306a36Sopenharmony_ci if (ret) { 8162306a36Sopenharmony_ci dev_err(dev, "ACP reset failed\n"); 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci acp63_enable_interrupts(acp_base); 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int acp63_deinit(void __iomem *acp_base, struct device *dev) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci acp63_disable_interrupts(acp_base); 9362306a36Sopenharmony_ci ret = acp63_reset(acp_base); 9462306a36Sopenharmony_ci if (ret) { 9562306a36Sopenharmony_ci dev_err(dev, "ACP reset failed\n"); 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci writel(0, acp_base + ACP_CONTROL); 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic irqreturn_t acp63_irq_thread(int irq, void *context) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct sdw_dma_dev_data *sdw_dma_data; 10562306a36Sopenharmony_ci struct acp63_dev_data *adata = context; 10662306a36Sopenharmony_ci u32 stream_index; 10762306a36Sopenharmony_ci u16 pdev_index; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pdev_index = adata->sdw_dma_dev_index; 11062306a36Sopenharmony_ci sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) { 11362306a36Sopenharmony_ci if (adata->sdw0_dma_intr_stat[stream_index]) { 11462306a36Sopenharmony_ci if (sdw_dma_data->sdw0_dma_stream[stream_index]) 11562306a36Sopenharmony_ci snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]); 11662306a36Sopenharmony_ci adata->sdw0_dma_intr_stat[stream_index] = 0; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) { 12062306a36Sopenharmony_ci if (adata->sdw1_dma_intr_stat[stream_index]) { 12162306a36Sopenharmony_ci if (sdw_dma_data->sdw1_dma_stream[stream_index]) 12262306a36Sopenharmony_ci snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]); 12362306a36Sopenharmony_ci adata->sdw1_dma_intr_stat[stream_index] = 0; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci return IRQ_HANDLED; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic irqreturn_t acp63_irq_handler(int irq, void *dev_id) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct acp63_dev_data *adata; 13262306a36Sopenharmony_ci struct pdm_dev_data *ps_pdm_data; 13362306a36Sopenharmony_ci struct amd_sdw_manager *amd_manager; 13462306a36Sopenharmony_ci u32 ext_intr_stat, ext_intr_stat1; 13562306a36Sopenharmony_ci u32 stream_id = 0; 13662306a36Sopenharmony_ci u16 irq_flag = 0; 13762306a36Sopenharmony_ci u16 sdw_dma_irq_flag = 0; 13862306a36Sopenharmony_ci u16 pdev_index; 13962306a36Sopenharmony_ci u16 index; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci adata = dev_id; 14262306a36Sopenharmony_ci if (!adata) 14362306a36Sopenharmony_ci return IRQ_NONE; 14462306a36Sopenharmony_ci /* ACP interrupts will be cleared by reading particular bit and writing 14562306a36Sopenharmony_ci * same value to the status register. writing zero's doesn't have any 14662306a36Sopenharmony_ci * effect. 14762306a36Sopenharmony_ci * Bit by bit checking of IRQ field is implemented. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); 15062306a36Sopenharmony_ci if (ext_intr_stat & ACP_SDW0_STAT) { 15162306a36Sopenharmony_ci writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT); 15262306a36Sopenharmony_ci pdev_index = adata->sdw0_dev_index; 15362306a36Sopenharmony_ci amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); 15462306a36Sopenharmony_ci if (amd_manager) 15562306a36Sopenharmony_ci schedule_work(&amd_manager->amd_sdw_irq_thread); 15662306a36Sopenharmony_ci irq_flag = 1; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); 16062306a36Sopenharmony_ci if (ext_intr_stat1 & ACP_SDW1_STAT) { 16162306a36Sopenharmony_ci writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); 16262306a36Sopenharmony_ci pdev_index = adata->sdw1_dev_index; 16362306a36Sopenharmony_ci amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); 16462306a36Sopenharmony_ci if (amd_manager) 16562306a36Sopenharmony_ci schedule_work(&amd_manager->amd_sdw_irq_thread); 16662306a36Sopenharmony_ci irq_flag = 1; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (ext_intr_stat & ACP_ERROR_IRQ) { 17062306a36Sopenharmony_ci writel(ACP_ERROR_IRQ, adata->acp63_base + ACP_EXTERNAL_INTR_STAT); 17162306a36Sopenharmony_ci /* TODO: Report SoundWire Manager instance errors */ 17262306a36Sopenharmony_ci writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON); 17362306a36Sopenharmony_ci writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON); 17462306a36Sopenharmony_ci writel(0, adata->acp63_base + ACP_ERROR_STATUS); 17562306a36Sopenharmony_ci irq_flag = 1; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (ext_intr_stat & BIT(PDM_DMA_STAT)) { 17962306a36Sopenharmony_ci pdev_index = adata->pdm_dev_index; 18062306a36Sopenharmony_ci ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); 18162306a36Sopenharmony_ci writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); 18262306a36Sopenharmony_ci if (ps_pdm_data->capture_stream) 18362306a36Sopenharmony_ci snd_pcm_period_elapsed(ps_pdm_data->capture_stream); 18462306a36Sopenharmony_ci irq_flag = 1; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) { 18762306a36Sopenharmony_ci for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) { 18862306a36Sopenharmony_ci if (ext_intr_stat & BIT(index)) { 18962306a36Sopenharmony_ci writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); 19062306a36Sopenharmony_ci switch (index) { 19162306a36Sopenharmony_ci case ACP_AUDIO0_TX_THRESHOLD: 19262306a36Sopenharmony_ci stream_id = ACP_SDW0_AUDIO0_TX; 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci case ACP_AUDIO1_TX_THRESHOLD: 19562306a36Sopenharmony_ci stream_id = ACP_SDW0_AUDIO1_TX; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci case ACP_AUDIO2_TX_THRESHOLD: 19862306a36Sopenharmony_ci stream_id = ACP_SDW0_AUDIO2_TX; 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci case ACP_AUDIO0_RX_THRESHOLD: 20162306a36Sopenharmony_ci stream_id = ACP_SDW0_AUDIO0_RX; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci case ACP_AUDIO1_RX_THRESHOLD: 20462306a36Sopenharmony_ci stream_id = ACP_SDW0_AUDIO1_RX; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci case ACP_AUDIO2_RX_THRESHOLD: 20762306a36Sopenharmony_ci stream_id = ACP_SDW0_AUDIO2_RX; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci adata->sdw0_dma_intr_stat[stream_id] = 1; 21262306a36Sopenharmony_ci sdw_dma_irq_flag = 1; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) { 21862306a36Sopenharmony_ci writel(ACP_P1_AUDIO1_RX_THRESHOLD, 21962306a36Sopenharmony_ci adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); 22062306a36Sopenharmony_ci adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1; 22162306a36Sopenharmony_ci sdw_dma_irq_flag = 1; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) { 22562306a36Sopenharmony_ci writel(ACP_P1_AUDIO1_TX_THRESHOLD, 22662306a36Sopenharmony_ci adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); 22762306a36Sopenharmony_ci adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1; 22862306a36Sopenharmony_ci sdw_dma_irq_flag = 1; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (sdw_dma_irq_flag) 23262306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (irq_flag) 23562306a36Sopenharmony_ci return IRQ_HANDLED; 23662306a36Sopenharmony_ci else 23762306a36Sopenharmony_ci return IRQ_NONE; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int sdw_amd_scan_controller(struct device *dev) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct acp63_dev_data *acp_data; 24362306a36Sopenharmony_ci struct fwnode_handle *link; 24462306a36Sopenharmony_ci char name[32]; 24562306a36Sopenharmony_ci u32 sdw_manager_bitmap; 24662306a36Sopenharmony_ci u8 count = 0; 24762306a36Sopenharmony_ci u32 acp_sdw_power_mode = 0; 24862306a36Sopenharmony_ci int index; 24962306a36Sopenharmony_ci int ret; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci acp_data = dev_get_drvdata(dev); 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci * Current implementation is based on MIPI DisCo 2.0 spec. 25462306a36Sopenharmony_ci * Found controller, find links supported. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list", 25762306a36Sopenharmony_ci &sdw_manager_bitmap, 1); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci dev_dbg(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret); 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci count = hweight32(sdw_manager_bitmap); 26462306a36Sopenharmony_ci /* Check count is within bounds */ 26562306a36Sopenharmony_ci if (count > AMD_SDW_MAX_MANAGERS) { 26662306a36Sopenharmony_ci dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS); 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!count) { 27162306a36Sopenharmony_ci dev_dbg(dev, "No SoundWire Managers detected\n"); 27262306a36Sopenharmony_ci return -EINVAL; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci dev_dbg(dev, "ACPI reports %d SoundWire Manager devices\n", count); 27562306a36Sopenharmony_ci acp_data->sdw_manager_count = count; 27662306a36Sopenharmony_ci for (index = 0; index < count; index++) { 27762306a36Sopenharmony_ci snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index); 27862306a36Sopenharmony_ci link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name); 27962306a36Sopenharmony_ci if (!link) { 28062306a36Sopenharmony_ci dev_err(dev, "Manager node %s not found\n", name); 28162306a36Sopenharmony_ci return -EIO; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ret = fwnode_property_read_u32(link, "amd-sdw-power-mode", &acp_sdw_power_mode); 28562306a36Sopenharmony_ci if (ret) 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * when SoundWire configuration is selected from acp pin config, 28962306a36Sopenharmony_ci * based on manager instances count, acp init/de-init sequence should be 29062306a36Sopenharmony_ci * executed as part of PM ops only when Bus reset is applied for the active 29162306a36Sopenharmony_ci * SoundWire manager instances. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) { 29462306a36Sopenharmony_ci acp_data->acp_reset = false; 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct acpi_device *dmic_dev; 30462306a36Sopenharmony_ci struct acpi_device *sdw_dev; 30562306a36Sopenharmony_ci const union acpi_object *obj; 30662306a36Sopenharmony_ci bool is_dmic_dev = false; 30762306a36Sopenharmony_ci bool is_sdw_dev = false; 30862306a36Sopenharmony_ci int ret; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); 31162306a36Sopenharmony_ci if (dmic_dev) { 31262306a36Sopenharmony_ci /* is_dmic_dev flag will be set when ACP PDM controller device exists */ 31362306a36Sopenharmony_ci if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type", 31462306a36Sopenharmony_ci ACPI_TYPE_INTEGER, &obj) && 31562306a36Sopenharmony_ci obj->integer.value == ACP_DMIC_DEV) 31662306a36Sopenharmony_ci is_dmic_dev = true; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0); 32062306a36Sopenharmony_ci if (sdw_dev) { 32162306a36Sopenharmony_ci acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev); 32262306a36Sopenharmony_ci ret = sdw_amd_scan_controller(&pci->dev); 32362306a36Sopenharmony_ci /* is_sdw_dev flag will be set when SoundWire Manager device exists */ 32462306a36Sopenharmony_ci if (!ret) 32562306a36Sopenharmony_ci is_sdw_dev = true; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci if (!is_dmic_dev && !is_sdw_dev) 32862306a36Sopenharmony_ci return -ENODEV; 32962306a36Sopenharmony_ci dev_dbg(&pci->dev, "Audio Mode %d\n", config); 33062306a36Sopenharmony_ci switch (config) { 33162306a36Sopenharmony_ci case ACP_CONFIG_4: 33262306a36Sopenharmony_ci case ACP_CONFIG_5: 33362306a36Sopenharmony_ci case ACP_CONFIG_10: 33462306a36Sopenharmony_ci case ACP_CONFIG_11: 33562306a36Sopenharmony_ci if (is_dmic_dev) { 33662306a36Sopenharmony_ci acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; 33762306a36Sopenharmony_ci acp_data->pdev_count = ACP63_PDM_MODE_DEVS; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case ACP_CONFIG_2: 34162306a36Sopenharmony_ci case ACP_CONFIG_3: 34262306a36Sopenharmony_ci if (is_sdw_dev) { 34362306a36Sopenharmony_ci switch (acp_data->sdw_manager_count) { 34462306a36Sopenharmony_ci case 1: 34562306a36Sopenharmony_ci acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; 34662306a36Sopenharmony_ci acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci case 2: 34962306a36Sopenharmony_ci acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; 35062306a36Sopenharmony_ci acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci default: 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci case ACP_CONFIG_6: 35862306a36Sopenharmony_ci case ACP_CONFIG_7: 35962306a36Sopenharmony_ci case ACP_CONFIG_12: 36062306a36Sopenharmony_ci case ACP_CONFIG_8: 36162306a36Sopenharmony_ci case ACP_CONFIG_13: 36262306a36Sopenharmony_ci case ACP_CONFIG_14: 36362306a36Sopenharmony_ci if (is_dmic_dev && is_sdw_dev) { 36462306a36Sopenharmony_ci switch (acp_data->sdw_manager_count) { 36562306a36Sopenharmony_ci case 1: 36662306a36Sopenharmony_ci acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; 36762306a36Sopenharmony_ci acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS; 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case 2: 37062306a36Sopenharmony_ci acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; 37162306a36Sopenharmony_ci acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS; 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci default: 37462306a36Sopenharmony_ci return -EINVAL; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } else if (is_dmic_dev) { 37762306a36Sopenharmony_ci acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; 37862306a36Sopenharmony_ci acp_data->pdev_count = ACP63_PDM_MODE_DEVS; 37962306a36Sopenharmony_ci } else if (is_sdw_dev) { 38062306a36Sopenharmony_ci switch (acp_data->sdw_manager_count) { 38162306a36Sopenharmony_ci case 1: 38262306a36Sopenharmony_ci acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; 38362306a36Sopenharmony_ci acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci case 2: 38662306a36Sopenharmony_ci acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; 38762306a36Sopenharmony_ci acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci default: 39062306a36Sopenharmony_ci return -EINVAL; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci default: 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, 40162306a36Sopenharmony_ci struct device *parent, 40262306a36Sopenharmony_ci struct fwnode_handle *fw_node, 40362306a36Sopenharmony_ci char *name, unsigned int id, 40462306a36Sopenharmony_ci const struct resource *res, 40562306a36Sopenharmony_ci unsigned int num_res, 40662306a36Sopenharmony_ci const void *data, 40762306a36Sopenharmony_ci size_t size_data) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci pdevinfo->name = name; 41062306a36Sopenharmony_ci pdevinfo->id = id; 41162306a36Sopenharmony_ci pdevinfo->parent = parent; 41262306a36Sopenharmony_ci pdevinfo->num_res = num_res; 41362306a36Sopenharmony_ci pdevinfo->res = res; 41462306a36Sopenharmony_ci pdevinfo->data = data; 41562306a36Sopenharmony_ci pdevinfo->size_data = size_data; 41662306a36Sopenharmony_ci pdevinfo->fwnode = fw_node; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct acp_sdw_pdata *sdw_pdata; 42262306a36Sopenharmony_ci struct platform_device_info pdevinfo[ACP63_DEVS]; 42362306a36Sopenharmony_ci struct device *parent; 42462306a36Sopenharmony_ci int index; 42562306a36Sopenharmony_ci int ret; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci parent = &pci->dev; 42862306a36Sopenharmony_ci dev_dbg(&pci->dev, 42962306a36Sopenharmony_ci "%s pdev_config:0x%x pdev_count:0x%x\n", __func__, adata->pdev_config, 43062306a36Sopenharmony_ci adata->pdev_count); 43162306a36Sopenharmony_ci if (adata->pdev_config) { 43262306a36Sopenharmony_ci adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL); 43362306a36Sopenharmony_ci if (!adata->res) { 43462306a36Sopenharmony_ci ret = -ENOMEM; 43562306a36Sopenharmony_ci goto de_init; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci adata->res->flags = IORESOURCE_MEM; 43862306a36Sopenharmony_ci adata->res->start = addr; 43962306a36Sopenharmony_ci adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START); 44062306a36Sopenharmony_ci memset(&pdevinfo, 0, sizeof(pdevinfo)); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci switch (adata->pdev_config) { 44462306a36Sopenharmony_ci case ACP63_PDM_DEV_CONFIG: 44562306a36Sopenharmony_ci adata->pdm_dev_index = 0; 44662306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", 44762306a36Sopenharmony_ci 0, adata->res, 1, NULL, 0); 44862306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "dmic-codec", 44962306a36Sopenharmony_ci 0, NULL, 0, NULL, 0); 45062306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach", 45162306a36Sopenharmony_ci 0, NULL, 0, NULL, 0); 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case ACP63_SDW_DEV_CONFIG: 45462306a36Sopenharmony_ci if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) { 45562306a36Sopenharmony_ci sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), 45662306a36Sopenharmony_ci GFP_KERNEL); 45762306a36Sopenharmony_ci if (!sdw_pdata) { 45862306a36Sopenharmony_ci ret = -ENOMEM; 45962306a36Sopenharmony_ci goto de_init; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci sdw_pdata->instance = 0; 46362306a36Sopenharmony_ci sdw_pdata->acp_sdw_lock = &adata->acp_lock; 46462306a36Sopenharmony_ci adata->sdw0_dev_index = 0; 46562306a36Sopenharmony_ci adata->sdw_dma_dev_index = 1; 46662306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, 46762306a36Sopenharmony_ci "amd_sdw_manager", 0, adata->res, 1, 46862306a36Sopenharmony_ci sdw_pdata, sizeof(struct acp_sdw_pdata)); 46962306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma", 47062306a36Sopenharmony_ci 0, adata->res, 1, NULL, 0); 47162306a36Sopenharmony_ci } else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) { 47262306a36Sopenharmony_ci sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, 47362306a36Sopenharmony_ci GFP_KERNEL); 47462306a36Sopenharmony_ci if (!sdw_pdata) { 47562306a36Sopenharmony_ci ret = -ENOMEM; 47662306a36Sopenharmony_ci goto de_init; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci sdw_pdata[0].instance = 0; 48062306a36Sopenharmony_ci sdw_pdata[1].instance = 1; 48162306a36Sopenharmony_ci sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; 48262306a36Sopenharmony_ci sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; 48362306a36Sopenharmony_ci sdw_pdata->acp_sdw_lock = &adata->acp_lock; 48462306a36Sopenharmony_ci adata->sdw0_dev_index = 0; 48562306a36Sopenharmony_ci adata->sdw1_dev_index = 1; 48662306a36Sopenharmony_ci adata->sdw_dma_dev_index = 2; 48762306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, 48862306a36Sopenharmony_ci "amd_sdw_manager", 0, adata->res, 1, 48962306a36Sopenharmony_ci &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); 49062306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, 49162306a36Sopenharmony_ci "amd_sdw_manager", 1, adata->res, 1, 49262306a36Sopenharmony_ci &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); 49362306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", 49462306a36Sopenharmony_ci 0, adata->res, 1, NULL, 0); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case ACP63_SDW_PDM_DEV_CONFIG: 49862306a36Sopenharmony_ci if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) { 49962306a36Sopenharmony_ci sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), 50062306a36Sopenharmony_ci GFP_KERNEL); 50162306a36Sopenharmony_ci if (!sdw_pdata) { 50262306a36Sopenharmony_ci ret = -ENOMEM; 50362306a36Sopenharmony_ci goto de_init; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci sdw_pdata->instance = 0; 50762306a36Sopenharmony_ci sdw_pdata->acp_sdw_lock = &adata->acp_lock; 50862306a36Sopenharmony_ci adata->pdm_dev_index = 0; 50962306a36Sopenharmony_ci adata->sdw0_dev_index = 1; 51062306a36Sopenharmony_ci adata->sdw_dma_dev_index = 2; 51162306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", 51262306a36Sopenharmony_ci 0, adata->res, 1, NULL, 0); 51362306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, 51462306a36Sopenharmony_ci "amd_sdw_manager", 0, adata->res, 1, 51562306a36Sopenharmony_ci sdw_pdata, sizeof(struct acp_sdw_pdata)); 51662306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", 51762306a36Sopenharmony_ci 0, adata->res, 1, NULL, 0); 51862306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec", 51962306a36Sopenharmony_ci 0, NULL, 0, NULL, 0); 52062306a36Sopenharmony_ci } else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) { 52162306a36Sopenharmony_ci sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, 52262306a36Sopenharmony_ci GFP_KERNEL); 52362306a36Sopenharmony_ci if (!sdw_pdata) { 52462306a36Sopenharmony_ci ret = -ENOMEM; 52562306a36Sopenharmony_ci goto de_init; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci sdw_pdata[0].instance = 0; 52862306a36Sopenharmony_ci sdw_pdata[1].instance = 1; 52962306a36Sopenharmony_ci sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; 53062306a36Sopenharmony_ci sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; 53162306a36Sopenharmony_ci adata->pdm_dev_index = 0; 53262306a36Sopenharmony_ci adata->sdw0_dev_index = 1; 53362306a36Sopenharmony_ci adata->sdw1_dev_index = 2; 53462306a36Sopenharmony_ci adata->sdw_dma_dev_index = 3; 53562306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", 53662306a36Sopenharmony_ci 0, adata->res, 1, NULL, 0); 53762306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, 53862306a36Sopenharmony_ci "amd_sdw_manager", 0, adata->res, 1, 53962306a36Sopenharmony_ci &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); 54062306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node, 54162306a36Sopenharmony_ci "amd_sdw_manager", 1, adata->res, 1, 54262306a36Sopenharmony_ci &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); 54362306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma", 54462306a36Sopenharmony_ci 0, adata->res, 1, NULL, 0); 54562306a36Sopenharmony_ci acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec", 54662306a36Sopenharmony_ci 0, NULL, 0, NULL, 0); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci default: 55062306a36Sopenharmony_ci dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n"); 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci for (index = 0; index < adata->pdev_count; index++) { 55562306a36Sopenharmony_ci adata->pdev[index] = platform_device_register_full(&pdevinfo[index]); 55662306a36Sopenharmony_ci if (IS_ERR(adata->pdev[index])) { 55762306a36Sopenharmony_ci dev_err(&pci->dev, 55862306a36Sopenharmony_ci "cannot register %s device\n", pdevinfo[index].name); 55962306a36Sopenharmony_ci ret = PTR_ERR(adata->pdev[index]); 56062306a36Sopenharmony_ci goto unregister_devs; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ciunregister_devs: 56562306a36Sopenharmony_ci for (--index; index >= 0; index--) 56662306a36Sopenharmony_ci platform_device_unregister(adata->pdev[index]); 56762306a36Sopenharmony_cide_init: 56862306a36Sopenharmony_ci if (acp63_deinit(adata->acp63_base, &pci->dev)) 56962306a36Sopenharmony_ci dev_err(&pci->dev, "ACP de-init failed\n"); 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int snd_acp63_probe(struct pci_dev *pci, 57462306a36Sopenharmony_ci const struct pci_device_id *pci_id) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct acp63_dev_data *adata; 57762306a36Sopenharmony_ci u32 addr; 57862306a36Sopenharmony_ci u32 irqflags, flag; 57962306a36Sopenharmony_ci int val; 58062306a36Sopenharmony_ci int ret; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci irqflags = IRQF_SHARED; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Return if acp config flag is defined */ 58562306a36Sopenharmony_ci flag = snd_amd_acp_find_config(pci); 58662306a36Sopenharmony_ci if (flag) 58762306a36Sopenharmony_ci return -ENODEV; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Pink Sardine device check */ 59062306a36Sopenharmony_ci switch (pci->revision) { 59162306a36Sopenharmony_ci case 0x63: 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci default: 59462306a36Sopenharmony_ci dev_dbg(&pci->dev, "acp63 pci device not found\n"); 59562306a36Sopenharmony_ci return -ENODEV; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci if (pci_enable_device(pci)) { 59862306a36Sopenharmony_ci dev_err(&pci->dev, "pci_enable_device failed\n"); 59962306a36Sopenharmony_ci return -ENODEV; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ret = pci_request_regions(pci, "AMD ACP6.2 audio"); 60362306a36Sopenharmony_ci if (ret < 0) { 60462306a36Sopenharmony_ci dev_err(&pci->dev, "pci_request_regions failed\n"); 60562306a36Sopenharmony_ci goto disable_pci; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci adata = devm_kzalloc(&pci->dev, sizeof(struct acp63_dev_data), 60862306a36Sopenharmony_ci GFP_KERNEL); 60962306a36Sopenharmony_ci if (!adata) { 61062306a36Sopenharmony_ci ret = -ENOMEM; 61162306a36Sopenharmony_ci goto release_regions; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci addr = pci_resource_start(pci, 0); 61562306a36Sopenharmony_ci adata->acp63_base = devm_ioremap(&pci->dev, addr, 61662306a36Sopenharmony_ci pci_resource_len(pci, 0)); 61762306a36Sopenharmony_ci if (!adata->acp63_base) { 61862306a36Sopenharmony_ci ret = -ENOMEM; 61962306a36Sopenharmony_ci goto release_regions; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init() 62362306a36Sopenharmony_ci * will be invoked for all ACP configurations during suspend/resume callbacks. 62462306a36Sopenharmony_ci * This flag should be set to false only when SoundWire manager power mode 62562306a36Sopenharmony_ci * set to ClockStopMode. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci adata->acp_reset = true; 62862306a36Sopenharmony_ci pci_set_master(pci); 62962306a36Sopenharmony_ci pci_set_drvdata(pci, adata); 63062306a36Sopenharmony_ci mutex_init(&adata->acp_lock); 63162306a36Sopenharmony_ci ret = acp63_init(adata->acp63_base, &pci->dev); 63262306a36Sopenharmony_ci if (ret) 63362306a36Sopenharmony_ci goto release_regions; 63462306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler, 63562306a36Sopenharmony_ci acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata); 63662306a36Sopenharmony_ci if (ret) { 63762306a36Sopenharmony_ci dev_err(&pci->dev, "ACP PCI IRQ request failed\n"); 63862306a36Sopenharmony_ci goto de_init; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci val = readl(adata->acp63_base + ACP_PIN_CONFIG); 64162306a36Sopenharmony_ci ret = get_acp63_device_config(val, pci, adata); 64262306a36Sopenharmony_ci /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */ 64362306a36Sopenharmony_ci if (ret) { 64462306a36Sopenharmony_ci dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret); 64562306a36Sopenharmony_ci goto skip_pdev_creation; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci ret = create_acp63_platform_devs(pci, adata, addr); 64862306a36Sopenharmony_ci if (ret < 0) { 64962306a36Sopenharmony_ci dev_err(&pci->dev, "ACP platform devices creation failed\n"); 65062306a36Sopenharmony_ci goto de_init; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ciskip_pdev_creation: 65362306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS); 65462306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pci->dev); 65562306a36Sopenharmony_ci pm_runtime_put_noidle(&pci->dev); 65662306a36Sopenharmony_ci pm_runtime_allow(&pci->dev); 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_cide_init: 65962306a36Sopenharmony_ci if (acp63_deinit(adata->acp63_base, &pci->dev)) 66062306a36Sopenharmony_ci dev_err(&pci->dev, "ACP de-init failed\n"); 66162306a36Sopenharmony_cirelease_regions: 66262306a36Sopenharmony_ci pci_release_regions(pci); 66362306a36Sopenharmony_cidisable_pci: 66462306a36Sopenharmony_ci pci_disable_device(pci); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int __maybe_unused snd_acp63_suspend(struct device *dev) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct acp63_dev_data *adata; 67262306a36Sopenharmony_ci int ret = 0; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci adata = dev_get_drvdata(dev); 67562306a36Sopenharmony_ci if (adata->acp_reset) { 67662306a36Sopenharmony_ci ret = acp63_deinit(adata->acp63_base, dev); 67762306a36Sopenharmony_ci if (ret) 67862306a36Sopenharmony_ci dev_err(dev, "ACP de-init failed\n"); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int __maybe_unused snd_acp63_resume(struct device *dev) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct acp63_dev_data *adata; 68662306a36Sopenharmony_ci int ret = 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci adata = dev_get_drvdata(dev); 68962306a36Sopenharmony_ci if (adata->acp_reset) { 69062306a36Sopenharmony_ci ret = acp63_init(adata->acp63_base, dev); 69162306a36Sopenharmony_ci if (ret) 69262306a36Sopenharmony_ci dev_err(dev, "ACP init failed\n"); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic const struct dev_pm_ops acp63_pm_ops = { 69862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_resume, NULL) 69962306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume) 70062306a36Sopenharmony_ci}; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic void snd_acp63_remove(struct pci_dev *pci) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct acp63_dev_data *adata; 70562306a36Sopenharmony_ci int ret, index; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci adata = pci_get_drvdata(pci); 70862306a36Sopenharmony_ci for (index = 0; index < adata->pdev_count; index++) 70962306a36Sopenharmony_ci platform_device_unregister(adata->pdev[index]); 71062306a36Sopenharmony_ci ret = acp63_deinit(adata->acp63_base, &pci->dev); 71162306a36Sopenharmony_ci if (ret) 71262306a36Sopenharmony_ci dev_err(&pci->dev, "ACP de-init failed\n"); 71362306a36Sopenharmony_ci pm_runtime_forbid(&pci->dev); 71462306a36Sopenharmony_ci pm_runtime_get_noresume(&pci->dev); 71562306a36Sopenharmony_ci pci_release_regions(pci); 71662306a36Sopenharmony_ci pci_disable_device(pci); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic const struct pci_device_id snd_acp63_ids[] = { 72062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID), 72162306a36Sopenharmony_ci .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, 72262306a36Sopenharmony_ci .class_mask = 0xffffff }, 72362306a36Sopenharmony_ci { 0, }, 72462306a36Sopenharmony_ci}; 72562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_acp63_ids); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic struct pci_driver ps_acp63_driver = { 72862306a36Sopenharmony_ci .name = KBUILD_MODNAME, 72962306a36Sopenharmony_ci .id_table = snd_acp63_ids, 73062306a36Sopenharmony_ci .probe = snd_acp63_probe, 73162306a36Sopenharmony_ci .remove = snd_acp63_remove, 73262306a36Sopenharmony_ci .driver = { 73362306a36Sopenharmony_ci .pm = &acp63_pm_ops, 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cimodule_pci_driver(ps_acp63_driver); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ciMODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 74062306a36Sopenharmony_ciMODULE_AUTHOR("Syed.SabaKareem@amd.com"); 74162306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver"); 74262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 743