| .. | .. |
|---|
| 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); |
|---|