xref: /openbmc/linux/sound/soc/bcm/cygnus-ssp.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
12aec85b2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22aec85b2SThomas Gleixner // Copyright (C) 2014-2015 Broadcom Corporation
3a6ee05d9SSimran Rai #include <linux/clk.h>
4a6ee05d9SSimran Rai #include <linux/delay.h>
5a6ee05d9SSimran Rai #include <linux/init.h>
6a6ee05d9SSimran Rai #include <linux/io.h>
7a6ee05d9SSimran Rai #include <linux/module.h>
8a6ee05d9SSimran Rai #include <linux/of_device.h>
9a6ee05d9SSimran Rai #include <linux/slab.h>
10a6ee05d9SSimran Rai #include <sound/core.h>
11a6ee05d9SSimran Rai #include <sound/pcm.h>
12a6ee05d9SSimran Rai #include <sound/pcm_params.h>
13a6ee05d9SSimran Rai #include <sound/soc.h>
14a6ee05d9SSimran Rai #include <sound/soc-dai.h>
15a6ee05d9SSimran Rai 
16a6ee05d9SSimran Rai #include "cygnus-ssp.h"
17a6ee05d9SSimran Rai 
18a6ee05d9SSimran Rai #define DEFAULT_VCO    1354750204
19a6ee05d9SSimran Rai 
20a6ee05d9SSimran Rai #define CAPTURE_FCI_ID_BASE 0x180
21a6ee05d9SSimran Rai #define CYGNUS_SSP_TRISTATE_MASK 0x001fff
22a6ee05d9SSimran Rai #define CYGNUS_PLLCLKSEL_MASK 0xf
23a6ee05d9SSimran Rai 
24a6ee05d9SSimran Rai /* Used with stream_on field to indicate which streams are active */
25a6ee05d9SSimran Rai #define  PLAYBACK_STREAM_MASK   BIT(0)
26a6ee05d9SSimran Rai #define  CAPTURE_STREAM_MASK    BIT(1)
27a6ee05d9SSimran Rai 
28a6ee05d9SSimran Rai #define I2S_STREAM_CFG_MASK      0xff003ff
29a6ee05d9SSimran Rai #define I2S_CAP_STREAM_CFG_MASK  0xf0
30a6ee05d9SSimran Rai #define SPDIF_STREAM_CFG_MASK    0x3ff
31a6ee05d9SSimran Rai #define CH_GRP_STEREO            0x1
32a6ee05d9SSimran Rai 
33a6ee05d9SSimran Rai /* Begin register offset defines */
34a6ee05d9SSimran Rai #define AUD_MISC_SEROUT_OE_REG_BASE  0x01c
35a6ee05d9SSimran Rai #define AUD_MISC_SEROUT_SPDIF_OE  12
36a6ee05d9SSimran Rai #define AUD_MISC_SEROUT_MCLK_OE   3
37a6ee05d9SSimran Rai #define AUD_MISC_SEROUT_LRCK_OE   2
38a6ee05d9SSimran Rai #define AUD_MISC_SEROUT_SCLK_OE   1
39a6ee05d9SSimran Rai #define AUD_MISC_SEROUT_SDAT_OE   0
40a6ee05d9SSimran Rai 
41a6ee05d9SSimran Rai /* AUD_FMM_BF_CTRL_xxx regs */
42a6ee05d9SSimran Rai #define BF_DST_CFG0_OFFSET  0x100
43a6ee05d9SSimran Rai #define BF_DST_CFG1_OFFSET  0x104
44a6ee05d9SSimran Rai #define BF_DST_CFG2_OFFSET  0x108
45a6ee05d9SSimran Rai 
46a6ee05d9SSimran Rai #define BF_DST_CTRL0_OFFSET 0x130
47a6ee05d9SSimran Rai #define BF_DST_CTRL1_OFFSET 0x134
48a6ee05d9SSimran Rai #define BF_DST_CTRL2_OFFSET 0x138
49a6ee05d9SSimran Rai 
50a6ee05d9SSimran Rai #define BF_SRC_CFG0_OFFSET  0x148
51a6ee05d9SSimran Rai #define BF_SRC_CFG1_OFFSET  0x14c
52a6ee05d9SSimran Rai #define BF_SRC_CFG2_OFFSET  0x150
53a6ee05d9SSimran Rai #define BF_SRC_CFG3_OFFSET  0x154
54a6ee05d9SSimran Rai 
55a6ee05d9SSimran Rai #define BF_SRC_CTRL0_OFFSET 0x1c0
56a6ee05d9SSimran Rai #define BF_SRC_CTRL1_OFFSET 0x1c4
57a6ee05d9SSimran Rai #define BF_SRC_CTRL2_OFFSET 0x1c8
58a6ee05d9SSimran Rai #define BF_SRC_CTRL3_OFFSET 0x1cc
59a6ee05d9SSimran Rai 
60a6ee05d9SSimran Rai #define BF_SRC_GRP0_OFFSET  0x1fc
61a6ee05d9SSimran Rai #define BF_SRC_GRP1_OFFSET  0x200
62a6ee05d9SSimran Rai #define BF_SRC_GRP2_OFFSET  0x204
63a6ee05d9SSimran Rai #define BF_SRC_GRP3_OFFSET  0x208
64a6ee05d9SSimran Rai 
65a6ee05d9SSimran Rai #define BF_SRC_GRP_EN_OFFSET        0x320
66a6ee05d9SSimran Rai #define BF_SRC_GRP_FLOWON_OFFSET    0x324
67a6ee05d9SSimran Rai #define BF_SRC_GRP_SYNC_DIS_OFFSET  0x328
68a6ee05d9SSimran Rai 
69a6ee05d9SSimran Rai /* AUD_FMM_IOP_OUT_I2S_xxx regs */
70a6ee05d9SSimran Rai #define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00
71a6ee05d9SSimran Rai #define OUT_I2S_0_CFG_OFFSET        0xa04
72a6ee05d9SSimran Rai #define OUT_I2S_0_MCLK_CFG_OFFSET   0xa0c
73a6ee05d9SSimran Rai 
74a6ee05d9SSimran Rai #define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40
75a6ee05d9SSimran Rai #define OUT_I2S_1_CFG_OFFSET        0xa44
76a6ee05d9SSimran Rai #define OUT_I2S_1_MCLK_CFG_OFFSET   0xa4c
77a6ee05d9SSimran Rai 
78a6ee05d9SSimran Rai #define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80
79a6ee05d9SSimran Rai #define OUT_I2S_2_CFG_OFFSET        0xa84
80a6ee05d9SSimran Rai #define OUT_I2S_2_MCLK_CFG_OFFSET   0xa8c
81a6ee05d9SSimran Rai 
82a6ee05d9SSimran Rai /* AUD_FMM_IOP_OUT_SPDIF_xxx regs */
83a6ee05d9SSimran Rai #define SPDIF_STREAM_CFG_OFFSET  0xac0
84a6ee05d9SSimran Rai #define SPDIF_CTRL_OFFSET        0xac4
85a6ee05d9SSimran Rai #define SPDIF_FORMAT_CFG_OFFSET  0xad8
86a6ee05d9SSimran Rai #define SPDIF_MCLK_CFG_OFFSET    0xadc
87a6ee05d9SSimran Rai 
88a6ee05d9SSimran Rai /* AUD_FMM_IOP_PLL_0_xxx regs */
89a6ee05d9SSimran Rai #define IOP_PLL_0_MACRO_OFFSET    0xb00
90a6ee05d9SSimran Rai #define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14
91a6ee05d9SSimran Rai #define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18
92a6ee05d9SSimran Rai #define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c
93a6ee05d9SSimran Rai 
94a6ee05d9SSimran Rai #define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30
95a6ee05d9SSimran Rai #define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34
96a6ee05d9SSimran Rai #define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38
97a6ee05d9SSimran Rai 
98a6ee05d9SSimran Rai /* AUD_FMM_IOP_xxx regs */
99a6ee05d9SSimran Rai #define IOP_PLL_0_CONTROL_OFFSET     0xb04
100a6ee05d9SSimran Rai #define IOP_PLL_0_USER_NDIV_OFFSET   0xb08
101a6ee05d9SSimran Rai #define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20
102a6ee05d9SSimran Rai #define IOP_PLL_0_RESET_OFFSET       0xb5c
103a6ee05d9SSimran Rai 
104a6ee05d9SSimran Rai /* AUD_FMM_IOP_IN_I2S_xxx regs */
105a6ee05d9SSimran Rai #define IN_I2S_0_STREAM_CFG_OFFSET 0x00
106a6ee05d9SSimran Rai #define IN_I2S_0_CFG_OFFSET        0x04
107a6ee05d9SSimran Rai #define IN_I2S_1_STREAM_CFG_OFFSET 0x40
108a6ee05d9SSimran Rai #define IN_I2S_1_CFG_OFFSET        0x44
109a6ee05d9SSimran Rai #define IN_I2S_2_STREAM_CFG_OFFSET 0x80
110a6ee05d9SSimran Rai #define IN_I2S_2_CFG_OFFSET        0x84
111a6ee05d9SSimran Rai 
112a6ee05d9SSimran Rai /* AUD_FMM_IOP_MISC_xxx regs */
113a6ee05d9SSimran Rai #define IOP_SW_INIT_LOGIC          0x1c0
114a6ee05d9SSimran Rai 
115a6ee05d9SSimran Rai /* End register offset defines */
116a6ee05d9SSimran Rai 
117a6ee05d9SSimran Rai 
118a6ee05d9SSimran Rai /* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */
119a6ee05d9SSimran Rai #define I2S_OUT_MCLKRATE_SHIFT 16
120a6ee05d9SSimran Rai 
121a6ee05d9SSimran Rai /* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */
122a6ee05d9SSimran Rai #define I2S_OUT_PLLCLKSEL_SHIFT  0
123a6ee05d9SSimran Rai 
124a6ee05d9SSimran Rai /* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */
125a6ee05d9SSimran Rai #define I2S_OUT_STREAM_ENA  31
126a6ee05d9SSimran Rai #define I2S_OUT_STREAM_CFG_GROUP_ID  20
127a6ee05d9SSimran Rai #define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING  24
128a6ee05d9SSimran Rai 
129a6ee05d9SSimran Rai /* AUD_FMM_IOP_IN_I2S_x_CAP */
130a6ee05d9SSimran Rai #define I2S_IN_STREAM_CFG_CAP_ENA   31
131a6ee05d9SSimran Rai #define I2S_IN_STREAM_CFG_0_GROUP_ID 4
132a6ee05d9SSimran Rai 
133a6ee05d9SSimran Rai /* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */
134a6ee05d9SSimran Rai #define I2S_OUT_CFGX_CLK_ENA         0
135a6ee05d9SSimran Rai #define I2S_OUT_CFGX_DATA_ENABLE     1
136a6ee05d9SSimran Rai #define I2S_OUT_CFGX_DATA_ALIGNMENT  6
137a6ee05d9SSimran Rai #define I2S_OUT_CFGX_BITS_PER_SLOT  13
138a6ee05d9SSimran Rai #define I2S_OUT_CFGX_VALID_SLOT     14
139a6ee05d9SSimran Rai #define I2S_OUT_CFGX_FSYNC_WIDTH    18
140a6ee05d9SSimran Rai #define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26
141a6ee05d9SSimran Rai #define I2S_OUT_CFGX_SLAVE_MODE     30
142a6ee05d9SSimran Rai #define I2S_OUT_CFGX_TDM_MODE       31
143a6ee05d9SSimran Rai 
144a6ee05d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */
145a6ee05d9SSimran Rai #define BF_SRC_CFGX_SFIFO_ENA              0
146a6ee05d9SSimran Rai #define BF_SRC_CFGX_BUFFER_PAIR_ENABLE     1
147a6ee05d9SSimran Rai #define BF_SRC_CFGX_SAMPLE_CH_MODE         2
148a6ee05d9SSimran Rai #define BF_SRC_CFGX_SFIFO_SZ_DOUBLE        5
149a6ee05d9SSimran Rai #define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY  10
150a6ee05d9SSimran Rai #define BF_SRC_CFGX_BIT_RES               20
151a6ee05d9SSimran Rai #define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID  31
152a6ee05d9SSimran Rai 
153a6ee05d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */
154a6ee05d9SSimran Rai #define BF_DST_CFGX_CAP_ENA              0
155a6ee05d9SSimran Rai #define BF_DST_CFGX_BUFFER_PAIR_ENABLE   1
156a6ee05d9SSimran Rai #define BF_DST_CFGX_DFIFO_SZ_DOUBLE      2
157a6ee05d9SSimran Rai #define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11
158a6ee05d9SSimran Rai #define BF_DST_CFGX_FCI_ID              12
159a6ee05d9SSimran Rai #define BF_DST_CFGX_CAP_MODE            24
160a6ee05d9SSimran Rai #define BF_DST_CFGX_PROC_SEQ_ID_VALID   31
161a6ee05d9SSimran Rai 
162a6ee05d9SSimran Rai /* AUD_FMM_IOP_OUT_SPDIF_xxx */
163a6ee05d9SSimran Rai #define SPDIF_0_OUT_DITHER_ENA     3
164a6ee05d9SSimran Rai #define SPDIF_0_OUT_STREAM_ENA    31
165a6ee05d9SSimran Rai 
166a6ee05d9SSimran Rai /* AUD_FMM_IOP_PLL_0_USER */
167a6ee05d9SSimran Rai #define IOP_PLL_0_USER_NDIV_FRAC   10
168a6ee05d9SSimran Rai 
169a6ee05d9SSimran Rai /* AUD_FMM_IOP_PLL_0_ACTIVE */
170a6ee05d9SSimran Rai #define IOP_PLL_0_ACTIVE_NDIV_FRAC 10
171a6ee05d9SSimran Rai 
172a6ee05d9SSimran Rai 
173a6ee05d9SSimran Rai #define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \
174a6ee05d9SSimran Rai 		.i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \
175a6ee05d9SSimran Rai 		.i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \
176a6ee05d9SSimran Rai 		.i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \
177a6ee05d9SSimran Rai 		.i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \
178a6ee05d9SSimran Rai 		.i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \
179a6ee05d9SSimran Rai 		.bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \
180a6ee05d9SSimran Rai 		.bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \
181a6ee05d9SSimran Rai 		.bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \
182a6ee05d9SSimran Rai 		.bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \
183a6ee05d9SSimran Rai 		.bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \
184a6ee05d9SSimran Rai }
185a6ee05d9SSimran Rai 
186a6ee05d9SSimran Rai struct pll_macro_entry {
187a6ee05d9SSimran Rai 	u32 mclk;
188a6ee05d9SSimran Rai 	u32 pll_ch_num;
189a6ee05d9SSimran Rai };
190a6ee05d9SSimran Rai 
191a6ee05d9SSimran Rai /*
192a6ee05d9SSimran Rai  * PLL has 3 output channels (1x, 2x, and 4x). Below are
193a6ee05d9SSimran Rai  * the common MCLK frequencies used by audio driver
194a6ee05d9SSimran Rai  */
195a6ee05d9SSimran Rai static const struct pll_macro_entry pll_predef_mclk[] = {
196a6ee05d9SSimran Rai 	{ 4096000, 0},
197a6ee05d9SSimran Rai 	{ 8192000, 1},
198a6ee05d9SSimran Rai 	{16384000, 2},
199a6ee05d9SSimran Rai 
200a6ee05d9SSimran Rai 	{ 5644800, 0},
201a6ee05d9SSimran Rai 	{11289600, 1},
202a6ee05d9SSimran Rai 	{22579200, 2},
203a6ee05d9SSimran Rai 
204a6ee05d9SSimran Rai 	{ 6144000, 0},
205a6ee05d9SSimran Rai 	{12288000, 1},
206a6ee05d9SSimran Rai 	{24576000, 2},
207a6ee05d9SSimran Rai 
208a6ee05d9SSimran Rai 	{12288000, 0},
209a6ee05d9SSimran Rai 	{24576000, 1},
210a6ee05d9SSimran Rai 	{49152000, 2},
211a6ee05d9SSimran Rai 
212a6ee05d9SSimran Rai 	{22579200, 0},
213a6ee05d9SSimran Rai 	{45158400, 1},
214a6ee05d9SSimran Rai 	{90316800, 2},
215a6ee05d9SSimran Rai 
216a6ee05d9SSimran Rai 	{24576000, 0},
217a6ee05d9SSimran Rai 	{49152000, 1},
218a6ee05d9SSimran Rai 	{98304000, 2},
219a6ee05d9SSimran Rai };
220a6ee05d9SSimran Rai 
2218937ea0fSLori Hikichi #define CYGNUS_RATE_MIN     8000
2228937ea0fSLori Hikichi #define CYGNUS_RATE_MAX   384000
2238937ea0fSLori Hikichi 
224a6ee05d9SSimran Rai /* List of valid frame sizes for tdm mode */
225a6ee05d9SSimran Rai static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};
226a6ee05d9SSimran Rai 
2278937ea0fSLori Hikichi static const unsigned int cygnus_rates[] = {
2288937ea0fSLori Hikichi 	 8000, 11025,  16000,  22050,  32000,  44100, 48000,
2298937ea0fSLori Hikichi 	88200, 96000, 176400, 192000, 352800, 384000
230a6ee05d9SSimran Rai };
231a6ee05d9SSimran Rai 
2328937ea0fSLori Hikichi static const struct snd_pcm_hw_constraint_list cygnus_rate_constraint = {
2338937ea0fSLori Hikichi 	.count = ARRAY_SIZE(cygnus_rates),
2348937ea0fSLori Hikichi 	.list = cygnus_rates,
235a6ee05d9SSimran Rai };
236a6ee05d9SSimran Rai 
cygnus_dai_get_portinfo(struct snd_soc_dai * dai)237a6ee05d9SSimran Rai static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai)
238a6ee05d9SSimran Rai {
239a6ee05d9SSimran Rai 	struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
240a6ee05d9SSimran Rai 
241a6ee05d9SSimran Rai 	return &cygaud->portinfo[dai->id];
242a6ee05d9SSimran Rai }
243a6ee05d9SSimran Rai 
audio_ssp_init_portregs(struct cygnus_aio_port * aio)244a6ee05d9SSimran Rai static int audio_ssp_init_portregs(struct cygnus_aio_port *aio)
245a6ee05d9SSimran Rai {
246a6ee05d9SSimran Rai 	u32 value, fci_id;
247a6ee05d9SSimran Rai 	int status = 0;
248a6ee05d9SSimran Rai 
249a6ee05d9SSimran Rai 	switch (aio->port_type) {
250a6ee05d9SSimran Rai 	case PORT_TDM:
251a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
252a6ee05d9SSimran Rai 		value &= ~I2S_STREAM_CFG_MASK;
253a6ee05d9SSimran Rai 
254a6ee05d9SSimran Rai 		/* Set Group ID */
255a6ee05d9SSimran Rai 		writel(aio->portnum,
256a6ee05d9SSimran Rai 			aio->cygaud->audio + aio->regs.bf_sourcech_grp);
257a6ee05d9SSimran Rai 
258a6ee05d9SSimran Rai 		/* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */
259a6ee05d9SSimran Rai 		value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID;
260a6ee05d9SSimran Rai 		value |= aio->portnum; /* FCI ID is the port num */
261a6ee05d9SSimran Rai 		value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING;
262a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
263a6ee05d9SSimran Rai 
264a6ee05d9SSimran Rai 		/* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */
265a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
266a6ee05d9SSimran Rai 		value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
267a6ee05d9SSimran Rai 		value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
268a6ee05d9SSimran Rai 		value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
269a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
270a6ee05d9SSimran Rai 
271a6ee05d9SSimran Rai 		/* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */
272a6ee05d9SSimran Rai 		value = readl(aio->cygaud->i2s_in +
273a6ee05d9SSimran Rai 			aio->regs.i2s_cap_stream_cfg);
274a6ee05d9SSimran Rai 		value &= ~I2S_CAP_STREAM_CFG_MASK;
275a6ee05d9SSimran Rai 		value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID;
276a6ee05d9SSimran Rai 		writel(value, aio->cygaud->i2s_in +
277a6ee05d9SSimran Rai 			aio->regs.i2s_cap_stream_cfg);
278a6ee05d9SSimran Rai 
279a6ee05d9SSimran Rai 		/* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */
280a6ee05d9SSimran Rai 		fci_id = CAPTURE_FCI_ID_BASE + aio->portnum;
281a6ee05d9SSimran Rai 
282a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
283a6ee05d9SSimran Rai 		value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE);
284a6ee05d9SSimran Rai 		value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL);
285a6ee05d9SSimran Rai 		value |= (fci_id << BF_DST_CFGX_FCI_ID);
286a6ee05d9SSimran Rai 		value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID);
287a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
288a6ee05d9SSimran Rai 
289a6ee05d9SSimran Rai 		/* Enable the transmit pin for this port */
290a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
291a6ee05d9SSimran Rai 		value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE);
292a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
293a6ee05d9SSimran Rai 		break;
294a6ee05d9SSimran Rai 	case PORT_SPDIF:
295a6ee05d9SSimran Rai 		writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET);
296a6ee05d9SSimran Rai 
297a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET);
298a6ee05d9SSimran Rai 		value |= BIT(SPDIF_0_OUT_DITHER_ENA);
299a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET);
300a6ee05d9SSimran Rai 
301a6ee05d9SSimran Rai 		/* Enable and set the FCI ID for the SPDIF channel */
302a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
303a6ee05d9SSimran Rai 		value &= ~SPDIF_STREAM_CFG_MASK;
304a6ee05d9SSimran Rai 		value |= aio->portnum; /* FCI ID is the port num */
305a6ee05d9SSimran Rai 		value |= BIT(SPDIF_0_OUT_STREAM_ENA);
306a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
307a6ee05d9SSimran Rai 
308a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
309a6ee05d9SSimran Rai 		value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
310a6ee05d9SSimran Rai 		value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
311a6ee05d9SSimran Rai 		value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
312a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
313a6ee05d9SSimran Rai 
314a6ee05d9SSimran Rai 		/* Enable the spdif output pin */
315a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
316a6ee05d9SSimran Rai 		value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE);
317a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
318a6ee05d9SSimran Rai 		break;
319a6ee05d9SSimran Rai 	default:
320a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev, "Port not supported\n");
321a6ee05d9SSimran Rai 		status = -EINVAL;
322a6ee05d9SSimran Rai 	}
323a6ee05d9SSimran Rai 
324a6ee05d9SSimran Rai 	return status;
325a6ee05d9SSimran Rai }
326a6ee05d9SSimran Rai 
audio_ssp_in_enable(struct cygnus_aio_port * aio)327a6ee05d9SSimran Rai static void audio_ssp_in_enable(struct cygnus_aio_port *aio)
328a6ee05d9SSimran Rai {
329a6ee05d9SSimran Rai 	u32 value;
330a6ee05d9SSimran Rai 
331a6ee05d9SSimran Rai 	value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
332a6ee05d9SSimran Rai 	value |= BIT(BF_DST_CFGX_CAP_ENA);
333a6ee05d9SSimran Rai 	writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
334a6ee05d9SSimran Rai 
335a6ee05d9SSimran Rai 	writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
336a6ee05d9SSimran Rai 
337a6ee05d9SSimran Rai 	value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
338a6ee05d9SSimran Rai 	value |= BIT(I2S_OUT_CFGX_CLK_ENA);
339a6ee05d9SSimran Rai 	value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
340a6ee05d9SSimran Rai 	writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
341a6ee05d9SSimran Rai 
342a6ee05d9SSimran Rai 	value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
343a6ee05d9SSimran Rai 	value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA);
344a6ee05d9SSimran Rai 	writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
345a6ee05d9SSimran Rai 
346a6ee05d9SSimran Rai 	aio->streams_on |= CAPTURE_STREAM_MASK;
347a6ee05d9SSimran Rai }
348a6ee05d9SSimran Rai 
audio_ssp_in_disable(struct cygnus_aio_port * aio)349a6ee05d9SSimran Rai static void audio_ssp_in_disable(struct cygnus_aio_port *aio)
350a6ee05d9SSimran Rai {
351a6ee05d9SSimran Rai 	u32 value;
352a6ee05d9SSimran Rai 
353a6ee05d9SSimran Rai 	value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
354a6ee05d9SSimran Rai 	value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA);
355a6ee05d9SSimran Rai 	writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
356a6ee05d9SSimran Rai 
357a6ee05d9SSimran Rai 	aio->streams_on &= ~CAPTURE_STREAM_MASK;
358a6ee05d9SSimran Rai 
359a6ee05d9SSimran Rai 	/* If both playback and capture are off */
360a6ee05d9SSimran Rai 	if (!aio->streams_on) {
361a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
362a6ee05d9SSimran Rai 		value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
363a6ee05d9SSimran Rai 		value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
364a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
365a6ee05d9SSimran Rai 	}
366a6ee05d9SSimran Rai 
367a6ee05d9SSimran Rai 	writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
368a6ee05d9SSimran Rai 
369a6ee05d9SSimran Rai 	value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
370a6ee05d9SSimran Rai 	value &= ~BIT(BF_DST_CFGX_CAP_ENA);
371a6ee05d9SSimran Rai 	writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
372a6ee05d9SSimran Rai }
373a6ee05d9SSimran Rai 
audio_ssp_out_enable(struct cygnus_aio_port * aio)374a6ee05d9SSimran Rai static int audio_ssp_out_enable(struct cygnus_aio_port *aio)
375a6ee05d9SSimran Rai {
376a6ee05d9SSimran Rai 	u32 value;
377a6ee05d9SSimran Rai 	int status = 0;
378a6ee05d9SSimran Rai 
379a6ee05d9SSimran Rai 	switch (aio->port_type) {
380a6ee05d9SSimran Rai 	case PORT_TDM:
381a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
382a6ee05d9SSimran Rai 		value |= BIT(I2S_OUT_STREAM_ENA);
383a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
384a6ee05d9SSimran Rai 
385a6ee05d9SSimran Rai 		writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
386a6ee05d9SSimran Rai 
387a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
388a6ee05d9SSimran Rai 		value |= BIT(I2S_OUT_CFGX_CLK_ENA);
389a6ee05d9SSimran Rai 		value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
390a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
391a6ee05d9SSimran Rai 
392a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
393a6ee05d9SSimran Rai 		value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
394a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
395a6ee05d9SSimran Rai 
396a6ee05d9SSimran Rai 		aio->streams_on |= PLAYBACK_STREAM_MASK;
397a6ee05d9SSimran Rai 		break;
398a6ee05d9SSimran Rai 	case PORT_SPDIF:
399a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
400a6ee05d9SSimran Rai 		value |= 0x3;
401a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
402a6ee05d9SSimran Rai 
403a6ee05d9SSimran Rai 		writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
404a6ee05d9SSimran Rai 
405a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
406a6ee05d9SSimran Rai 		value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
407a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
408a6ee05d9SSimran Rai 		break;
409a6ee05d9SSimran Rai 	default:
410a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev,
411a6ee05d9SSimran Rai 			"Port not supported %d\n", aio->portnum);
412a6ee05d9SSimran Rai 		status = -EINVAL;
413a6ee05d9SSimran Rai 	}
414a6ee05d9SSimran Rai 
415a6ee05d9SSimran Rai 	return status;
416a6ee05d9SSimran Rai }
417a6ee05d9SSimran Rai 
audio_ssp_out_disable(struct cygnus_aio_port * aio)418a6ee05d9SSimran Rai static int audio_ssp_out_disable(struct cygnus_aio_port *aio)
419a6ee05d9SSimran Rai {
420a6ee05d9SSimran Rai 	u32 value;
421a6ee05d9SSimran Rai 	int status = 0;
422a6ee05d9SSimran Rai 
423a6ee05d9SSimran Rai 	switch (aio->port_type) {
424a6ee05d9SSimran Rai 	case PORT_TDM:
425a6ee05d9SSimran Rai 		aio->streams_on &= ~PLAYBACK_STREAM_MASK;
426a6ee05d9SSimran Rai 
427a6ee05d9SSimran Rai 		/* If both playback and capture are off */
428a6ee05d9SSimran Rai 		if (!aio->streams_on) {
429a6ee05d9SSimran Rai 			value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
430a6ee05d9SSimran Rai 			value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
431a6ee05d9SSimran Rai 			value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
432a6ee05d9SSimran Rai 			writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
433a6ee05d9SSimran Rai 		}
434a6ee05d9SSimran Rai 
435a6ee05d9SSimran Rai 		/* set group_sync_dis = 1 */
436a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
437a6ee05d9SSimran Rai 		value |= BIT(aio->portnum);
438a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
439a6ee05d9SSimran Rai 
440a6ee05d9SSimran Rai 		writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
441a6ee05d9SSimran Rai 
442a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
443a6ee05d9SSimran Rai 		value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
444a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
445a6ee05d9SSimran Rai 
446a6ee05d9SSimran Rai 		/* set group_sync_dis = 0 */
447a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
448a6ee05d9SSimran Rai 		value &= ~BIT(aio->portnum);
449a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
450a6ee05d9SSimran Rai 
451a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
452a6ee05d9SSimran Rai 		value &= ~BIT(I2S_OUT_STREAM_ENA);
453a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
454a6ee05d9SSimran Rai 
455a6ee05d9SSimran Rai 		/* IOP SW INIT on OUT_I2S_x */
456a6ee05d9SSimran Rai 		value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
457a6ee05d9SSimran Rai 		value |= BIT(aio->portnum);
458a6ee05d9SSimran Rai 		writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
459a6ee05d9SSimran Rai 		value &= ~BIT(aio->portnum);
460a6ee05d9SSimran Rai 		writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
461a6ee05d9SSimran Rai 		break;
462a6ee05d9SSimran Rai 	case PORT_SPDIF:
463a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
464a6ee05d9SSimran Rai 		value &= ~0x3;
465a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
466a6ee05d9SSimran Rai 		writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
467a6ee05d9SSimran Rai 
468a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
469a6ee05d9SSimran Rai 		value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
470a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
471a6ee05d9SSimran Rai 		break;
472a6ee05d9SSimran Rai 	default:
473a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev,
474a6ee05d9SSimran Rai 			"Port not supported %d\n", aio->portnum);
475a6ee05d9SSimran Rai 		status = -EINVAL;
476a6ee05d9SSimran Rai 	}
477a6ee05d9SSimran Rai 
478a6ee05d9SSimran Rai 	return status;
479a6ee05d9SSimran Rai }
480a6ee05d9SSimran Rai 
pll_configure_mclk(struct cygnus_audio * cygaud,u32 mclk,struct cygnus_aio_port * aio)481a6ee05d9SSimran Rai static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk,
482a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio)
483a6ee05d9SSimran Rai {
484a6ee05d9SSimran Rai 	int i = 0, error;
485a6ee05d9SSimran Rai 	bool found = false;
486a6ee05d9SSimran Rai 	const struct pll_macro_entry *p_entry;
487a6ee05d9SSimran Rai 	struct clk *ch_clk;
488a6ee05d9SSimran Rai 
489a6ee05d9SSimran Rai 	for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) {
490a6ee05d9SSimran Rai 		p_entry = &pll_predef_mclk[i];
491a6ee05d9SSimran Rai 		if (p_entry->mclk == mclk) {
492a6ee05d9SSimran Rai 			found = true;
493a6ee05d9SSimran Rai 			break;
494a6ee05d9SSimran Rai 		}
495a6ee05d9SSimran Rai 	}
496a6ee05d9SSimran Rai 	if (!found) {
497a6ee05d9SSimran Rai 		dev_err(cygaud->dev,
498a6ee05d9SSimran Rai 			"%s No valid mclk freq (%u) found!\n", __func__, mclk);
499a6ee05d9SSimran Rai 		return -EINVAL;
500a6ee05d9SSimran Rai 	}
501a6ee05d9SSimran Rai 
502a6ee05d9SSimran Rai 	ch_clk = cygaud->audio_clk[p_entry->pll_ch_num];
503a6ee05d9SSimran Rai 
504a6ee05d9SSimran Rai 	if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) {
505a6ee05d9SSimran Rai 		error = clk_prepare_enable(ch_clk);
506a6ee05d9SSimran Rai 		if (error) {
507a6ee05d9SSimran Rai 			dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
508a6ee05d9SSimran Rai 				__func__, error);
509a6ee05d9SSimran Rai 			return error;
510a6ee05d9SSimran Rai 		}
511a6ee05d9SSimran Rai 		aio->clk_trace.cap_clk_en = true;
512a6ee05d9SSimran Rai 	}
513a6ee05d9SSimran Rai 
514a6ee05d9SSimran Rai 	if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) {
515a6ee05d9SSimran Rai 		error = clk_prepare_enable(ch_clk);
516a6ee05d9SSimran Rai 		if (error) {
517a6ee05d9SSimran Rai 			dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
518a6ee05d9SSimran Rai 				__func__, error);
519a6ee05d9SSimran Rai 			return error;
520a6ee05d9SSimran Rai 		}
521a6ee05d9SSimran Rai 		aio->clk_trace.play_clk_en = true;
522a6ee05d9SSimran Rai 	}
523a6ee05d9SSimran Rai 
524a6ee05d9SSimran Rai 	error = clk_set_rate(ch_clk, mclk);
525a6ee05d9SSimran Rai 	if (error) {
526a6ee05d9SSimran Rai 		dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n",
527a6ee05d9SSimran Rai 			__func__, error);
528a6ee05d9SSimran Rai 		return error;
529a6ee05d9SSimran Rai 	}
530a6ee05d9SSimran Rai 
531a6ee05d9SSimran Rai 	return p_entry->pll_ch_num;
532a6ee05d9SSimran Rai }
533a6ee05d9SSimran Rai 
cygnus_ssp_set_clocks(struct cygnus_aio_port * aio)5348937ea0fSLori Hikichi static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio)
535a6ee05d9SSimran Rai {
5368937ea0fSLori Hikichi 	u32 value;
537a6ee05d9SSimran Rai 	u32 mask = 0xf;
538a6ee05d9SSimran Rai 	u32 sclk;
5398937ea0fSLori Hikichi 	u32 mclk_rate;
5408937ea0fSLori Hikichi 	unsigned int bit_rate;
5418937ea0fSLori Hikichi 	unsigned int ratio;
542a6ee05d9SSimran Rai 
5438937ea0fSLori Hikichi 	bit_rate = aio->bit_per_frame * aio->lrclk;
5448937ea0fSLori Hikichi 
5458937ea0fSLori Hikichi 	/*
5468937ea0fSLori Hikichi 	 * Check if the bit clock can be generated from the given MCLK.
5478937ea0fSLori Hikichi 	 * MCLK must be a perfect multiple of bit clock and must be one of the
5488937ea0fSLori Hikichi 	 * following values... (2,4,6,8,10,12,14)
5498937ea0fSLori Hikichi 	 */
5508937ea0fSLori Hikichi 	if ((aio->mclk % bit_rate) != 0)
5518937ea0fSLori Hikichi 		return -EINVAL;
5528937ea0fSLori Hikichi 
5538937ea0fSLori Hikichi 	ratio = aio->mclk / bit_rate;
5548937ea0fSLori Hikichi 	switch (ratio) {
5558937ea0fSLori Hikichi 	case 2:
5568937ea0fSLori Hikichi 	case 4:
5578937ea0fSLori Hikichi 	case 6:
5588937ea0fSLori Hikichi 	case 8:
5598937ea0fSLori Hikichi 	case 10:
5608937ea0fSLori Hikichi 	case 12:
5618937ea0fSLori Hikichi 	case 14:
5628937ea0fSLori Hikichi 		mclk_rate = ratio / 2;
563a6ee05d9SSimran Rai 		break;
5648937ea0fSLori Hikichi 
5658937ea0fSLori Hikichi 	default:
566a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev,
5678937ea0fSLori Hikichi 			"Invalid combination of MCLK and BCLK\n");
568a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",
569a6ee05d9SSimran Rai 			aio->lrclk, aio->bit_per_frame, aio->mclk);
570a6ee05d9SSimran Rai 		return -EINVAL;
571a6ee05d9SSimran Rai 	}
572a6ee05d9SSimran Rai 
573a6ee05d9SSimran Rai 	/* Set sclk rate */
574a6ee05d9SSimran Rai 	switch (aio->port_type) {
575a6ee05d9SSimran Rai 	case PORT_TDM:
5768937ea0fSLori Hikichi 		sclk = aio->bit_per_frame;
5778937ea0fSLori Hikichi 		if (sclk == 512)
5788937ea0fSLori Hikichi 			sclk = 0;
5798937ea0fSLori Hikichi 
5808937ea0fSLori Hikichi 		/* sclks_per_1fs_div = sclk cycles/32 */
5818937ea0fSLori Hikichi 		sclk /= 32;
5828937ea0fSLori Hikichi 
583a6ee05d9SSimran Rai 		/* Set number of bitclks per frame */
584a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
585a6ee05d9SSimran Rai 		value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32);
586a6ee05d9SSimran Rai 		value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32;
587a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
588a6ee05d9SSimran Rai 		dev_dbg(aio->cygaud->dev,
589a6ee05d9SSimran Rai 			"SCLKS_PER_1FS_DIV32 = 0x%x\n", value);
590a6ee05d9SSimran Rai 		break;
591a6ee05d9SSimran Rai 	case PORT_SPDIF:
592a6ee05d9SSimran Rai 		break;
593a6ee05d9SSimran Rai 	default:
594a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev, "Unknown port type\n");
595a6ee05d9SSimran Rai 		return -EINVAL;
596a6ee05d9SSimran Rai 	}
597a6ee05d9SSimran Rai 
598a6ee05d9SSimran Rai 	/* Set MCLK_RATE ssp port (spdif and ssp are the same) */
599a6ee05d9SSimran Rai 	value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
600a6ee05d9SSimran Rai 	value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT);
6018937ea0fSLori Hikichi 	value |= (mclk_rate << I2S_OUT_MCLKRATE_SHIFT);
602a6ee05d9SSimran Rai 	writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
603a6ee05d9SSimran Rai 
604a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value);
605a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n",
606a6ee05d9SSimran Rai 			aio->bit_per_frame, aio->mclk, aio->lrclk);
607a6ee05d9SSimran Rai 	return 0;
608a6ee05d9SSimran Rai }
609a6ee05d9SSimran Rai 
cygnus_ssp_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)610a6ee05d9SSimran Rai static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream,
611a6ee05d9SSimran Rai 				 struct snd_pcm_hw_params *params,
612a6ee05d9SSimran Rai 				 struct snd_soc_dai *dai)
613a6ee05d9SSimran Rai {
614a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
615a6ee05d9SSimran Rai 	int rate, bitres;
616a6ee05d9SSimran Rai 	u32 value;
617a6ee05d9SSimran Rai 	u32 mask = 0x1f;
618a6ee05d9SSimran Rai 	int ret = 0;
619a6ee05d9SSimran Rai 
620a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum);
621a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "params_channels %d\n",
622a6ee05d9SSimran Rai 			params_channels(params));
623a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params));
624a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params));
625a6ee05d9SSimran Rai 
626a6ee05d9SSimran Rai 	rate = params_rate(params);
627a6ee05d9SSimran Rai 
628a6ee05d9SSimran Rai 	switch (aio->mode) {
629a6ee05d9SSimran Rai 	case CYGNUS_SSPMODE_TDM:
630a6ee05d9SSimran Rai 		if ((rate == 192000) && (params_channels(params) > 4)) {
631a6ee05d9SSimran Rai 			dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n",
632a6ee05d9SSimran Rai 				params_channels(params), rate);
633a6ee05d9SSimran Rai 			return -EINVAL;
634a6ee05d9SSimran Rai 		}
635a6ee05d9SSimran Rai 		break;
636a6ee05d9SSimran Rai 	case CYGNUS_SSPMODE_I2S:
637a6ee05d9SSimran Rai 		aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */
638a6ee05d9SSimran Rai 		break;
639a6ee05d9SSimran Rai 	default:
640a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev,
641a6ee05d9SSimran Rai 			"%s port running in unknown mode\n", __func__);
642a6ee05d9SSimran Rai 		return -EINVAL;
643a6ee05d9SSimran Rai 	}
644a6ee05d9SSimran Rai 
645a6ee05d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
646a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
647a6ee05d9SSimran Rai 		value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE);
648a6ee05d9SSimran Rai 		value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
649a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
650a6ee05d9SSimran Rai 
651a6ee05d9SSimran Rai 		switch (params_format(params)) {
652a6ee05d9SSimran Rai 		case SNDRV_PCM_FORMAT_S16_LE:
653a6ee05d9SSimran Rai 			bitres = 16;
654a6ee05d9SSimran Rai 			break;
655a6ee05d9SSimran Rai 
656a6ee05d9SSimran Rai 		case SNDRV_PCM_FORMAT_S32_LE:
657a6ee05d9SSimran Rai 			/* 32 bit mode is coded as 0 */
658a6ee05d9SSimran Rai 			bitres = 0;
659a6ee05d9SSimran Rai 			break;
660a6ee05d9SSimran Rai 
661a6ee05d9SSimran Rai 		default:
662a6ee05d9SSimran Rai 			return -EINVAL;
663a6ee05d9SSimran Rai 		}
664a6ee05d9SSimran Rai 
665a6ee05d9SSimran Rai 		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
666a6ee05d9SSimran Rai 		value &= ~(mask << BF_SRC_CFGX_BIT_RES);
667a6ee05d9SSimran Rai 		value |= (bitres << BF_SRC_CFGX_BIT_RES);
668a6ee05d9SSimran Rai 		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
669a6ee05d9SSimran Rai 
670a6ee05d9SSimran Rai 	} else {
671a6ee05d9SSimran Rai 
672a6ee05d9SSimran Rai 		switch (params_format(params)) {
673a6ee05d9SSimran Rai 		case SNDRV_PCM_FORMAT_S16_LE:
674a6ee05d9SSimran Rai 			value = readl(aio->cygaud->audio +
675a6ee05d9SSimran Rai 					aio->regs.bf_destch_cfg);
676a6ee05d9SSimran Rai 			value |= BIT(BF_DST_CFGX_CAP_MODE);
677a6ee05d9SSimran Rai 			writel(value, aio->cygaud->audio +
678a6ee05d9SSimran Rai 					aio->regs.bf_destch_cfg);
679a6ee05d9SSimran Rai 			break;
680a6ee05d9SSimran Rai 
681a6ee05d9SSimran Rai 		case SNDRV_PCM_FORMAT_S32_LE:
682a6ee05d9SSimran Rai 			value = readl(aio->cygaud->audio +
683a6ee05d9SSimran Rai 					aio->regs.bf_destch_cfg);
684a6ee05d9SSimran Rai 			value &= ~BIT(BF_DST_CFGX_CAP_MODE);
685a6ee05d9SSimran Rai 			writel(value, aio->cygaud->audio +
686a6ee05d9SSimran Rai 					aio->regs.bf_destch_cfg);
687a6ee05d9SSimran Rai 			break;
688a6ee05d9SSimran Rai 
689a6ee05d9SSimran Rai 		default:
690a6ee05d9SSimran Rai 			return -EINVAL;
691a6ee05d9SSimran Rai 		}
692a6ee05d9SSimran Rai 	}
693a6ee05d9SSimran Rai 
694a6ee05d9SSimran Rai 	aio->lrclk = rate;
695a6ee05d9SSimran Rai 
696a6ee05d9SSimran Rai 	if (!aio->is_slave)
6978937ea0fSLori Hikichi 		ret = cygnus_ssp_set_clocks(aio);
698a6ee05d9SSimran Rai 
699a6ee05d9SSimran Rai 	return ret;
700a6ee05d9SSimran Rai }
701a6ee05d9SSimran Rai 
702a6ee05d9SSimran Rai /*
703a6ee05d9SSimran Rai  * This function sets the mclk frequency for pll clock
704a6ee05d9SSimran Rai  */
cygnus_ssp_set_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)705a6ee05d9SSimran Rai static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai,
706a6ee05d9SSimran Rai 			int clk_id, unsigned int freq, int dir)
707a6ee05d9SSimran Rai {
708a6ee05d9SSimran Rai 	int sel;
709a6ee05d9SSimran Rai 	u32 value;
710a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
711a6ee05d9SSimran Rai 	struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
712a6ee05d9SSimran Rai 
713a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev,
714a6ee05d9SSimran Rai 		"%s Enter port = %d\n", __func__, aio->portnum);
715a6ee05d9SSimran Rai 	sel = pll_configure_mclk(cygaud, freq, aio);
716a6ee05d9SSimran Rai 	if (sel < 0) {
717a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev,
718a6ee05d9SSimran Rai 			"%s Setting mclk failed.\n", __func__);
719a6ee05d9SSimran Rai 		return -EINVAL;
720a6ee05d9SSimran Rai 	}
721a6ee05d9SSimran Rai 
722a6ee05d9SSimran Rai 	aio->mclk = freq;
723a6ee05d9SSimran Rai 
724a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel);
725a6ee05d9SSimran Rai 	value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
726a6ee05d9SSimran Rai 	value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT);
727a6ee05d9SSimran Rai 	value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT);
728a6ee05d9SSimran Rai 	writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
729a6ee05d9SSimran Rai 
730a6ee05d9SSimran Rai 	return 0;
731a6ee05d9SSimran Rai }
732a6ee05d9SSimran Rai 
cygnus_ssp_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)733a6ee05d9SSimran Rai static int cygnus_ssp_startup(struct snd_pcm_substream *substream,
734a6ee05d9SSimran Rai 			       struct snd_soc_dai *dai)
735a6ee05d9SSimran Rai {
736a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
737a6ee05d9SSimran Rai 
738a6ee05d9SSimran Rai 	snd_soc_dai_set_dma_data(dai, substream, aio);
739a6ee05d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
740a6ee05d9SSimran Rai 		aio->clk_trace.play_en = true;
741a6ee05d9SSimran Rai 	else
742a6ee05d9SSimran Rai 		aio->clk_trace.cap_en = true;
743a6ee05d9SSimran Rai 
7448937ea0fSLori Hikichi 	substream->runtime->hw.rate_min = CYGNUS_RATE_MIN;
7458937ea0fSLori Hikichi 	substream->runtime->hw.rate_max = CYGNUS_RATE_MAX;
7468937ea0fSLori Hikichi 
7478937ea0fSLori Hikichi 	snd_pcm_hw_constraint_list(substream->runtime, 0,
7488937ea0fSLori Hikichi 			SNDRV_PCM_HW_PARAM_RATE, &cygnus_rate_constraint);
749a6ee05d9SSimran Rai 	return 0;
750a6ee05d9SSimran Rai }
751a6ee05d9SSimran Rai 
cygnus_ssp_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)752a6ee05d9SSimran Rai static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream,
753a6ee05d9SSimran Rai 			       struct snd_soc_dai *dai)
754a6ee05d9SSimran Rai {
755a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
756a6ee05d9SSimran Rai 
757a6ee05d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
758a6ee05d9SSimran Rai 		aio->clk_trace.play_en = false;
759a6ee05d9SSimran Rai 	else
760a6ee05d9SSimran Rai 		aio->clk_trace.cap_en = false;
761a6ee05d9SSimran Rai 
762a6ee05d9SSimran Rai 	if (!aio->is_slave) {
763a6ee05d9SSimran Rai 		u32 val;
764a6ee05d9SSimran Rai 
765a6ee05d9SSimran Rai 		val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
766a6ee05d9SSimran Rai 		val &= CYGNUS_PLLCLKSEL_MASK;
767a6ee05d9SSimran Rai 		if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
768a6ee05d9SSimran Rai 			dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
769a6ee05d9SSimran Rai 				val);
770a6ee05d9SSimran Rai 			return;
771a6ee05d9SSimran Rai 		}
772a6ee05d9SSimran Rai 
773a6ee05d9SSimran Rai 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
774a6ee05d9SSimran Rai 			if (aio->clk_trace.play_clk_en) {
775a6ee05d9SSimran Rai 				clk_disable_unprepare(aio->cygaud->
776a6ee05d9SSimran Rai 						audio_clk[val]);
777a6ee05d9SSimran Rai 				aio->clk_trace.play_clk_en = false;
778a6ee05d9SSimran Rai 			}
779a6ee05d9SSimran Rai 		} else {
780a6ee05d9SSimran Rai 			if (aio->clk_trace.cap_clk_en) {
781a6ee05d9SSimran Rai 				clk_disable_unprepare(aio->cygaud->
782a6ee05d9SSimran Rai 						audio_clk[val]);
783a6ee05d9SSimran Rai 				aio->clk_trace.cap_clk_en = false;
784a6ee05d9SSimran Rai 			}
785a6ee05d9SSimran Rai 		}
786a6ee05d9SSimran Rai 	}
787a6ee05d9SSimran Rai }
788a6ee05d9SSimran Rai 
789a6ee05d9SSimran Rai /*
790a6ee05d9SSimran Rai  * Bit    Update  Notes
791a6ee05d9SSimran Rai  * 31     Yes     TDM Mode        (1 = TDM, 0 = i2s)
792a6ee05d9SSimran Rai  * 30     Yes     Slave Mode	  (1 = Slave, 0 = Master)
793a6ee05d9SSimran Rai  * 29:26  No      Sclks per frame
794a6ee05d9SSimran Rai  * 25:18  Yes     FS Width
795a6ee05d9SSimran Rai  * 17:14  No      Valid Slots
796a6ee05d9SSimran Rai  * 13     No      Bits		  (1 = 16 bits, 0 = 32 bits)
797a6ee05d9SSimran Rai  * 12:08  No     Bits per samp
798a6ee05d9SSimran Rai  * 07     Yes     Justifcation    (1 = LSB, 0 = MSB)
799a6ee05d9SSimran Rai  * 06     Yes     Alignment       (1 = Delay 1 clk, 0 = no delay
800a6ee05d9SSimran Rai  * 05     Yes     SCLK polarity   (1 = Rising, 0 = Falling)
801a6ee05d9SSimran Rai  * 04     Yes     LRCLK Polarity  (1 = High for left, 0 = Low for left)
802a6ee05d9SSimran Rai  * 03:02  Yes     Reserved - write as zero
803a6ee05d9SSimran Rai  * 01     No      Data Enable
804a6ee05d9SSimran Rai  * 00     No      CLK Enable
805a6ee05d9SSimran Rai  */
806a6ee05d9SSimran Rai #define I2S_OUT_CFG_REG_UPDATE_MASK   0x3C03FF03
807a6ee05d9SSimran Rai 
808a6ee05d9SSimran Rai /* Input cfg is same as output, but the FS width is not a valid field */
809a6ee05d9SSimran Rai #define I2S_IN_CFG_REG_UPDATE_MASK  (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000)
810a6ee05d9SSimran Rai 
cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai * cpu_dai,int len)811a6ee05d9SSimran Rai int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len)
812a6ee05d9SSimran Rai {
813a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
814a6ee05d9SSimran Rai 
815a6ee05d9SSimran Rai 	if ((len > 0) && (len < 256)) {
816a6ee05d9SSimran Rai 		aio->fsync_width = len;
817a6ee05d9SSimran Rai 		return 0;
818a6ee05d9SSimran Rai 	} else {
819a6ee05d9SSimran Rai 		return -EINVAL;
820a6ee05d9SSimran Rai 	}
821a6ee05d9SSimran Rai }
822d8302aa6SLori Hikichi EXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width);
823a6ee05d9SSimran Rai 
cygnus_ssp_set_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)824a6ee05d9SSimran Rai static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
825a6ee05d9SSimran Rai {
826a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
827a6ee05d9SSimran Rai 	u32 ssp_curcfg;
828a6ee05d9SSimran Rai 	u32 ssp_newcfg;
829a6ee05d9SSimran Rai 	u32 ssp_outcfg;
830a6ee05d9SSimran Rai 	u32 ssp_incfg;
831a6ee05d9SSimran Rai 	u32 val;
832a6ee05d9SSimran Rai 	u32 mask;
833a6ee05d9SSimran Rai 
834a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "%s Enter  fmt: %x\n", __func__, fmt);
835a6ee05d9SSimran Rai 
836a6ee05d9SSimran Rai 	if (aio->port_type == PORT_SPDIF)
837a6ee05d9SSimran Rai 		return -EINVAL;
838a6ee05d9SSimran Rai 
839a6ee05d9SSimran Rai 	ssp_newcfg = 0;
840a6ee05d9SSimran Rai 
841a91b0e5bSMark Brown 	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
84204ea2404SCharles Keepax 	case SND_SOC_DAIFMT_BC_FC:
843a6ee05d9SSimran Rai 		ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
844a6ee05d9SSimran Rai 		aio->is_slave = 1;
845a6ee05d9SSimran Rai 		break;
84604ea2404SCharles Keepax 	case SND_SOC_DAIFMT_BP_FP:
847a6ee05d9SSimran Rai 		ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
848a6ee05d9SSimran Rai 		aio->is_slave = 0;
849a6ee05d9SSimran Rai 		break;
850a6ee05d9SSimran Rai 	default:
851a6ee05d9SSimran Rai 		return -EINVAL;
852a6ee05d9SSimran Rai 	}
853a6ee05d9SSimran Rai 
854a6ee05d9SSimran Rai 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
855a6ee05d9SSimran Rai 	case SND_SOC_DAIFMT_I2S:
856a6ee05d9SSimran Rai 		ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
857a6ee05d9SSimran Rai 		ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
858a6ee05d9SSimran Rai 		aio->mode = CYGNUS_SSPMODE_I2S;
859a6ee05d9SSimran Rai 		break;
860a6ee05d9SSimran Rai 
861a6ee05d9SSimran Rai 	case SND_SOC_DAIFMT_DSP_A:
862a6ee05d9SSimran Rai 	case SND_SOC_DAIFMT_DSP_B:
863a6ee05d9SSimran Rai 		ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE);
864a6ee05d9SSimran Rai 
865a6ee05d9SSimran Rai 		/* DSP_A = data after FS, DSP_B = data during FS */
866a6ee05d9SSimran Rai 		if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)
867a6ee05d9SSimran Rai 			ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
868a6ee05d9SSimran Rai 
869a6ee05d9SSimran Rai 		if ((aio->fsync_width > 0) && (aio->fsync_width < 256))
870a6ee05d9SSimran Rai 			ssp_newcfg |=
871a6ee05d9SSimran Rai 				(aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH);
872a6ee05d9SSimran Rai 		else
873a6ee05d9SSimran Rai 			ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
874a6ee05d9SSimran Rai 
875a6ee05d9SSimran Rai 		aio->mode = CYGNUS_SSPMODE_TDM;
876a6ee05d9SSimran Rai 		break;
877a6ee05d9SSimran Rai 
878a6ee05d9SSimran Rai 	default:
879a6ee05d9SSimran Rai 		return -EINVAL;
880a6ee05d9SSimran Rai 	}
881a6ee05d9SSimran Rai 
882a6ee05d9SSimran Rai 	/*
883a6ee05d9SSimran Rai 	 * SSP out cfg.
884a6ee05d9SSimran Rai 	 * Retain bits we do not want to update, then OR in new bits
885a6ee05d9SSimran Rai 	 */
886a6ee05d9SSimran Rai 	ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
887a6ee05d9SSimran Rai 	ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg;
888a6ee05d9SSimran Rai 	writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg);
889a6ee05d9SSimran Rai 
890a6ee05d9SSimran Rai 	/*
891a6ee05d9SSimran Rai 	 * SSP in cfg.
892a6ee05d9SSimran Rai 	 * Retain bits we do not want to update, then OR in new bits
893a6ee05d9SSimran Rai 	 */
894a6ee05d9SSimran Rai 	ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
895a6ee05d9SSimran Rai 	ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg;
896a6ee05d9SSimran Rai 	writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
897a6ee05d9SSimran Rai 
898a6ee05d9SSimran Rai 	val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
899a6ee05d9SSimran Rai 
900a6ee05d9SSimran Rai 	/*
901a6ee05d9SSimran Rai 	 * Configure the word clk and bit clk as output or tristate
902a6ee05d9SSimran Rai 	 * Each port has 4 bits for controlling its pins.
903a6ee05d9SSimran Rai 	 * Shift the mask based upon port number.
904a6ee05d9SSimran Rai 	 */
905a6ee05d9SSimran Rai 	mask = BIT(AUD_MISC_SEROUT_LRCK_OE)
906a6ee05d9SSimran Rai 			| BIT(AUD_MISC_SEROUT_SCLK_OE)
907a6ee05d9SSimran Rai 			| BIT(AUD_MISC_SEROUT_MCLK_OE);
908a6ee05d9SSimran Rai 	mask = mask << (aio->portnum * 4);
909a6ee05d9SSimran Rai 	if (aio->is_slave)
910a6ee05d9SSimran Rai 		/* Set bit for tri-state */
911a6ee05d9SSimran Rai 		val |= mask;
912a6ee05d9SSimran Rai 	else
913a6ee05d9SSimran Rai 		/* Clear bit for drive */
914a6ee05d9SSimran Rai 		val &= ~mask;
915a6ee05d9SSimran Rai 
916a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "%s  Set OE bits 0x%x\n", __func__, val);
917a6ee05d9SSimran Rai 	writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
918a6ee05d9SSimran Rai 
919a6ee05d9SSimran Rai 	return 0;
920a6ee05d9SSimran Rai }
921a6ee05d9SSimran Rai 
cygnus_ssp_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)922a6ee05d9SSimran Rai static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
923a6ee05d9SSimran Rai 			       struct snd_soc_dai *dai)
924a6ee05d9SSimran Rai {
925a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
926a6ee05d9SSimran Rai 	struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
927a6ee05d9SSimran Rai 
928a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev,
929a6ee05d9SSimran Rai 		"%s cmd %d at port = %d\n", __func__, cmd, aio->portnum);
930a6ee05d9SSimran Rai 
931a6ee05d9SSimran Rai 	switch (cmd) {
932a6ee05d9SSimran Rai 	case SNDRV_PCM_TRIGGER_START:
933a6ee05d9SSimran Rai 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
934a6ee05d9SSimran Rai 	case SNDRV_PCM_TRIGGER_RESUME:
935a6ee05d9SSimran Rai 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
936a6ee05d9SSimran Rai 			audio_ssp_out_enable(aio);
937a6ee05d9SSimran Rai 		else
938a6ee05d9SSimran Rai 			audio_ssp_in_enable(aio);
939a6ee05d9SSimran Rai 		cygaud->active_ports++;
940a6ee05d9SSimran Rai 
941a6ee05d9SSimran Rai 		break;
942a6ee05d9SSimran Rai 
943a6ee05d9SSimran Rai 	case SNDRV_PCM_TRIGGER_STOP:
944a6ee05d9SSimran Rai 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
945a6ee05d9SSimran Rai 	case SNDRV_PCM_TRIGGER_SUSPEND:
946a6ee05d9SSimran Rai 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
947a6ee05d9SSimran Rai 			audio_ssp_out_disable(aio);
948a6ee05d9SSimran Rai 		else
949a6ee05d9SSimran Rai 			audio_ssp_in_disable(aio);
950a6ee05d9SSimran Rai 		cygaud->active_ports--;
951a6ee05d9SSimran Rai 		break;
952a6ee05d9SSimran Rai 
953a6ee05d9SSimran Rai 	default:
954a6ee05d9SSimran Rai 		return -EINVAL;
955a6ee05d9SSimran Rai 	}
956a6ee05d9SSimran Rai 
957a6ee05d9SSimran Rai 	return 0;
958a6ee05d9SSimran Rai }
959a6ee05d9SSimran Rai 
cygnus_set_dai_tdm_slot(struct snd_soc_dai * cpu_dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)960a6ee05d9SSimran Rai static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
961a6ee05d9SSimran Rai 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
962a6ee05d9SSimran Rai {
963a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
964a6ee05d9SSimran Rai 	u32 value;
965a6ee05d9SSimran Rai 	int bits_per_slot = 0;     /* default to 32-bits per slot */
966a6ee05d9SSimran Rai 	int frame_bits;
967a6ee05d9SSimran Rai 	unsigned int active_slots;
968a6ee05d9SSimran Rai 	bool found = false;
969a6ee05d9SSimran Rai 	int i;
970a6ee05d9SSimran Rai 
971a6ee05d9SSimran Rai 	if (tx_mask != rx_mask) {
972a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev,
973a6ee05d9SSimran Rai 			"%s tx_mask must equal rx_mask\n", __func__);
974a6ee05d9SSimran Rai 		return -EINVAL;
975a6ee05d9SSimran Rai 	}
976a6ee05d9SSimran Rai 
977a6ee05d9SSimran Rai 	active_slots = hweight32(tx_mask);
978a6ee05d9SSimran Rai 
9794c75968aSChristos Gkekas 	if (active_slots > 16)
980a6ee05d9SSimran Rai 		return -EINVAL;
981a6ee05d9SSimran Rai 
982a6ee05d9SSimran Rai 	/* Slot value must be even */
983a6ee05d9SSimran Rai 	if (active_slots % 2)
984a6ee05d9SSimran Rai 		return -EINVAL;
985a6ee05d9SSimran Rai 
986a6ee05d9SSimran Rai 	/* We encode 16 slots as 0 in the reg */
987a6ee05d9SSimran Rai 	if (active_slots == 16)
988a6ee05d9SSimran Rai 		active_slots = 0;
989a6ee05d9SSimran Rai 
990a6ee05d9SSimran Rai 	/* Slot Width is either 16 or 32 */
991a6ee05d9SSimran Rai 	switch (slot_width) {
992a6ee05d9SSimran Rai 	case 16:
993a6ee05d9SSimran Rai 		bits_per_slot = 1;
994a6ee05d9SSimran Rai 		break;
995a6ee05d9SSimran Rai 	case 32:
996a6ee05d9SSimran Rai 		bits_per_slot = 0;
997a6ee05d9SSimran Rai 		break;
998a6ee05d9SSimran Rai 	default:
999a6ee05d9SSimran Rai 		bits_per_slot = 0;
1000a6ee05d9SSimran Rai 		dev_warn(aio->cygaud->dev,
1001a6ee05d9SSimran Rai 			"%s Defaulting Slot Width to 32\n", __func__);
1002a6ee05d9SSimran Rai 	}
1003a6ee05d9SSimran Rai 
1004a6ee05d9SSimran Rai 	frame_bits = slots * slot_width;
1005a6ee05d9SSimran Rai 
1006a6ee05d9SSimran Rai 	for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) {
1007a6ee05d9SSimran Rai 		if (ssp_valid_tdm_framesize[i] == frame_bits) {
1008a6ee05d9SSimran Rai 			found = true;
1009a6ee05d9SSimran Rai 			break;
1010a6ee05d9SSimran Rai 		}
1011a6ee05d9SSimran Rai 	}
1012a6ee05d9SSimran Rai 
1013a6ee05d9SSimran Rai 	if (!found) {
1014a6ee05d9SSimran Rai 		dev_err(aio->cygaud->dev,
1015a6ee05d9SSimran Rai 			"%s In TDM mode, frame bits INVALID (%d)\n",
1016a6ee05d9SSimran Rai 			__func__, frame_bits);
1017a6ee05d9SSimran Rai 		return -EINVAL;
1018a6ee05d9SSimran Rai 	}
1019a6ee05d9SSimran Rai 
1020a6ee05d9SSimran Rai 	aio->bit_per_frame = frame_bits;
1021a6ee05d9SSimran Rai 
1022a6ee05d9SSimran Rai 	dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n",
1023a6ee05d9SSimran Rai 			__func__, active_slots, frame_bits);
1024a6ee05d9SSimran Rai 
1025a6ee05d9SSimran Rai 	/* Set capture side of ssp port */
1026a6ee05d9SSimran Rai 	value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
1027a6ee05d9SSimran Rai 	value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
1028a6ee05d9SSimran Rai 	value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
1029a6ee05d9SSimran Rai 	value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
1030a6ee05d9SSimran Rai 	value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
1031a6ee05d9SSimran Rai 	writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
1032a6ee05d9SSimran Rai 
1033a6ee05d9SSimran Rai 	/* Set playback side of ssp port */
1034a6ee05d9SSimran Rai 	value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
1035a6ee05d9SSimran Rai 	value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
1036a6ee05d9SSimran Rai 	value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
1037a6ee05d9SSimran Rai 	value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
1038a6ee05d9SSimran Rai 	value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
1039a6ee05d9SSimran Rai 	writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
1040a6ee05d9SSimran Rai 
1041a6ee05d9SSimran Rai 	return 0;
1042a6ee05d9SSimran Rai }
1043a6ee05d9SSimran Rai 
1044a6ee05d9SSimran Rai #ifdef CONFIG_PM_SLEEP
__cygnus_ssp_suspend(struct snd_soc_dai * cpu_dai)10457307d33aSKuninori Morimoto static int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
1046a6ee05d9SSimran Rai {
1047a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
1048a6ee05d9SSimran Rai 
10498ca4602dSKuninori Morimoto 	if (!snd_soc_dai_active(cpu_dai))
10507307d33aSKuninori Morimoto 		return 0;
10517307d33aSKuninori Morimoto 
1052a6ee05d9SSimran Rai 	if (!aio->is_slave) {
1053a6ee05d9SSimran Rai 		u32 val;
1054a6ee05d9SSimran Rai 
1055a6ee05d9SSimran Rai 		val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
1056a6ee05d9SSimran Rai 		val &= CYGNUS_PLLCLKSEL_MASK;
1057a6ee05d9SSimran Rai 		if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
1058a6ee05d9SSimran Rai 			dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
1059a6ee05d9SSimran Rai 				val);
1060a6ee05d9SSimran Rai 			return -EINVAL;
1061a6ee05d9SSimran Rai 		}
1062a6ee05d9SSimran Rai 
1063a6ee05d9SSimran Rai 		if (aio->clk_trace.cap_clk_en)
1064a6ee05d9SSimran Rai 			clk_disable_unprepare(aio->cygaud->audio_clk[val]);
1065a6ee05d9SSimran Rai 		if (aio->clk_trace.play_clk_en)
1066a6ee05d9SSimran Rai 			clk_disable_unprepare(aio->cygaud->audio_clk[val]);
1067a6ee05d9SSimran Rai 
1068a6ee05d9SSimran Rai 		aio->pll_clk_num = val;
1069a6ee05d9SSimran Rai 	}
1070a6ee05d9SSimran Rai 
1071a6ee05d9SSimran Rai 	return 0;
1072a6ee05d9SSimran Rai }
1073a6ee05d9SSimran Rai 
cygnus_ssp_suspend(struct snd_soc_component * component)10747307d33aSKuninori Morimoto static int cygnus_ssp_suspend(struct snd_soc_component *component)
10757307d33aSKuninori Morimoto {
10767307d33aSKuninori Morimoto 	struct snd_soc_dai *dai;
10777307d33aSKuninori Morimoto 	int ret = 0;
10787307d33aSKuninori Morimoto 
10797307d33aSKuninori Morimoto 	for_each_component_dais(component, dai)
10807307d33aSKuninori Morimoto 		ret |= __cygnus_ssp_suspend(dai);
10817307d33aSKuninori Morimoto 
10827307d33aSKuninori Morimoto 	return ret;
10837307d33aSKuninori Morimoto }
10847307d33aSKuninori Morimoto 
__cygnus_ssp_resume(struct snd_soc_dai * cpu_dai)10857307d33aSKuninori Morimoto static int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
1086a6ee05d9SSimran Rai {
1087a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
1088a6ee05d9SSimran Rai 	int error;
1089a6ee05d9SSimran Rai 
10908ca4602dSKuninori Morimoto 	if (!snd_soc_dai_active(cpu_dai))
10917307d33aSKuninori Morimoto 		return 0;
10927307d33aSKuninori Morimoto 
1093a6ee05d9SSimran Rai 	if (!aio->is_slave) {
1094a6ee05d9SSimran Rai 		if (aio->clk_trace.cap_clk_en) {
1095a6ee05d9SSimran Rai 			error = clk_prepare_enable(aio->cygaud->
1096a6ee05d9SSimran Rai 					audio_clk[aio->pll_clk_num]);
1097a6ee05d9SSimran Rai 			if (error) {
1098a6ee05d9SSimran Rai 				dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
1099a6ee05d9SSimran Rai 					__func__);
1100a6ee05d9SSimran Rai 				return -EINVAL;
1101a6ee05d9SSimran Rai 			}
1102a6ee05d9SSimran Rai 		}
1103a6ee05d9SSimran Rai 		if (aio->clk_trace.play_clk_en) {
1104a6ee05d9SSimran Rai 			error = clk_prepare_enable(aio->cygaud->
1105a6ee05d9SSimran Rai 					audio_clk[aio->pll_clk_num]);
1106a6ee05d9SSimran Rai 			if (error) {
1107a6ee05d9SSimran Rai 				if (aio->clk_trace.cap_clk_en)
1108a6ee05d9SSimran Rai 					clk_disable_unprepare(aio->cygaud->
1109a6ee05d9SSimran Rai 						audio_clk[aio->pll_clk_num]);
1110a6ee05d9SSimran Rai 				dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
1111a6ee05d9SSimran Rai 					__func__);
1112a6ee05d9SSimran Rai 				return -EINVAL;
1113a6ee05d9SSimran Rai 			}
1114a6ee05d9SSimran Rai 		}
1115a6ee05d9SSimran Rai 	}
1116a6ee05d9SSimran Rai 
1117a6ee05d9SSimran Rai 	return 0;
1118a6ee05d9SSimran Rai }
11197307d33aSKuninori Morimoto 
cygnus_ssp_resume(struct snd_soc_component * component)11207307d33aSKuninori Morimoto static int cygnus_ssp_resume(struct snd_soc_component *component)
11217307d33aSKuninori Morimoto {
11227307d33aSKuninori Morimoto 	struct snd_soc_dai *dai;
11237307d33aSKuninori Morimoto 	int ret = 0;
11247307d33aSKuninori Morimoto 
11257307d33aSKuninori Morimoto 	for_each_component_dais(component, dai)
11267307d33aSKuninori Morimoto 		ret |= __cygnus_ssp_resume(dai);
11277307d33aSKuninori Morimoto 
11287307d33aSKuninori Morimoto 	return ret;
11297307d33aSKuninori Morimoto }
11307307d33aSKuninori Morimoto 
1131a6ee05d9SSimran Rai #else
1132a6ee05d9SSimran Rai #define cygnus_ssp_suspend NULL
1133a6ee05d9SSimran Rai #define cygnus_ssp_resume  NULL
1134a6ee05d9SSimran Rai #endif
1135a6ee05d9SSimran Rai 
1136a6ee05d9SSimran Rai static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = {
1137a6ee05d9SSimran Rai 	.startup	= cygnus_ssp_startup,
1138a6ee05d9SSimran Rai 	.shutdown	= cygnus_ssp_shutdown,
1139a6ee05d9SSimran Rai 	.trigger	= cygnus_ssp_trigger,
1140a6ee05d9SSimran Rai 	.hw_params	= cygnus_ssp_hw_params,
1141a6ee05d9SSimran Rai 	.set_fmt	= cygnus_ssp_set_fmt,
1142a6ee05d9SSimran Rai 	.set_sysclk	= cygnus_ssp_set_sysclk,
1143a6ee05d9SSimran Rai 	.set_tdm_slot	= cygnus_set_dai_tdm_slot,
1144a6ee05d9SSimran Rai };
1145a6ee05d9SSimran Rai 
1146fcf30f3bSLori Hikichi static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = {
1147fcf30f3bSLori Hikichi 	.startup	= cygnus_ssp_startup,
1148fcf30f3bSLori Hikichi 	.shutdown	= cygnus_ssp_shutdown,
1149fcf30f3bSLori Hikichi 	.trigger	= cygnus_ssp_trigger,
1150fcf30f3bSLori Hikichi 	.hw_params	= cygnus_ssp_hw_params,
1151fcf30f3bSLori Hikichi 	.set_sysclk	= cygnus_ssp_set_sysclk,
1152fcf30f3bSLori Hikichi };
1153a6ee05d9SSimran Rai 
1154a6ee05d9SSimran Rai #define INIT_CPU_DAI(num) { \
1155a6ee05d9SSimran Rai 	.name = "cygnus-ssp" #num, \
1156a6ee05d9SSimran Rai 	.playback = { \
1157934e4885SLori Hikichi 		.channels_min = 2, \
1158a6ee05d9SSimran Rai 		.channels_max = 16, \
11598937ea0fSLori Hikichi 		.rates = SNDRV_PCM_RATE_KNOT, \
1160934e4885SLori Hikichi 		.formats = SNDRV_PCM_FMTBIT_S16_LE | \
1161a6ee05d9SSimran Rai 				SNDRV_PCM_FMTBIT_S32_LE, \
1162a6ee05d9SSimran Rai 	}, \
1163a6ee05d9SSimran Rai 	.capture = { \
1164a6ee05d9SSimran Rai 		.channels_min = 2, \
1165a6ee05d9SSimran Rai 		.channels_max = 16, \
11668937ea0fSLori Hikichi 		.rates = SNDRV_PCM_RATE_KNOT, \
1167a6ee05d9SSimran Rai 		.formats =  SNDRV_PCM_FMTBIT_S16_LE | \
1168a6ee05d9SSimran Rai 				SNDRV_PCM_FMTBIT_S32_LE, \
1169a6ee05d9SSimran Rai 	}, \
1170a6ee05d9SSimran Rai 	.ops = &cygnus_ssp_dai_ops, \
1171a6ee05d9SSimran Rai }
1172a6ee05d9SSimran Rai 
1173a6ee05d9SSimran Rai static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
1174a6ee05d9SSimran Rai 	INIT_CPU_DAI(0),
1175a6ee05d9SSimran Rai 	INIT_CPU_DAI(1),
1176a6ee05d9SSimran Rai 	INIT_CPU_DAI(2),
1177a6ee05d9SSimran Rai };
1178a6ee05d9SSimran Rai 
1179cc6e2563SBhumika Goyal static const struct snd_soc_dai_driver cygnus_spdif_dai_info = {
1180a6ee05d9SSimran Rai 	.name = "cygnus-spdif",
1181a6ee05d9SSimran Rai 	.playback = {
1182a6ee05d9SSimran Rai 		.channels_min = 2,
1183a6ee05d9SSimran Rai 		.channels_max = 2,
11848937ea0fSLori Hikichi 		.rates = SNDRV_PCM_RATE_KNOT,
1185a6ee05d9SSimran Rai 		.formats = SNDRV_PCM_FMTBIT_S16_LE |
1186a6ee05d9SSimran Rai 			SNDRV_PCM_FMTBIT_S32_LE,
1187a6ee05d9SSimran Rai 	},
1188fcf30f3bSLori Hikichi 	.ops = &cygnus_spdif_dai_ops,
1189a6ee05d9SSimran Rai };
1190a6ee05d9SSimran Rai 
1191a6ee05d9SSimran Rai static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];
1192a6ee05d9SSimran Rai 
1193a6ee05d9SSimran Rai static const struct snd_soc_component_driver cygnus_ssp_component = {
1194a6ee05d9SSimran Rai 	.name			= "cygnus-audio",
11957307d33aSKuninori Morimoto 	.suspend		= cygnus_ssp_suspend,
11967307d33aSKuninori Morimoto 	.resume			= cygnus_ssp_resume,
1197b9a0db0aSCharles Keepax 	.legacy_dai_naming	= 1,
1198a6ee05d9SSimran Rai };
1199a6ee05d9SSimran Rai 
1200a6ee05d9SSimran Rai /*
1201a6ee05d9SSimran Rai  * Return < 0 if error
1202a6ee05d9SSimran Rai  * Return 0 if disabled
1203a6ee05d9SSimran Rai  * Return 1 if enabled and node is parsed successfully
1204a6ee05d9SSimran Rai  */
parse_ssp_child_node(struct platform_device * pdev,struct device_node * dn,struct cygnus_audio * cygaud,struct snd_soc_dai_driver * p_dai)1205a6ee05d9SSimran Rai static int parse_ssp_child_node(struct platform_device *pdev,
1206a6ee05d9SSimran Rai 				struct device_node *dn,
1207a6ee05d9SSimran Rai 				struct cygnus_audio *cygaud,
1208a6ee05d9SSimran Rai 				struct snd_soc_dai_driver *p_dai)
1209a6ee05d9SSimran Rai {
1210a6ee05d9SSimran Rai 	struct cygnus_aio_port *aio;
1211a6ee05d9SSimran Rai 	struct cygnus_ssp_regs ssp_regs[3];
1212a6ee05d9SSimran Rai 	u32 rawval;
1213a6ee05d9SSimran Rai 	int portnum = -1;
1214a6ee05d9SSimran Rai 	enum cygnus_audio_port_type port_type;
1215a6ee05d9SSimran Rai 
1216a6ee05d9SSimran Rai 	if (of_property_read_u32(dn, "reg", &rawval)) {
1217a6ee05d9SSimran Rai 		dev_err(&pdev->dev, "Missing reg property\n");
1218a6ee05d9SSimran Rai 		return -EINVAL;
1219a6ee05d9SSimran Rai 	}
1220a6ee05d9SSimran Rai 
1221a6ee05d9SSimran Rai 	portnum = rawval;
1222a6ee05d9SSimran Rai 	switch (rawval) {
1223a6ee05d9SSimran Rai 	case 0:
1224a6ee05d9SSimran Rai 		ssp_regs[0] = INIT_SSP_REGS(0);
1225a6ee05d9SSimran Rai 		port_type = PORT_TDM;
1226a6ee05d9SSimran Rai 		break;
1227a6ee05d9SSimran Rai 	case 1:
1228a6ee05d9SSimran Rai 		ssp_regs[1] = INIT_SSP_REGS(1);
1229a6ee05d9SSimran Rai 		port_type = PORT_TDM;
1230a6ee05d9SSimran Rai 		break;
1231a6ee05d9SSimran Rai 	case 2:
1232a6ee05d9SSimran Rai 		ssp_regs[2] = INIT_SSP_REGS(2);
1233a6ee05d9SSimran Rai 		port_type = PORT_TDM;
1234a6ee05d9SSimran Rai 		break;
1235a6ee05d9SSimran Rai 	case 3:
1236a6ee05d9SSimran Rai 		port_type = PORT_SPDIF;
1237a6ee05d9SSimran Rai 		break;
1238a6ee05d9SSimran Rai 	default:
1239a6ee05d9SSimran Rai 		dev_err(&pdev->dev, "Bad value for reg %u\n", rawval);
1240a6ee05d9SSimran Rai 		return -EINVAL;
1241a6ee05d9SSimran Rai 	}
1242a6ee05d9SSimran Rai 
1243a6ee05d9SSimran Rai 	aio = &cygaud->portinfo[portnum];
1244a6ee05d9SSimran Rai 	aio->cygaud = cygaud;
1245a6ee05d9SSimran Rai 	aio->portnum = portnum;
1246a6ee05d9SSimran Rai 	aio->port_type = port_type;
1247a6ee05d9SSimran Rai 	aio->fsync_width = -1;
1248a6ee05d9SSimran Rai 
1249a6ee05d9SSimran Rai 	switch (port_type) {
1250a6ee05d9SSimran Rai 	case PORT_TDM:
1251a6ee05d9SSimran Rai 		aio->regs = ssp_regs[portnum];
1252a6ee05d9SSimran Rai 		*p_dai = cygnus_ssp_dai_info[portnum];
1253a6ee05d9SSimran Rai 		aio->mode = CYGNUS_SSPMODE_UNKNOWN;
1254a6ee05d9SSimran Rai 		break;
1255a6ee05d9SSimran Rai 
1256a6ee05d9SSimran Rai 	case PORT_SPDIF:
1257a6ee05d9SSimran Rai 		aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET;
1258a6ee05d9SSimran Rai 		aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET;
1259a6ee05d9SSimran Rai 		aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET;
1260a6ee05d9SSimran Rai 		aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET;
1261a6ee05d9SSimran Rai 		*p_dai = cygnus_spdif_dai_info;
1262a6ee05d9SSimran Rai 
1263a6ee05d9SSimran Rai 		/* For the purposes of this code SPDIF can be I2S mode */
1264a6ee05d9SSimran Rai 		aio->mode = CYGNUS_SSPMODE_I2S;
1265a6ee05d9SSimran Rai 		break;
1266a6ee05d9SSimran Rai 	default:
1267a6ee05d9SSimran Rai 		dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type);
1268a6ee05d9SSimran Rai 		return -EINVAL;
1269a6ee05d9SSimran Rai 	}
1270a6ee05d9SSimran Rai 
1271a6ee05d9SSimran Rai 	dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum);
1272a6ee05d9SSimran Rai 	aio->streams_on = 0;
1273a6ee05d9SSimran Rai 	aio->cygaud->dev = &pdev->dev;
1274a6ee05d9SSimran Rai 	aio->clk_trace.play_en = false;
1275a6ee05d9SSimran Rai 	aio->clk_trace.cap_en = false;
1276a6ee05d9SSimran Rai 
1277a6ee05d9SSimran Rai 	audio_ssp_init_portregs(aio);
1278a6ee05d9SSimran Rai 	return 0;
1279a6ee05d9SSimran Rai }
1280a6ee05d9SSimran Rai 
audio_clk_init(struct platform_device * pdev,struct cygnus_audio * cygaud)1281a6ee05d9SSimran Rai static int audio_clk_init(struct platform_device *pdev,
1282a6ee05d9SSimran Rai 						struct cygnus_audio *cygaud)
1283a6ee05d9SSimran Rai {
1284a6ee05d9SSimran Rai 	int i;
1285a6ee05d9SSimran Rai 	char clk_name[PROP_LEN_MAX];
1286a6ee05d9SSimran Rai 
1287a6ee05d9SSimran Rai 	for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) {
1288a6ee05d9SSimran Rai 		snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i);
1289a6ee05d9SSimran Rai 
1290a6ee05d9SSimran Rai 		cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name);
1291a6ee05d9SSimran Rai 		if (IS_ERR(cygaud->audio_clk[i]))
1292a6ee05d9SSimran Rai 			return PTR_ERR(cygaud->audio_clk[i]);
1293a6ee05d9SSimran Rai 	}
1294a6ee05d9SSimran Rai 
1295a6ee05d9SSimran Rai 	return 0;
1296a6ee05d9SSimran Rai }
1297a6ee05d9SSimran Rai 
cygnus_ssp_probe(struct platform_device * pdev)1298a6ee05d9SSimran Rai static int cygnus_ssp_probe(struct platform_device *pdev)
1299a6ee05d9SSimran Rai {
1300a6ee05d9SSimran Rai 	struct device *dev = &pdev->dev;
1301a6ee05d9SSimran Rai 	struct device_node *child_node;
1302a6ee05d9SSimran Rai 	struct cygnus_audio *cygaud;
1303ce362420SPierre-Louis Bossart 	int err;
1304a6ee05d9SSimran Rai 	int node_count;
1305a6ee05d9SSimran Rai 	int active_port_count;
1306a6ee05d9SSimran Rai 
1307a6ee05d9SSimran Rai 	cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL);
1308a6ee05d9SSimran Rai 	if (!cygaud)
1309a6ee05d9SSimran Rai 		return -ENOMEM;
1310a6ee05d9SSimran Rai 
1311a6ee05d9SSimran Rai 	dev_set_drvdata(dev, cygaud);
1312a6ee05d9SSimran Rai 
13133814c417SYang Yingliang 	cygaud->audio = devm_platform_ioremap_resource_byname(pdev, "aud");
1314a6ee05d9SSimran Rai 	if (IS_ERR(cygaud->audio))
1315a6ee05d9SSimran Rai 		return PTR_ERR(cygaud->audio);
1316a6ee05d9SSimran Rai 
13173814c417SYang Yingliang 	cygaud->i2s_in = devm_platform_ioremap_resource_byname(pdev, "i2s_in");
1318a6ee05d9SSimran Rai 	if (IS_ERR(cygaud->i2s_in))
1319a6ee05d9SSimran Rai 		return PTR_ERR(cygaud->i2s_in);
1320a6ee05d9SSimran Rai 
1321a6ee05d9SSimran Rai 	/* Tri-state all controlable pins until we know that we need them */
1322a6ee05d9SSimran Rai 	writel(CYGNUS_SSP_TRISTATE_MASK,
1323a6ee05d9SSimran Rai 			cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
1324a6ee05d9SSimran Rai 
1325a6ee05d9SSimran Rai 	node_count = of_get_child_count(pdev->dev.of_node);
1326a6ee05d9SSimran Rai 	if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) {
1327a6ee05d9SSimran Rai 		dev_err(dev, "child nodes is %d.  Must be between 1 and %d\n",
1328a6ee05d9SSimran Rai 			node_count, CYGNUS_MAX_PORTS);
1329a6ee05d9SSimran Rai 		return -EINVAL;
1330a6ee05d9SSimran Rai 	}
1331a6ee05d9SSimran Rai 
1332a6ee05d9SSimran Rai 	active_port_count = 0;
1333a6ee05d9SSimran Rai 
1334a6ee05d9SSimran Rai 	for_each_available_child_of_node(pdev->dev.of_node, child_node) {
1335a6ee05d9SSimran Rai 		err = parse_ssp_child_node(pdev, child_node, cygaud,
1336a6ee05d9SSimran Rai 					&cygnus_ssp_dai[active_port_count]);
1337a6ee05d9SSimran Rai 
1338a6ee05d9SSimran Rai 		/* negative is err, 0 is active and good, 1 is disabled */
1339aa320c7cSkernel test robot 		if (err < 0) {
1340aa320c7cSkernel test robot 			of_node_put(child_node);
1341a6ee05d9SSimran Rai 			return err;
1342aa320c7cSkernel test robot 		}
1343a6ee05d9SSimran Rai 		else if (!err) {
1344a6ee05d9SSimran Rai 			dev_dbg(dev, "Activating DAI: %s\n",
1345a6ee05d9SSimran Rai 				cygnus_ssp_dai[active_port_count].name);
1346a6ee05d9SSimran Rai 			active_port_count++;
1347a6ee05d9SSimran Rai 		}
1348a6ee05d9SSimran Rai 	}
1349a6ee05d9SSimran Rai 
1350a6ee05d9SSimran Rai 	cygaud->dev = dev;
1351a6ee05d9SSimran Rai 	cygaud->active_ports = 0;
1352a6ee05d9SSimran Rai 
1353a6ee05d9SSimran Rai 	dev_dbg(dev, "Registering %d DAIs\n", active_port_count);
1354570f75b9SKuninori Morimoto 	err = devm_snd_soc_register_component(dev, &cygnus_ssp_component,
1355a6ee05d9SSimran Rai 				cygnus_ssp_dai, active_port_count);
1356a6ee05d9SSimran Rai 	if (err) {
1357a6ee05d9SSimran Rai 		dev_err(dev, "snd_soc_register_dai failed\n");
1358a6ee05d9SSimran Rai 		return err;
1359a6ee05d9SSimran Rai 	}
1360a6ee05d9SSimran Rai 
1361a6ee05d9SSimran Rai 	cygaud->irq_num = platform_get_irq(pdev, 0);
1362cf9441adSStephen Boyd 	if (cygaud->irq_num <= 0)
1363cf9441adSStephen Boyd 		return cygaud->irq_num;
1364a6ee05d9SSimran Rai 
1365a6ee05d9SSimran Rai 	err = audio_clk_init(pdev, cygaud);
1366a6ee05d9SSimran Rai 	if (err) {
1367a6ee05d9SSimran Rai 		dev_err(dev, "audio clock initialization failed\n");
1368570f75b9SKuninori Morimoto 		return err;
1369a6ee05d9SSimran Rai 	}
1370a6ee05d9SSimran Rai 
1371a6ee05d9SSimran Rai 	err = cygnus_soc_platform_register(dev, cygaud);
1372a6ee05d9SSimran Rai 	if (err) {
1373a6ee05d9SSimran Rai 		dev_err(dev, "platform reg error %d\n", err);
1374570f75b9SKuninori Morimoto 		return err;
1375a6ee05d9SSimran Rai 	}
1376a6ee05d9SSimran Rai 
1377a6ee05d9SSimran Rai 	return 0;
1378a6ee05d9SSimran Rai }
1379a6ee05d9SSimran Rai 
cygnus_ssp_remove(struct platform_device * pdev)1380*cf004d9aSUwe Kleine-König static void cygnus_ssp_remove(struct platform_device *pdev)
1381a6ee05d9SSimran Rai {
1382a6ee05d9SSimran Rai 	cygnus_soc_platform_unregister(&pdev->dev);
1383a6ee05d9SSimran Rai }
1384a6ee05d9SSimran Rai 
1385a6ee05d9SSimran Rai static const struct of_device_id cygnus_ssp_of_match[] = {
1386a6ee05d9SSimran Rai 	{ .compatible = "brcm,cygnus-audio" },
1387a6ee05d9SSimran Rai 	{},
1388a6ee05d9SSimran Rai };
1389a6ee05d9SSimran Rai MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);
1390a6ee05d9SSimran Rai 
1391a6ee05d9SSimran Rai static struct platform_driver cygnus_ssp_driver = {
1392a6ee05d9SSimran Rai 	.probe		= cygnus_ssp_probe,
1393*cf004d9aSUwe Kleine-König 	.remove_new	= cygnus_ssp_remove,
1394a6ee05d9SSimran Rai 	.driver		= {
1395a6ee05d9SSimran Rai 		.name	= "cygnus-ssp",
1396a6ee05d9SSimran Rai 		.of_match_table = cygnus_ssp_of_match,
1397a6ee05d9SSimran Rai 	},
1398a6ee05d9SSimran Rai };
1399a6ee05d9SSimran Rai 
1400a6ee05d9SSimran Rai module_platform_driver(cygnus_ssp_driver);
1401a6ee05d9SSimran Rai 
1402a6ee05d9SSimran Rai MODULE_ALIAS("platform:cygnus-ssp");
1403a6ee05d9SSimran Rai MODULE_LICENSE("GPL v2");
1404a6ee05d9SSimran Rai MODULE_AUTHOR("Broadcom");
1405a6ee05d9SSimran Rai MODULE_DESCRIPTION("Cygnus ASoC SSP Interface");
1406