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