Commit | Line | Data |
---|---|---|
1a11d88f JB |
1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | // | |
3 | // Copyright (c) 2018 BayLibre, SAS. | |
4 | // Author: Jerome Brunet <jbrunet@baylibre.com> | |
5 | ||
6 | #include <linux/clk.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/of_platform.h> | |
9 | #include <linux/regmap.h> | |
751bd5db | 10 | #include <linux/reset.h> |
1a11d88f JB |
11 | #include <sound/soc.h> |
12 | ||
13 | #include "axg-tdm-formatter.h" | |
14 | ||
15 | struct axg_tdm_formatter { | |
16 | struct list_head list; | |
17 | struct axg_tdm_stream *stream; | |
18 | const struct axg_tdm_formatter_driver *drv; | |
19 | struct clk *pclk; | |
20 | struct clk *sclk; | |
21 | struct clk *lrclk; | |
22 | struct clk *sclk_sel; | |
23 | struct clk *lrclk_sel; | |
751bd5db | 24 | struct reset_control *reset; |
1a11d88f JB |
25 | bool enabled; |
26 | struct regmap *map; | |
27 | }; | |
28 | ||
29 | int axg_tdm_formatter_set_channel_masks(struct regmap *map, | |
30 | struct axg_tdm_stream *ts, | |
31 | unsigned int offset) | |
32 | { | |
33 | unsigned int val, ch = ts->channels; | |
34 | unsigned long mask; | |
35 | int i, j; | |
36 | ||
37 | /* | |
38 | * Distribute the channels of the stream over the available slots | |
39 | * of each TDM lane | |
40 | */ | |
41 | for (i = 0; i < AXG_TDM_NUM_LANES; i++) { | |
42 | val = 0; | |
43 | mask = ts->mask[i]; | |
44 | ||
45 | for (j = find_first_bit(&mask, 32); | |
46 | (j < 32) && ch; | |
47 | j = find_next_bit(&mask, 32, j + 1)) { | |
48 | val |= 1 << j; | |
49 | ch -= 1; | |
50 | } | |
51 | ||
52 | regmap_write(map, offset, val); | |
53 | offset += regmap_get_reg_stride(map); | |
54 | } | |
55 | ||
56 | /* | |
57 | * If we still have channel left at the end of the process, it means | |
58 | * the stream has more channels than we can accommodate and we should | |
59 | * have caught this earlier. | |
60 | */ | |
61 | if (WARN_ON(ch != 0)) { | |
62 | pr_err("channel mask error\n"); | |
63 | return -EINVAL; | |
64 | } | |
65 | ||
66 | return 0; | |
67 | } | |
68 | EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks); | |
69 | ||
70 | static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) | |
71 | { | |
72 | struct axg_tdm_stream *ts = formatter->stream; | |
f01bc67f | 73 | bool invert = formatter->drv->quirks->invert_sclk; |
1a11d88f JB |
74 | int ret; |
75 | ||
76 | /* Do nothing if the formatter is already enabled */ | |
77 | if (formatter->enabled) | |
78 | return 0; | |
79 | ||
751bd5db JB |
80 | /* |
81 | * On the g12a (and possibly other SoCs), when a stream using | |
82 | * multiple lanes is restarted, it will sometimes not start | |
83 | * from the first lane, but randomly from another used one. | |
84 | * The result is an unexpected and random channel shift. | |
85 | * | |
86 | * The hypothesis is that an HW counter is not properly reset | |
87 | * and the formatter simply starts on the lane it stopped | |
88 | * before. Unfortunately, there does not seems to be a way to | |
89 | * reset this through the registers of the block. | |
90 | * | |
91 | * However, the g12a has indenpendent reset lines for each audio | |
92 | * devices. Using this reset before each start solves the issue. | |
93 | */ | |
94 | ret = reset_control_reset(formatter->reset); | |
95 | if (ret) | |
96 | return ret; | |
97 | ||
1a11d88f JB |
98 | /* |
99 | * If sclk is inverted, invert it back and provide the inversion | |
100 | * required by the formatter | |
101 | */ | |
102 | invert ^= axg_tdm_sclk_invert(ts->iface->fmt); | |
103 | ret = clk_set_phase(formatter->sclk, invert ? 180 : 0); | |
104 | if (ret) | |
105 | return ret; | |
106 | ||
107 | /* Setup the stream parameter in the formatter */ | |
f01bc67f JB |
108 | ret = formatter->drv->ops->prepare(formatter->map, |
109 | formatter->drv->quirks, | |
110 | formatter->stream); | |
1a11d88f JB |
111 | if (ret) |
112 | return ret; | |
113 | ||
114 | /* Enable the signal clocks feeding the formatter */ | |
115 | ret = clk_prepare_enable(formatter->sclk); | |
116 | if (ret) | |
117 | return ret; | |
118 | ||
119 | ret = clk_prepare_enable(formatter->lrclk); | |
120 | if (ret) { | |
121 | clk_disable_unprepare(formatter->sclk); | |
122 | return ret; | |
123 | } | |
124 | ||
125 | /* Finally, actually enable the formatter */ | |
126 | formatter->drv->ops->enable(formatter->map); | |
127 | formatter->enabled = true; | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
132 | static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) | |
133 | { | |
134 | /* Do nothing if the formatter is already disabled */ | |
135 | if (!formatter->enabled) | |
136 | return; | |
137 | ||
138 | formatter->drv->ops->disable(formatter->map); | |
139 | clk_disable_unprepare(formatter->lrclk); | |
140 | clk_disable_unprepare(formatter->sclk); | |
141 | formatter->enabled = false; | |
142 | } | |
143 | ||
144 | static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) | |
145 | { | |
146 | struct axg_tdm_stream *ts = formatter->stream; | |
147 | int ret = 0; | |
148 | ||
149 | mutex_lock(&ts->lock); | |
150 | ||
151 | /* Catch up if the stream is already running when we attach */ | |
152 | if (ts->ready) { | |
153 | ret = axg_tdm_formatter_enable(formatter); | |
154 | if (ret) { | |
155 | pr_err("failed to enable formatter\n"); | |
156 | goto out; | |
157 | } | |
158 | } | |
159 | ||
160 | list_add_tail(&formatter->list, &ts->formatter_list); | |
161 | out: | |
162 | mutex_unlock(&ts->lock); | |
163 | return ret; | |
164 | } | |
165 | ||
166 | static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) | |
167 | { | |
168 | struct axg_tdm_stream *ts = formatter->stream; | |
169 | ||
170 | mutex_lock(&ts->lock); | |
171 | list_del(&formatter->list); | |
172 | mutex_unlock(&ts->lock); | |
173 | ||
174 | axg_tdm_formatter_disable(formatter); | |
175 | } | |
176 | ||
177 | static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, | |
178 | struct snd_soc_dapm_widget *w) | |
179 | { | |
180 | struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); | |
181 | int ret; | |
182 | ||
183 | /* | |
184 | * If we don't get a stream at this stage, it would mean that the | |
185 | * widget is powering up but is not attached to any backend DAI. | |
186 | * It should not happen, ever ! | |
187 | */ | |
188 | if (WARN_ON(!ts)) | |
189 | return -ENODEV; | |
190 | ||
191 | /* Clock our device */ | |
192 | ret = clk_prepare_enable(formatter->pclk); | |
193 | if (ret) | |
194 | return ret; | |
195 | ||
196 | /* Reparent the bit clock to the TDM interface */ | |
197 | ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); | |
198 | if (ret) | |
199 | goto disable_pclk; | |
200 | ||
201 | /* Reparent the sample clock to the TDM interface */ | |
202 | ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); | |
203 | if (ret) | |
204 | goto disable_pclk; | |
205 | ||
206 | formatter->stream = ts; | |
207 | ret = axg_tdm_formatter_attach(formatter); | |
208 | if (ret) | |
209 | goto disable_pclk; | |
210 | ||
211 | return 0; | |
212 | ||
213 | disable_pclk: | |
214 | clk_disable_unprepare(formatter->pclk); | |
215 | return ret; | |
216 | } | |
217 | ||
218 | static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) | |
219 | { | |
220 | axg_tdm_formatter_dettach(formatter); | |
221 | clk_disable_unprepare(formatter->pclk); | |
222 | formatter->stream = NULL; | |
223 | } | |
224 | ||
225 | int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, | |
226 | struct snd_kcontrol *control, | |
227 | int event) | |
228 | { | |
229 | struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); | |
230 | struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); | |
231 | int ret = 0; | |
232 | ||
233 | switch (event) { | |
234 | case SND_SOC_DAPM_PRE_PMU: | |
235 | ret = axg_tdm_formatter_power_up(formatter, w); | |
236 | break; | |
237 | ||
238 | case SND_SOC_DAPM_PRE_PMD: | |
239 | axg_tdm_formatter_power_down(formatter); | |
240 | break; | |
241 | ||
242 | default: | |
243 | dev_err(c->dev, "Unexpected event %d\n", event); | |
244 | return -EINVAL; | |
245 | } | |
246 | ||
247 | return ret; | |
248 | } | |
249 | EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); | |
250 | ||
251 | int axg_tdm_formatter_probe(struct platform_device *pdev) | |
252 | { | |
253 | struct device *dev = &pdev->dev; | |
254 | const struct axg_tdm_formatter_driver *drv; | |
255 | struct axg_tdm_formatter *formatter; | |
256 | struct resource *res; | |
257 | void __iomem *regs; | |
258 | int ret; | |
259 | ||
260 | drv = of_device_get_match_data(dev); | |
261 | if (!drv) { | |
262 | dev_err(dev, "failed to match device\n"); | |
263 | return -ENODEV; | |
264 | } | |
265 | ||
266 | formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); | |
267 | if (!formatter) | |
268 | return -ENOMEM; | |
269 | platform_set_drvdata(pdev, formatter); | |
270 | formatter->drv = drv; | |
271 | ||
272 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
273 | regs = devm_ioremap_resource(dev, res); | |
274 | if (IS_ERR(regs)) | |
275 | return PTR_ERR(regs); | |
276 | ||
277 | formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); | |
278 | if (IS_ERR(formatter->map)) { | |
279 | dev_err(dev, "failed to init regmap: %ld\n", | |
280 | PTR_ERR(formatter->map)); | |
281 | return PTR_ERR(formatter->map); | |
282 | } | |
283 | ||
284 | /* Peripharal clock */ | |
285 | formatter->pclk = devm_clk_get(dev, "pclk"); | |
286 | if (IS_ERR(formatter->pclk)) { | |
287 | ret = PTR_ERR(formatter->pclk); | |
288 | if (ret != -EPROBE_DEFER) | |
289 | dev_err(dev, "failed to get pclk: %d\n", ret); | |
290 | return ret; | |
291 | } | |
292 | ||
293 | /* Formatter bit clock */ | |
294 | formatter->sclk = devm_clk_get(dev, "sclk"); | |
295 | if (IS_ERR(formatter->sclk)) { | |
296 | ret = PTR_ERR(formatter->sclk); | |
297 | if (ret != -EPROBE_DEFER) | |
298 | dev_err(dev, "failed to get sclk: %d\n", ret); | |
299 | return ret; | |
300 | } | |
301 | ||
302 | /* Formatter sample clock */ | |
303 | formatter->lrclk = devm_clk_get(dev, "lrclk"); | |
304 | if (IS_ERR(formatter->lrclk)) { | |
305 | ret = PTR_ERR(formatter->lrclk); | |
306 | if (ret != -EPROBE_DEFER) | |
307 | dev_err(dev, "failed to get lrclk: %d\n", ret); | |
308 | return ret; | |
309 | } | |
310 | ||
311 | /* Formatter bit clock input multiplexer */ | |
312 | formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); | |
313 | if (IS_ERR(formatter->sclk_sel)) { | |
314 | ret = PTR_ERR(formatter->sclk_sel); | |
315 | if (ret != -EPROBE_DEFER) | |
316 | dev_err(dev, "failed to get sclk_sel: %d\n", ret); | |
317 | return ret; | |
318 | } | |
319 | ||
320 | /* Formatter sample clock input multiplexer */ | |
321 | formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); | |
322 | if (IS_ERR(formatter->lrclk_sel)) { | |
323 | ret = PTR_ERR(formatter->lrclk_sel); | |
324 | if (ret != -EPROBE_DEFER) | |
325 | dev_err(dev, "failed to get lrclk_sel: %d\n", ret); | |
326 | return ret; | |
327 | } | |
328 | ||
751bd5db JB |
329 | /* Formatter dedicated reset line */ |
330 | formatter->reset = reset_control_get_optional_exclusive(dev, NULL); | |
331 | if (IS_ERR(formatter->reset)) { | |
332 | ret = PTR_ERR(formatter->reset); | |
333 | if (ret != -EPROBE_DEFER) | |
334 | dev_err(dev, "failed to get reset: %d\n", ret); | |
335 | return ret; | |
336 | } | |
337 | ||
1a11d88f JB |
338 | return devm_snd_soc_register_component(dev, drv->component_drv, |
339 | NULL, 0); | |
340 | } | |
341 | EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); | |
342 | ||
343 | int axg_tdm_stream_start(struct axg_tdm_stream *ts) | |
344 | { | |
345 | struct axg_tdm_formatter *formatter; | |
346 | int ret = 0; | |
347 | ||
348 | mutex_lock(&ts->lock); | |
349 | ts->ready = true; | |
350 | ||
351 | /* Start all the formatters attached to the stream */ | |
352 | list_for_each_entry(formatter, &ts->formatter_list, list) { | |
353 | ret = axg_tdm_formatter_enable(formatter); | |
354 | if (ret) { | |
355 | pr_err("failed to start tdm stream\n"); | |
356 | goto out; | |
357 | } | |
358 | } | |
359 | ||
360 | out: | |
361 | mutex_unlock(&ts->lock); | |
362 | return ret; | |
363 | } | |
364 | EXPORT_SYMBOL_GPL(axg_tdm_stream_start); | |
365 | ||
366 | void axg_tdm_stream_stop(struct axg_tdm_stream *ts) | |
367 | { | |
368 | struct axg_tdm_formatter *formatter; | |
369 | ||
370 | mutex_lock(&ts->lock); | |
371 | ts->ready = false; | |
372 | ||
373 | /* Stop all the formatters attached to the stream */ | |
374 | list_for_each_entry(formatter, &ts->formatter_list, list) { | |
375 | axg_tdm_formatter_disable(formatter); | |
376 | } | |
377 | ||
378 | mutex_unlock(&ts->lock); | |
379 | } | |
380 | EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); | |
381 | ||
382 | struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) | |
383 | { | |
384 | struct axg_tdm_stream *ts; | |
385 | ||
386 | ts = kzalloc(sizeof(*ts), GFP_KERNEL); | |
387 | if (ts) { | |
388 | INIT_LIST_HEAD(&ts->formatter_list); | |
389 | mutex_init(&ts->lock); | |
390 | ts->iface = iface; | |
391 | } | |
392 | ||
393 | return ts; | |
394 | } | |
395 | EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); | |
396 | ||
397 | void axg_tdm_stream_free(struct axg_tdm_stream *ts) | |
398 | { | |
399 | /* | |
400 | * If the list is not empty, it would mean that one of the formatter | |
401 | * widget is still powered and attached to the interface while we | |
402 | * we are removing the TDM DAI. It should not be possible | |
403 | */ | |
404 | WARN_ON(!list_empty(&ts->formatter_list)); | |
405 | mutex_destroy(&ts->lock); | |
406 | kfree(ts); | |
407 | } | |
408 | EXPORT_SYMBOL_GPL(axg_tdm_stream_free); | |
409 | ||
410 | MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); | |
411 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); | |
412 | MODULE_LICENSE("GPL v2"); |