diff options
author | Arnaud Pouliquen <arnaud.pouliquen@st.com> | 2015-07-16 05:36:04 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-07-16 16:38:23 -0400 |
commit | f3bd847eb0a7744b1a86f6b585149434cc6f57ff (patch) | |
tree | e9bd54a45b2e7f3473593295547e2021c74eb985 | |
parent | c3a0003aaf0dc2ffd006a3bb5abc1f5b639552a7 (diff) |
ASoC: sti: Add uniperipheral dai driver
ASoc uniperipheral dai driver that manages uniperipheral DAIs and registers
associated generic dma engine platform.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/sti/sti_uniperif.c | 253 | ||||
-rw-r--r-- | sound/soc/sti/uniperif.h | 9 |
2 files changed, 262 insertions, 0 deletions
diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c new file mode 100644 index 000000000000..749e6b294184 --- /dev/null +++ b/sound/soc/sti/sti_uniperif.c | |||
@@ -0,0 +1,253 @@ | |||
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"); | ||
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h index f396958e8a08..f1e583de3c6f 100644 --- a/sound/soc/sti/uniperif.h +++ b/sound/soc/sti/uniperif.h | |||
@@ -1215,4 +1215,13 @@ int uni_player_resume(struct uniperif *player); | |||
1215 | /* uniperiph reader */ | 1215 | /* uniperiph reader */ |
1216 | int uni_reader_init(struct platform_device *pdev, | 1216 | int uni_reader_init(struct platform_device *pdev, |
1217 | struct uniperif *uni_reader); | 1217 | struct uniperif *uni_reader); |
1218 | |||
1219 | /* common */ | ||
1220 | int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, | ||
1221 | unsigned int fmt); | ||
1222 | |||
1223 | int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream, | ||
1224 | struct snd_pcm_hw_params *params, | ||
1225 | struct snd_soc_dai *dai); | ||
1226 | |||
1218 | #endif | 1227 | #endif |