blob: 39bcefe5eea0c329910f31671575c11f8385092d [file] [log] [blame]
Arnaud Pouliquenf3bd8472015-07-16 11:36:04 +02001/*
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>
Mark Brownbf9185d2015-07-20 18:36:15 +01009#include <linux/pinctrl/consumer.h>
Arnaud Pouliquenf3bd8472015-07-16 11:36:04 +020010
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 */
kbuild test robotf2da4542015-07-17 07:44:09 +080018static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
Arnaud Pouliquenf3bd8472015-07-16 11:36:04 +020019{
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 */
44int 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
59int 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
68static 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
90static 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
111static 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
128static 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
134static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
135 .name = "sti_cpu_dai",
136};
137
138static 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);
Dan Carpentera4642e92015-07-21 11:57:36 +0300178 if (uni->irq < 0) {
Arnaud Pouliquenf3bd8472015-07-16 11:36:04 +0200179 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
203static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
204 .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
205};
206
207static 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
Axel Line1290912015-08-29 23:06:32 +0800227 ret = devm_snd_soc_register_component(&pdev->dev,
228 &sti_uniperiph_dai_component,
229 priv->dai, 1);
Arnaud Pouliquenf3bd8472015-07-16 11:36:04 +0200230 if (ret < 0)
231 return ret;
232
233 return devm_snd_dmaengine_pcm_register(&pdev->dev,
234 &dmaengine_pcm_config, 0);
235}
236
237static const struct of_device_id snd_soc_sti_match[] = {
238 { .compatible = "st,sti-uni-player", },
239 { .compatible = "st,sti-uni-reader", },
240 {},
241};
242
243static 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};
250module_platform_driver(sti_uniperiph_driver);
251
252MODULE_DESCRIPTION("uniperipheral DAI driver");
253MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
254MODULE_LICENSE("GPL v2");