.. | .. |
---|
7 | 7 | #include <linux/module.h> |
---|
8 | 8 | #include <linux/of_platform.h> |
---|
9 | 9 | #include <linux/regmap.h> |
---|
| 10 | +#include <linux/reset.h> |
---|
10 | 11 | #include <sound/soc.h> |
---|
11 | 12 | |
---|
12 | 13 | #include "axg-tdm-formatter.h" |
---|
.. | .. |
---|
20 | 21 | struct clk *lrclk; |
---|
21 | 22 | struct clk *sclk_sel; |
---|
22 | 23 | struct clk *lrclk_sel; |
---|
| 24 | + struct reset_control *reset; |
---|
23 | 25 | bool enabled; |
---|
24 | 26 | struct regmap *map; |
---|
25 | 27 | }; |
---|
.. | .. |
---|
68 | 70 | static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) |
---|
69 | 71 | { |
---|
70 | 72 | struct axg_tdm_stream *ts = formatter->stream; |
---|
71 | | - bool invert = formatter->drv->invert_sclk; |
---|
| 73 | + bool invert; |
---|
72 | 74 | int ret; |
---|
73 | 75 | |
---|
74 | 76 | /* Do nothing if the formatter is already enabled */ |
---|
.. | .. |
---|
76 | 78 | return 0; |
---|
77 | 79 | |
---|
78 | 80 | /* |
---|
79 | | - * If sclk is inverted, invert it back and provide the inversion |
---|
80 | | - * required by the formatter |
---|
| 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. |
---|
81 | 93 | */ |
---|
82 | | - invert ^= axg_tdm_sclk_invert(ts->iface->fmt); |
---|
83 | | - ret = clk_set_phase(formatter->sclk, invert ? 180 : 0); |
---|
| 94 | + ret = reset_control_reset(formatter->reset); |
---|
| 95 | + if (ret) |
---|
| 96 | + return ret; |
---|
| 97 | + |
---|
| 98 | + /* |
---|
| 99 | + * If sclk is inverted, it means the bit should latched on the |
---|
| 100 | + * rising edge which is what our HW expects. If not, we need to |
---|
| 101 | + * invert it before the formatter. |
---|
| 102 | + */ |
---|
| 103 | + invert = axg_tdm_sclk_invert(ts->iface->fmt); |
---|
| 104 | + ret = clk_set_phase(formatter->sclk, invert ? 0 : 180); |
---|
84 | 105 | if (ret) |
---|
85 | 106 | return ret; |
---|
86 | 107 | |
---|
87 | 108 | /* Setup the stream parameter in the formatter */ |
---|
88 | | - ret = formatter->drv->ops->prepare(formatter->map, formatter->stream); |
---|
| 109 | + ret = formatter->drv->ops->prepare(formatter->map, |
---|
| 110 | + formatter->drv->quirks, |
---|
| 111 | + formatter->stream); |
---|
89 | 112 | if (ret) |
---|
90 | 113 | return ret; |
---|
91 | 114 | |
---|
.. | .. |
---|
231 | 254 | struct device *dev = &pdev->dev; |
---|
232 | 255 | const struct axg_tdm_formatter_driver *drv; |
---|
233 | 256 | struct axg_tdm_formatter *formatter; |
---|
234 | | - struct resource *res; |
---|
235 | 257 | void __iomem *regs; |
---|
236 | 258 | int ret; |
---|
237 | 259 | |
---|
.. | .. |
---|
247 | 269 | platform_set_drvdata(pdev, formatter); |
---|
248 | 270 | formatter->drv = drv; |
---|
249 | 271 | |
---|
250 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
251 | | - regs = devm_ioremap_resource(dev, res); |
---|
| 272 | + regs = devm_platform_ioremap_resource(pdev, 0); |
---|
252 | 273 | if (IS_ERR(regs)) |
---|
253 | 274 | return PTR_ERR(regs); |
---|
254 | 275 | |
---|
.. | .. |
---|
301 | 322 | ret = PTR_ERR(formatter->lrclk_sel); |
---|
302 | 323 | if (ret != -EPROBE_DEFER) |
---|
303 | 324 | dev_err(dev, "failed to get lrclk_sel: %d\n", ret); |
---|
| 325 | + return ret; |
---|
| 326 | + } |
---|
| 327 | + |
---|
| 328 | + /* Formatter dedicated reset line */ |
---|
| 329 | + formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); |
---|
| 330 | + if (IS_ERR(formatter->reset)) { |
---|
| 331 | + ret = PTR_ERR(formatter->reset); |
---|
| 332 | + if (ret != -EPROBE_DEFER) |
---|
| 333 | + dev_err(dev, "failed to get reset: %d\n", ret); |
---|
304 | 334 | return ret; |
---|
305 | 335 | } |
---|
306 | 336 | |
---|
.. | .. |
---|
368 | 398 | /* |
---|
369 | 399 | * If the list is not empty, it would mean that one of the formatter |
---|
370 | 400 | * widget is still powered and attached to the interface while we |
---|
371 | | - * we are removing the TDM DAI. It should not be possible |
---|
| 401 | + * are removing the TDM DAI. It should not be possible |
---|
372 | 402 | */ |
---|
373 | 403 | WARN_ON(!list_empty(&ts->formatter_list)); |
---|
374 | 404 | mutex_destroy(&ts->lock); |
---|