1*1fc55a2bSKeguang Zhang // SPDX-License-Identifier: GPL-2.0-or-later
2*1fc55a2bSKeguang Zhang /*
3*1fc55a2bSKeguang Zhang * AC97 Controller Driver for Loongson-1 SoC
4*1fc55a2bSKeguang Zhang *
5*1fc55a2bSKeguang Zhang * Copyright (C) 2025 Keguang Zhang <keguang.zhang@gmail.com>
6*1fc55a2bSKeguang Zhang */
7*1fc55a2bSKeguang Zhang
8*1fc55a2bSKeguang Zhang #include <linux/bitfield.h>
9*1fc55a2bSKeguang Zhang #include <linux/dma-mapping.h>
10*1fc55a2bSKeguang Zhang #include <linux/init.h>
11*1fc55a2bSKeguang Zhang #include <linux/module.h>
12*1fc55a2bSKeguang Zhang #include <linux/platform_device.h>
13*1fc55a2bSKeguang Zhang #include <linux/regmap.h>
14*1fc55a2bSKeguang Zhang
15*1fc55a2bSKeguang Zhang #include <sound/dmaengine_pcm.h>
16*1fc55a2bSKeguang Zhang #include <sound/pcm.h>
17*1fc55a2bSKeguang Zhang #include <sound/pcm_params.h>
18*1fc55a2bSKeguang Zhang #include <sound/soc.h>
19*1fc55a2bSKeguang Zhang
20*1fc55a2bSKeguang Zhang /* Loongson-1 AC97 Controller Registers */
21*1fc55a2bSKeguang Zhang #define AC97_CSR 0x0
22*1fc55a2bSKeguang Zhang #define AC97_OCC0 0x4
23*1fc55a2bSKeguang Zhang #define AC97_ICC 0x10
24*1fc55a2bSKeguang Zhang #define AC97_CRAC 0x18
25*1fc55a2bSKeguang Zhang #define AC97_INTRAW 0x54
26*1fc55a2bSKeguang Zhang #define AC97_INTM 0x58
27*1fc55a2bSKeguang Zhang #define AC97_INT_CW_CLR 0x68
28*1fc55a2bSKeguang Zhang #define AC97_INT_CR_CLR 0x6c
29*1fc55a2bSKeguang Zhang
30*1fc55a2bSKeguang Zhang /* Control Status Register Bits (CSR) */
31*1fc55a2bSKeguang Zhang #define CSR_RESUME BIT(1)
32*1fc55a2bSKeguang Zhang #define CSR_RST_FORCE BIT(0)
33*1fc55a2bSKeguang Zhang
34*1fc55a2bSKeguang Zhang /* MIC Channel Configuration Bits */
35*1fc55a2bSKeguang Zhang #define M_DMA_EN BIT(22)
36*1fc55a2bSKeguang Zhang #define M_FIFO_THRES GENMASK(21, 20)
37*1fc55a2bSKeguang Zhang #define M_FIFO_THRES_FULL FIELD_PREP(M_FIFO_THRES, 3)
38*1fc55a2bSKeguang Zhang #define M_FIFO_THRES_HALF FIELD_PREP(M_FIFO_THRES, 1)
39*1fc55a2bSKeguang Zhang #define M_FIFO_THRES_QUARTER FIELD_PREP(M_FIFO_THRES, 0)
40*1fc55a2bSKeguang Zhang #define M_SW GENMASK(19, 18)
41*1fc55a2bSKeguang Zhang #define M_SW_16_BITS FIELD_PREP(M_SW, 2)
42*1fc55a2bSKeguang Zhang #define M_SW_8_BITS FIELD_PREP(M_SW, 0)
43*1fc55a2bSKeguang Zhang #define M_VSR BIT(17)
44*1fc55a2bSKeguang Zhang #define M_CH_EN BIT(16)
45*1fc55a2bSKeguang Zhang /* Right Channel Configuration Bits */
46*1fc55a2bSKeguang Zhang #define R_DMA_EN BIT(14)
47*1fc55a2bSKeguang Zhang #define R_FIFO_THRES GENMASK(13, 12)
48*1fc55a2bSKeguang Zhang #define R_FIFO_THRES_EMPTY FIELD_PREP(R_FIFO_THRES, 3)
49*1fc55a2bSKeguang Zhang #define R_FIFO_THRES_HALF FIELD_PREP(R_FIFO_THRES, 1)
50*1fc55a2bSKeguang Zhang #define R_FIFO_THRES_QUARTER FIELD_PREP(R_FIFO_THRES, 0)
51*1fc55a2bSKeguang Zhang #define R_SW GENMASK(11, 10)
52*1fc55a2bSKeguang Zhang #define R_SW_16_BITS FIELD_PREP(R_SW, 2)
53*1fc55a2bSKeguang Zhang #define R_SW_8_BITS FIELD_PREP(R_SW, 0)
54*1fc55a2bSKeguang Zhang #define R_VSR BIT(9)
55*1fc55a2bSKeguang Zhang #define R_CH_EN BIT(8)
56*1fc55a2bSKeguang Zhang /* Left Channel Configuration Bits */
57*1fc55a2bSKeguang Zhang #define L_DMA_EN BIT(6)
58*1fc55a2bSKeguang Zhang #define L_FIFO_THRES GENMASK(5, 4)
59*1fc55a2bSKeguang Zhang #define L_FIFO_THRES_EMPTY FIELD_PREP(L_FIFO_THRES, 3)
60*1fc55a2bSKeguang Zhang #define L_FIFO_THRES_HALF FIELD_PREP(L_FIFO_THRES, 1)
61*1fc55a2bSKeguang Zhang #define L_FIFO_THRES_QUARTER FIELD_PREP(L_FIFO_THRES, 0)
62*1fc55a2bSKeguang Zhang #define L_SW GENMASK(3, 2)
63*1fc55a2bSKeguang Zhang #define L_SW_16_BITS FIELD_PREP(L_SW, 2)
64*1fc55a2bSKeguang Zhang #define L_SW_8_BITS FIELD_PREP(L_SW, 0)
65*1fc55a2bSKeguang Zhang #define L_VSR BIT(1)
66*1fc55a2bSKeguang Zhang #define L_CH_EN BIT(0)
67*1fc55a2bSKeguang Zhang
68*1fc55a2bSKeguang Zhang /* Codec Register Access Command Bits (CRAC) */
69*1fc55a2bSKeguang Zhang #define CODEC_WR BIT(31)
70*1fc55a2bSKeguang Zhang #define CODEC_ADR GENMASK(22, 16)
71*1fc55a2bSKeguang Zhang #define CODEC_DAT GENMASK(15, 0)
72*1fc55a2bSKeguang Zhang
73*1fc55a2bSKeguang Zhang /* Interrupt Register (INTRAW) */
74*1fc55a2bSKeguang Zhang #define CW_DONE BIT(1)
75*1fc55a2bSKeguang Zhang #define CR_DONE BIT(0)
76*1fc55a2bSKeguang Zhang
77*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_TX_EN BIT(31)
78*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_STEREO BIT(30)
79*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_TX_BYTES GENMASK(29, 28)
80*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_TX_4_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 2)
81*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_TX_2_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 1)
82*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_TX_1_BYTE FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 0)
83*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_DADDR_MASK GENMASK(27, 0)
84*1fc55a2bSKeguang Zhang
85*1fc55a2bSKeguang Zhang #define LS1X_AC97_DMA_FIFO_SIZE 128
86*1fc55a2bSKeguang Zhang
87*1fc55a2bSKeguang Zhang #define LS1X_AC97_TIMEOUT 3000
88*1fc55a2bSKeguang Zhang
89*1fc55a2bSKeguang Zhang struct ls1x_ac97 {
90*1fc55a2bSKeguang Zhang void __iomem *reg_base;
91*1fc55a2bSKeguang Zhang struct regmap *regmap;
92*1fc55a2bSKeguang Zhang dma_addr_t tx_dma_base;
93*1fc55a2bSKeguang Zhang dma_addr_t rx_dma_base;
94*1fc55a2bSKeguang Zhang struct snd_dmaengine_dai_dma_data capture_dma_data;
95*1fc55a2bSKeguang Zhang struct snd_dmaengine_dai_dma_data playback_dma_data;
96*1fc55a2bSKeguang Zhang };
97*1fc55a2bSKeguang Zhang
98*1fc55a2bSKeguang Zhang static struct ls1x_ac97 *ls1x_ac97;
99*1fc55a2bSKeguang Zhang
100*1fc55a2bSKeguang Zhang static const struct regmap_config ls1x_ac97_regmap_config = {
101*1fc55a2bSKeguang Zhang .reg_bits = 32,
102*1fc55a2bSKeguang Zhang .val_bits = 32,
103*1fc55a2bSKeguang Zhang .reg_stride = 4,
104*1fc55a2bSKeguang Zhang };
105*1fc55a2bSKeguang Zhang
ls1x_ac97_reset(struct snd_ac97 * ac97)106*1fc55a2bSKeguang Zhang static void ls1x_ac97_reset(struct snd_ac97 *ac97)
107*1fc55a2bSKeguang Zhang {
108*1fc55a2bSKeguang Zhang int val;
109*1fc55a2bSKeguang Zhang
110*1fc55a2bSKeguang Zhang regmap_write(ls1x_ac97->regmap, AC97_CSR, CSR_RST_FORCE);
111*1fc55a2bSKeguang Zhang regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
112*1fc55a2bSKeguang Zhang !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
113*1fc55a2bSKeguang Zhang }
114*1fc55a2bSKeguang Zhang
ls1x_ac97_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short val)115*1fc55a2bSKeguang Zhang static void ls1x_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
116*1fc55a2bSKeguang Zhang {
117*1fc55a2bSKeguang Zhang int tmp, ret;
118*1fc55a2bSKeguang Zhang
119*1fc55a2bSKeguang Zhang tmp = FIELD_PREP(CODEC_ADR, reg) | FIELD_PREP(CODEC_DAT, val);
120*1fc55a2bSKeguang Zhang regmap_write(ls1x_ac97->regmap, AC97_CRAC, tmp);
121*1fc55a2bSKeguang Zhang ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, tmp,
122*1fc55a2bSKeguang Zhang (tmp & CW_DONE), 0, LS1X_AC97_TIMEOUT);
123*1fc55a2bSKeguang Zhang if (ret)
124*1fc55a2bSKeguang Zhang pr_err("timeout on AC97 write! %d\n", ret);
125*1fc55a2bSKeguang Zhang
126*1fc55a2bSKeguang Zhang regmap_read(ls1x_ac97->regmap, AC97_INT_CW_CLR, &ret);
127*1fc55a2bSKeguang Zhang }
128*1fc55a2bSKeguang Zhang
ls1x_ac97_read(struct snd_ac97 * ac97,unsigned short reg)129*1fc55a2bSKeguang Zhang static unsigned short ls1x_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
130*1fc55a2bSKeguang Zhang {
131*1fc55a2bSKeguang Zhang int val, ret;
132*1fc55a2bSKeguang Zhang
133*1fc55a2bSKeguang Zhang val = CODEC_WR | FIELD_PREP(CODEC_ADR, reg);
134*1fc55a2bSKeguang Zhang regmap_write(ls1x_ac97->regmap, AC97_CRAC, val);
135*1fc55a2bSKeguang Zhang ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, val,
136*1fc55a2bSKeguang Zhang (val & CR_DONE), 0, LS1X_AC97_TIMEOUT);
137*1fc55a2bSKeguang Zhang if (ret) {
138*1fc55a2bSKeguang Zhang pr_err("timeout on AC97 read! %d\n", ret);
139*1fc55a2bSKeguang Zhang return ret;
140*1fc55a2bSKeguang Zhang }
141*1fc55a2bSKeguang Zhang
142*1fc55a2bSKeguang Zhang regmap_read(ls1x_ac97->regmap, AC97_INT_CR_CLR, &ret);
143*1fc55a2bSKeguang Zhang regmap_read(ls1x_ac97->regmap, AC97_CRAC, &ret);
144*1fc55a2bSKeguang Zhang
145*1fc55a2bSKeguang Zhang return (ret & CODEC_DAT);
146*1fc55a2bSKeguang Zhang }
147*1fc55a2bSKeguang Zhang
ls1x_ac97_init(struct snd_ac97 * ac97)148*1fc55a2bSKeguang Zhang static void ls1x_ac97_init(struct snd_ac97 *ac97)
149*1fc55a2bSKeguang Zhang {
150*1fc55a2bSKeguang Zhang writel(0, ls1x_ac97->reg_base + AC97_INTRAW);
151*1fc55a2bSKeguang Zhang writel(0, ls1x_ac97->reg_base + AC97_INTM);
152*1fc55a2bSKeguang Zhang
153*1fc55a2bSKeguang Zhang /* Config output channels */
154*1fc55a2bSKeguang Zhang regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0,
155*1fc55a2bSKeguang Zhang R_DMA_EN | R_FIFO_THRES | R_CH_EN |
156*1fc55a2bSKeguang Zhang L_DMA_EN | L_FIFO_THRES | L_CH_EN,
157*1fc55a2bSKeguang Zhang R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
158*1fc55a2bSKeguang Zhang L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
159*1fc55a2bSKeguang Zhang
160*1fc55a2bSKeguang Zhang /* Config inputs channel */
161*1fc55a2bSKeguang Zhang regmap_update_bits(ls1x_ac97->regmap, AC97_ICC,
162*1fc55a2bSKeguang Zhang M_DMA_EN | M_FIFO_THRES | M_CH_EN |
163*1fc55a2bSKeguang Zhang R_DMA_EN | R_FIFO_THRES | R_CH_EN |
164*1fc55a2bSKeguang Zhang L_DMA_EN | L_FIFO_THRES | L_CH_EN,
165*1fc55a2bSKeguang Zhang M_DMA_EN | M_FIFO_THRES_FULL | M_CH_EN |
166*1fc55a2bSKeguang Zhang R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
167*1fc55a2bSKeguang Zhang L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
168*1fc55a2bSKeguang Zhang
169*1fc55a2bSKeguang Zhang if (ac97->ext_id & AC97_EI_VRA) {
170*1fc55a2bSKeguang Zhang regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0, R_VSR | L_VSR, R_VSR | L_VSR);
171*1fc55a2bSKeguang Zhang regmap_update_bits(ls1x_ac97->regmap, AC97_ICC, M_VSR, M_VSR);
172*1fc55a2bSKeguang Zhang }
173*1fc55a2bSKeguang Zhang }
174*1fc55a2bSKeguang Zhang
175*1fc55a2bSKeguang Zhang static struct snd_ac97_bus_ops ls1x_ac97_ops = {
176*1fc55a2bSKeguang Zhang .reset = ls1x_ac97_reset,
177*1fc55a2bSKeguang Zhang .write = ls1x_ac97_write,
178*1fc55a2bSKeguang Zhang .read = ls1x_ac97_read,
179*1fc55a2bSKeguang Zhang .init = ls1x_ac97_init,
180*1fc55a2bSKeguang Zhang };
181*1fc55a2bSKeguang Zhang
ls1x_ac97_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * cpu_dai)182*1fc55a2bSKeguang Zhang static int ls1x_ac97_hw_params(struct snd_pcm_substream *substream,
183*1fc55a2bSKeguang Zhang struct snd_pcm_hw_params *params,
184*1fc55a2bSKeguang Zhang struct snd_soc_dai *cpu_dai)
185*1fc55a2bSKeguang Zhang {
186*1fc55a2bSKeguang Zhang struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
187*1fc55a2bSKeguang Zhang struct snd_dmaengine_dai_dma_data *dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
188*1fc55a2bSKeguang Zhang
189*1fc55a2bSKeguang Zhang switch (params_channels(params)) {
190*1fc55a2bSKeguang Zhang case 1:
191*1fc55a2bSKeguang Zhang dma_data->addr &= ~LS1X_AC97_DMA_STEREO;
192*1fc55a2bSKeguang Zhang break;
193*1fc55a2bSKeguang Zhang case 2:
194*1fc55a2bSKeguang Zhang dma_data->addr |= LS1X_AC97_DMA_STEREO;
195*1fc55a2bSKeguang Zhang break;
196*1fc55a2bSKeguang Zhang default:
197*1fc55a2bSKeguang Zhang dev_err(cpu_dai->dev, "unsupported channels! %d\n", params_channels(params));
198*1fc55a2bSKeguang Zhang return -EINVAL;
199*1fc55a2bSKeguang Zhang }
200*1fc55a2bSKeguang Zhang
201*1fc55a2bSKeguang Zhang switch (params_format(params)) {
202*1fc55a2bSKeguang Zhang case SNDRV_PCM_FORMAT_S8:
203*1fc55a2bSKeguang Zhang case SNDRV_PCM_FORMAT_U8:
204*1fc55a2bSKeguang Zhang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
205*1fc55a2bSKeguang Zhang regmap_update_bits(ac97->regmap, AC97_OCC0,
206*1fc55a2bSKeguang Zhang R_SW | L_SW,
207*1fc55a2bSKeguang Zhang R_SW_8_BITS | L_SW_8_BITS);
208*1fc55a2bSKeguang Zhang else
209*1fc55a2bSKeguang Zhang regmap_update_bits(ac97->regmap, AC97_ICC,
210*1fc55a2bSKeguang Zhang M_SW | R_SW | L_SW,
211*1fc55a2bSKeguang Zhang M_SW_8_BITS | R_SW_8_BITS | L_SW_8_BITS);
212*1fc55a2bSKeguang Zhang break;
213*1fc55a2bSKeguang Zhang case SNDRV_PCM_FORMAT_S16_LE:
214*1fc55a2bSKeguang Zhang case SNDRV_PCM_FORMAT_U16_LE:
215*1fc55a2bSKeguang Zhang case SNDRV_PCM_FORMAT_S16_BE:
216*1fc55a2bSKeguang Zhang case SNDRV_PCM_FORMAT_U16_BE:
217*1fc55a2bSKeguang Zhang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
218*1fc55a2bSKeguang Zhang regmap_update_bits(ac97->regmap, AC97_OCC0,
219*1fc55a2bSKeguang Zhang R_SW | L_SW,
220*1fc55a2bSKeguang Zhang R_SW_16_BITS | L_SW_16_BITS);
221*1fc55a2bSKeguang Zhang else
222*1fc55a2bSKeguang Zhang regmap_update_bits(ac97->regmap, AC97_ICC,
223*1fc55a2bSKeguang Zhang M_SW | R_SW | L_SW,
224*1fc55a2bSKeguang Zhang M_SW_16_BITS | R_SW_16_BITS | L_SW_16_BITS);
225*1fc55a2bSKeguang Zhang break;
226*1fc55a2bSKeguang Zhang default:
227*1fc55a2bSKeguang Zhang dev_err(cpu_dai->dev, "unsupported format! %d\n", params_format(params));
228*1fc55a2bSKeguang Zhang return -EINVAL;
229*1fc55a2bSKeguang Zhang }
230*1fc55a2bSKeguang Zhang
231*1fc55a2bSKeguang Zhang return 0;
232*1fc55a2bSKeguang Zhang }
233*1fc55a2bSKeguang Zhang
ls1x_ac97_dai_probe(struct snd_soc_dai * cpu_dai)234*1fc55a2bSKeguang Zhang static int ls1x_ac97_dai_probe(struct snd_soc_dai *cpu_dai)
235*1fc55a2bSKeguang Zhang {
236*1fc55a2bSKeguang Zhang struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
237*1fc55a2bSKeguang Zhang
238*1fc55a2bSKeguang Zhang ac97->capture_dma_data.addr = ac97->rx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
239*1fc55a2bSKeguang Zhang ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
240*1fc55a2bSKeguang Zhang ac97->capture_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
241*1fc55a2bSKeguang Zhang
242*1fc55a2bSKeguang Zhang ac97->playback_dma_data.addr = ac97->tx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
243*1fc55a2bSKeguang Zhang ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_4_BYTES;
244*1fc55a2bSKeguang Zhang ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_EN;
245*1fc55a2bSKeguang Zhang ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
246*1fc55a2bSKeguang Zhang ac97->playback_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
247*1fc55a2bSKeguang Zhang
248*1fc55a2bSKeguang Zhang snd_soc_dai_init_dma_data(cpu_dai, &ac97->playback_dma_data, &ac97->capture_dma_data);
249*1fc55a2bSKeguang Zhang snd_soc_dai_set_drvdata(cpu_dai, ac97);
250*1fc55a2bSKeguang Zhang
251*1fc55a2bSKeguang Zhang return 0;
252*1fc55a2bSKeguang Zhang }
253*1fc55a2bSKeguang Zhang
254*1fc55a2bSKeguang Zhang static const struct snd_soc_dai_ops ls1x_ac97_dai_ops = {
255*1fc55a2bSKeguang Zhang .probe = ls1x_ac97_dai_probe,
256*1fc55a2bSKeguang Zhang .hw_params = ls1x_ac97_hw_params,
257*1fc55a2bSKeguang Zhang };
258*1fc55a2bSKeguang Zhang
259*1fc55a2bSKeguang Zhang #define LS1X_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |\
260*1fc55a2bSKeguang Zhang SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
261*1fc55a2bSKeguang Zhang SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE)
262*1fc55a2bSKeguang Zhang
263*1fc55a2bSKeguang Zhang static struct snd_soc_dai_driver ls1x_ac97_dai[] = {
264*1fc55a2bSKeguang Zhang {
265*1fc55a2bSKeguang Zhang .name = "ls1x-ac97",
266*1fc55a2bSKeguang Zhang .playback = {
267*1fc55a2bSKeguang Zhang .stream_name = "AC97 Playback",
268*1fc55a2bSKeguang Zhang .channels_min = 1,
269*1fc55a2bSKeguang Zhang .channels_max = 2,
270*1fc55a2bSKeguang Zhang .rates = SNDRV_PCM_RATE_8000_48000,
271*1fc55a2bSKeguang Zhang .formats = LS1X_AC97_FMTS,
272*1fc55a2bSKeguang Zhang },
273*1fc55a2bSKeguang Zhang .capture = {
274*1fc55a2bSKeguang Zhang .stream_name = "AC97 Capture",
275*1fc55a2bSKeguang Zhang .channels_min = 1,
276*1fc55a2bSKeguang Zhang .channels_max = 2,
277*1fc55a2bSKeguang Zhang .rates = SNDRV_PCM_RATE_8000_48000,
278*1fc55a2bSKeguang Zhang .formats = LS1X_AC97_FMTS,
279*1fc55a2bSKeguang Zhang },
280*1fc55a2bSKeguang Zhang .ops = &ls1x_ac97_dai_ops,
281*1fc55a2bSKeguang Zhang },
282*1fc55a2bSKeguang Zhang };
283*1fc55a2bSKeguang Zhang
284*1fc55a2bSKeguang Zhang static const struct snd_soc_component_driver ls1x_ac97_component = {
285*1fc55a2bSKeguang Zhang .name = KBUILD_MODNAME,
286*1fc55a2bSKeguang Zhang .legacy_dai_naming = 1,
287*1fc55a2bSKeguang Zhang };
288*1fc55a2bSKeguang Zhang
ls1x_ac97_probe(struct platform_device * pdev)289*1fc55a2bSKeguang Zhang static int ls1x_ac97_probe(struct platform_device *pdev)
290*1fc55a2bSKeguang Zhang {
291*1fc55a2bSKeguang Zhang struct device *dev = &pdev->dev;
292*1fc55a2bSKeguang Zhang struct ls1x_ac97 *ac97;
293*1fc55a2bSKeguang Zhang struct resource *res;
294*1fc55a2bSKeguang Zhang int ret;
295*1fc55a2bSKeguang Zhang
296*1fc55a2bSKeguang Zhang ac97 = devm_kzalloc(dev, sizeof(struct ls1x_ac97), GFP_KERNEL);
297*1fc55a2bSKeguang Zhang if (!ac97)
298*1fc55a2bSKeguang Zhang return -ENOMEM;
299*1fc55a2bSKeguang Zhang ls1x_ac97 = ac97;
300*1fc55a2bSKeguang Zhang platform_set_drvdata(pdev, ac97);
301*1fc55a2bSKeguang Zhang
302*1fc55a2bSKeguang Zhang ac97->reg_base = devm_platform_ioremap_resource(pdev, 0);
303*1fc55a2bSKeguang Zhang if (IS_ERR(ac97->reg_base))
304*1fc55a2bSKeguang Zhang return PTR_ERR(ac97->reg_base);
305*1fc55a2bSKeguang Zhang
306*1fc55a2bSKeguang Zhang ac97->regmap = devm_regmap_init_mmio(dev, ac97->reg_base, &ls1x_ac97_regmap_config);
307*1fc55a2bSKeguang Zhang if (IS_ERR(ac97->regmap))
308*1fc55a2bSKeguang Zhang return dev_err_probe(dev, PTR_ERR(ac97->regmap), "devm_regmap_init_mmio failed\n");
309*1fc55a2bSKeguang Zhang
310*1fc55a2bSKeguang Zhang res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-tx");
311*1fc55a2bSKeguang Zhang if (!res)
312*1fc55a2bSKeguang Zhang return dev_err_probe(dev, -EINVAL, "Missing 'audio-tx' in reg-names property\n");
313*1fc55a2bSKeguang Zhang
314*1fc55a2bSKeguang Zhang ac97->tx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
315*1fc55a2bSKeguang Zhang DMA_TO_DEVICE, 0);
316*1fc55a2bSKeguang Zhang if (dma_mapping_error(dev, ac97->tx_dma_base))
317*1fc55a2bSKeguang Zhang return -ENXIO;
318*1fc55a2bSKeguang Zhang
319*1fc55a2bSKeguang Zhang res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-rx");
320*1fc55a2bSKeguang Zhang if (!res)
321*1fc55a2bSKeguang Zhang return dev_err_probe(dev, -EINVAL, "Missing 'audio-rx' in reg-names property\n");
322*1fc55a2bSKeguang Zhang
323*1fc55a2bSKeguang Zhang ac97->rx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
324*1fc55a2bSKeguang Zhang DMA_FROM_DEVICE, 0);
325*1fc55a2bSKeguang Zhang if (dma_mapping_error(dev, ac97->rx_dma_base))
326*1fc55a2bSKeguang Zhang return -ENXIO;
327*1fc55a2bSKeguang Zhang
328*1fc55a2bSKeguang Zhang ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
329*1fc55a2bSKeguang Zhang if (ret)
330*1fc55a2bSKeguang Zhang dev_err_probe(dev, ret, "failed to register PCM\n");
331*1fc55a2bSKeguang Zhang
332*1fc55a2bSKeguang Zhang ret = devm_snd_soc_register_component(dev, &ls1x_ac97_component,
333*1fc55a2bSKeguang Zhang ls1x_ac97_dai, ARRAY_SIZE(ls1x_ac97_dai));
334*1fc55a2bSKeguang Zhang if (ret)
335*1fc55a2bSKeguang Zhang dev_err_probe(dev, ret, "failed to register DAI\n");
336*1fc55a2bSKeguang Zhang
337*1fc55a2bSKeguang Zhang return snd_soc_set_ac97_ops(&ls1x_ac97_ops);
338*1fc55a2bSKeguang Zhang }
339*1fc55a2bSKeguang Zhang
ls1x_ac97_remove(struct platform_device * pdev)340*1fc55a2bSKeguang Zhang static void ls1x_ac97_remove(struct platform_device *pdev)
341*1fc55a2bSKeguang Zhang {
342*1fc55a2bSKeguang Zhang ls1x_ac97 = NULL;
343*1fc55a2bSKeguang Zhang snd_soc_set_ac97_ops(NULL);
344*1fc55a2bSKeguang Zhang }
345*1fc55a2bSKeguang Zhang
346*1fc55a2bSKeguang Zhang #ifdef CONFIG_PM_SLEEP
ls1x_ac97_suspend(struct device * dev)347*1fc55a2bSKeguang Zhang static int ls1x_ac97_suspend(struct device *dev)
348*1fc55a2bSKeguang Zhang {
349*1fc55a2bSKeguang Zhang int val;
350*1fc55a2bSKeguang Zhang
351*1fc55a2bSKeguang Zhang regmap_clear_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
352*1fc55a2bSKeguang Zhang regmap_clear_bits(ls1x_ac97->regmap, AC97_ICC,
353*1fc55a2bSKeguang Zhang M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
354*1fc55a2bSKeguang Zhang regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
355*1fc55a2bSKeguang Zhang
356*1fc55a2bSKeguang Zhang return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
357*1fc55a2bSKeguang Zhang (val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
358*1fc55a2bSKeguang Zhang }
359*1fc55a2bSKeguang Zhang
ls1x_ac97_resume(struct device * dev)360*1fc55a2bSKeguang Zhang static int ls1x_ac97_resume(struct device *dev)
361*1fc55a2bSKeguang Zhang {
362*1fc55a2bSKeguang Zhang int val;
363*1fc55a2bSKeguang Zhang
364*1fc55a2bSKeguang Zhang regmap_set_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
365*1fc55a2bSKeguang Zhang regmap_set_bits(ls1x_ac97->regmap, AC97_ICC,
366*1fc55a2bSKeguang Zhang M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
367*1fc55a2bSKeguang Zhang regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
368*1fc55a2bSKeguang Zhang
369*1fc55a2bSKeguang Zhang return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
370*1fc55a2bSKeguang Zhang !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
371*1fc55a2bSKeguang Zhang }
372*1fc55a2bSKeguang Zhang #endif
373*1fc55a2bSKeguang Zhang
374*1fc55a2bSKeguang Zhang static const struct dev_pm_ops ls1x_ac97_pm_ops = {
375*1fc55a2bSKeguang Zhang SET_SYSTEM_SLEEP_PM_OPS(ls1x_ac97_suspend, ls1x_ac97_resume)
376*1fc55a2bSKeguang Zhang };
377*1fc55a2bSKeguang Zhang
378*1fc55a2bSKeguang Zhang static const struct of_device_id ls1x_ac97_match[] = {
379*1fc55a2bSKeguang Zhang { .compatible = "loongson,ls1b-ac97" },
380*1fc55a2bSKeguang Zhang { /* sentinel */ }
381*1fc55a2bSKeguang Zhang };
382*1fc55a2bSKeguang Zhang MODULE_DEVICE_TABLE(of, ls1x_ac97_match);
383*1fc55a2bSKeguang Zhang
384*1fc55a2bSKeguang Zhang static struct platform_driver ls1x_ac97_driver = {
385*1fc55a2bSKeguang Zhang .probe = ls1x_ac97_probe,
386*1fc55a2bSKeguang Zhang .remove = ls1x_ac97_remove,
387*1fc55a2bSKeguang Zhang .driver = {
388*1fc55a2bSKeguang Zhang .name = KBUILD_MODNAME,
389*1fc55a2bSKeguang Zhang .of_match_table = ls1x_ac97_match,
390*1fc55a2bSKeguang Zhang .pm = &ls1x_ac97_pm_ops,
391*1fc55a2bSKeguang Zhang },
392*1fc55a2bSKeguang Zhang };
393*1fc55a2bSKeguang Zhang
394*1fc55a2bSKeguang Zhang module_platform_driver(ls1x_ac97_driver);
395*1fc55a2bSKeguang Zhang
396*1fc55a2bSKeguang Zhang MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
397*1fc55a2bSKeguang Zhang MODULE_DESCRIPTION("Loongson-1 AC97 Controller Driver");
398*1fc55a2bSKeguang Zhang MODULE_LICENSE("GPL");
399