]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - sound/soc/sti/sti_uniperif.c
Merge tag 'armsoc-dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[mirror_ubuntu-zesty-kernel.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 #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");