OSDN Git Service

clk: at91: fix masterck name
[uclinux-h8/linux.git] / sound / soc / xilinx / xlnx_i2s.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx ASoC I2S audio support
4  *
5  * Copyright (C) 2018 Xilinx, Inc.
6  *
7  * Author: Praveen Vuppala <praveenv@xilinx.com>
8  * Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
9  */
10
11 #include <linux/io.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_platform.h>
15 #include <linux/platform_device.h>
16 #include <sound/pcm_params.h>
17 #include <sound/soc.h>
18
19 #define DRV_NAME "xlnx_i2s"
20
21 #define I2S_CORE_CTRL_OFFSET            0x08
22 #define I2S_I2STIM_OFFSET               0x20
23 #define I2S_CH0_OFFSET                  0x30
24 #define I2S_I2STIM_VALID_MASK           GENMASK(7, 0)
25
26 static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai,
27                                     int div_id, int div)
28 {
29         void __iomem *base = snd_soc_dai_get_drvdata(cpu_dai);
30
31         if (!div || (div & ~I2S_I2STIM_VALID_MASK))
32                 return -EINVAL;
33
34         writel(div, base + I2S_I2STIM_OFFSET);
35
36         return 0;
37 }
38
39 static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream,
40                               struct snd_pcm_hw_params *params,
41                               struct snd_soc_dai *i2s_dai)
42 {
43         u32 reg_off, chan_id;
44         void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai);
45
46         chan_id = params_channels(params) / 2;
47
48         while (chan_id > 0) {
49                 reg_off = I2S_CH0_OFFSET + ((chan_id - 1) * 4);
50                 writel(chan_id, base + reg_off);
51                 chan_id--;
52         }
53
54         return 0;
55 }
56
57 static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
58                             struct snd_soc_dai *i2s_dai)
59 {
60         void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai);
61
62         switch (cmd) {
63         case SNDRV_PCM_TRIGGER_START:
64         case SNDRV_PCM_TRIGGER_RESUME:
65         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
66                 writel(1, base + I2S_CORE_CTRL_OFFSET);
67                 break;
68         case SNDRV_PCM_TRIGGER_STOP:
69         case SNDRV_PCM_TRIGGER_SUSPEND:
70         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
71                 writel(0, base + I2S_CORE_CTRL_OFFSET);
72                 break;
73         default:
74                 return -EINVAL;
75         }
76
77         return 0;
78 }
79
80 static const struct snd_soc_dai_ops xlnx_i2s_dai_ops = {
81         .trigger = xlnx_i2s_trigger,
82         .set_clkdiv = xlnx_i2s_set_sclkout_div,
83         .hw_params = xlnx_i2s_hw_params
84 };
85
86 static const struct snd_soc_component_driver xlnx_i2s_component = {
87         .name = DRV_NAME,
88 };
89
90 static const struct of_device_id xlnx_i2s_of_match[] = {
91         { .compatible = "xlnx,i2s-transmitter-1.0", },
92         { .compatible = "xlnx,i2s-receiver-1.0", },
93         {},
94 };
95 MODULE_DEVICE_TABLE(of, xlnx_i2s_of_match);
96
97 static int xlnx_i2s_probe(struct platform_device *pdev)
98 {
99         struct resource *res;
100         void __iomem *base;
101         struct snd_soc_dai_driver *dai_drv;
102         int ret;
103         u32 ch, format, data_width;
104         struct device *dev = &pdev->dev;
105         struct device_node *node = dev->of_node;
106
107         dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL);
108         if (!dai_drv)
109                 return -ENOMEM;
110
111         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
112         base = devm_ioremap_resource(&pdev->dev, res);
113         if (IS_ERR(base))
114                 return PTR_ERR(base);
115
116         ret = of_property_read_u32(node, "xlnx,num-channels", &ch);
117         if (ret < 0) {
118                 dev_err(dev, "cannot get supported channels\n");
119                 return ret;
120         }
121         ch = ch * 2;
122
123         ret = of_property_read_u32(node, "xlnx,dwidth", &data_width);
124         if (ret < 0) {
125                 dev_err(dev, "cannot get data width\n");
126                 return ret;
127         }
128         switch (data_width) {
129         case 16:
130                 format = SNDRV_PCM_FMTBIT_S16_LE;
131                 break;
132         case 24:
133                 format = SNDRV_PCM_FMTBIT_S24_LE;
134                 break;
135         default:
136                 return -EINVAL;
137         }
138
139         if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) {
140                 dai_drv->name = "xlnx_i2s_playback";
141                 dai_drv->playback.stream_name = "Playback";
142                 dai_drv->playback.formats = format;
143                 dai_drv->playback.channels_min = ch;
144                 dai_drv->playback.channels_max = ch;
145                 dai_drv->playback.rates = SNDRV_PCM_RATE_8000_192000;
146                 dai_drv->ops = &xlnx_i2s_dai_ops;
147         } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) {
148                 dai_drv->name = "xlnx_i2s_capture";
149                 dai_drv->capture.stream_name = "Capture";
150                 dai_drv->capture.formats = format;
151                 dai_drv->capture.channels_min = ch;
152                 dai_drv->capture.channels_max = ch;
153                 dai_drv->capture.rates = SNDRV_PCM_RATE_8000_192000;
154                 dai_drv->ops = &xlnx_i2s_dai_ops;
155         } else {
156                 return -ENODEV;
157         }
158
159         dev_set_drvdata(&pdev->dev, base);
160
161         ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component,
162                                               dai_drv, 1);
163         if (ret) {
164                 dev_err(&pdev->dev, "i2s component registration failed\n");
165                 return ret;
166         }
167
168         dev_info(&pdev->dev, "%s DAI registered\n", dai_drv->name);
169
170         return ret;
171 }
172
173 static struct platform_driver xlnx_i2s_aud_driver = {
174         .driver = {
175                 .name = DRV_NAME,
176                 .of_match_table = xlnx_i2s_of_match,
177         },
178         .probe = xlnx_i2s_probe,
179 };
180
181 module_platform_driver(xlnx_i2s_aud_driver);
182
183 MODULE_LICENSE("GPL v2");
184 MODULE_AUTHOR("Praveen Vuppala  <praveenv@xilinx.com>");
185 MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>");