1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2012-2013, Analog Devices Inc.
4*4882a593Smuzhiyun * Author: Lars-Peter Clausen <lars@metafoo.de>
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/clk.h>
8*4882a593Smuzhiyun #include <linux/init.h>
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/of.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/regmap.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <sound/core.h>
17*4882a593Smuzhiyun #include <sound/pcm.h>
18*4882a593Smuzhiyun #include <sound/pcm_params.h>
19*4882a593Smuzhiyun #include <sound/soc.h>
20*4882a593Smuzhiyun #include <sound/dmaengine_pcm.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define AXI_I2S_REG_RESET 0x00
23*4882a593Smuzhiyun #define AXI_I2S_REG_CTRL 0x04
24*4882a593Smuzhiyun #define AXI_I2S_REG_CLK_CTRL 0x08
25*4882a593Smuzhiyun #define AXI_I2S_REG_STATUS 0x10
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define AXI_I2S_REG_RX_FIFO 0x28
28*4882a593Smuzhiyun #define AXI_I2S_REG_TX_FIFO 0x2C
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define AXI_I2S_RESET_GLOBAL BIT(0)
31*4882a593Smuzhiyun #define AXI_I2S_RESET_TX_FIFO BIT(1)
32*4882a593Smuzhiyun #define AXI_I2S_RESET_RX_FIFO BIT(2)
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define AXI_I2S_CTRL_TX_EN BIT(0)
35*4882a593Smuzhiyun #define AXI_I2S_CTRL_RX_EN BIT(1)
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /* The frame size is configurable, but for now we always set it 64 bit */
38*4882a593Smuzhiyun #define AXI_I2S_BITS_PER_FRAME 64
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun struct axi_i2s {
41*4882a593Smuzhiyun struct regmap *regmap;
42*4882a593Smuzhiyun struct clk *clk;
43*4882a593Smuzhiyun struct clk *clk_ref;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun bool has_capture;
46*4882a593Smuzhiyun bool has_playback;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun struct snd_soc_dai_driver dai_driver;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun struct snd_dmaengine_dai_dma_data capture_dma_data;
51*4882a593Smuzhiyun struct snd_dmaengine_dai_dma_data playback_dma_data;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun struct snd_ratnum ratnum;
54*4882a593Smuzhiyun struct snd_pcm_hw_constraint_ratnums rate_constraints;
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun
axi_i2s_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)57*4882a593Smuzhiyun static int axi_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
58*4882a593Smuzhiyun struct snd_soc_dai *dai)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
61*4882a593Smuzhiyun unsigned int mask, val;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
64*4882a593Smuzhiyun mask = AXI_I2S_CTRL_RX_EN;
65*4882a593Smuzhiyun else
66*4882a593Smuzhiyun mask = AXI_I2S_CTRL_TX_EN;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun switch (cmd) {
69*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_START:
70*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_RESUME:
71*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
72*4882a593Smuzhiyun val = mask;
73*4882a593Smuzhiyun break;
74*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_STOP:
75*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_SUSPEND:
76*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
77*4882a593Smuzhiyun val = 0;
78*4882a593Smuzhiyun break;
79*4882a593Smuzhiyun default:
80*4882a593Smuzhiyun return -EINVAL;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun regmap_update_bits(i2s->regmap, AXI_I2S_REG_CTRL, mask, val);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun return 0;
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
axi_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)88*4882a593Smuzhiyun static int axi_i2s_hw_params(struct snd_pcm_substream *substream,
89*4882a593Smuzhiyun struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
92*4882a593Smuzhiyun unsigned int bclk_div, word_size;
93*4882a593Smuzhiyun unsigned int bclk_rate;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun bclk_rate = params_rate(params) * AXI_I2S_BITS_PER_FRAME;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun word_size = AXI_I2S_BITS_PER_FRAME / 2 - 1;
98*4882a593Smuzhiyun bclk_div = DIV_ROUND_UP(clk_get_rate(i2s->clk_ref), bclk_rate) / 2 - 1;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun regmap_write(i2s->regmap, AXI_I2S_REG_CLK_CTRL, (word_size << 16) |
101*4882a593Smuzhiyun bclk_div);
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun return 0;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
axi_i2s_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)106*4882a593Smuzhiyun static int axi_i2s_startup(struct snd_pcm_substream *substream,
107*4882a593Smuzhiyun struct snd_soc_dai *dai)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
110*4882a593Smuzhiyun uint32_t mask;
111*4882a593Smuzhiyun int ret;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
114*4882a593Smuzhiyun mask = AXI_I2S_RESET_RX_FIFO;
115*4882a593Smuzhiyun else
116*4882a593Smuzhiyun mask = AXI_I2S_RESET_TX_FIFO;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun regmap_write(i2s->regmap, AXI_I2S_REG_RESET, mask);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
121*4882a593Smuzhiyun SNDRV_PCM_HW_PARAM_RATE,
122*4882a593Smuzhiyun &i2s->rate_constraints);
123*4882a593Smuzhiyun if (ret)
124*4882a593Smuzhiyun return ret;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun return clk_prepare_enable(i2s->clk_ref);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
axi_i2s_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)129*4882a593Smuzhiyun static void axi_i2s_shutdown(struct snd_pcm_substream *substream,
130*4882a593Smuzhiyun struct snd_soc_dai *dai)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun clk_disable_unprepare(i2s->clk_ref);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
axi_i2s_dai_probe(struct snd_soc_dai * dai)137*4882a593Smuzhiyun static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun snd_soc_dai_init_dma_data(
142*4882a593Smuzhiyun dai,
143*4882a593Smuzhiyun i2s->has_playback ? &i2s->playback_dma_data : NULL,
144*4882a593Smuzhiyun i2s->has_capture ? &i2s->capture_dma_data : NULL);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun return 0;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
150*4882a593Smuzhiyun .startup = axi_i2s_startup,
151*4882a593Smuzhiyun .shutdown = axi_i2s_shutdown,
152*4882a593Smuzhiyun .trigger = axi_i2s_trigger,
153*4882a593Smuzhiyun .hw_params = axi_i2s_hw_params,
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun static struct snd_soc_dai_driver axi_i2s_dai = {
157*4882a593Smuzhiyun .probe = axi_i2s_dai_probe,
158*4882a593Smuzhiyun .ops = &axi_i2s_dai_ops,
159*4882a593Smuzhiyun .symmetric_rates = 1,
160*4882a593Smuzhiyun };
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun static const struct snd_soc_component_driver axi_i2s_component = {
163*4882a593Smuzhiyun .name = "axi-i2s",
164*4882a593Smuzhiyun };
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun static const struct regmap_config axi_i2s_regmap_config = {
167*4882a593Smuzhiyun .reg_bits = 32,
168*4882a593Smuzhiyun .reg_stride = 4,
169*4882a593Smuzhiyun .val_bits = 32,
170*4882a593Smuzhiyun .max_register = AXI_I2S_REG_STATUS,
171*4882a593Smuzhiyun };
172*4882a593Smuzhiyun
axi_i2s_parse_of(struct axi_i2s * i2s,const struct device_node * np)173*4882a593Smuzhiyun static void axi_i2s_parse_of(struct axi_i2s *i2s, const struct device_node *np)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun struct property *dma_names;
176*4882a593Smuzhiyun const char *dma_name;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun of_property_for_each_string(np, "dma-names", dma_names, dma_name) {
179*4882a593Smuzhiyun if (strcmp(dma_name, "rx") == 0)
180*4882a593Smuzhiyun i2s->has_capture = true;
181*4882a593Smuzhiyun if (strcmp(dma_name, "tx") == 0)
182*4882a593Smuzhiyun i2s->has_playback = true;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
axi_i2s_probe(struct platform_device * pdev)186*4882a593Smuzhiyun static int axi_i2s_probe(struct platform_device *pdev)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun struct resource *res;
189*4882a593Smuzhiyun struct axi_i2s *i2s;
190*4882a593Smuzhiyun void __iomem *base;
191*4882a593Smuzhiyun int ret;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
194*4882a593Smuzhiyun if (!i2s)
195*4882a593Smuzhiyun return -ENOMEM;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun platform_set_drvdata(pdev, i2s);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun axi_i2s_parse_of(i2s, pdev->dev.of_node);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
202*4882a593Smuzhiyun base = devm_ioremap_resource(&pdev->dev, res);
203*4882a593Smuzhiyun if (IS_ERR(base))
204*4882a593Smuzhiyun return PTR_ERR(base);
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun i2s->regmap = devm_regmap_init_mmio(&pdev->dev, base,
207*4882a593Smuzhiyun &axi_i2s_regmap_config);
208*4882a593Smuzhiyun if (IS_ERR(i2s->regmap))
209*4882a593Smuzhiyun return PTR_ERR(i2s->regmap);
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun i2s->clk = devm_clk_get(&pdev->dev, "axi");
212*4882a593Smuzhiyun if (IS_ERR(i2s->clk))
213*4882a593Smuzhiyun return PTR_ERR(i2s->clk);
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
216*4882a593Smuzhiyun if (IS_ERR(i2s->clk_ref))
217*4882a593Smuzhiyun return PTR_ERR(i2s->clk_ref);
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun ret = clk_prepare_enable(i2s->clk);
220*4882a593Smuzhiyun if (ret)
221*4882a593Smuzhiyun return ret;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun if (i2s->has_playback) {
224*4882a593Smuzhiyun axi_i2s_dai.playback.channels_min = 2;
225*4882a593Smuzhiyun axi_i2s_dai.playback.channels_max = 2;
226*4882a593Smuzhiyun axi_i2s_dai.playback.rates = SNDRV_PCM_RATE_KNOT;
227*4882a593Smuzhiyun axi_i2s_dai.playback.formats =
228*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
231*4882a593Smuzhiyun i2s->playback_dma_data.addr_width = 4;
232*4882a593Smuzhiyun i2s->playback_dma_data.maxburst = 1;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun if (i2s->has_capture) {
236*4882a593Smuzhiyun axi_i2s_dai.capture.channels_min = 2;
237*4882a593Smuzhiyun axi_i2s_dai.capture.channels_max = 2;
238*4882a593Smuzhiyun axi_i2s_dai.capture.rates = SNDRV_PCM_RATE_KNOT;
239*4882a593Smuzhiyun axi_i2s_dai.capture.formats =
240*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
243*4882a593Smuzhiyun i2s->capture_dma_data.addr_width = 4;
244*4882a593Smuzhiyun i2s->capture_dma_data.maxburst = 1;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
248*4882a593Smuzhiyun i2s->ratnum.den_step = 1;
249*4882a593Smuzhiyun i2s->ratnum.den_min = 1;
250*4882a593Smuzhiyun i2s->ratnum.den_max = 64;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun i2s->rate_constraints.rats = &i2s->ratnum;
253*4882a593Smuzhiyun i2s->rate_constraints.nrats = 1;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun regmap_write(i2s->regmap, AXI_I2S_REG_RESET, AXI_I2S_RESET_GLOBAL);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun ret = devm_snd_soc_register_component(&pdev->dev, &axi_i2s_component,
258*4882a593Smuzhiyun &axi_i2s_dai, 1);
259*4882a593Smuzhiyun if (ret)
260*4882a593Smuzhiyun goto err_clk_disable;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
263*4882a593Smuzhiyun if (ret)
264*4882a593Smuzhiyun goto err_clk_disable;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
267*4882a593Smuzhiyun i2s->has_capture ? "enabled" : "disabled",
268*4882a593Smuzhiyun i2s->has_playback ? "enabled" : "disabled");
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun return 0;
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun err_clk_disable:
273*4882a593Smuzhiyun clk_disable_unprepare(i2s->clk);
274*4882a593Smuzhiyun return ret;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun
axi_i2s_dev_remove(struct platform_device * pdev)277*4882a593Smuzhiyun static int axi_i2s_dev_remove(struct platform_device *pdev)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun struct axi_i2s *i2s = platform_get_drvdata(pdev);
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun clk_disable_unprepare(i2s->clk);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun return 0;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun static const struct of_device_id axi_i2s_of_match[] = {
287*4882a593Smuzhiyun { .compatible = "adi,axi-i2s-1.00.a", },
288*4882a593Smuzhiyun {},
289*4882a593Smuzhiyun };
290*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, axi_i2s_of_match);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun static struct platform_driver axi_i2s_driver = {
293*4882a593Smuzhiyun .driver = {
294*4882a593Smuzhiyun .name = "axi-i2s",
295*4882a593Smuzhiyun .of_match_table = axi_i2s_of_match,
296*4882a593Smuzhiyun },
297*4882a593Smuzhiyun .probe = axi_i2s_probe,
298*4882a593Smuzhiyun .remove = axi_i2s_dev_remove,
299*4882a593Smuzhiyun };
300*4882a593Smuzhiyun module_platform_driver(axi_i2s_driver);
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
303*4882a593Smuzhiyun MODULE_DESCRIPTION("AXI I2S driver");
304*4882a593Smuzhiyun MODULE_LICENSE("GPL");
305