Unverified Commit 9bd1f9db authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: amd: ps: add SoundWire support

Merge series from Vijendar Mukunda <Vijendar.Mukunda@amd.com>:

This patch series add support for
	- Platform device creation for SoundWire Manager instances and
	  PDM controller.
	- SoundWire DMA driver.
	- Interrupt handling for SoundWire manager related interrupts,
	  SoundWire DMA interrupts and ACP error interrupts.
	- ACP PCI driver PM ops modification with respect to SoundWire
	  Power modes.
parents 3067e020 198c93e2
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -138,7 +138,8 @@ config SND_SOC_AMD_PS
        help
          This option enables Audio Coprocessor i.e ACP v6.3 support on
          AMD Pink sardine platform. By enabling this flag build will be
          triggered for ACP PCI driver, ACP PDM DMA driver.
          triggered for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire
          DMA driver.
          Say m if you have such a device.
          If unsure select "N".

+2 −0
Original line number Diff line number Diff line
@@ -3,7 +3,9 @@
snd-pci-ps-objs := pci-ps.o
snd-ps-pdm-dma-objs := ps-pdm-dma.o
snd-soc-ps-mach-objs := ps-mach.o
snd-ps-sdw-dma-objs := ps-sdw-dma.o

obj-$(CONFIG_SND_SOC_AMD_PS) += snd-pci-ps.o
obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-pdm-dma.o
obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-sdw-dma.o
obj-$(CONFIG_SND_SOC_AMD_PS_MACH)   += snd-soc-ps-mach.o
+167 −5
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@
#define ACP_DEVICE_ID 0x15E2
#define ACP63_REG_START		0x1240000
#define ACP63_REG_END		0x1250200
#define ACP63_DEVS		3
#define ACP63_DEVS		5

#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
#define ACP_PGFSM_CNTL_POWER_ON_MASK	1
@@ -53,14 +53,99 @@
/* time in ms for runtime suspend delay */
#define ACP_SUSPEND_DELAY_MS	2000

#define ACP63_DMIC_ADDR		2
#define ACP63_PDM_MODE_DEVS		3
#define ACP63_PDM_DEV_MASK		1
#define ACP_DMIC_DEV	2

/* ACP63_PDM_MODE_DEVS corresponds to platform devices count for ACP PDM configuration */
#define ACP63_PDM_MODE_DEVS		3

/*
 * ACP63_SDW0_MODE_DEVS corresponds to platform devices count for
 * SW0 SoundWire manager instance configuration
 */
#define ACP63_SDW0_MODE_DEVS		2

/*
 * ACP63_SDW0_SDW1_MODE_DEVS corresponds to platform devices count for SW0 + SW1 SoundWire manager
 * instances configuration
 */
#define ACP63_SDW0_SDW1_MODE_DEVS	3

/*
 * ACP63_SDW0_PDM_MODE_DEVS corresponds to platform devices count for SW0 manager
 * instance + ACP PDM controller configuration
 */
#define ACP63_SDW0_PDM_MODE_DEVS	4

/*
 * ACP63_SDW0_SDW1_PDM_MODE_DEVS corresponds to platform devices count for
 * SW0 + SW1 SoundWire manager instances + ACP PDM controller configuration
 */
#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
#define ACP63_DMIC_ADDR			2
#define ACP63_SDW_ADDR			5
#define AMD_SDW_MAX_MANAGERS		2

/* time in ms for acp timeout */
#define ACP_TIMEOUT		500

/* ACP63_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM controller */
#define ACP63_PDM_DEV_CONFIG		BIT(0)

/* ACP63_SDW_DEV_CONFIG corresponds to platform device configuration for SDW manager instances */
#define ACP63_SDW_DEV_CONFIG		BIT(1)

/*
 * ACP63_SDW_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM + SoundWire
 * manager instance combination.
 */
#define ACP63_SDW_PDM_DEV_CONFIG	GENMASK(1, 0)
#define ACP_SDW0_STAT			BIT(21)
#define ACP_SDW1_STAT			BIT(2)
#define ACP_ERROR_IRQ			BIT(29)

#define ACP_AUDIO0_TX_THRESHOLD		0x1c
#define ACP_AUDIO1_TX_THRESHOLD		0x1a
#define ACP_AUDIO2_TX_THRESHOLD		0x18
#define ACP_AUDIO0_RX_THRESHOLD		0x1b
#define ACP_AUDIO1_RX_THRESHOLD		0x19
#define ACP_AUDIO2_RX_THRESHOLD		0x17
#define ACP_P1_AUDIO1_TX_THRESHOLD	BIT(6)
#define ACP_P1_AUDIO1_RX_THRESHOLD	BIT(5)
#define ACP_SDW_DMA_IRQ_MASK		0x1F800000
#define ACP_P1_SDW_DMA_IRQ_MASK		0x60
#define ACP63_SDW0_DMA_MAX_STREAMS	6
#define ACP63_SDW1_DMA_MAX_STREAMS	2
#define ACP_P1_AUDIO_TX_THRESHOLD	6
#define SDW0_DMA_TX_IRQ_MASK(i)	(ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
#define SDW0_DMA_RX_IRQ_MASK(i)	(ACP_AUDIO0_RX_THRESHOLD - (2 * (i)))
#define SDW1_DMA_IRQ_MASK(i)	(ACP_P1_AUDIO_TX_THRESHOLD - (i))

#define ACP_DELAY_US		5
#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
#define SDW0_MEM_WINDOW_START	0x4800000
#define ACP_SDW_SRAM_PTE_OFFSET	0x03800400
#define SDW0_PTE_OFFSET		0x400
#define SDW_FIFO_SIZE		0x100
#define SDW_DMA_SIZE		0x40
#define ACP_SDW0_FIFO_OFFSET	0x100
#define ACP_SDW_PTE_OFFSET	0x100
#define SDW_FIFO_OFFSET		0x100
#define SDW_PTE_OFFSET(i)	(SDW0_PTE_OFFSET + ((i) * 0x600))
#define ACP_SDW_FIFO_OFFSET(i)	(ACP_SDW0_FIFO_OFFSET + ((i) * 0x500))
#define SDW_MEM_WINDOW_START(i)	(SDW0_MEM_WINDOW_START + ((i) * 0xC0000))

#define SDW_PLAYBACK_MIN_NUM_PERIODS    2
#define SDW_PLAYBACK_MAX_NUM_PERIODS    8
#define SDW_PLAYBACK_MAX_PERIOD_SIZE    8192
#define SDW_PLAYBACK_MIN_PERIOD_SIZE    1024
#define SDW_CAPTURE_MIN_NUM_PERIODS     2
#define SDW_CAPTURE_MAX_NUM_PERIODS     8
#define SDW_CAPTURE_MAX_PERIOD_SIZE     8192
#define SDW_CAPTURE_MIN_PERIOD_SIZE     1024

#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
#define SDW_MIN_BUFFER SDW_MAX_BUFFER

enum acp_config {
	ACP_CONFIG_0 = 0,
	ACP_CONFIG_1,
@@ -80,6 +165,20 @@ enum acp_config {
	ACP_CONFIG_15,
};

enum amd_sdw0_channel {
	ACP_SDW0_AUDIO0_TX = 0,
	ACP_SDW0_AUDIO1_TX,
	ACP_SDW0_AUDIO2_TX,
	ACP_SDW0_AUDIO0_RX,
	ACP_SDW0_AUDIO1_RX,
	ACP_SDW0_AUDIO2_RX,
};

enum amd_sdw1_channel {
	ACP_SDW1_AUDIO1_TX,
	ACP_SDW1_AUDIO1_RX,
};

struct pdm_stream_instance {
	u16 num_pages;
	u16 channels;
@@ -95,14 +194,77 @@ struct pdm_dev_data {
	struct snd_pcm_substream *capture_stream;
};

struct sdw_dma_dev_data {
	void __iomem *acp_base;
	struct mutex *acp_lock; /* used to protect acp common register access */
	struct snd_pcm_substream *sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
	struct snd_pcm_substream *sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
};

struct acp_sdw_dma_stream {
	u16 num_pages;
	u16 channels;
	u32 stream_id;
	u32 instance;
	dma_addr_t dma_addr;
	u64 bytescount;
};

union acp_sdw_dma_count {
	struct {
		u32 low;
		u32 high;
	} bcount;
	u64 bytescount;
};

struct sdw_dma_ring_buf_reg {
	u32 reg_dma_size;
	u32 reg_fifo_addr;
	u32 reg_fifo_size;
	u32 reg_ring_buf_size;
	u32 reg_ring_buf_addr;
	u32 water_mark_size_reg;
	u32 pos_low_reg;
	u32 pos_high_reg;
};

/**
 * struct acp63_dev_data - acp pci driver context
 * @acp63_base: acp mmio base
 * @res: resource
 * @pdev: array of child platform device node structures
 * @acp_lock: used to protect acp common registers
 * @sdw_fw_node: SoundWire controller fw node handle
 * @pdev_config: platform device configuration
 * @pdev_count: platform devices count
 * @pdm_dev_index: pdm platform device index
 * @sdw_manager_count: SoundWire manager instance count
 * @sdw0_dev_index: SoundWire Manager-0 platform device index
 * @sdw1_dev_index: SoundWire Manager-1 platform device index
 * @sdw_dma_dev_index: SoundWire DMA controller platform device index
 * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
 * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
 * @acp_reset: flag set to true when bus reset is applied across all
 * the active SoundWire manager instances
 */

struct acp63_dev_data {
	void __iomem *acp63_base;
	struct resource *res;
	struct platform_device *pdev[ACP63_DEVS];
	struct mutex acp_lock; /* protect shared registers */
	u16 pdev_mask;
	struct fwnode_handle *sdw_fw_node;
	u16 pdev_config;
	u16 pdev_count;
	u16 pdm_dev_index;
	u8 sdw_manager_count;
	u16 sdw0_dev_index;
	u16 sdw1_dev_index;
	u16 sdw_dma_dev_index;
	u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
	u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
	bool acp_reset;
};

int snd_amd_acp_find_config(struct pci_dev *pci);
+389 −30
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
 */

#include <linux/pci.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -15,6 +16,7 @@
#include <sound/pcm_params.h>
#include <linux/pm_runtime.h>
#include <linux/iopoll.h>
#include <linux/soundwire/sdw_amd.h>

#include "acp63.h"

@@ -54,6 +56,7 @@ static int acp63_reset(void __iomem *acp_base)
static void acp63_enable_interrupts(void __iomem *acp_base)
{
	writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
	writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
}

static void acp63_disable_interrupts(void __iomem *acp_base)
@@ -96,60 +99,302 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
	return 0;
}

static irqreturn_t acp63_irq_thread(int irq, void *context)
{
	struct sdw_dma_dev_data *sdw_dma_data;
	struct acp63_dev_data *adata = context;
	u32 stream_index;
	u16 pdev_index;

	pdev_index = adata->sdw_dma_dev_index;
	sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);

	for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
		if (adata->sdw0_dma_intr_stat[stream_index]) {
			if (sdw_dma_data->sdw0_dma_stream[stream_index])
				snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]);
			adata->sdw0_dma_intr_stat[stream_index] = 0;
		}
	}
	for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) {
		if (adata->sdw1_dma_intr_stat[stream_index]) {
			if (sdw_dma_data->sdw1_dma_stream[stream_index])
				snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]);
			adata->sdw1_dma_intr_stat[stream_index] = 0;
		}
	}
	return IRQ_HANDLED;
}

static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
{
	struct acp63_dev_data *adata;
	struct pdm_dev_data *ps_pdm_data;
	u32 val;
	struct amd_sdw_manager *amd_manager;
	u32 ext_intr_stat, ext_intr_stat1;
	u32 stream_id = 0;
	u16 irq_flag = 0;
	u16 sdw_dma_irq_flag = 0;
	u16 pdev_index;
	u16 index;

	adata = dev_id;
	if (!adata)
		return IRQ_NONE;

	val = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
	if (val & BIT(PDM_DMA_STAT)) {
	/* ACP interrupts will be cleared by reading particular bit and writing
	 * same value to the status register. writing zero's doesn't have any
	 * effect.
	 * Bit by bit checking of IRQ field is implemented.
	 */
	ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
	if (ext_intr_stat & ACP_SDW0_STAT) {
		writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
		pdev_index = adata->sdw0_dev_index;
		amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
		if (amd_manager)
			schedule_work(&amd_manager->amd_sdw_irq_thread);
		irq_flag = 1;
	}

	ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
	if (ext_intr_stat1 & ACP_SDW1_STAT) {
		writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
		pdev_index = adata->sdw1_dev_index;
		amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
		if (amd_manager)
			schedule_work(&amd_manager->amd_sdw_irq_thread);
		irq_flag = 1;
	}

	if (ext_intr_stat & ACP_ERROR_IRQ) {
		writel(ACP_ERROR_IRQ, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
		/* TODO: Report SoundWire Manager instance errors */
		writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON);
		writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON);
		writel(0, adata->acp63_base + ACP_ERROR_STATUS);
		irq_flag = 1;
	}

	if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
		pdev_index = adata->pdm_dev_index;
		ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
		writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
		if (ps_pdm_data->capture_stream)
			snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
		return IRQ_HANDLED;
		irq_flag = 1;
	}
	if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
		for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
			if (ext_intr_stat & BIT(index)) {
				writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
				switch (index) {
				case ACP_AUDIO0_TX_THRESHOLD:
					stream_id = ACP_SDW0_AUDIO0_TX;
					break;
				case ACP_AUDIO1_TX_THRESHOLD:
					stream_id = ACP_SDW0_AUDIO1_TX;
					break;
				case ACP_AUDIO2_TX_THRESHOLD:
					stream_id = ACP_SDW0_AUDIO2_TX;
					break;
				case ACP_AUDIO0_RX_THRESHOLD:
					stream_id = ACP_SDW0_AUDIO0_RX;
					break;
				case ACP_AUDIO1_RX_THRESHOLD:
					stream_id = ACP_SDW0_AUDIO1_RX;
					break;
				case ACP_AUDIO2_RX_THRESHOLD:
					stream_id = ACP_SDW0_AUDIO2_RX;
					break;
				}

				adata->sdw0_dma_intr_stat[stream_id] = 1;
				sdw_dma_irq_flag = 1;
			}
		}
	}

	if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) {
		writel(ACP_P1_AUDIO1_RX_THRESHOLD,
		       adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
		adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1;
		sdw_dma_irq_flag = 1;
	}

	if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) {
		writel(ACP_P1_AUDIO1_TX_THRESHOLD,
		       adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
		adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1;
		sdw_dma_irq_flag = 1;
	}

	if (sdw_dma_irq_flag)
		return IRQ_WAKE_THREAD;

	if (irq_flag)
		return IRQ_HANDLED;
	else
		return IRQ_NONE;
}

static void get_acp63_device_config(u32 config, struct pci_dev *pci,
				    struct acp63_dev_data *acp_data)
static int sdw_amd_scan_controller(struct device *dev)
{
	struct acp63_dev_data *acp_data;
	struct fwnode_handle *link;
	char name[32];
	u32 sdw_manager_bitmap;
	u8 count = 0;
	u32 acp_sdw_power_mode = 0;
	int index;
	int ret;

	acp_data = dev_get_drvdata(dev);
	/*
	 * Current implementation is based on MIPI DisCo 2.0 spec.
	 * Found controller, find links supported.
	 */
	ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list",
					     &sdw_manager_bitmap, 1);

	if (ret) {
		dev_err(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret);
		return -EINVAL;
	}
	count = hweight32(sdw_manager_bitmap);
	/* Check count is within bounds */
	if (count > AMD_SDW_MAX_MANAGERS) {
		dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS);
		return -EINVAL;
	}

	if (!count) {
		dev_dbg(dev, "No SoundWire Managers detected\n");
		return -EINVAL;
	}
	dev_dbg(dev, "ACPI reports %d SoundWire Manager devices\n", count);
	acp_data->sdw_manager_count = count;
	for (index = 0; index < count; index++) {
		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
		if (!link) {
			dev_err(dev, "Manager node %s not found\n", name);
			return -EIO;
		}

		ret = fwnode_property_read_u32(link, "amd-sdw-power-mode", &acp_sdw_power_mode);
		if (ret)
			return ret;
		/*
		 * when SoundWire configuration is selected from acp pin config,
		 * based on manager instances count, acp init/de-init sequence should be
		 * executed as part of PM ops only when Bus reset is applied for the active
		 * SoundWire manager instances.
		 */
		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) {
			acp_data->acp_reset = false;
			return 0;
		}
	}
	return 0;
}

static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
{
	struct acpi_device *dmic_dev;
	struct acpi_device *sdw_dev;
	const union acpi_object *obj;
	bool is_dmic_dev = false;
	bool is_sdw_dev = false;
	int ret;

	dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
	if (dmic_dev) {
		/* is_dmic_dev flag will be set when ACP PDM controller device exists */
		if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type",
					   ACPI_TYPE_INTEGER, &obj) &&
					   obj->integer.value == ACP_DMIC_DEV)
			is_dmic_dev = true;
	}

	sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
	if (sdw_dev) {
		acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
		ret = sdw_amd_scan_controller(&pci->dev);
		/* is_sdw_dev flag will be set when SoundWire Manager device exists */
		if (!ret)
			is_sdw_dev = true;
	}
	if (!is_dmic_dev && !is_sdw_dev)
		return -ENODEV;
	dev_dbg(&pci->dev, "Audio Mode %d\n", config);
	switch (config) {
	case ACP_CONFIG_0:
	case ACP_CONFIG_1:
	case ACP_CONFIG_4:
	case ACP_CONFIG_5:
	case ACP_CONFIG_10:
	case ACP_CONFIG_11:
		if (is_dmic_dev) {
			acp_data->pdev_config = ACP63_PDM_DEV_CONFIG;
			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
		}
		break;
	case ACP_CONFIG_2:
	case ACP_CONFIG_3:
	case ACP_CONFIG_9:
	case ACP_CONFIG_15:
		dev_dbg(&pci->dev, "Audio Mode %d\n", config);
		if (is_sdw_dev) {
			switch (acp_data->sdw_manager_count) {
			case 1:
				acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
				break;
			case 2:
				acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
				break;
			default:
		if (is_dmic_dev) {
			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
				return -EINVAL;
			}
		}
		break;
	case ACP_CONFIG_6:
	case ACP_CONFIG_7:
	case ACP_CONFIG_12:
	case ACP_CONFIG_8:
	case ACP_CONFIG_13:
	case ACP_CONFIG_14:
		if (is_dmic_dev && is_sdw_dev) {
			switch (acp_data->sdw_manager_count) {
			case 1:
				acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG;
				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
				break;
			case 2:
				acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG;
				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
				break;
			default:
				return -EINVAL;
			}
		} else if (is_dmic_dev) {
			acp_data->pdev_config = ACP63_PDM_DEV_CONFIG;
			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
		} else if (is_sdw_dev) {
			switch (acp_data->sdw_manager_count) {
			case 1:
				acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
				break;
			case 2:
				acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
				break;
			default:
				return -EINVAL;
			}
		}
		break;
	default:
		break;
	}
	return 0;
}

static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
@@ -173,6 +418,7 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,

static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
{
	struct acp_sdw_pdata *sdw_pdata;
	struct platform_device_info pdevinfo[ACP63_DEVS];
	struct device *parent;
	int index;
@@ -180,9 +426,9 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data

	parent = &pci->dev;
	dev_dbg(&pci->dev,
		"%s pdev_mask:0x%x pdev_count:0x%x\n", __func__, adata->pdev_mask,
		"%s pdev_config:0x%x pdev_count:0x%x\n", __func__, adata->pdev_config,
		adata->pdev_count);
	if (adata->pdev_mask) {
	if (adata->pdev_config) {
		adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
		if (!adata->res) {
			ret = -ENOMEM;
@@ -194,8 +440,8 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
		memset(&pdevinfo, 0, sizeof(pdevinfo));
	}

	switch (adata->pdev_mask) {
	case ACP63_PDM_DEV_MASK:
	switch (adata->pdev_config) {
	case ACP63_PDM_DEV_CONFIG:
		adata->pdm_dev_index  = 0;
		acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
					     0, adata->res, 1, NULL, 0);
@@ -204,8 +450,104 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
		acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach",
					     0, NULL, 0, NULL, 0);
		break;
	case ACP63_SDW_DEV_CONFIG:
		if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) {
			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
						 GFP_KERNEL);
			if (!sdw_pdata) {
				ret = -ENOMEM;
				goto de_init;
			}

			sdw_pdata->instance = 0;
			sdw_pdata->acp_sdw_lock = &adata->acp_lock;
			adata->sdw0_dev_index = 0;
			adata->sdw_dma_dev_index = 1;
			acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
						     "amd_sdw_manager", 0, adata->res, 1,
						     sdw_pdata, sizeof(struct acp_sdw_pdata));
			acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma",
						     0, adata->res, 1, NULL, 0);
		} else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) {
			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
						 GFP_KERNEL);
			if (!sdw_pdata) {
				ret = -ENOMEM;
				goto de_init;
			}

			sdw_pdata[0].instance = 0;
			sdw_pdata[1].instance = 1;
			sdw_pdata[0].acp_sdw_lock = &adata->acp_lock;
			sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
			sdw_pdata->acp_sdw_lock = &adata->acp_lock;
			adata->sdw0_dev_index = 0;
			adata->sdw1_dev_index = 1;
			adata->sdw_dma_dev_index = 2;
			acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
						     "amd_sdw_manager", 0, adata->res, 1,
						     &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
						     "amd_sdw_manager", 1, adata->res, 1,
						     &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
			acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
						     0, adata->res, 1, NULL, 0);
		}
		break;
	case ACP63_SDW_PDM_DEV_CONFIG:
		if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) {
			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
						 GFP_KERNEL);
			if (!sdw_pdata) {
				ret = -ENOMEM;
				goto de_init;
			}

			sdw_pdata->instance = 0;
			sdw_pdata->acp_sdw_lock = &adata->acp_lock;
			adata->pdm_dev_index = 0;
			adata->sdw0_dev_index = 1;
			adata->sdw_dma_dev_index = 2;
			acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
						     0, adata->res, 1, NULL, 0);
			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
						     "amd_sdw_manager", 0, adata->res, 1,
						     sdw_pdata, sizeof(struct acp_sdw_pdata));
			acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
						     0, adata->res, 1, NULL, 0);
			acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec",
						     0, NULL, 0, NULL, 0);
		} else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) {
			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
						 GFP_KERNEL);
			if (!sdw_pdata) {
				ret = -ENOMEM;
				goto de_init;
			}
			sdw_pdata[0].instance = 0;
			sdw_pdata[1].instance = 1;
			sdw_pdata[0].acp_sdw_lock = &adata->acp_lock;
			sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
			adata->pdm_dev_index = 0;
			adata->sdw0_dev_index = 1;
			adata->sdw1_dev_index = 2;
			adata->sdw_dma_dev_index = 3;
			acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
						     0, adata->res, 1, NULL, 0);
			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
						     "amd_sdw_manager", 0, adata->res, 1,
						     &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
			acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node,
						     "amd_sdw_manager", 1, adata->res, 1,
						     &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
			acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma",
						     0, adata->res, 1, NULL, 0);
			acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec",
						     0, NULL, 0, NULL, 0);
		}
		break;
	default:
		dev_dbg(&pci->dev, "No PDM devices found\n");
		dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
		return 0;
	}

@@ -276,25 +618,38 @@ static int snd_acp63_probe(struct pci_dev *pci,
		ret = -ENOMEM;
		goto release_regions;
	}
	/*
	 * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init()
	 * will be invoked for all ACP configurations during suspend/resume callbacks.
	 * This flag should be set to false only when SoundWire manager power mode
	 * set to ClockStopMode.
	 */
	adata->acp_reset = true;
	pci_set_master(pci);
	pci_set_drvdata(pci, adata);
	mutex_init(&adata->acp_lock);
	ret = acp63_init(adata->acp63_base, &pci->dev);
	if (ret)
		goto release_regions;
	ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
			       irqflags, "ACP_PCI_IRQ", adata);
	ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
					acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata);
	if (ret) {
		dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
		goto de_init;
	}
	val = readl(adata->acp63_base + ACP_PIN_CONFIG);
	get_acp63_device_config(val, pci, adata);
	ret = get_acp63_device_config(val, pci, adata);
	/* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
	if (ret) {
		dev_err(&pci->dev, "get acp device config failed:%d\n", ret);
		goto skip_pdev_creation;
	}
	ret = create_acp63_platform_devs(pci, adata, addr);
	if (ret < 0) {
		dev_err(&pci->dev, "ACP platform devices creation failed\n");
		goto de_init;
	}
skip_pdev_creation:
	pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
	pm_runtime_use_autosuspend(&pci->dev);
	pm_runtime_put_noidle(&pci->dev);
@@ -314,24 +669,28 @@ static int snd_acp63_probe(struct pci_dev *pci,
static int __maybe_unused snd_acp63_suspend(struct device *dev)
{
	struct acp63_dev_data *adata;
	int ret;
	int ret = 0;

	adata = dev_get_drvdata(dev);
	if (adata->acp_reset) {
		ret = acp63_deinit(adata->acp63_base, dev);
		if (ret)
			dev_err(dev, "ACP de-init failed\n");
	}
	return ret;
}

static int __maybe_unused snd_acp63_resume(struct device *dev)
{
	struct acp63_dev_data *adata;
	int ret;
	int ret = 0;

	adata = dev_get_drvdata(dev);
	if (adata->acp_reset) {
		ret = acp63_init(adata->acp63_base, dev);
		if (ret)
			dev_err(dev, "ACP init failed\n");
	}
	return ret;
}

+555 −0

File added.

Preview size limit exceeded, changes collapsed.