ASoC: Add ability to build sti drivers
[firefly-linux-kernel-4.4.55.git] / sound / soc / sti / sti_uniperif.c
1 /*
2  * Copyright (C) STMicroelectronics SA 2015
3  * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
4  *          for STMicroelectronics.
5  * License terms:  GNU General Public License (GPL), version 2
6  */
7
8 #include <linux/module.h>
9
10 #include "uniperif.h"
11
12 /*
13  * sti_uniperiph_dai_create_ctrl
14  * This function is used to create Ctrl associated to DAI but also pcm device.
15  * Request is done by front end to associate ctrl with pcm device id
16  */
17 int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
18 {
19         struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
20         struct uniperif *uni = priv->dai_data.uni;
21         struct snd_kcontrol_new *ctrl;
22         int i;
23
24         if (!uni->num_ctrls)
25                 return 0;
26
27         for (i = 0; i < uni->num_ctrls; i++) {
28                 /*
29                  * Several Control can have same name. Controls are indexed on
30                  * Uniperipheral instance ID
31                  */
32                 ctrl = &uni->snd_ctrls[i];
33                 ctrl->index = uni->info->id;
34                 ctrl->device = uni->info->id;
35         }
36
37         return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls);
38 }
39
40 /*
41  * DAI
42  */
43 int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
44                                 struct snd_pcm_hw_params *params,
45                                 struct snd_soc_dai *dai)
46 {
47         struct snd_dmaengine_dai_dma_data *dma_data;
48         int transfer_size;
49
50         transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
51
52         dma_data = snd_soc_dai_get_dma_data(dai, substream);
53         dma_data->maxburst = transfer_size;
54
55         return 0;
56 }
57
58 int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
59 {
60         struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
61
62         priv->dai_data.uni->daifmt = fmt;
63
64         return 0;
65 }
66
67 static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai)
68 {
69         struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
70         struct uniperif *uni = priv->dai_data.uni;
71         int ret;
72
73         /* The uniperipheral should be in stopped state */
74         if (uni->state != UNIPERIF_STATE_STOPPED) {
75                 dev_err(uni->dev, "%s: invalid uni state( %d)",
76                         __func__, (int)uni->state);
77                 return -EBUSY;
78         }
79
80         /* Pinctrl: switch pinstate to sleep */
81         ret = pinctrl_pm_select_sleep_state(uni->dev);
82         if (ret)
83                 dev_err(uni->dev, "%s: failed to select pinctrl state",
84                         __func__);
85
86         return ret;
87 }
88
89 static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai)
90 {
91         struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
92         struct uniperif *uni = priv->dai_data.uni;
93         int ret;
94
95         if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) {
96                 ret = uni_player_resume(uni);
97                 if (ret)
98                         return ret;
99         }
100
101         /* pinctrl: switch pinstate to default */
102         ret = pinctrl_pm_select_default_state(uni->dev);
103         if (ret)
104                 dev_err(uni->dev, "%s: failed to select pinctrl state",
105                         __func__);
106
107         return ret;
108 }
109
110 static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai)
111 {
112         struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
113         struct sti_uniperiph_dai *dai_data = &priv->dai_data;
114
115         /* DMA settings*/
116         if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player"))
117                 snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL);
118         else
119                 snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data);
120
121         dai_data->dma_data.addr = dai_data->uni->fifo_phys_address;
122         dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
123
124         return sti_uniperiph_dai_create_ctrl(dai);
125 }
126
127 static const struct snd_soc_dai_driver sti_uniperiph_dai_template = {
128         .probe = sti_uniperiph_dai_probe,
129         .suspend = sti_uniperiph_dai_suspend,
130         .resume = sti_uniperiph_dai_resume
131 };
132
133 static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
134         .name = "sti_cpu_dai",
135 };
136
137 static int sti_uniperiph_cpu_dai_of(struct device_node *node,
138                                     struct sti_uniperiph_data *priv)
139 {
140         const char *str;
141         int ret;
142         struct device *dev = &priv->pdev->dev;
143         struct sti_uniperiph_dai *dai_data = &priv->dai_data;
144         struct snd_soc_dai_driver *dai = priv->dai;
145         struct snd_soc_pcm_stream *stream;
146         struct uniperif *uni;
147
148         uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL);
149         if (!uni)
150                 return -ENOMEM;
151
152         *dai = sti_uniperiph_dai_template;
153         ret = of_property_read_string(node, "dai-name", &str);
154         if (ret < 0) {
155                 dev_err(dev, "%s: dai name missing.\n", __func__);
156                 return -EINVAL;
157         }
158         dai->name = str;
159
160         /* Get resources */
161         uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0);
162
163         if (!uni->mem_region) {
164                 dev_err(dev, "Failed to get memory resource");
165                 return -ENODEV;
166         }
167
168         uni->base = devm_ioremap_resource(dev, uni->mem_region);
169
170         if (IS_ERR(uni->base))
171                 return PTR_ERR(uni->base);
172
173         uni->fifo_phys_address = uni->mem_region->start +
174                                      UNIPERIF_FIFO_DATA_OFFSET(uni);
175
176         uni->irq = platform_get_irq(priv->pdev, 0);
177         if (!uni->irq < 0) {
178                 dev_err(dev, "Failed to get IRQ resource");
179                 return -ENXIO;
180         }
181
182         dai_data->uni = uni;
183
184         if (of_device_is_compatible(node, "st,sti-uni-player")) {
185                 uni_player_init(priv->pdev, uni);
186                 stream = &dai->playback;
187         } else {
188                 uni_reader_init(priv->pdev, uni);
189                 stream = &dai->capture;
190         }
191         dai->ops = uni->dai_ops;
192
193         stream->stream_name = dai->name;
194         stream->channels_min = uni->hw->channels_min;
195         stream->channels_max = uni->hw->channels_max;
196         stream->rates = uni->hw->rates;
197         stream->formats = uni->hw->formats;
198
199         return 0;
200 }
201
202 static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
203         .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
204 };
205
206 static int sti_uniperiph_probe(struct platform_device *pdev)
207 {
208         struct sti_uniperiph_data *priv;
209         struct device_node *node = pdev->dev.of_node;
210         int ret;
211
212         /* Allocate the private data and the CPU_DAI array */
213         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
214         if (!priv)
215                 return -ENOMEM;
216         priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai), GFP_KERNEL);
217         if (!priv->dai)
218                 return -ENOMEM;
219
220         priv->pdev = pdev;
221
222         ret = sti_uniperiph_cpu_dai_of(node, priv);
223
224         dev_set_drvdata(&pdev->dev, priv);
225
226         ret = snd_soc_register_component(&pdev->dev,
227                                          &sti_uniperiph_dai_component,
228                                          priv->dai, 1);
229         if (ret < 0)
230                 return ret;
231
232         return devm_snd_dmaengine_pcm_register(&pdev->dev,
233                                                &dmaengine_pcm_config, 0);
234 }
235
236 static const struct of_device_id snd_soc_sti_match[] = {
237         { .compatible = "st,sti-uni-player", },
238         { .compatible = "st,sti-uni-reader", },
239         {},
240 };
241
242 static struct platform_driver sti_uniperiph_driver = {
243         .driver = {
244                 .name = "sti-uniperiph-dai",
245                 .of_match_table = snd_soc_sti_match,
246         },
247         .probe = sti_uniperiph_probe,
248 };
249 module_platform_driver(sti_uniperiph_driver);
250
251 MODULE_DESCRIPTION("uniperipheral DAI driver");
252 MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
253 MODULE_LICENSE("GPL v2");