xref: /linux/drivers/soundwire/intel.c (revision 2890a6363a8d42f6a24f0bbe6a48fae53d8bd875)
171bb8a1bSVinod Koul // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
271bb8a1bSVinod Koul // Copyright(c) 2015-17 Intel Corporation.
371bb8a1bSVinod Koul 
471bb8a1bSVinod Koul /*
571bb8a1bSVinod Koul  * Soundwire Intel Master Driver
671bb8a1bSVinod Koul  */
771bb8a1bSVinod Koul 
871bb8a1bSVinod Koul #include <linux/acpi.h>
971bb8a1bSVinod Koul #include <linux/delay.h>
104abbd783SPaul Gortmaker #include <linux/module.h>
1171bb8a1bSVinod Koul #include <linux/interrupt.h>
1271bb8a1bSVinod Koul #include <linux/platform_device.h>
1337a2d22bSVinod Koul #include <sound/pcm_params.h>
1437a2d22bSVinod Koul #include <sound/soc.h>
1571bb8a1bSVinod Koul #include <linux/soundwire/sdw_registers.h>
1671bb8a1bSVinod Koul #include <linux/soundwire/sdw.h>
1771bb8a1bSVinod Koul #include <linux/soundwire/sdw_intel.h>
1871bb8a1bSVinod Koul #include "cadence_master.h"
1971bb8a1bSVinod Koul #include "intel.h"
2071bb8a1bSVinod Koul 
2171bb8a1bSVinod Koul /* Intel SHIM Registers Definition */
2271bb8a1bSVinod Koul #define SDW_SHIM_LCAP			0x0
2371bb8a1bSVinod Koul #define SDW_SHIM_LCTL			0x4
2471bb8a1bSVinod Koul #define SDW_SHIM_IPPTR			0x8
2571bb8a1bSVinod Koul #define SDW_SHIM_SYNC			0xC
2671bb8a1bSVinod Koul 
277cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLSCAP(x)		(0x010 + 0x60 * (x))
287cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS0CM(x)		(0x012 + 0x60 * (x))
297cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS1CM(x)		(0x014 + 0x60 * (x))
307cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS2CM(x)		(0x016 + 0x60 * (x))
317cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS3CM(x)		(0x018 + 0x60 * (x))
327cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSCAP(x)		(0x020 + 0x60 * (x))
3371bb8a1bSVinod Koul 
347cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHM(x, y)		(0x022 + (0x60 * (x)) + (0x2 * (y)))
357cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHC(x, y)		(0x042 + (0x60 * (x)) + (0x2 * (y)))
367cc6e315SPierre-Louis Bossart #define SDW_SHIM_PDMSCAP(x)		(0x062 + 0x60 * (x))
377cc6e315SPierre-Louis Bossart #define SDW_SHIM_IOCTL(x)		(0x06C + 0x60 * (x))
387cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTMCTL(x)		(0x06E + 0x60 * (x))
3971bb8a1bSVinod Koul 
4071bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN			0x190
4171bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS		0x192
4271bb8a1bSVinod Koul 
4371bb8a1bSVinod Koul #define SDW_SHIM_LCTL_SPA		BIT(0)
4471bb8a1bSVinod Koul #define SDW_SHIM_LCTL_CPA		BIT(8)
4571bb8a1bSVinod Koul 
4671bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCPRD_VAL	0x176F
4771bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCPRD		GENMASK(14, 0)
4871bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCCPU		BIT(15)
4971bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC_MASK	GENMASK(19, 16)
5071bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC		BIT(16)
5171bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCGO		BIT(24)
5271bb8a1bSVinod Koul 
5371bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_ISS		GENMASK(3, 0)
5471bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_OSS		GENMASK(7, 4)
5571bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_BSS		GENMASK(12, 8)
5671bb8a1bSVinod Koul 
5771bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_LCHN		GENMASK(3, 0)
5871bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_HCHN		GENMASK(7, 4)
5971bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_STREAM		GENMASK(13, 8)
6071bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_DIR		BIT(15)
6171bb8a1bSVinod Koul 
6271bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_ISS		GENMASK(3, 0)
6371bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_OSS		GENMASK(7, 4)
6471bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_BSS		GENMASK(12, 8)
6571bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_CPSS		GENMASK(15, 13)
6671bb8a1bSVinod Koul 
6771bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_MIF		BIT(0)
6871bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CO		BIT(1)
6971bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_COE		BIT(2)
7071bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DO		BIT(3)
7171bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DOE		BIT(4)
7271bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_BKE		BIT(5)
7371bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_WPDD		BIT(6)
7471bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CIBD		BIT(8)
7571bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DIBD		BIT(9)
7671bb8a1bSVinod Koul 
7771bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DACTQE		BIT(0)
7871bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DODS		BIT(1)
7971bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DOAIS		GENMASK(4, 3)
8071bb8a1bSVinod Koul 
8171bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN_ENABLE		BIT(0)
8271bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS_STATUS		BIT(0)
8371bb8a1bSVinod Koul 
8471bb8a1bSVinod Koul /* Intel ALH Register definitions */
857cc6e315SPierre-Louis Bossart #define SDW_ALH_STRMZCFG(x)		(0x000 + (0x4 * (x)))
8671bb8a1bSVinod Koul 
8771bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT_VAL	0x3
8871bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT		GENMASK(7, 0)
8971bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_CHN		GENMASK(19, 16)
9071bb8a1bSVinod Koul 
91c46302ecSVinod Koul enum intel_pdi_type {
92c46302ecSVinod Koul 	INTEL_PDI_IN = 0,
93c46302ecSVinod Koul 	INTEL_PDI_OUT = 1,
94c46302ecSVinod Koul 	INTEL_PDI_BD = 2,
95c46302ecSVinod Koul };
96c46302ecSVinod Koul 
9771bb8a1bSVinod Koul struct sdw_intel {
9871bb8a1bSVinod Koul 	struct sdw_cdns cdns;
9971bb8a1bSVinod Koul 	int instance;
10071bb8a1bSVinod Koul 	struct sdw_intel_link_res *res;
10171bb8a1bSVinod Koul };
10271bb8a1bSVinod Koul 
10371bb8a1bSVinod Koul #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
10471bb8a1bSVinod Koul 
10571bb8a1bSVinod Koul /*
10671bb8a1bSVinod Koul  * Read, write helpers for HW registers
10771bb8a1bSVinod Koul  */
10871bb8a1bSVinod Koul static inline int intel_readl(void __iomem *base, int offset)
10971bb8a1bSVinod Koul {
11071bb8a1bSVinod Koul 	return readl(base + offset);
11171bb8a1bSVinod Koul }
11271bb8a1bSVinod Koul 
11371bb8a1bSVinod Koul static inline void intel_writel(void __iomem *base, int offset, int value)
11471bb8a1bSVinod Koul {
11571bb8a1bSVinod Koul 	writel(value, base + offset);
11671bb8a1bSVinod Koul }
11771bb8a1bSVinod Koul 
11871bb8a1bSVinod Koul static inline u16 intel_readw(void __iomem *base, int offset)
11971bb8a1bSVinod Koul {
12071bb8a1bSVinod Koul 	return readw(base + offset);
12171bb8a1bSVinod Koul }
12271bb8a1bSVinod Koul 
12371bb8a1bSVinod Koul static inline void intel_writew(void __iomem *base, int offset, u16 value)
12471bb8a1bSVinod Koul {
12571bb8a1bSVinod Koul 	writew(value, base + offset);
12671bb8a1bSVinod Koul }
12771bb8a1bSVinod Koul 
12871bb8a1bSVinod Koul static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
12971bb8a1bSVinod Koul {
13071bb8a1bSVinod Koul 	int timeout = 10;
13171bb8a1bSVinod Koul 	u32 reg_read;
13271bb8a1bSVinod Koul 
13371bb8a1bSVinod Koul 	writel(value, base + offset);
13471bb8a1bSVinod Koul 	do {
13571bb8a1bSVinod Koul 		reg_read = readl(base + offset);
13671bb8a1bSVinod Koul 		if (!(reg_read & mask))
13771bb8a1bSVinod Koul 			return 0;
13871bb8a1bSVinod Koul 
13971bb8a1bSVinod Koul 		timeout--;
14071bb8a1bSVinod Koul 		udelay(50);
14171bb8a1bSVinod Koul 	} while (timeout != 0);
14271bb8a1bSVinod Koul 
14371bb8a1bSVinod Koul 	return -EAGAIN;
14471bb8a1bSVinod Koul }
14571bb8a1bSVinod Koul 
14671bb8a1bSVinod Koul static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
14771bb8a1bSVinod Koul {
14871bb8a1bSVinod Koul 	int timeout = 10;
14971bb8a1bSVinod Koul 	u32 reg_read;
15071bb8a1bSVinod Koul 
15171bb8a1bSVinod Koul 	writel(value, base + offset);
15271bb8a1bSVinod Koul 	do {
15371bb8a1bSVinod Koul 		reg_read = readl(base + offset);
15471bb8a1bSVinod Koul 		if (reg_read & mask)
15571bb8a1bSVinod Koul 			return 0;
15671bb8a1bSVinod Koul 
15771bb8a1bSVinod Koul 		timeout--;
15871bb8a1bSVinod Koul 		udelay(50);
15971bb8a1bSVinod Koul 	} while (timeout != 0);
16071bb8a1bSVinod Koul 
16171bb8a1bSVinod Koul 	return -EAGAIN;
16271bb8a1bSVinod Koul }
16371bb8a1bSVinod Koul 
16471bb8a1bSVinod Koul /*
16571bb8a1bSVinod Koul  * shim ops
16671bb8a1bSVinod Koul  */
16771bb8a1bSVinod Koul 
16871bb8a1bSVinod Koul static int intel_link_power_up(struct sdw_intel *sdw)
16971bb8a1bSVinod Koul {
17071bb8a1bSVinod Koul 	unsigned int link_id = sdw->instance;
17171bb8a1bSVinod Koul 	void __iomem *shim = sdw->res->shim;
17271bb8a1bSVinod Koul 	int spa_mask, cpa_mask;
17371bb8a1bSVinod Koul 	int link_control, ret;
17471bb8a1bSVinod Koul 
17571bb8a1bSVinod Koul 	/* Link power up sequence */
17671bb8a1bSVinod Koul 	link_control = intel_readl(shim, SDW_SHIM_LCTL);
17771bb8a1bSVinod Koul 	spa_mask = (SDW_SHIM_LCTL_SPA << link_id);
17871bb8a1bSVinod Koul 	cpa_mask = (SDW_SHIM_LCTL_CPA << link_id);
17971bb8a1bSVinod Koul 	link_control |=  spa_mask;
18071bb8a1bSVinod Koul 
18171bb8a1bSVinod Koul 	ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
18271bb8a1bSVinod Koul 	if (ret < 0)
18371bb8a1bSVinod Koul 		return ret;
18471bb8a1bSVinod Koul 
18571bb8a1bSVinod Koul 	sdw->cdns.link_up = true;
18671bb8a1bSVinod Koul 	return 0;
18771bb8a1bSVinod Koul }
18871bb8a1bSVinod Koul 
18971bb8a1bSVinod Koul static int intel_shim_init(struct sdw_intel *sdw)
19071bb8a1bSVinod Koul {
19171bb8a1bSVinod Koul 	void __iomem *shim = sdw->res->shim;
19271bb8a1bSVinod Koul 	unsigned int link_id = sdw->instance;
19371bb8a1bSVinod Koul 	int sync_reg, ret;
19471bb8a1bSVinod Koul 	u16 ioctl = 0, act = 0;
19571bb8a1bSVinod Koul 
19671bb8a1bSVinod Koul 	/* Initialize Shim */
19771bb8a1bSVinod Koul 	ioctl |= SDW_SHIM_IOCTL_BKE;
19871bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
19971bb8a1bSVinod Koul 
20071bb8a1bSVinod Koul 	ioctl |= SDW_SHIM_IOCTL_WPDD;
20171bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
20271bb8a1bSVinod Koul 
20371bb8a1bSVinod Koul 	ioctl |= SDW_SHIM_IOCTL_DO;
20471bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
20571bb8a1bSVinod Koul 
20671bb8a1bSVinod Koul 	ioctl |= SDW_SHIM_IOCTL_DOE;
20771bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
20871bb8a1bSVinod Koul 
20971bb8a1bSVinod Koul 	/* Switch to MIP from Glue logic */
21071bb8a1bSVinod Koul 	ioctl = intel_readw(shim,  SDW_SHIM_IOCTL(link_id));
21171bb8a1bSVinod Koul 
21271bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DOE);
21371bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
21471bb8a1bSVinod Koul 
21571bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DO);
21671bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
21771bb8a1bSVinod Koul 
21871bb8a1bSVinod Koul 	ioctl |= (SDW_SHIM_IOCTL_MIF);
21971bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
22071bb8a1bSVinod Koul 
22171bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_BKE);
22271bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_COE);
22371bb8a1bSVinod Koul 
22471bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
22571bb8a1bSVinod Koul 
22671bb8a1bSVinod Koul 	act |= 0x1 << SDW_REG_SHIFT(SDW_SHIM_CTMCTL_DOAIS);
22771bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DACTQE;
22871bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DODS;
22971bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
23071bb8a1bSVinod Koul 
23171bb8a1bSVinod Koul 	/* Now set SyncPRD period */
23271bb8a1bSVinod Koul 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
23371bb8a1bSVinod Koul 	sync_reg |= (SDW_SHIM_SYNC_SYNCPRD_VAL <<
23471bb8a1bSVinod Koul 			SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD));
23571bb8a1bSVinod Koul 
23671bb8a1bSVinod Koul 	/* Set SyncCPU bit */
23771bb8a1bSVinod Koul 	sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
23871bb8a1bSVinod Koul 	ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
23971bb8a1bSVinod Koul 			      SDW_SHIM_SYNC_SYNCCPU);
24071bb8a1bSVinod Koul 	if (ret < 0)
24171bb8a1bSVinod Koul 		dev_err(sdw->cdns.dev, "Failed to set sync period: %d", ret);
24271bb8a1bSVinod Koul 
24371bb8a1bSVinod Koul 	return ret;
24471bb8a1bSVinod Koul }
24571bb8a1bSVinod Koul 
24637a2d22bSVinod Koul /*
24737a2d22bSVinod Koul  * PDI routines
24837a2d22bSVinod Koul  */
24937a2d22bSVinod Koul static void intel_pdi_init(struct sdw_intel *sdw,
25037a2d22bSVinod Koul 			   struct sdw_cdns_stream_config *config)
25137a2d22bSVinod Koul {
25237a2d22bSVinod Koul 	void __iomem *shim = sdw->res->shim;
25337a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
25437a2d22bSVinod Koul 	int pcm_cap, pdm_cap;
25537a2d22bSVinod Koul 
25637a2d22bSVinod Koul 	/* PCM Stream Capability */
25737a2d22bSVinod Koul 	pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id));
25837a2d22bSVinod Koul 
25937a2d22bSVinod Koul 	config->pcm_bd = (pcm_cap & SDW_SHIM_PCMSCAP_BSS) >>
26037a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_BSS);
26137a2d22bSVinod Koul 	config->pcm_in = (pcm_cap & SDW_SHIM_PCMSCAP_ISS) >>
26237a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_ISS);
26337a2d22bSVinod Koul 	config->pcm_out = (pcm_cap & SDW_SHIM_PCMSCAP_OSS) >>
26437a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_OSS);
26537a2d22bSVinod Koul 
26637a2d22bSVinod Koul 	/* PDM Stream Capability */
26737a2d22bSVinod Koul 	pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
26837a2d22bSVinod Koul 
26937a2d22bSVinod Koul 	config->pdm_bd = (pdm_cap & SDW_SHIM_PDMSCAP_BSS) >>
27037a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_BSS);
27137a2d22bSVinod Koul 	config->pdm_in = (pdm_cap & SDW_SHIM_PDMSCAP_ISS) >>
27237a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_ISS);
27337a2d22bSVinod Koul 	config->pdm_out = (pdm_cap & SDW_SHIM_PDMSCAP_OSS) >>
27437a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_OSS);
27537a2d22bSVinod Koul }
27637a2d22bSVinod Koul 
27737a2d22bSVinod Koul static int
27837a2d22bSVinod Koul intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
27937a2d22bSVinod Koul {
28037a2d22bSVinod Koul 	void __iomem *shim = sdw->res->shim;
28137a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
28237a2d22bSVinod Koul 	int count;
28337a2d22bSVinod Koul 
28437a2d22bSVinod Koul 	if (pcm) {
28537a2d22bSVinod Koul 		count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
28637a2d22bSVinod Koul 	} else {
28737a2d22bSVinod Koul 		count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
28837a2d22bSVinod Koul 		count = ((count & SDW_SHIM_PDMSCAP_CPSS) >>
28937a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_CPSS));
29037a2d22bSVinod Koul 	}
29137a2d22bSVinod Koul 
29237a2d22bSVinod Koul 	/* zero based values for channel count in register */
29337a2d22bSVinod Koul 	count++;
29437a2d22bSVinod Koul 
29537a2d22bSVinod Koul 	return count;
29637a2d22bSVinod Koul }
29737a2d22bSVinod Koul 
29837a2d22bSVinod Koul static int intel_pdi_get_ch_update(struct sdw_intel *sdw,
29937a2d22bSVinod Koul 				   struct sdw_cdns_pdi *pdi,
30037a2d22bSVinod Koul 				   unsigned int num_pdi,
30137a2d22bSVinod Koul 				   unsigned int *num_ch, bool pcm)
30237a2d22bSVinod Koul {
30337a2d22bSVinod Koul 	int i, ch_count = 0;
30437a2d22bSVinod Koul 
30537a2d22bSVinod Koul 	for (i = 0; i < num_pdi; i++) {
30637a2d22bSVinod Koul 		pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm);
30737a2d22bSVinod Koul 		ch_count += pdi->ch_count;
30837a2d22bSVinod Koul 		pdi++;
30937a2d22bSVinod Koul 	}
31037a2d22bSVinod Koul 
31137a2d22bSVinod Koul 	*num_ch = ch_count;
31237a2d22bSVinod Koul 	return 0;
31337a2d22bSVinod Koul }
31437a2d22bSVinod Koul 
31537a2d22bSVinod Koul static int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
31637a2d22bSVinod Koul 				      struct sdw_cdns_streams *stream, bool pcm)
31737a2d22bSVinod Koul {
31837a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
31937a2d22bSVinod Koul 				&stream->num_ch_bd, pcm);
32037a2d22bSVinod Koul 
32137a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
32237a2d22bSVinod Koul 				&stream->num_ch_in, pcm);
32337a2d22bSVinod Koul 
32437a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
32537a2d22bSVinod Koul 				&stream->num_ch_out, pcm);
32637a2d22bSVinod Koul 
32737a2d22bSVinod Koul 	return 0;
32837a2d22bSVinod Koul }
32937a2d22bSVinod Koul 
33037a2d22bSVinod Koul static int intel_pdi_ch_update(struct sdw_intel *sdw)
33137a2d22bSVinod Koul {
33237a2d22bSVinod Koul 	/* First update PCM streams followed by PDM streams */
33337a2d22bSVinod Koul 	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true);
33437a2d22bSVinod Koul 	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false);
33537a2d22bSVinod Koul 
33637a2d22bSVinod Koul 	return 0;
33737a2d22bSVinod Koul }
33837a2d22bSVinod Koul 
33937a2d22bSVinod Koul static void
34037a2d22bSVinod Koul intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
34137a2d22bSVinod Koul {
34237a2d22bSVinod Koul 	void __iomem *shim = sdw->res->shim;
34337a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
34437a2d22bSVinod Koul 	int pdi_conf = 0;
34537a2d22bSVinod Koul 
34637a2d22bSVinod Koul 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 5;
34737a2d22bSVinod Koul 
34837a2d22bSVinod Koul 	/*
34937a2d22bSVinod Koul 	 * Program stream parameters to stream SHIM register
35037a2d22bSVinod Koul 	 * This is applicable for PCM stream only.
35137a2d22bSVinod Koul 	 */
35237a2d22bSVinod Koul 	if (pdi->type != SDW_STREAM_PCM)
35337a2d22bSVinod Koul 		return;
35437a2d22bSVinod Koul 
35537a2d22bSVinod Koul 	if (pdi->dir == SDW_DATA_DIR_RX)
35637a2d22bSVinod Koul 		pdi_conf |= SDW_SHIM_PCMSYCM_DIR;
35737a2d22bSVinod Koul 	else
35837a2d22bSVinod Koul 		pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR);
35937a2d22bSVinod Koul 
36037a2d22bSVinod Koul 	pdi_conf |= (pdi->intel_alh_id <<
36137a2d22bSVinod Koul 			SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_STREAM));
36237a2d22bSVinod Koul 	pdi_conf |= (pdi->l_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_LCHN));
36337a2d22bSVinod Koul 	pdi_conf |= (pdi->h_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_HCHN));
36437a2d22bSVinod Koul 
36537a2d22bSVinod Koul 	intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf);
36637a2d22bSVinod Koul }
36737a2d22bSVinod Koul 
36837a2d22bSVinod Koul static void
36937a2d22bSVinod Koul intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
37037a2d22bSVinod Koul {
37137a2d22bSVinod Koul 	void __iomem *alh = sdw->res->alh;
37237a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
37337a2d22bSVinod Koul 	unsigned int conf;
37437a2d22bSVinod Koul 
37537a2d22bSVinod Koul 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 5;
37637a2d22bSVinod Koul 
37737a2d22bSVinod Koul 	/* Program Stream config ALH register */
37837a2d22bSVinod Koul 	conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
37937a2d22bSVinod Koul 
38037a2d22bSVinod Koul 	conf |= (SDW_ALH_STRMZCFG_DMAT_VAL <<
38137a2d22bSVinod Koul 			SDW_REG_SHIFT(SDW_ALH_STRMZCFG_DMAT));
38237a2d22bSVinod Koul 
38337a2d22bSVinod Koul 	conf |= ((pdi->ch_count - 1) <<
38437a2d22bSVinod Koul 			SDW_REG_SHIFT(SDW_ALH_STRMZCFG_CHN));
38537a2d22bSVinod Koul 
38637a2d22bSVinod Koul 	intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
38737a2d22bSVinod Koul }
38837a2d22bSVinod Koul 
389c46302ecSVinod Koul static int intel_config_stream(struct sdw_intel *sdw,
390c46302ecSVinod Koul 			       struct snd_pcm_substream *substream,
391c46302ecSVinod Koul 			       struct snd_soc_dai *dai,
392c46302ecSVinod Koul 			       struct snd_pcm_hw_params *hw_params, int link_id)
393c46302ecSVinod Koul {
394c46302ecSVinod Koul 	if (sdw->res->ops && sdw->res->ops->config_stream)
395c46302ecSVinod Koul 		return sdw->res->ops->config_stream(sdw->res->arg,
396c46302ecSVinod Koul 				substream, dai, hw_params, link_id);
397c46302ecSVinod Koul 
398c46302ecSVinod Koul 	return -EIO;
399c46302ecSVinod Koul }
400c46302ecSVinod Koul 
401c46302ecSVinod Koul /*
40230246e2dSShreyas NC  * bank switch routines
40330246e2dSShreyas NC  */
40430246e2dSShreyas NC 
40530246e2dSShreyas NC static int intel_pre_bank_switch(struct sdw_bus *bus)
40630246e2dSShreyas NC {
40730246e2dSShreyas NC 	struct sdw_cdns *cdns = bus_to_cdns(bus);
40830246e2dSShreyas NC 	struct sdw_intel *sdw = cdns_to_intel(cdns);
40930246e2dSShreyas NC 	void __iomem *shim = sdw->res->shim;
41030246e2dSShreyas NC 	int sync_reg;
41130246e2dSShreyas NC 
41230246e2dSShreyas NC 	/* Write to register only for multi-link */
41330246e2dSShreyas NC 	if (!bus->multi_link)
41430246e2dSShreyas NC 		return 0;
41530246e2dSShreyas NC 
41630246e2dSShreyas NC 	/* Read SYNC register */
41730246e2dSShreyas NC 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
41830246e2dSShreyas NC 	sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance;
41930246e2dSShreyas NC 	intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
42030246e2dSShreyas NC 
42130246e2dSShreyas NC 	return 0;
42230246e2dSShreyas NC }
42330246e2dSShreyas NC 
42430246e2dSShreyas NC static int intel_post_bank_switch(struct sdw_bus *bus)
42530246e2dSShreyas NC {
42630246e2dSShreyas NC 	struct sdw_cdns *cdns = bus_to_cdns(bus);
42730246e2dSShreyas NC 	struct sdw_intel *sdw = cdns_to_intel(cdns);
42830246e2dSShreyas NC 	void __iomem *shim = sdw->res->shim;
42930246e2dSShreyas NC 	int sync_reg, ret;
43030246e2dSShreyas NC 
43130246e2dSShreyas NC 	/* Write to register only for multi-link */
43230246e2dSShreyas NC 	if (!bus->multi_link)
43330246e2dSShreyas NC 		return 0;
43430246e2dSShreyas NC 
43530246e2dSShreyas NC 	/* Read SYNC register */
43630246e2dSShreyas NC 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
43730246e2dSShreyas NC 
43830246e2dSShreyas NC 	/*
43930246e2dSShreyas NC 	 * post_bank_switch() ops is called from the bus in loop for
44030246e2dSShreyas NC 	 * all the Masters in the steam with the expectation that
44130246e2dSShreyas NC 	 * we trigger the bankswitch for the only first Master in the list
44230246e2dSShreyas NC 	 * and do nothing for the other Masters
44330246e2dSShreyas NC 	 *
44430246e2dSShreyas NC 	 * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
44530246e2dSShreyas NC 	 */
44630246e2dSShreyas NC 	if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK))
44730246e2dSShreyas NC 		return 0;
44830246e2dSShreyas NC 
44930246e2dSShreyas NC 	/*
45030246e2dSShreyas NC 	 * Set SyncGO bit to synchronously trigger a bank switch for
45130246e2dSShreyas NC 	 * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
45230246e2dSShreyas NC 	 * the Masters.
45330246e2dSShreyas NC 	 */
45430246e2dSShreyas NC 	sync_reg |= SDW_SHIM_SYNC_SYNCGO;
45530246e2dSShreyas NC 
45630246e2dSShreyas NC 	ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
45730246e2dSShreyas NC 			      SDW_SHIM_SYNC_SYNCGO);
45830246e2dSShreyas NC 	if (ret < 0)
45930246e2dSShreyas NC 		dev_err(sdw->cdns.dev, "Post bank switch failed: %d", ret);
46030246e2dSShreyas NC 
46130246e2dSShreyas NC 	return ret;
46230246e2dSShreyas NC }
46330246e2dSShreyas NC 
46430246e2dSShreyas NC /*
465c46302ecSVinod Koul  * DAI routines
466c46302ecSVinod Koul  */
467c46302ecSVinod Koul 
468c46302ecSVinod Koul static struct sdw_cdns_port *intel_alloc_port(struct sdw_intel *sdw,
469c46302ecSVinod Koul 					      u32 ch, u32 dir, bool pcm)
470c46302ecSVinod Koul {
471c46302ecSVinod Koul 	struct sdw_cdns *cdns = &sdw->cdns;
472c46302ecSVinod Koul 	struct sdw_cdns_port *port = NULL;
473c46302ecSVinod Koul 	int i, ret = 0;
474c46302ecSVinod Koul 
475c46302ecSVinod Koul 	for (i = 0; i < cdns->num_ports; i++) {
476*2890a636SPierre-Louis Bossart 		if (cdns->ports[i].assigned)
477c46302ecSVinod Koul 			continue;
478c46302ecSVinod Koul 
479c46302ecSVinod Koul 		port = &cdns->ports[i];
480c46302ecSVinod Koul 		port->assigned = true;
481c46302ecSVinod Koul 		port->direction = dir;
482c46302ecSVinod Koul 		port->ch = ch;
483c46302ecSVinod Koul 		break;
484c46302ecSVinod Koul 	}
485c46302ecSVinod Koul 
486c46302ecSVinod Koul 	if (!port) {
487c46302ecSVinod Koul 		dev_err(cdns->dev, "Unable to find a free port\n");
488c46302ecSVinod Koul 		return NULL;
489c46302ecSVinod Koul 	}
490c46302ecSVinod Koul 
491c46302ecSVinod Koul 	if (pcm) {
492c46302ecSVinod Koul 		ret = sdw_cdns_alloc_stream(cdns, &cdns->pcm, port, ch, dir);
493c46302ecSVinod Koul 		if (ret)
494c46302ecSVinod Koul 			goto out;
495c46302ecSVinod Koul 
496c46302ecSVinod Koul 		intel_pdi_shim_configure(sdw, port->pdi);
497c46302ecSVinod Koul 		sdw_cdns_config_stream(cdns, port, ch, dir, port->pdi);
498c46302ecSVinod Koul 
499c46302ecSVinod Koul 		intel_pdi_alh_configure(sdw, port->pdi);
500c46302ecSVinod Koul 
501c46302ecSVinod Koul 	} else {
502c46302ecSVinod Koul 		ret = sdw_cdns_alloc_stream(cdns, &cdns->pdm, port, ch, dir);
503c46302ecSVinod Koul 	}
504c46302ecSVinod Koul 
505c46302ecSVinod Koul out:
506c46302ecSVinod Koul 	if (ret) {
507c46302ecSVinod Koul 		port->assigned = false;
508c46302ecSVinod Koul 		port = NULL;
509c46302ecSVinod Koul 	}
510c46302ecSVinod Koul 
511c46302ecSVinod Koul 	return port;
512c46302ecSVinod Koul }
513c46302ecSVinod Koul 
514c46302ecSVinod Koul static void intel_port_cleanup(struct sdw_cdns_dma_data *dma)
515c46302ecSVinod Koul {
516c46302ecSVinod Koul 	int i;
517c46302ecSVinod Koul 
518c46302ecSVinod Koul 	for (i = 0; i < dma->nr_ports; i++) {
519c46302ecSVinod Koul 		if (dma->port[i]) {
520c46302ecSVinod Koul 			dma->port[i]->pdi->assigned = false;
521c46302ecSVinod Koul 			dma->port[i]->pdi = NULL;
522c46302ecSVinod Koul 			dma->port[i]->assigned = false;
523c46302ecSVinod Koul 			dma->port[i] = NULL;
524c46302ecSVinod Koul 		}
525c46302ecSVinod Koul 	}
526c46302ecSVinod Koul }
527c46302ecSVinod Koul 
528c46302ecSVinod Koul static int intel_hw_params(struct snd_pcm_substream *substream,
529c46302ecSVinod Koul 			   struct snd_pcm_hw_params *params,
530c46302ecSVinod Koul 			   struct snd_soc_dai *dai)
531c46302ecSVinod Koul {
532c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
533c46302ecSVinod Koul 	struct sdw_intel *sdw = cdns_to_intel(cdns);
534c46302ecSVinod Koul 	struct sdw_cdns_dma_data *dma;
535c46302ecSVinod Koul 	struct sdw_stream_config sconfig;
536c46302ecSVinod Koul 	struct sdw_port_config *pconfig;
537c46302ecSVinod Koul 	int ret, i, ch, dir;
538c46302ecSVinod Koul 	bool pcm = true;
539c46302ecSVinod Koul 
540c46302ecSVinod Koul 	dma = snd_soc_dai_get_dma_data(dai, substream);
541c46302ecSVinod Koul 	if (!dma)
542c46302ecSVinod Koul 		return -EIO;
543c46302ecSVinod Koul 
544c46302ecSVinod Koul 	ch = params_channels(params);
545c46302ecSVinod Koul 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
546c46302ecSVinod Koul 		dir = SDW_DATA_DIR_RX;
547c46302ecSVinod Koul 	else
548c46302ecSVinod Koul 		dir = SDW_DATA_DIR_TX;
549c46302ecSVinod Koul 
550c46302ecSVinod Koul 	if (dma->stream_type == SDW_STREAM_PDM) {
551c46302ecSVinod Koul 		/* TODO: Check whether PDM decimator is already in use */
552c46302ecSVinod Koul 		dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pdm, ch, dir);
553c46302ecSVinod Koul 		pcm = false;
554c46302ecSVinod Koul 	} else {
555c46302ecSVinod Koul 		dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pcm, ch, dir);
556c46302ecSVinod Koul 	}
557c46302ecSVinod Koul 
558c46302ecSVinod Koul 	if (!dma->nr_ports) {
559c46302ecSVinod Koul 		dev_err(dai->dev, "ports/resources not available");
560c46302ecSVinod Koul 		return -EINVAL;
561c46302ecSVinod Koul 	}
562c46302ecSVinod Koul 
563c46302ecSVinod Koul 	dma->port = kcalloc(dma->nr_ports, sizeof(*dma->port), GFP_KERNEL);
564c46302ecSVinod Koul 	if (!dma->port)
565c46302ecSVinod Koul 		return -ENOMEM;
566c46302ecSVinod Koul 
567c46302ecSVinod Koul 	for (i = 0; i < dma->nr_ports; i++) {
568c46302ecSVinod Koul 		dma->port[i] = intel_alloc_port(sdw, ch, dir, pcm);
569c46302ecSVinod Koul 		if (!dma->port[i]) {
570c46302ecSVinod Koul 			ret = -EINVAL;
571c46302ecSVinod Koul 			goto port_error;
572c46302ecSVinod Koul 		}
573c46302ecSVinod Koul 	}
574c46302ecSVinod Koul 
575c46302ecSVinod Koul 	/* Inform DSP about PDI stream number */
576c46302ecSVinod Koul 	for (i = 0; i < dma->nr_ports; i++) {
577c46302ecSVinod Koul 		ret = intel_config_stream(sdw, substream, dai, params,
578c46302ecSVinod Koul 					  dma->port[i]->pdi->intel_alh_id);
579c46302ecSVinod Koul 		if (ret)
580c46302ecSVinod Koul 			goto port_error;
581c46302ecSVinod Koul 	}
582c46302ecSVinod Koul 
583c46302ecSVinod Koul 	sconfig.direction = dir;
584c46302ecSVinod Koul 	sconfig.ch_count = ch;
585c46302ecSVinod Koul 	sconfig.frame_rate = params_rate(params);
586c46302ecSVinod Koul 	sconfig.type = dma->stream_type;
587c46302ecSVinod Koul 
588c46302ecSVinod Koul 	if (dma->stream_type == SDW_STREAM_PDM) {
589c46302ecSVinod Koul 		sconfig.frame_rate *= 50;
590c46302ecSVinod Koul 		sconfig.bps = 1;
591c46302ecSVinod Koul 	} else {
592c46302ecSVinod Koul 		sconfig.bps = snd_pcm_format_width(params_format(params));
593c46302ecSVinod Koul 	}
594c46302ecSVinod Koul 
595c46302ecSVinod Koul 	/* Port configuration */
596c46302ecSVinod Koul 	pconfig = kcalloc(dma->nr_ports, sizeof(*pconfig), GFP_KERNEL);
597c46302ecSVinod Koul 	if (!pconfig) {
598c46302ecSVinod Koul 		ret =  -ENOMEM;
599c46302ecSVinod Koul 		goto port_error;
600c46302ecSVinod Koul 	}
601c46302ecSVinod Koul 
602c46302ecSVinod Koul 	for (i = 0; i < dma->nr_ports; i++) {
603c46302ecSVinod Koul 		pconfig[i].num = dma->port[i]->num;
604c46302ecSVinod Koul 		pconfig[i].ch_mask = (1 << ch) - 1;
605c46302ecSVinod Koul 	}
606c46302ecSVinod Koul 
607c46302ecSVinod Koul 	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
608c46302ecSVinod Koul 				    pconfig, dma->nr_ports, dma->stream);
609c46302ecSVinod Koul 	if (ret) {
610c46302ecSVinod Koul 		dev_err(cdns->dev, "add master to stream failed:%d", ret);
611c46302ecSVinod Koul 		goto stream_error;
612c46302ecSVinod Koul 	}
613c46302ecSVinod Koul 
614c46302ecSVinod Koul 	kfree(pconfig);
615c46302ecSVinod Koul 	return ret;
616c46302ecSVinod Koul 
617c46302ecSVinod Koul stream_error:
618c46302ecSVinod Koul 	kfree(pconfig);
619c46302ecSVinod Koul port_error:
620c46302ecSVinod Koul 	intel_port_cleanup(dma);
621c46302ecSVinod Koul 	kfree(dma->port);
622c46302ecSVinod Koul 	return ret;
623c46302ecSVinod Koul }
624c46302ecSVinod Koul 
625c46302ecSVinod Koul static int
626c46302ecSVinod Koul intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
627c46302ecSVinod Koul {
628c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
629c46302ecSVinod Koul 	struct sdw_cdns_dma_data *dma;
630c46302ecSVinod Koul 	int ret;
631c46302ecSVinod Koul 
632c46302ecSVinod Koul 	dma = snd_soc_dai_get_dma_data(dai, substream);
633c46302ecSVinod Koul 	if (!dma)
634c46302ecSVinod Koul 		return -EIO;
635c46302ecSVinod Koul 
636c46302ecSVinod Koul 	ret = sdw_stream_remove_master(&cdns->bus, dma->stream);
637c46302ecSVinod Koul 	if (ret < 0)
638c46302ecSVinod Koul 		dev_err(dai->dev, "remove master from stream %s failed: %d",
639c46302ecSVinod Koul 			dma->stream->name, ret);
640c46302ecSVinod Koul 
641c46302ecSVinod Koul 	intel_port_cleanup(dma);
642c46302ecSVinod Koul 	kfree(dma->port);
643c46302ecSVinod Koul 	return ret;
644c46302ecSVinod Koul }
645c46302ecSVinod Koul 
646c46302ecSVinod Koul static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
647c46302ecSVinod Koul 				    void *stream, int direction)
648c46302ecSVinod Koul {
649c46302ecSVinod Koul 	return cdns_set_sdw_stream(dai, stream, true, direction);
650c46302ecSVinod Koul }
651c46302ecSVinod Koul 
652c46302ecSVinod Koul static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
653c46302ecSVinod Koul 				    void *stream, int direction)
654c46302ecSVinod Koul {
655c46302ecSVinod Koul 	return cdns_set_sdw_stream(dai, stream, false, direction);
656c46302ecSVinod Koul }
657c46302ecSVinod Koul 
658b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
659c46302ecSVinod Koul 	.hw_params = intel_hw_params,
660c46302ecSVinod Koul 	.hw_free = intel_hw_free,
661c46302ecSVinod Koul 	.shutdown = sdw_cdns_shutdown,
662c46302ecSVinod Koul 	.set_sdw_stream = intel_pcm_set_sdw_stream,
663c46302ecSVinod Koul };
664c46302ecSVinod Koul 
665b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
666c46302ecSVinod Koul 	.hw_params = intel_hw_params,
667c46302ecSVinod Koul 	.hw_free = intel_hw_free,
668c46302ecSVinod Koul 	.shutdown = sdw_cdns_shutdown,
669c46302ecSVinod Koul 	.set_sdw_stream = intel_pdm_set_sdw_stream,
670c46302ecSVinod Koul };
671c46302ecSVinod Koul 
672c46302ecSVinod Koul static const struct snd_soc_component_driver dai_component = {
673c46302ecSVinod Koul 	.name           = "soundwire",
674c46302ecSVinod Koul };
675c46302ecSVinod Koul 
676c46302ecSVinod Koul static int intel_create_dai(struct sdw_cdns *cdns,
677c46302ecSVinod Koul 			    struct snd_soc_dai_driver *dais,
678c46302ecSVinod Koul 			    enum intel_pdi_type type,
679c46302ecSVinod Koul 			    u32 num, u32 off, u32 max_ch, bool pcm)
680c46302ecSVinod Koul {
681c46302ecSVinod Koul 	int i;
682c46302ecSVinod Koul 
683c46302ecSVinod Koul 	if (num == 0)
684c46302ecSVinod Koul 		return 0;
685c46302ecSVinod Koul 
686c46302ecSVinod Koul 	 /* TODO: Read supported rates/formats from hardware */
687c46302ecSVinod Koul 	for (i = off; i < (off + num); i++) {
688c46302ecSVinod Koul 		dais[i].name = kasprintf(GFP_KERNEL, "SDW%d Pin%d",
689c46302ecSVinod Koul 					 cdns->instance, i);
690c46302ecSVinod Koul 		if (!dais[i].name)
691c46302ecSVinod Koul 			return -ENOMEM;
692c46302ecSVinod Koul 
693c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
694c46302ecSVinod Koul 			dais[i].playback.stream_name = kasprintf(GFP_KERNEL,
695c46302ecSVinod Koul 							"SDW%d Tx%d",
696c46302ecSVinod Koul 							cdns->instance, i);
697c46302ecSVinod Koul 			if (!dais[i].playback.stream_name) {
698c46302ecSVinod Koul 				kfree(dais[i].name);
699c46302ecSVinod Koul 				return -ENOMEM;
700c46302ecSVinod Koul 			}
701c46302ecSVinod Koul 
702c46302ecSVinod Koul 			dais[i].playback.channels_min = 1;
703c46302ecSVinod Koul 			dais[i].playback.channels_max = max_ch;
704c46302ecSVinod Koul 			dais[i].playback.rates = SNDRV_PCM_RATE_48000;
705c46302ecSVinod Koul 			dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
706c46302ecSVinod Koul 		}
707c46302ecSVinod Koul 
708c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
709c46302ecSVinod Koul 			dais[i].capture.stream_name = kasprintf(GFP_KERNEL,
710c46302ecSVinod Koul 							"SDW%d Rx%d",
711c46302ecSVinod Koul 							cdns->instance, i);
712c46302ecSVinod Koul 			if (!dais[i].capture.stream_name) {
713c46302ecSVinod Koul 				kfree(dais[i].name);
714c46302ecSVinod Koul 				kfree(dais[i].playback.stream_name);
715c46302ecSVinod Koul 				return -ENOMEM;
716c46302ecSVinod Koul 			}
717c46302ecSVinod Koul 
718c46302ecSVinod Koul 			dais[i].playback.channels_min = 1;
719c46302ecSVinod Koul 			dais[i].playback.channels_max = max_ch;
720c46302ecSVinod Koul 			dais[i].capture.rates = SNDRV_PCM_RATE_48000;
721c46302ecSVinod Koul 			dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
722c46302ecSVinod Koul 		}
723c46302ecSVinod Koul 
724c46302ecSVinod Koul 		dais[i].id = SDW_DAI_ID_RANGE_START + i;
725c46302ecSVinod Koul 
726c46302ecSVinod Koul 		if (pcm)
727c46302ecSVinod Koul 			dais[i].ops = &intel_pcm_dai_ops;
728c46302ecSVinod Koul 		else
729c46302ecSVinod Koul 			dais[i].ops = &intel_pdm_dai_ops;
730c46302ecSVinod Koul 	}
731c46302ecSVinod Koul 
732c46302ecSVinod Koul 	return 0;
733c46302ecSVinod Koul }
734c46302ecSVinod Koul 
735c46302ecSVinod Koul static int intel_register_dai(struct sdw_intel *sdw)
736c46302ecSVinod Koul {
737c46302ecSVinod Koul 	struct sdw_cdns *cdns = &sdw->cdns;
738c46302ecSVinod Koul 	struct sdw_cdns_streams *stream;
739c46302ecSVinod Koul 	struct snd_soc_dai_driver *dais;
740c46302ecSVinod Koul 	int num_dai, ret, off = 0;
741c46302ecSVinod Koul 
742c46302ecSVinod Koul 	/* DAIs are created based on total number of PDIs supported */
743c46302ecSVinod Koul 	num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi;
744c46302ecSVinod Koul 
745c46302ecSVinod Koul 	dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
746c46302ecSVinod Koul 	if (!dais)
747c46302ecSVinod Koul 		return -ENOMEM;
748c46302ecSVinod Koul 
749c46302ecSVinod Koul 	/* Create PCM DAIs */
750c46302ecSVinod Koul 	stream = &cdns->pcm;
751c46302ecSVinod Koul 
752c46302ecSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN,
753c46302ecSVinod Koul 			stream->num_in, off, stream->num_ch_in, true);
754c46302ecSVinod Koul 	if (ret)
755c46302ecSVinod Koul 		return ret;
756c46302ecSVinod Koul 
757c46302ecSVinod Koul 	off += cdns->pcm.num_in;
758c46302ecSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT,
759c46302ecSVinod Koul 			cdns->pcm.num_out, off, stream->num_ch_out, true);
760c46302ecSVinod Koul 	if (ret)
761c46302ecSVinod Koul 		return ret;
762c46302ecSVinod Koul 
763c46302ecSVinod Koul 	off += cdns->pcm.num_out;
764c46302ecSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD,
765c46302ecSVinod Koul 			cdns->pcm.num_bd, off, stream->num_ch_bd, true);
766c46302ecSVinod Koul 	if (ret)
767c46302ecSVinod Koul 		return ret;
768c46302ecSVinod Koul 
769c46302ecSVinod Koul 	/* Create PDM DAIs */
770c46302ecSVinod Koul 	stream = &cdns->pdm;
771c46302ecSVinod Koul 	off += cdns->pcm.num_bd;
772c46302ecSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN,
773c46302ecSVinod Koul 			cdns->pdm.num_in, off, stream->num_ch_in, false);
774c46302ecSVinod Koul 	if (ret)
775c46302ecSVinod Koul 		return ret;
776c46302ecSVinod Koul 
777c46302ecSVinod Koul 	off += cdns->pdm.num_in;
778c46302ecSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT,
779c46302ecSVinod Koul 			cdns->pdm.num_out, off, stream->num_ch_out, false);
780c46302ecSVinod Koul 	if (ret)
781c46302ecSVinod Koul 		return ret;
782c46302ecSVinod Koul 
783c46302ecSVinod Koul 	off += cdns->pdm.num_bd;
784c46302ecSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD,
785c46302ecSVinod Koul 			cdns->pdm.num_bd, off, stream->num_ch_bd, false);
786c46302ecSVinod Koul 	if (ret)
787c46302ecSVinod Koul 		return ret;
788c46302ecSVinod Koul 
789c46302ecSVinod Koul 	return snd_soc_register_component(cdns->dev, &dai_component,
790c46302ecSVinod Koul 					  dais, num_dai);
791c46302ecSVinod Koul }
792c46302ecSVinod Koul 
79371bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus)
79471bb8a1bSVinod Koul {
79571bb8a1bSVinod Koul 	/* Initialize with default handler to read all DisCo properties */
79671bb8a1bSVinod Koul 	sdw_master_read_prop(bus);
79771bb8a1bSVinod Koul 
79871bb8a1bSVinod Koul 	/* BIOS is not giving some values correctly. So, lets override them */
79971bb8a1bSVinod Koul 	bus->prop.num_freq = 1;
8004ac5627aSPierre-Louis Bossart 	bus->prop.freq = devm_kcalloc(bus->dev, bus->prop.num_freq,
8014ac5627aSPierre-Louis Bossart 				      sizeof(*bus->prop.freq), GFP_KERNEL);
80271bb8a1bSVinod Koul 	if (!bus->prop.freq)
80371bb8a1bSVinod Koul 		return -ENOMEM;
80471bb8a1bSVinod Koul 
80571bb8a1bSVinod Koul 	bus->prop.freq[0] = bus->prop.max_freq;
80671bb8a1bSVinod Koul 	bus->prop.err_threshold = 5;
80771bb8a1bSVinod Koul 
80871bb8a1bSVinod Koul 	return 0;
80971bb8a1bSVinod Koul }
81071bb8a1bSVinod Koul 
811c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = {
812c91605f4SShreyas NC 	.read_prop = sdw_master_read_prop,
813c91605f4SShreyas NC 	.xfer_msg = cdns_xfer_msg,
814c91605f4SShreyas NC 	.xfer_msg_defer = cdns_xfer_msg_defer,
815c91605f4SShreyas NC 	.reset_page_addr = cdns_reset_page_addr,
81607abeff1SVinod Koul 	.set_bus_conf = cdns_bus_conf,
81730246e2dSShreyas NC 	.pre_bank_switch = intel_pre_bank_switch,
81830246e2dSShreyas NC 	.post_bank_switch = intel_post_bank_switch,
819c91605f4SShreyas NC };
820c91605f4SShreyas NC 
82171bb8a1bSVinod Koul /*
82271bb8a1bSVinod Koul  * probe and init
82371bb8a1bSVinod Koul  */
82471bb8a1bSVinod Koul static int intel_probe(struct platform_device *pdev)
82571bb8a1bSVinod Koul {
82637a2d22bSVinod Koul 	struct sdw_cdns_stream_config config;
82771bb8a1bSVinod Koul 	struct sdw_intel *sdw;
82871bb8a1bSVinod Koul 	int ret;
82971bb8a1bSVinod Koul 
83071bb8a1bSVinod Koul 	sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL);
83171bb8a1bSVinod Koul 	if (!sdw)
83271bb8a1bSVinod Koul 		return -ENOMEM;
83371bb8a1bSVinod Koul 
83471bb8a1bSVinod Koul 	sdw->instance = pdev->id;
83571bb8a1bSVinod Koul 	sdw->res = dev_get_platdata(&pdev->dev);
83671bb8a1bSVinod Koul 	sdw->cdns.dev = &pdev->dev;
83771bb8a1bSVinod Koul 	sdw->cdns.registers = sdw->res->registers;
83871bb8a1bSVinod Koul 	sdw->cdns.instance = sdw->instance;
83971bb8a1bSVinod Koul 	sdw->cdns.msg_count = 0;
84071bb8a1bSVinod Koul 	sdw->cdns.bus.dev = &pdev->dev;
84171bb8a1bSVinod Koul 	sdw->cdns.bus.link_id = pdev->id;
84271bb8a1bSVinod Koul 
84371bb8a1bSVinod Koul 	sdw_cdns_probe(&sdw->cdns);
84471bb8a1bSVinod Koul 
84571bb8a1bSVinod Koul 	/* Set property read ops */
846c91605f4SShreyas NC 	sdw_intel_ops.read_prop = intel_prop_read;
847c91605f4SShreyas NC 	sdw->cdns.bus.ops = &sdw_intel_ops;
84871bb8a1bSVinod Koul 
84971bb8a1bSVinod Koul 	platform_set_drvdata(pdev, sdw);
85071bb8a1bSVinod Koul 
85171bb8a1bSVinod Koul 	ret = sdw_add_bus_master(&sdw->cdns.bus);
85271bb8a1bSVinod Koul 	if (ret) {
85371bb8a1bSVinod Koul 		dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret);
85471bb8a1bSVinod Koul 		goto err_master_reg;
85571bb8a1bSVinod Koul 	}
85671bb8a1bSVinod Koul 
85771bb8a1bSVinod Koul 	/* Initialize shim and controller */
85871bb8a1bSVinod Koul 	intel_link_power_up(sdw);
85971bb8a1bSVinod Koul 	intel_shim_init(sdw);
86071bb8a1bSVinod Koul 
86171bb8a1bSVinod Koul 	ret = sdw_cdns_init(&sdw->cdns);
86271bb8a1bSVinod Koul 	if (ret)
86371bb8a1bSVinod Koul 		goto err_init;
86471bb8a1bSVinod Koul 
8657094dc2bSColin Ian King 	ret = sdw_cdns_enable_interrupt(&sdw->cdns);
86637a2d22bSVinod Koul 
86737a2d22bSVinod Koul 	/* Read the PDI config and initialize cadence PDI */
86837a2d22bSVinod Koul 	intel_pdi_init(sdw, &config);
86937a2d22bSVinod Koul 	ret = sdw_cdns_pdi_init(&sdw->cdns, config);
87071bb8a1bSVinod Koul 	if (ret)
87171bb8a1bSVinod Koul 		goto err_init;
87271bb8a1bSVinod Koul 
87337a2d22bSVinod Koul 	intel_pdi_ch_update(sdw);
87437a2d22bSVinod Koul 
87571bb8a1bSVinod Koul 	/* Acquire IRQ */
876d542bc9eSPierre-Louis Bossart 	ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, sdw_cdns_thread,
877d542bc9eSPierre-Louis Bossart 				   IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns);
87871bb8a1bSVinod Koul 	if (ret < 0) {
87971bb8a1bSVinod Koul 		dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n",
88071bb8a1bSVinod Koul 			sdw->res->irq);
88171bb8a1bSVinod Koul 		goto err_init;
88271bb8a1bSVinod Koul 	}
88371bb8a1bSVinod Koul 
884c46302ecSVinod Koul 	/* Register DAIs */
885c46302ecSVinod Koul 	ret = intel_register_dai(sdw);
886c46302ecSVinod Koul 	if (ret) {
887c46302ecSVinod Koul 		dev_err(sdw->cdns.dev, "DAI registration failed: %d", ret);
888c46302ecSVinod Koul 		snd_soc_unregister_component(sdw->cdns.dev);
889c46302ecSVinod Koul 		goto err_dai;
890c46302ecSVinod Koul 	}
891c46302ecSVinod Koul 
89271bb8a1bSVinod Koul 	return 0;
89371bb8a1bSVinod Koul 
894c46302ecSVinod Koul err_dai:
895c46302ecSVinod Koul 	free_irq(sdw->res->irq, sdw);
89671bb8a1bSVinod Koul err_init:
89771bb8a1bSVinod Koul 	sdw_delete_bus_master(&sdw->cdns.bus);
89871bb8a1bSVinod Koul err_master_reg:
89971bb8a1bSVinod Koul 	return ret;
90071bb8a1bSVinod Koul }
90171bb8a1bSVinod Koul 
90271bb8a1bSVinod Koul static int intel_remove(struct platform_device *pdev)
90371bb8a1bSVinod Koul {
90471bb8a1bSVinod Koul 	struct sdw_intel *sdw;
90571bb8a1bSVinod Koul 
90671bb8a1bSVinod Koul 	sdw = platform_get_drvdata(pdev);
90771bb8a1bSVinod Koul 
90871bb8a1bSVinod Koul 	free_irq(sdw->res->irq, sdw);
909c46302ecSVinod Koul 	snd_soc_unregister_component(sdw->cdns.dev);
91071bb8a1bSVinod Koul 	sdw_delete_bus_master(&sdw->cdns.bus);
91171bb8a1bSVinod Koul 
91271bb8a1bSVinod Koul 	return 0;
91371bb8a1bSVinod Koul }
91471bb8a1bSVinod Koul 
91571bb8a1bSVinod Koul static struct platform_driver sdw_intel_drv = {
91671bb8a1bSVinod Koul 	.probe = intel_probe,
91771bb8a1bSVinod Koul 	.remove = intel_remove,
91871bb8a1bSVinod Koul 	.driver = {
91971bb8a1bSVinod Koul 		.name = "int-sdw",
92071bb8a1bSVinod Koul 
92171bb8a1bSVinod Koul 	},
92271bb8a1bSVinod Koul };
92371bb8a1bSVinod Koul 
92471bb8a1bSVinod Koul module_platform_driver(sdw_intel_drv);
92571bb8a1bSVinod Koul 
92671bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL");
92771bb8a1bSVinod Koul MODULE_ALIAS("platform:int-sdw");
92871bb8a1bSVinod Koul MODULE_DESCRIPTION("Intel Soundwire Master Driver");
929