xref: /openbmc/linux/sound/soc/pxa/pxa-ssp.c (revision 14f6863328164a9e66024bce5f2fa27de7dc00f0)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21b340bd7SMark Brown /*
31b340bd7SMark Brown  * pxa-ssp.c  --  ALSA Soc Audio Layer
41b340bd7SMark Brown  *
51b340bd7SMark Brown  * Copyright 2005,2008 Wolfson Microelectronics PLC.
61b340bd7SMark Brown  * Author: Liam Girdwood
71b340bd7SMark Brown  *         Mark Brown <broonie@opensource.wolfsonmicro.com>
81b340bd7SMark Brown  *
91b340bd7SMark Brown  * TODO:
101b340bd7SMark Brown  *  o Test network mode for > 16bit sample size
111b340bd7SMark Brown  */
121b340bd7SMark Brown 
131b340bd7SMark Brown #include <linux/init.h>
141b340bd7SMark Brown #include <linux/module.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
161b340bd7SMark Brown #include <linux/platform_device.h>
171b340bd7SMark Brown #include <linux/clk.h>
181b340bd7SMark Brown #include <linux/io.h>
198348c259SSebastian Andrzej Siewior #include <linux/pxa2xx_ssp.h>
202023c90cSDaniel Mack #include <linux/of.h>
21d65a1458SDaniel Mack #include <linux/dmaengine.h>
221b340bd7SMark Brown 
230664678aSPhilipp Zabel #include <asm/irq.h>
240664678aSPhilipp Zabel 
251b340bd7SMark Brown #include <sound/core.h>
261b340bd7SMark Brown #include <sound/pcm.h>
271b340bd7SMark Brown #include <sound/initval.h>
281b340bd7SMark Brown #include <sound/pcm_params.h>
291b340bd7SMark Brown #include <sound/soc.h>
301b340bd7SMark Brown #include <sound/pxa2xx-lib.h>
31d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h>
321b340bd7SMark Brown 
331b340bd7SMark Brown #include "pxa-ssp.h"
341b340bd7SMark Brown 
351b340bd7SMark Brown /*
361b340bd7SMark Brown  * SSP audio private data
371b340bd7SMark Brown  */
381b340bd7SMark Brown struct ssp_priv {
39f9efc9dfSEric Miao 	struct ssp_device *ssp;
4090eb6b59SDaniel Mack 	struct clk *extclk;
4105739375SDaniel Mack 	unsigned long ssp_clk;
421b340bd7SMark Brown 	unsigned int sysclk;
43737e370aSDaniel Mack 	unsigned int dai_fmt;
44737e370aSDaniel Mack 	unsigned int configured_dai_fmt;
451b340bd7SMark Brown #ifdef CONFIG_PM
46f9efc9dfSEric Miao 	uint32_t	cr0;
47f9efc9dfSEric Miao 	uint32_t	cr1;
48f9efc9dfSEric Miao 	uint32_t	to;
49f9efc9dfSEric Miao 	uint32_t	psp;
501b340bd7SMark Brown #endif
511b340bd7SMark Brown };
521b340bd7SMark Brown 
dump_registers(struct ssp_device * ssp)531b340bd7SMark Brown static void dump_registers(struct ssp_device *ssp)
541b340bd7SMark Brown {
554f3d9577SAndy Shevchenko 	dev_dbg(ssp->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
56baffe169SHaojian Zhuang 		 pxa_ssp_read_reg(ssp, SSCR0), pxa_ssp_read_reg(ssp, SSCR1),
57baffe169SHaojian Zhuang 		 pxa_ssp_read_reg(ssp, SSTO));
581b340bd7SMark Brown 
594f3d9577SAndy Shevchenko 	dev_dbg(ssp->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n",
60baffe169SHaojian Zhuang 		 pxa_ssp_read_reg(ssp, SSPSP), pxa_ssp_read_reg(ssp, SSSR),
61baffe169SHaojian Zhuang 		 pxa_ssp_read_reg(ssp, SSACD));
621b340bd7SMark Brown }
631b340bd7SMark Brown 
pxa_ssp_set_dma_params(struct ssp_device * ssp,int width4,int out,struct snd_dmaengine_dai_dma_data * dma)64d93ca1aeSguoyh static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4,
65d65a1458SDaniel Mack 			int out, struct snd_dmaengine_dai_dma_data *dma)
662d7e71faSEric Miao {
67d65a1458SDaniel Mack 	dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES :
68d65a1458SDaniel Mack 				   DMA_SLAVE_BUSWIDTH_2_BYTES;
69d65a1458SDaniel Mack 	dma->maxburst = 16;
70d65a1458SDaniel Mack 	dma->addr = ssp->phys_base + SSDR;
712d7e71faSEric Miao }
722d7e71faSEric Miao 
pxa_ssp_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)73dee89c4dSMark Brown static int pxa_ssp_startup(struct snd_pcm_substream *substream,
74f0fba2adSLiam Girdwood 			   struct snd_soc_dai *cpu_dai)
751b340bd7SMark Brown {
76f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
77f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
78d65a1458SDaniel Mack 	struct snd_dmaengine_dai_dma_data *dma;
791b340bd7SMark Brown 	int ret = 0;
801b340bd7SMark Brown 
81aaeb5fb5SKuninori Morimoto 	if (!snd_soc_dai_active(cpu_dai)) {
826d3efa40SDmitry Eremin-Solenikov 		clk_prepare_enable(ssp->clk);
83baffe169SHaojian Zhuang 		pxa_ssp_disable(ssp);
841b340bd7SMark Brown 	}
852d7e71faSEric Miao 
86cfe9ee5fSDaniel Mack 	clk_prepare_enable(priv->extclk);
87cfe9ee5fSDaniel Mack 
88d65a1458SDaniel Mack 	dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL);
89d93ca1aeSguoyh 	if (!dma)
90d93ca1aeSguoyh 		return -ENOMEM;
91cd31b807SRobert Jarzmik 	dma->chan_name = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
92cd31b807SRobert Jarzmik 		"tx" : "rx";
93a671468dSDaniel Mack 
94d65a1458SDaniel Mack 	snd_soc_dai_set_dma_data(cpu_dai, substream, dma);
955f712b2bSDaniel Mack 
961b340bd7SMark Brown 	return ret;
971b340bd7SMark Brown }
981b340bd7SMark Brown 
pxa_ssp_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)99dee89c4dSMark Brown static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
100f0fba2adSLiam Girdwood 			     struct snd_soc_dai *cpu_dai)
1011b340bd7SMark Brown {
102f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
103f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
1041b340bd7SMark Brown 
105aaeb5fb5SKuninori Morimoto 	if (!snd_soc_dai_active(cpu_dai)) {
106baffe169SHaojian Zhuang 		pxa_ssp_disable(ssp);
1076d3efa40SDmitry Eremin-Solenikov 		clk_disable_unprepare(ssp->clk);
1081b340bd7SMark Brown 	}
1092d7e71faSEric Miao 
110cfe9ee5fSDaniel Mack 	clk_disable_unprepare(priv->extclk);
111cfe9ee5fSDaniel Mack 
1125f712b2bSDaniel Mack 	kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
1135f712b2bSDaniel Mack 	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
1141b340bd7SMark Brown }
1151b340bd7SMark Brown 
1161b340bd7SMark Brown #ifdef CONFIG_PM
1171b340bd7SMark Brown 
pxa_ssp_suspend(struct snd_soc_component * component)1182c55f0beSKuninori Morimoto static int pxa_ssp_suspend(struct snd_soc_component *component)
1191b340bd7SMark Brown {
1202c55f0beSKuninori Morimoto 	struct ssp_priv *priv = snd_soc_component_get_drvdata(component);
121f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
1221b340bd7SMark Brown 
123aaeb5fb5SKuninori Morimoto 	if (!snd_soc_component_active(component))
1246d3efa40SDmitry Eremin-Solenikov 		clk_prepare_enable(ssp->clk);
1251b340bd7SMark Brown 
126f9efc9dfSEric Miao 	priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0);
127f9efc9dfSEric Miao 	priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1);
128f9efc9dfSEric Miao 	priv->to  = __raw_readl(ssp->mmio_base + SSTO);
129f9efc9dfSEric Miao 	priv->psp = __raw_readl(ssp->mmio_base + SSPSP);
130f9efc9dfSEric Miao 
131baffe169SHaojian Zhuang 	pxa_ssp_disable(ssp);
1326d3efa40SDmitry Eremin-Solenikov 	clk_disable_unprepare(ssp->clk);
1331b340bd7SMark Brown 	return 0;
1341b340bd7SMark Brown }
1351b340bd7SMark Brown 
pxa_ssp_resume(struct snd_soc_component * component)1362c55f0beSKuninori Morimoto static int pxa_ssp_resume(struct snd_soc_component *component)
1371b340bd7SMark Brown {
1382c55f0beSKuninori Morimoto 	struct ssp_priv *priv = snd_soc_component_get_drvdata(component);
139f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
140f9efc9dfSEric Miao 	uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE;
1411b340bd7SMark Brown 
1426d3efa40SDmitry Eremin-Solenikov 	clk_prepare_enable(ssp->clk);
1431b340bd7SMark Brown 
144f9efc9dfSEric Miao 	__raw_writel(sssr, ssp->mmio_base + SSSR);
145f9efc9dfSEric Miao 	__raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0);
146f9efc9dfSEric Miao 	__raw_writel(priv->cr1, ssp->mmio_base + SSCR1);
147f9efc9dfSEric Miao 	__raw_writel(priv->to,  ssp->mmio_base + SSTO);
148f9efc9dfSEric Miao 	__raw_writel(priv->psp, ssp->mmio_base + SSPSP);
149026384d6SDaniel Mack 
150aaeb5fb5SKuninori Morimoto 	if (snd_soc_component_active(component))
151baffe169SHaojian Zhuang 		pxa_ssp_enable(ssp);
152026384d6SDaniel Mack 	else
1536d3efa40SDmitry Eremin-Solenikov 		clk_disable_unprepare(ssp->clk);
1541b340bd7SMark Brown 
1551b340bd7SMark Brown 	return 0;
1561b340bd7SMark Brown }
1571b340bd7SMark Brown 
1581b340bd7SMark Brown #else
1591b340bd7SMark Brown #define pxa_ssp_suspend	NULL
1601b340bd7SMark Brown #define pxa_ssp_resume	NULL
1611b340bd7SMark Brown #endif
1621b340bd7SMark Brown 
163701f4727SLee Jones /*
1641b340bd7SMark Brown  * ssp_set_clkdiv - set SSP clock divider
1651b340bd7SMark Brown  * @div: serial clock rate divider
1661b340bd7SMark Brown  */
pxa_ssp_set_scr(struct ssp_device * ssp,u32 div)167baffe169SHaojian Zhuang static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div)
1681b340bd7SMark Brown {
169baffe169SHaojian Zhuang 	u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0);
1701b340bd7SMark Brown 
171972a55b6SQiao Zhou 	if (ssp->type == PXA25x_SSP) {
1721a297286SPhilipp Zabel 		sscr0 &= ~0x0000ff00;
1731a297286SPhilipp Zabel 		sscr0 |= ((div - 2)/2) << 8; /* 2..512 */
1741a297286SPhilipp Zabel 	} else {
1751a297286SPhilipp Zabel 		sscr0 &= ~0x000fff00;
1761a297286SPhilipp Zabel 		sscr0 |= (div - 1) << 8;     /* 1..4096 */
1771a297286SPhilipp Zabel 	}
178baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSCR0, sscr0);
1791a297286SPhilipp Zabel }
1801a297286SPhilipp Zabel 
1811b340bd7SMark Brown /*
1821b340bd7SMark Brown  * Set the SSP ports SYSCLK.
1831b340bd7SMark Brown  */
pxa_ssp_set_dai_sysclk(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,int dir)1841b340bd7SMark Brown static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
1851b340bd7SMark Brown 	int clk_id, unsigned int freq, int dir)
1861b340bd7SMark Brown {
187f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
188f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
1891b340bd7SMark Brown 
190baffe169SHaojian Zhuang 	u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) &
19120a41eacSDaniel Mack 		~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
1921b340bd7SMark Brown 
19390eb6b59SDaniel Mack 	if (priv->extclk) {
19490eb6b59SDaniel Mack 		int ret;
19590eb6b59SDaniel Mack 
19690eb6b59SDaniel Mack 		/*
19790eb6b59SDaniel Mack 		 * For DT based boards, if an extclk is given, use it
19890eb6b59SDaniel Mack 		 * here and configure PXA_SSP_CLK_EXT.
19990eb6b59SDaniel Mack 		 */
20090eb6b59SDaniel Mack 
20190eb6b59SDaniel Mack 		ret = clk_set_rate(priv->extclk, freq);
20290eb6b59SDaniel Mack 		if (ret < 0)
20390eb6b59SDaniel Mack 			return ret;
20490eb6b59SDaniel Mack 
20590eb6b59SDaniel Mack 		clk_id = PXA_SSP_CLK_EXT;
20690eb6b59SDaniel Mack 	}
20790eb6b59SDaniel Mack 
2084f3d9577SAndy Shevchenko 	dev_dbg(ssp->dev,
209449bd54dSRoel Kluin 		"pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n",
2101b340bd7SMark Brown 		cpu_dai->id, clk_id, freq);
2111b340bd7SMark Brown 
2121b340bd7SMark Brown 	switch (clk_id) {
2131b340bd7SMark Brown 	case PXA_SSP_CLK_NET_PLL:
2141b340bd7SMark Brown 		sscr0 |= SSCR0_MOD;
2151b340bd7SMark Brown 		break;
2161b340bd7SMark Brown 	case PXA_SSP_CLK_PLL:
2171b340bd7SMark Brown 		/* Internal PLL is fixed */
218972a55b6SQiao Zhou 		if (ssp->type == PXA25x_SSP)
2191b340bd7SMark Brown 			priv->sysclk = 1843200;
2201b340bd7SMark Brown 		else
2211b340bd7SMark Brown 			priv->sysclk = 13000000;
2221b340bd7SMark Brown 		break;
2231b340bd7SMark Brown 	case PXA_SSP_CLK_EXT:
2241b340bd7SMark Brown 		priv->sysclk = freq;
2251b340bd7SMark Brown 		sscr0 |= SSCR0_ECS;
2261b340bd7SMark Brown 		break;
2271b340bd7SMark Brown 	case PXA_SSP_CLK_NET:
2281b340bd7SMark Brown 		priv->sysclk = freq;
2291b340bd7SMark Brown 		sscr0 |= SSCR0_NCS | SSCR0_MOD;
2301b340bd7SMark Brown 		break;
2311b340bd7SMark Brown 	case PXA_SSP_CLK_AUDIO:
2321b340bd7SMark Brown 		priv->sysclk = 0;
233baffe169SHaojian Zhuang 		pxa_ssp_set_scr(ssp, 1);
23420a41eacSDaniel Mack 		sscr0 |= SSCR0_ACS;
2351b340bd7SMark Brown 		break;
2361b340bd7SMark Brown 	default:
2371b340bd7SMark Brown 		return -ENODEV;
2381b340bd7SMark Brown 	}
2391b340bd7SMark Brown 
2401b340bd7SMark Brown 	/* The SSP clock must be disabled when changing SSP clock mode
2411b340bd7SMark Brown 	 * on PXA2xx.  On PXA3xx it must be enabled when doing so. */
242972a55b6SQiao Zhou 	if (ssp->type != PXA3xx_SSP)
2436d3efa40SDmitry Eremin-Solenikov 		clk_disable_unprepare(ssp->clk);
24405f38281SDaniel Mack 	pxa_ssp_write_reg(ssp, SSCR0, sscr0);
245972a55b6SQiao Zhou 	if (ssp->type != PXA3xx_SSP)
2466d3efa40SDmitry Eremin-Solenikov 		clk_prepare_enable(ssp->clk);
2471b340bd7SMark Brown 
2481b340bd7SMark Brown 	return 0;
2491b340bd7SMark Brown }
2501b340bd7SMark Brown 
2511b340bd7SMark Brown /*
2521b340bd7SMark Brown  * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
2531b340bd7SMark Brown  */
pxa_ssp_set_pll(struct ssp_priv * priv,unsigned int freq)25405739375SDaniel Mack static int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq)
2551b340bd7SMark Brown {
256f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
257baffe169SHaojian Zhuang 	u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70;
2581b340bd7SMark Brown 
259972a55b6SQiao Zhou 	if (ssp->type == PXA3xx_SSP)
260baffe169SHaojian Zhuang 		pxa_ssp_write_reg(ssp, SSACDD, 0);
2611b340bd7SMark Brown 
26205739375SDaniel Mack 	switch (freq) {
2631b340bd7SMark Brown 	case 5622000:
2641b340bd7SMark Brown 		break;
2651b340bd7SMark Brown 	case 11345000:
2661b340bd7SMark Brown 		ssacd |= (0x1 << 4);
2671b340bd7SMark Brown 		break;
2681b340bd7SMark Brown 	case 12235000:
2691b340bd7SMark Brown 		ssacd |= (0x2 << 4);
2701b340bd7SMark Brown 		break;
2711b340bd7SMark Brown 	case 14857000:
2721b340bd7SMark Brown 		ssacd |= (0x3 << 4);
2731b340bd7SMark Brown 		break;
2741b340bd7SMark Brown 	case 32842000:
2751b340bd7SMark Brown 		ssacd |= (0x4 << 4);
2761b340bd7SMark Brown 		break;
2771b340bd7SMark Brown 	case 48000000:
2781b340bd7SMark Brown 		ssacd |= (0x5 << 4);
2791b340bd7SMark Brown 		break;
2801b340bd7SMark Brown 	case 0:
2811b340bd7SMark Brown 		/* Disable */
2821b340bd7SMark Brown 		break;
2831b340bd7SMark Brown 
2841b340bd7SMark Brown 	default:
2851b340bd7SMark Brown 		/* PXA3xx has a clock ditherer which can be used to generate
2861b340bd7SMark Brown 		 * a wider range of frequencies - calculate a value for it.
2871b340bd7SMark Brown 		 */
288972a55b6SQiao Zhou 		if (ssp->type == PXA3xx_SSP) {
2891b340bd7SMark Brown 			u32 val;
2901b340bd7SMark Brown 			u64 tmp = 19968;
2911dbe6923SCodrut Grosu 
2921b340bd7SMark Brown 			tmp *= 1000000;
29305739375SDaniel Mack 			do_div(tmp, freq);
2941b340bd7SMark Brown 			val = tmp;
2951b340bd7SMark Brown 
296a419aef8SJoe Perches 			val = (val << 16) | 64;
297baffe169SHaojian Zhuang 			pxa_ssp_write_reg(ssp, SSACDD, val);
2981b340bd7SMark Brown 
2991b340bd7SMark Brown 			ssacd |= (0x6 << 4);
3001b340bd7SMark Brown 
3014f3d9577SAndy Shevchenko 			dev_dbg(ssp->dev,
302449bd54dSRoel Kluin 				"Using SSACDD %x to supply %uHz\n",
30305739375SDaniel Mack 				val, freq);
3041b340bd7SMark Brown 			break;
3051b340bd7SMark Brown 		}
3061b340bd7SMark Brown 
3071b340bd7SMark Brown 		return -EINVAL;
3081b340bd7SMark Brown 	}
3091b340bd7SMark Brown 
310baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSACD, ssacd);
3111b340bd7SMark Brown 
3121b340bd7SMark Brown 	return 0;
3131b340bd7SMark Brown }
3141b340bd7SMark Brown 
3151b340bd7SMark Brown /*
3161b340bd7SMark Brown  * Set the active slots in TDM/Network mode
3171b340bd7SMark Brown  */
pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai * cpu_dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)3181b340bd7SMark Brown static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
319a5479e38SDaniel Ribeiro 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
3201b340bd7SMark Brown {
321f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
322f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
3231b340bd7SMark Brown 	u32 sscr0;
3241b340bd7SMark Brown 
325baffe169SHaojian Zhuang 	sscr0 = pxa_ssp_read_reg(ssp, SSCR0);
326a5479e38SDaniel Ribeiro 	sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS);
327a5479e38SDaniel Ribeiro 
328a5479e38SDaniel Ribeiro 	/* set slot width */
329a5479e38SDaniel Ribeiro 	if (slot_width > 16)
330a5479e38SDaniel Ribeiro 		sscr0 |= SSCR0_EDSS | SSCR0_DataSize(slot_width - 16);
331a5479e38SDaniel Ribeiro 	else
332a5479e38SDaniel Ribeiro 		sscr0 |= SSCR0_DataSize(slot_width);
333a5479e38SDaniel Ribeiro 
334a5479e38SDaniel Ribeiro 	if (slots > 1) {
335a5479e38SDaniel Ribeiro 		/* enable network mode */
336a5479e38SDaniel Ribeiro 		sscr0 |= SSCR0_MOD;
3371b340bd7SMark Brown 
3381b340bd7SMark Brown 		/* set number of active slots */
3391b340bd7SMark Brown 		sscr0 |= SSCR0_SlotsPerFrm(slots);
3401b340bd7SMark Brown 
3411b340bd7SMark Brown 		/* set active slot mask */
342baffe169SHaojian Zhuang 		pxa_ssp_write_reg(ssp, SSTSA, tx_mask);
343baffe169SHaojian Zhuang 		pxa_ssp_write_reg(ssp, SSRSA, rx_mask);
344a5479e38SDaniel Ribeiro 	}
345baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSCR0, sscr0);
346a5479e38SDaniel Ribeiro 
3471b340bd7SMark Brown 	return 0;
3481b340bd7SMark Brown }
3491b340bd7SMark Brown 
3501b340bd7SMark Brown /*
3511b340bd7SMark Brown  * Tristate the SSP DAI lines
3521b340bd7SMark Brown  */
pxa_ssp_set_dai_tristate(struct snd_soc_dai * cpu_dai,int tristate)3531b340bd7SMark Brown static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
3541b340bd7SMark Brown 	int tristate)
3551b340bd7SMark Brown {
356f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
357f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
3581b340bd7SMark Brown 	u32 sscr1;
3591b340bd7SMark Brown 
360baffe169SHaojian Zhuang 	sscr1 = pxa_ssp_read_reg(ssp, SSCR1);
3611b340bd7SMark Brown 	if (tristate)
3621b340bd7SMark Brown 		sscr1 &= ~SSCR1_TTE;
3631b340bd7SMark Brown 	else
3641b340bd7SMark Brown 		sscr1 |= SSCR1_TTE;
365baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSCR1, sscr1);
3661b340bd7SMark Brown 
3671b340bd7SMark Brown 	return 0;
3681b340bd7SMark Brown }
3691b340bd7SMark Brown 
pxa_ssp_set_dai_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)370737e370aSDaniel Mack static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
371737e370aSDaniel Mack 			       unsigned int fmt)
372737e370aSDaniel Mack {
373737e370aSDaniel Mack 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
374737e370aSDaniel Mack 
37584c5b47cSCharles Keepax 	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
37684c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BC_FC:
37784c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BC_FP:
37884c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BP_FP:
379737e370aSDaniel Mack 		break;
380737e370aSDaniel Mack 	default:
381737e370aSDaniel Mack 		return -EINVAL;
382737e370aSDaniel Mack 	}
383737e370aSDaniel Mack 
384737e370aSDaniel Mack 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
385737e370aSDaniel Mack 	case SND_SOC_DAIFMT_NB_NF:
386737e370aSDaniel Mack 	case SND_SOC_DAIFMT_NB_IF:
387737e370aSDaniel Mack 	case SND_SOC_DAIFMT_IB_IF:
388737e370aSDaniel Mack 	case SND_SOC_DAIFMT_IB_NF:
389737e370aSDaniel Mack 		break;
390737e370aSDaniel Mack 	default:
391737e370aSDaniel Mack 		return -EINVAL;
392737e370aSDaniel Mack 	}
393737e370aSDaniel Mack 
394737e370aSDaniel Mack 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
395737e370aSDaniel Mack 	case SND_SOC_DAIFMT_I2S:
396737e370aSDaniel Mack 	case SND_SOC_DAIFMT_DSP_A:
397737e370aSDaniel Mack 	case SND_SOC_DAIFMT_DSP_B:
398737e370aSDaniel Mack 		break;
399737e370aSDaniel Mack 
400737e370aSDaniel Mack 	default:
401737e370aSDaniel Mack 		return -EINVAL;
402737e370aSDaniel Mack 	}
403737e370aSDaniel Mack 
404737e370aSDaniel Mack 	/* Settings will be applied in hw_params() */
405737e370aSDaniel Mack 	priv->dai_fmt = fmt;
406737e370aSDaniel Mack 
407737e370aSDaniel Mack 	return 0;
408737e370aSDaniel Mack }
409737e370aSDaniel Mack 
4101b340bd7SMark Brown /*
4111b340bd7SMark Brown  * Set up the SSP DAI format.
4121b340bd7SMark Brown  * The SSP Port must be inactive before calling this function as the
4131b340bd7SMark Brown  * physical interface format is changed.
4141b340bd7SMark Brown  */
pxa_ssp_configure_dai_fmt(struct ssp_priv * priv)415737e370aSDaniel Mack static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv)
4161b340bd7SMark Brown {
417f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
418f5d1e5edSHaojian Zhuang 	u32 sscr0, sscr1, sspsp, scfr;
4191b340bd7SMark Brown 
420cbf1146dSDaniel Mack 	/* check if we need to change anything at all */
421737e370aSDaniel Mack 	if (priv->configured_dai_fmt == priv->dai_fmt)
422cbf1146dSDaniel Mack 		return 0;
423cbf1146dSDaniel Mack 
4241b340bd7SMark Brown 	/* reset port settings */
425baffe169SHaojian Zhuang 	sscr0 = pxa_ssp_read_reg(ssp, SSCR0) &
426737e370aSDaniel Mack 		~(SSCR0_PSP | SSCR0_MOD);
427737e370aSDaniel Mack 	sscr1 = pxa_ssp_read_reg(ssp, SSCR1) &
428737e370aSDaniel Mack 		~(SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR |
429737e370aSDaniel Mack 		  SSCR1_RWOT | SSCR1_TRAIL | SSCR1_TFT | SSCR1_RFT);
430737e370aSDaniel Mack 	sspsp = pxa_ssp_read_reg(ssp, SSPSP) &
431737e370aSDaniel Mack 		~(SSPSP_SFRMP | SSPSP_SCMODE(3));
4321b340bd7SMark Brown 
433737e370aSDaniel Mack 	sscr1 |= SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
434737e370aSDaniel Mack 
43584c5b47cSCharles Keepax 	switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
43684c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BC_FC:
437f5d1e5edSHaojian Zhuang 		sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR;
4381b340bd7SMark Brown 		break;
43984c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BC_FP:
440f5d1e5edSHaojian Zhuang 		sscr1 |= SSCR1_SCLKDIR | SSCR1_SCFR;
4411b340bd7SMark Brown 		break;
44284c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BP_FP:
4431b340bd7SMark Brown 		break;
4441b340bd7SMark Brown 	default:
4451b340bd7SMark Brown 		return -EINVAL;
4461b340bd7SMark Brown 	}
4471b340bd7SMark Brown 
448737e370aSDaniel Mack 	switch (priv->dai_fmt & SND_SOC_DAIFMT_INV_MASK) {
4491b340bd7SMark Brown 	case SND_SOC_DAIFMT_NB_NF:
45072d74664SDaniel Mack 		sspsp |= SSPSP_SFRMP;
4511b340bd7SMark Brown 		break;
4520ce36c5fSMark Brown 	case SND_SOC_DAIFMT_NB_IF:
4530ce36c5fSMark Brown 		break;
4541b340bd7SMark Brown 	case SND_SOC_DAIFMT_IB_IF:
455a8205320SDaniel Ribeiro 		sspsp |= SSPSP_SCMODE(2);
456a8205320SDaniel Ribeiro 		break;
457a8205320SDaniel Ribeiro 	case SND_SOC_DAIFMT_IB_NF:
458a8205320SDaniel Ribeiro 		sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
4591b340bd7SMark Brown 		break;
4601b340bd7SMark Brown 	default:
4611b340bd7SMark Brown 		return -EINVAL;
4621b340bd7SMark Brown 	}
463fa44c077SDaniel Ribeiro 
464737e370aSDaniel Mack 	switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
465fa44c077SDaniel Ribeiro 	case SND_SOC_DAIFMT_I2S:
466fa44c077SDaniel Ribeiro 		sscr0 |= SSCR0_PSP;
467fa44c077SDaniel Ribeiro 		sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
468fa44c077SDaniel Ribeiro 		/* See hw_params() */
4691b340bd7SMark Brown 		break;
4701b340bd7SMark Brown 
4711b340bd7SMark Brown 	case SND_SOC_DAIFMT_DSP_A:
4721b340bd7SMark Brown 		sspsp |= SSPSP_FSRT;
473df561f66SGustavo A. R. Silva 		fallthrough;
4741b340bd7SMark Brown 	case SND_SOC_DAIFMT_DSP_B:
4751b340bd7SMark Brown 		sscr0 |= SSCR0_MOD | SSCR0_PSP;
4761b340bd7SMark Brown 		sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
4771b340bd7SMark Brown 		break;
4781b340bd7SMark Brown 
4791b340bd7SMark Brown 	default:
4801b340bd7SMark Brown 		return -EINVAL;
4811b340bd7SMark Brown 	}
4821b340bd7SMark Brown 
483baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSCR0, sscr0);
484baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSCR1, sscr1);
485baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSPSP, sspsp);
4861b340bd7SMark Brown 
48784c5b47cSCharles Keepax 	switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
48884c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BC_FC:
48984c5b47cSCharles Keepax 	case SND_SOC_DAIFMT_BC_FP:
490f5d1e5edSHaojian Zhuang 		scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR;
491f5d1e5edSHaojian Zhuang 		pxa_ssp_write_reg(ssp, SSCR1, scfr);
492f5d1e5edSHaojian Zhuang 
493f5d1e5edSHaojian Zhuang 		while (pxa_ssp_read_reg(ssp, SSSR) & SSSR_BSY)
494f5d1e5edSHaojian Zhuang 			cpu_relax();
495f5d1e5edSHaojian Zhuang 		break;
496f5d1e5edSHaojian Zhuang 	}
497f5d1e5edSHaojian Zhuang 
4981b340bd7SMark Brown 	dump_registers(ssp);
4991b340bd7SMark Brown 
5001b340bd7SMark Brown 	/* Since we are configuring the timings for the format by hand
5011b340bd7SMark Brown 	 * we have to defer some things until hw_params() where we
5021b340bd7SMark Brown 	 * know parameters like the sample size.
5031b340bd7SMark Brown 	 */
504737e370aSDaniel Mack 	priv->configured_dai_fmt = priv->dai_fmt;
5051b340bd7SMark Brown 
5061b340bd7SMark Brown 	return 0;
5071b340bd7SMark Brown }
5081b340bd7SMark Brown 
50905739375SDaniel Mack struct pxa_ssp_clock_mode {
51005739375SDaniel Mack 	int rate;
51105739375SDaniel Mack 	int pll;
51205739375SDaniel Mack 	u8 acds;
51305739375SDaniel Mack 	u8 scdb;
51405739375SDaniel Mack };
51505739375SDaniel Mack 
51605739375SDaniel Mack static const struct pxa_ssp_clock_mode pxa_ssp_clock_modes[] = {
51705739375SDaniel Mack 	{ .rate =  8000, .pll = 32842000, .acds = SSACD_ACDS_32, .scdb = SSACD_SCDB_4X },
51805739375SDaniel Mack 	{ .rate = 11025, .pll =  5622000, .acds = SSACD_ACDS_4,  .scdb = SSACD_SCDB_4X },
51905739375SDaniel Mack 	{ .rate = 16000, .pll = 32842000, .acds = SSACD_ACDS_16, .scdb = SSACD_SCDB_4X },
52005739375SDaniel Mack 	{ .rate = 22050, .pll =  5622000, .acds = SSACD_ACDS_2,  .scdb = SSACD_SCDB_4X },
52105739375SDaniel Mack 	{ .rate = 44100, .pll = 11345000, .acds = SSACD_ACDS_2,  .scdb = SSACD_SCDB_4X },
52205739375SDaniel Mack 	{ .rate = 48000, .pll = 12235000, .acds = SSACD_ACDS_2,  .scdb = SSACD_SCDB_4X },
52305739375SDaniel Mack 	{ .rate = 96000, .pll = 12235000, .acds = SSACD_ACDS_4,  .scdb = SSACD_SCDB_1X },
52405739375SDaniel Mack 	{}
52505739375SDaniel Mack };
52605739375SDaniel Mack 
5271b340bd7SMark Brown /*
5281b340bd7SMark Brown  * Set the SSP audio DMA parameters and sample size.
5291b340bd7SMark Brown  * Can be called multiple times by oss emulation.
5301b340bd7SMark Brown  */
pxa_ssp_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * cpu_dai)5311b340bd7SMark Brown static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
532dee89c4dSMark Brown 				struct snd_pcm_hw_params *params,
533f0fba2adSLiam Girdwood 				struct snd_soc_dai *cpu_dai)
5341b340bd7SMark Brown {
535f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
536f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
5372d7e71faSEric Miao 	int chn = params_channels(params);
53805739375SDaniel Mack 	u32 sscr0, sspsp;
5391b340bd7SMark Brown 	int width = snd_pcm_format_physical_width(params_format(params));
540baffe169SHaojian Zhuang 	int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf;
541d65a1458SDaniel Mack 	struct snd_dmaengine_dai_dma_data *dma_data;
54205739375SDaniel Mack 	int rate = params_rate(params);
54305739375SDaniel Mack 	int bclk = rate * chn * (width / 8);
544737e370aSDaniel Mack 	int ret;
5455f712b2bSDaniel Mack 
546f0fba2adSLiam Girdwood 	dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
5471b340bd7SMark Brown 
54892429069SPhilipp Zabel 	/* Network mode with one active slot (ttsa == 1) can be used
54992429069SPhilipp Zabel 	 * to force 16-bit frame width on the wire (for S16_LE), even
55092429069SPhilipp Zabel 	 * with two channels. Use 16-bit DMA transfers for this case.
55192429069SPhilipp Zabel 	 */
552d93ca1aeSguoyh 	pxa_ssp_set_dma_params(ssp,
5532d7e71faSEric Miao 		((chn == 2) && (ttsa != 1)) || (width == 32),
554d93ca1aeSguoyh 		substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data);
5555f712b2bSDaniel Mack 
5561b340bd7SMark Brown 	/* we can only change the settings if the port is not in use */
557baffe169SHaojian Zhuang 	if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
5581b340bd7SMark Brown 		return 0;
5591b340bd7SMark Brown 
560737e370aSDaniel Mack 	ret = pxa_ssp_configure_dai_fmt(priv);
561737e370aSDaniel Mack 	if (ret < 0)
562737e370aSDaniel Mack 		return ret;
563737e370aSDaniel Mack 
5641b340bd7SMark Brown 	/* clear selected SSP bits */
565baffe169SHaojian Zhuang 	sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
5661b340bd7SMark Brown 
5671b340bd7SMark Brown 	/* bit size */
5681b340bd7SMark Brown 	switch (params_format(params)) {
5691b340bd7SMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
570972a55b6SQiao Zhou 		if (ssp->type == PXA3xx_SSP)
5711b340bd7SMark Brown 			sscr0 |= SSCR0_FPCKE;
5721b340bd7SMark Brown 		sscr0 |= SSCR0_DataSize(16);
5731b340bd7SMark Brown 		break;
5741b340bd7SMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
5751b340bd7SMark Brown 		sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
5761b340bd7SMark Brown 		break;
5771b340bd7SMark Brown 	case SNDRV_PCM_FORMAT_S32_LE:
5781b340bd7SMark Brown 		sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
5791b340bd7SMark Brown 		break;
5801b340bd7SMark Brown 	}
581baffe169SHaojian Zhuang 	pxa_ssp_write_reg(ssp, SSCR0, sscr0);
5821b340bd7SMark Brown 
58305739375SDaniel Mack 	if (sscr0 & SSCR0_ACS) {
58405739375SDaniel Mack 		ret = pxa_ssp_set_pll(priv, bclk);
58505739375SDaniel Mack 
58605739375SDaniel Mack 		/*
58705739375SDaniel Mack 		 * If we were able to generate the bclk directly,
58805739375SDaniel Mack 		 * all is fine. Otherwise, look up the closest rate
58905739375SDaniel Mack 		 * from the table and also set the dividers.
59005739375SDaniel Mack 		 */
59105739375SDaniel Mack 
59205739375SDaniel Mack 		if (ret < 0) {
59305739375SDaniel Mack 			const struct pxa_ssp_clock_mode *m;
594a932f45aSYu Liao 			int ssacd;
59505739375SDaniel Mack 
59605739375SDaniel Mack 			for (m = pxa_ssp_clock_modes; m->rate; m++) {
59705739375SDaniel Mack 				if (m->rate == rate)
59805739375SDaniel Mack 					break;
59905739375SDaniel Mack 			}
60005739375SDaniel Mack 
60105739375SDaniel Mack 			if (!m->rate)
60205739375SDaniel Mack 				return -EINVAL;
60305739375SDaniel Mack 
60405739375SDaniel Mack 			ret = pxa_ssp_set_pll(priv, bclk);
60505739375SDaniel Mack 			if (ret < 0)
60605739375SDaniel Mack 				return ret;
60705739375SDaniel Mack 
60805739375SDaniel Mack 			ssacd = pxa_ssp_read_reg(ssp, SSACD);
60905739375SDaniel Mack 			ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X);
61005739375SDaniel Mack 			ssacd |= SSACD_ACDS(m->acds);
61105739375SDaniel Mack 			ssacd |= m->scdb;
61205739375SDaniel Mack 			pxa_ssp_write_reg(ssp, SSACD, ssacd);
61305739375SDaniel Mack 		}
61405739375SDaniel Mack 	} else if (sscr0 & SSCR0_ECS) {
61505739375SDaniel Mack 		/*
61605739375SDaniel Mack 		 * For setups with external clocking, the PLL and its diviers
61705739375SDaniel Mack 		 * are not active. Instead, the SCR bits in SSCR0 can be used
61805739375SDaniel Mack 		 * to divide the clock.
61905739375SDaniel Mack 		 */
62005739375SDaniel Mack 		pxa_ssp_set_scr(ssp, bclk / rate);
62105739375SDaniel Mack 	}
62205739375SDaniel Mack 
6231b340bd7SMark Brown 	switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
6241b340bd7SMark Brown 	case SND_SOC_DAIFMT_I2S:
625baffe169SHaojian Zhuang 	       sspsp = pxa_ssp_read_reg(ssp, SSPSP);
62672d74664SDaniel Mack 
62705739375SDaniel Mack 		if (((priv->sysclk / bclk) == 64) && (width == 16)) {
62872d74664SDaniel Mack 			/* This is a special case where the bitclk is 64fs
62972d74664SDaniel Mack 			 * and we're not dealing with 2*32 bits of audio
63072d74664SDaniel Mack 			 * samples.
63172d74664SDaniel Mack 			 *
63272d74664SDaniel Mack 			 * The SSP values used for that are all found out by
63372d74664SDaniel Mack 			 * trying and failing a lot; some of the registers
63472d74664SDaniel Mack 			 * needed for that mode are only available on PXA3xx.
63572d74664SDaniel Mack 			 */
636972a55b6SQiao Zhou 			if (ssp->type != PXA3xx_SSP)
63772d74664SDaniel Mack 				return -EINVAL;
63872d74664SDaniel Mack 
63972d74664SDaniel Mack 			sspsp |= SSPSP_SFRMWDTH(width * 2);
64072d74664SDaniel Mack 			sspsp |= SSPSP_SFRMDLY(width * 4);
64172d74664SDaniel Mack 			sspsp |= SSPSP_EDMYSTOP(3);
64272d74664SDaniel Mack 			sspsp |= SSPSP_DMYSTOP(3);
64372d74664SDaniel Mack 			sspsp |= SSPSP_DMYSTRT(1);
6440ce36c5fSMark Brown 		} else {
6450ce36c5fSMark Brown 			/* The frame width is the width the LRCLK is
6460ce36c5fSMark Brown 			 * asserted for; the delay is expressed in
6470ce36c5fSMark Brown 			 * half cycle units.  We need the extra cycle
6480ce36c5fSMark Brown 			 * because the data starts clocking out one BCLK
6490ce36c5fSMark Brown 			 * after LRCLK changes polarity.
6500ce36c5fSMark Brown 			 */
6510ce36c5fSMark Brown 			sspsp |= SSPSP_SFRMWDTH(width + 1);
6520ce36c5fSMark Brown 			sspsp |= SSPSP_SFRMDLY((width + 1) * 2);
6530ce36c5fSMark Brown 			sspsp |= SSPSP_DMYSTRT(1);
6540ce36c5fSMark Brown 		}
65572d74664SDaniel Mack 
656baffe169SHaojian Zhuang 		pxa_ssp_write_reg(ssp, SSPSP, sspsp);
6571b340bd7SMark Brown 		break;
6581b340bd7SMark Brown 	default:
6591b340bd7SMark Brown 		break;
6601b340bd7SMark Brown 	}
6611b340bd7SMark Brown 
66272d74664SDaniel Mack 	/* When we use a network mode, we always require TDM slots
6631b340bd7SMark Brown 	 * - complain loudly and fail if they've not been set up yet.
6641b340bd7SMark Brown 	 */
66592429069SPhilipp Zabel 	if ((sscr0 & SSCR0_MOD) && !ttsa) {
6664f3d9577SAndy Shevchenko 		dev_err(ssp->dev, "No TDM timeslot configured\n");
6671b340bd7SMark Brown 		return -EINVAL;
6681b340bd7SMark Brown 	}
6691b340bd7SMark Brown 
6701b340bd7SMark Brown 	dump_registers(ssp);
6711b340bd7SMark Brown 
6721b340bd7SMark Brown 	return 0;
6731b340bd7SMark Brown }
6741b340bd7SMark Brown 
pxa_ssp_set_running_bit(struct snd_pcm_substream * substream,struct ssp_device * ssp,int value)675273b72c8SDaniel Mack static void pxa_ssp_set_running_bit(struct snd_pcm_substream *substream,
676273b72c8SDaniel Mack 				    struct ssp_device *ssp, int value)
677273b72c8SDaniel Mack {
678273b72c8SDaniel Mack 	uint32_t sscr0 = pxa_ssp_read_reg(ssp, SSCR0);
679273b72c8SDaniel Mack 	uint32_t sscr1 = pxa_ssp_read_reg(ssp, SSCR1);
680273b72c8SDaniel Mack 	uint32_t sspsp = pxa_ssp_read_reg(ssp, SSPSP);
681273b72c8SDaniel Mack 	uint32_t sssr = pxa_ssp_read_reg(ssp, SSSR);
682273b72c8SDaniel Mack 
683273b72c8SDaniel Mack 	if (value && (sscr0 & SSCR0_SSE))
684273b72c8SDaniel Mack 		pxa_ssp_write_reg(ssp, SSCR0, sscr0 & ~SSCR0_SSE);
685273b72c8SDaniel Mack 
686273b72c8SDaniel Mack 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
687273b72c8SDaniel Mack 		if (value)
688273b72c8SDaniel Mack 			sscr1 |= SSCR1_TSRE;
689273b72c8SDaniel Mack 		else
690273b72c8SDaniel Mack 			sscr1 &= ~SSCR1_TSRE;
691273b72c8SDaniel Mack 	} else {
692273b72c8SDaniel Mack 		if (value)
693273b72c8SDaniel Mack 			sscr1 |= SSCR1_RSRE;
694273b72c8SDaniel Mack 		else
695273b72c8SDaniel Mack 			sscr1 &= ~SSCR1_RSRE;
696273b72c8SDaniel Mack 	}
697273b72c8SDaniel Mack 
698273b72c8SDaniel Mack 	pxa_ssp_write_reg(ssp, SSCR1, sscr1);
699273b72c8SDaniel Mack 
700273b72c8SDaniel Mack 	if (value) {
701273b72c8SDaniel Mack 		pxa_ssp_write_reg(ssp, SSSR, sssr);
702273b72c8SDaniel Mack 		pxa_ssp_write_reg(ssp, SSPSP, sspsp);
703273b72c8SDaniel Mack 		pxa_ssp_write_reg(ssp, SSCR0, sscr0 | SSCR0_SSE);
704273b72c8SDaniel Mack 	}
705273b72c8SDaniel Mack }
706273b72c8SDaniel Mack 
pxa_ssp_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * cpu_dai)707dee89c4dSMark Brown static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
708f0fba2adSLiam Girdwood 			   struct snd_soc_dai *cpu_dai)
7091b340bd7SMark Brown {
7101b340bd7SMark Brown 	int ret = 0;
711f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
712f9efc9dfSEric Miao 	struct ssp_device *ssp = priv->ssp;
7131b340bd7SMark Brown 	int val;
7141b340bd7SMark Brown 
7151b340bd7SMark Brown 	switch (cmd) {
7161b340bd7SMark Brown 	case SNDRV_PCM_TRIGGER_RESUME:
717baffe169SHaojian Zhuang 		pxa_ssp_enable(ssp);
7181b340bd7SMark Brown 		break;
7191b340bd7SMark Brown 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
720273b72c8SDaniel Mack 		pxa_ssp_set_running_bit(substream, ssp, 1);
721baffe169SHaojian Zhuang 		val = pxa_ssp_read_reg(ssp, SSSR);
722baffe169SHaojian Zhuang 		pxa_ssp_write_reg(ssp, SSSR, val);
7231b340bd7SMark Brown 		break;
7241b340bd7SMark Brown 	case SNDRV_PCM_TRIGGER_START:
725273b72c8SDaniel Mack 		pxa_ssp_set_running_bit(substream, ssp, 1);
7261b340bd7SMark Brown 		break;
7271b340bd7SMark Brown 	case SNDRV_PCM_TRIGGER_STOP:
728273b72c8SDaniel Mack 		pxa_ssp_set_running_bit(substream, ssp, 0);
7291b340bd7SMark Brown 		break;
7301b340bd7SMark Brown 	case SNDRV_PCM_TRIGGER_SUSPEND:
731baffe169SHaojian Zhuang 		pxa_ssp_disable(ssp);
7321b340bd7SMark Brown 		break;
7331b340bd7SMark Brown 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
734273b72c8SDaniel Mack 		pxa_ssp_set_running_bit(substream, ssp, 0);
7351b340bd7SMark Brown 		break;
7361b340bd7SMark Brown 
7371b340bd7SMark Brown 	default:
7381b340bd7SMark Brown 		ret = -EINVAL;
7391b340bd7SMark Brown 	}
7401b340bd7SMark Brown 
7411b340bd7SMark Brown 	dump_registers(ssp);
7421b340bd7SMark Brown 
7431b340bd7SMark Brown 	return ret;
7441b340bd7SMark Brown }
7451b340bd7SMark Brown 
pxa_ssp_probe(struct snd_soc_dai * dai)746f0fba2adSLiam Girdwood static int pxa_ssp_probe(struct snd_soc_dai *dai)
7471b340bd7SMark Brown {
7482023c90cSDaniel Mack 	struct device *dev = dai->dev;
7491b340bd7SMark Brown 	struct ssp_priv *priv;
7501b340bd7SMark Brown 	int ret;
7511b340bd7SMark Brown 
7521b340bd7SMark Brown 	priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL);
7531b340bd7SMark Brown 	if (!priv)
7541b340bd7SMark Brown 		return -ENOMEM;
7551b340bd7SMark Brown 
7562023c90cSDaniel Mack 	if (dev->of_node) {
7572023c90cSDaniel Mack 		struct device_node *ssp_handle;
7582023c90cSDaniel Mack 
7592023c90cSDaniel Mack 		ssp_handle = of_parse_phandle(dev->of_node, "port", 0);
7602023c90cSDaniel Mack 		if (!ssp_handle) {
7612023c90cSDaniel Mack 			dev_err(dev, "unable to get 'port' phandle\n");
76245487289SDan Carpenter 			ret = -ENODEV;
76345487289SDan Carpenter 			goto err_priv;
7642023c90cSDaniel Mack 		}
7652023c90cSDaniel Mack 
7662023c90cSDaniel Mack 		priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio");
7672023c90cSDaniel Mack 		if (priv->ssp == NULL) {
7682023c90cSDaniel Mack 			ret = -ENODEV;
7692023c90cSDaniel Mack 			goto err_priv;
7702023c90cSDaniel Mack 		}
77190eb6b59SDaniel Mack 
77290eb6b59SDaniel Mack 		priv->extclk = devm_clk_get(dev, "extclk");
77390eb6b59SDaniel Mack 		if (IS_ERR(priv->extclk)) {
77490eb6b59SDaniel Mack 			ret = PTR_ERR(priv->extclk);
77590eb6b59SDaniel Mack 			if (ret == -EPROBE_DEFER)
776*aa6464edSDan Carpenter 				goto err_priv;
77790eb6b59SDaniel Mack 
77890eb6b59SDaniel Mack 			priv->extclk = NULL;
77990eb6b59SDaniel Mack 		}
7802023c90cSDaniel Mack 	} else {
781baffe169SHaojian Zhuang 		priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
782f9efc9dfSEric Miao 		if (priv->ssp == NULL) {
7831b340bd7SMark Brown 			ret = -ENODEV;
7841b340bd7SMark Brown 			goto err_priv;
7851b340bd7SMark Brown 		}
7862023c90cSDaniel Mack 	}
7871b340bd7SMark Brown 
788a5735b7eSDaniel Mack 	priv->dai_fmt = (unsigned int) -1;
789f0fba2adSLiam Girdwood 	snd_soc_dai_set_drvdata(dai, priv);
7901b340bd7SMark Brown 
7911b340bd7SMark Brown 	return 0;
7921b340bd7SMark Brown 
7931b340bd7SMark Brown err_priv:
7941b340bd7SMark Brown 	kfree(priv);
7951b340bd7SMark Brown 	return ret;
7961b340bd7SMark Brown }
7971b340bd7SMark Brown 
pxa_ssp_remove(struct snd_soc_dai * dai)798f0fba2adSLiam Girdwood static int pxa_ssp_remove(struct snd_soc_dai *dai)
7991b340bd7SMark Brown {
800f0fba2adSLiam Girdwood 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai);
801f0fba2adSLiam Girdwood 
802baffe169SHaojian Zhuang 	pxa_ssp_free(priv->ssp);
803014a2755SAxel Lin 	kfree(priv);
804f0fba2adSLiam Girdwood 	return 0;
8051b340bd7SMark Brown }
8061b340bd7SMark Brown 
8071b340bd7SMark Brown #define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
8081b340bd7SMark Brown 			  SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |	\
8098d8bf58bSQiao Zhou 			  SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |	\
8108d8bf58bSQiao Zhou 			  SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 |	\
8111b340bd7SMark Brown 			  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
8121b340bd7SMark Brown 
8139301503aSDaniel Mack #define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
8141b340bd7SMark Brown 
81585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ssp_dai_ops = {
816e4190677SKuninori Morimoto 	.probe		= pxa_ssp_probe,
817e4190677SKuninori Morimoto 	.remove		= pxa_ssp_remove,
8186335d055SEric Miao 	.startup	= pxa_ssp_startup,
8196335d055SEric Miao 	.shutdown	= pxa_ssp_shutdown,
8206335d055SEric Miao 	.trigger	= pxa_ssp_trigger,
8216335d055SEric Miao 	.hw_params	= pxa_ssp_hw_params,
8226335d055SEric Miao 	.set_sysclk	= pxa_ssp_set_dai_sysclk,
8238e2cc2b2SCharles Keepax 	.set_fmt	= pxa_ssp_set_dai_fmt,
8246335d055SEric Miao 	.set_tdm_slot	= pxa_ssp_set_dai_tdm_slot,
8256335d055SEric Miao 	.set_tristate	= pxa_ssp_set_dai_tristate,
8266335d055SEric Miao };
8276335d055SEric Miao 
828f0fba2adSLiam Girdwood static struct snd_soc_dai_driver pxa_ssp_dai = {
8291b340bd7SMark Brown 		.playback = {
8301b340bd7SMark Brown 			.channels_min = 1,
831f34762b6SGraeme Gregory 			.channels_max = 8,
8321b340bd7SMark Brown 			.rates = PXA_SSP_RATES,
8331b340bd7SMark Brown 			.formats = PXA_SSP_FORMATS,
8341b340bd7SMark Brown 		},
8351b340bd7SMark Brown 		.capture = {
8361b340bd7SMark Brown 			 .channels_min = 1,
837f34762b6SGraeme Gregory 			 .channels_max = 8,
8381b340bd7SMark Brown 			.rates = PXA_SSP_RATES,
8391b340bd7SMark Brown 			.formats = PXA_SSP_FORMATS,
8401b340bd7SMark Brown 		 },
8416335d055SEric Miao 		.ops = &pxa_ssp_dai_ops,
8421b340bd7SMark Brown };
843f0fba2adSLiam Girdwood 
844e580f1ceSKuninori Morimoto static const struct snd_soc_component_driver pxa_ssp_component = {
845e580f1ceSKuninori Morimoto 	.name			= "pxa-ssp",
846f8772e17SKuninori Morimoto 	.pcm_construct		= pxa2xx_soc_pcm_new,
847f8772e17SKuninori Morimoto 	.open			= pxa2xx_soc_pcm_open,
848f8772e17SKuninori Morimoto 	.close			= pxa2xx_soc_pcm_close,
849f8772e17SKuninori Morimoto 	.hw_params		= pxa2xx_soc_pcm_hw_params,
850f8772e17SKuninori Morimoto 	.prepare		= pxa2xx_soc_pcm_prepare,
851f8772e17SKuninori Morimoto 	.trigger		= pxa2xx_soc_pcm_trigger,
852f8772e17SKuninori Morimoto 	.pointer		= pxa2xx_soc_pcm_pointer,
8532c55f0beSKuninori Morimoto 	.suspend		= pxa_ssp_suspend,
8542c55f0beSKuninori Morimoto 	.resume			= pxa_ssp_resume,
85505603f15SCharles Keepax 	.legacy_dai_naming	= 1,
856e580f1ceSKuninori Morimoto };
857e580f1ceSKuninori Morimoto 
8582023c90cSDaniel Mack #ifdef CONFIG_OF
8592023c90cSDaniel Mack static const struct of_device_id pxa_ssp_of_ids[] = {
8602023c90cSDaniel Mack 	{ .compatible = "mrvl,pxa-ssp-dai" },
8614c715c75SStephen Boyd 	{}
8622023c90cSDaniel Mack };
863baafd373SLuis de Bethencourt MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
8642023c90cSDaniel Mack #endif
8652023c90cSDaniel Mack 
asoc_ssp_probe(struct platform_device * pdev)866570f6fe1SBill Pemberton static int asoc_ssp_probe(struct platform_device *pdev)
867f0fba2adSLiam Girdwood {
868637ce53aSAxel Lin 	return devm_snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
869e580f1ceSKuninori Morimoto 					       &pxa_ssp_dai, 1);
870f0fba2adSLiam Girdwood }
871f0fba2adSLiam Girdwood 
872f0fba2adSLiam Girdwood static struct platform_driver asoc_ssp_driver = {
873f0fba2adSLiam Girdwood 	.driver = {
874f0fba2adSLiam Girdwood 		.name = "pxa-ssp-dai",
8752023c90cSDaniel Mack 		.of_match_table = of_match_ptr(pxa_ssp_of_ids),
876f0fba2adSLiam Girdwood 	},
877f0fba2adSLiam Girdwood 
878f0fba2adSLiam Girdwood 	.probe = asoc_ssp_probe,
879f0fba2adSLiam Girdwood };
8801b340bd7SMark Brown 
8812f702a19SAxel Lin module_platform_driver(asoc_ssp_driver);
8823f4b783cSMark Brown 
8831b340bd7SMark Brown /* Module information */
8841b340bd7SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
8851b340bd7SMark Brown MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
8861b340bd7SMark Brown MODULE_LICENSE("GPL");
887e5b7d71aSAndrea Adami MODULE_ALIAS("platform:pxa-ssp-dai");
888