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