11a11d88fSJerome Brunet // SPDX-License-Identifier: (GPL-2.0 OR MIT) 21a11d88fSJerome Brunet // 31a11d88fSJerome Brunet // Copyright (c) 2018 BayLibre, SAS. 41a11d88fSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com> 51a11d88fSJerome Brunet 61a11d88fSJerome Brunet #include <linux/clk.h> 71a11d88fSJerome Brunet #include <linux/module.h> 81a11d88fSJerome Brunet #include <linux/of_platform.h> 91a11d88fSJerome Brunet #include <linux/regmap.h> 10751bd5dbSJerome Brunet #include <linux/reset.h> 111a11d88fSJerome Brunet #include <sound/soc.h> 121a11d88fSJerome Brunet 131a11d88fSJerome Brunet #include "axg-tdm-formatter.h" 141a11d88fSJerome Brunet 151a11d88fSJerome Brunet struct axg_tdm_formatter { 161a11d88fSJerome Brunet struct list_head list; 171a11d88fSJerome Brunet struct axg_tdm_stream *stream; 181a11d88fSJerome Brunet const struct axg_tdm_formatter_driver *drv; 191a11d88fSJerome Brunet struct clk *pclk; 201a11d88fSJerome Brunet struct clk *sclk; 211a11d88fSJerome Brunet struct clk *lrclk; 221a11d88fSJerome Brunet struct clk *sclk_sel; 231a11d88fSJerome Brunet struct clk *lrclk_sel; 24751bd5dbSJerome Brunet struct reset_control *reset; 251a11d88fSJerome Brunet bool enabled; 261a11d88fSJerome Brunet struct regmap *map; 271a11d88fSJerome Brunet }; 281a11d88fSJerome Brunet 291a11d88fSJerome Brunet int axg_tdm_formatter_set_channel_masks(struct regmap *map, 301a11d88fSJerome Brunet struct axg_tdm_stream *ts, 311a11d88fSJerome Brunet unsigned int offset) 321a11d88fSJerome Brunet { 33*c1f848f1SJerome Brunet unsigned int ch = ts->channels; 34*c1f848f1SJerome Brunet u32 val[AXG_TDM_NUM_LANES]; 35*c1f848f1SJerome Brunet int i, j, k; 36*c1f848f1SJerome Brunet 37*c1f848f1SJerome Brunet /* 38*c1f848f1SJerome Brunet * We need to mimick the slot distribution used by the HW to keep the 39*c1f848f1SJerome Brunet * channel placement consistent regardless of the number of channel 40*c1f848f1SJerome Brunet * in the stream. This is why the odd algorithm below is used. 41*c1f848f1SJerome Brunet */ 42*c1f848f1SJerome Brunet memset(val, 0, sizeof(*val) * AXG_TDM_NUM_LANES); 431a11d88fSJerome Brunet 441a11d88fSJerome Brunet /* 451a11d88fSJerome Brunet * Distribute the channels of the stream over the available slots 46*c1f848f1SJerome Brunet * of each TDM lane. We need to go over the 32 slots ... 471a11d88fSJerome Brunet */ 48*c1f848f1SJerome Brunet for (i = 0; (i < 32) && ch; i += 2) { 49*c1f848f1SJerome Brunet /* ... of all the lanes ... */ 50*c1f848f1SJerome Brunet for (j = 0; j < AXG_TDM_NUM_LANES; j++) { 51*c1f848f1SJerome Brunet /* ... then distribute the channels in pairs */ 52*c1f848f1SJerome Brunet for (k = 0; k < 2; k++) { 53*c1f848f1SJerome Brunet if ((BIT(i + k) & ts->mask[j]) && ch) { 54*c1f848f1SJerome Brunet val[j] |= BIT(i + k); 551a11d88fSJerome Brunet ch -= 1; 561a11d88fSJerome Brunet } 57*c1f848f1SJerome Brunet } 58*c1f848f1SJerome Brunet } 591a11d88fSJerome Brunet } 601a11d88fSJerome Brunet 611a11d88fSJerome Brunet /* 621a11d88fSJerome Brunet * If we still have channel left at the end of the process, it means 631a11d88fSJerome Brunet * the stream has more channels than we can accommodate and we should 641a11d88fSJerome Brunet * have caught this earlier. 651a11d88fSJerome Brunet */ 661a11d88fSJerome Brunet if (WARN_ON(ch != 0)) { 671a11d88fSJerome Brunet pr_err("channel mask error\n"); 681a11d88fSJerome Brunet return -EINVAL; 691a11d88fSJerome Brunet } 701a11d88fSJerome Brunet 71*c1f848f1SJerome Brunet for (i = 0; i < AXG_TDM_NUM_LANES; i++) { 72*c1f848f1SJerome Brunet regmap_write(map, offset, val[i]); 73*c1f848f1SJerome Brunet offset += regmap_get_reg_stride(map); 74*c1f848f1SJerome Brunet } 75*c1f848f1SJerome Brunet 761a11d88fSJerome Brunet return 0; 771a11d88fSJerome Brunet } 781a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks); 791a11d88fSJerome Brunet 801a11d88fSJerome Brunet static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) 811a11d88fSJerome Brunet { 821a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 830d3f01dcSJerome Brunet bool invert; 841a11d88fSJerome Brunet int ret; 851a11d88fSJerome Brunet 861a11d88fSJerome Brunet /* Do nothing if the formatter is already enabled */ 871a11d88fSJerome Brunet if (formatter->enabled) 881a11d88fSJerome Brunet return 0; 891a11d88fSJerome Brunet 901a11d88fSJerome Brunet /* 91751bd5dbSJerome Brunet * On the g12a (and possibly other SoCs), when a stream using 92751bd5dbSJerome Brunet * multiple lanes is restarted, it will sometimes not start 93751bd5dbSJerome Brunet * from the first lane, but randomly from another used one. 94751bd5dbSJerome Brunet * The result is an unexpected and random channel shift. 95751bd5dbSJerome Brunet * 96751bd5dbSJerome Brunet * The hypothesis is that an HW counter is not properly reset 97751bd5dbSJerome Brunet * and the formatter simply starts on the lane it stopped 98751bd5dbSJerome Brunet * before. Unfortunately, there does not seems to be a way to 99751bd5dbSJerome Brunet * reset this through the registers of the block. 100751bd5dbSJerome Brunet * 101751bd5dbSJerome Brunet * However, the g12a has indenpendent reset lines for each audio 102751bd5dbSJerome Brunet * devices. Using this reset before each start solves the issue. 103751bd5dbSJerome Brunet */ 104751bd5dbSJerome Brunet ret = reset_control_reset(formatter->reset); 105751bd5dbSJerome Brunet if (ret) 106751bd5dbSJerome Brunet return ret; 107751bd5dbSJerome Brunet 108751bd5dbSJerome Brunet /* 1090d3f01dcSJerome Brunet * If sclk is inverted, it means the bit should latched on the 1100d3f01dcSJerome Brunet * rising edge which is what our HW expects. If not, we need to 1110d3f01dcSJerome Brunet * invert it before the formatter. 1121a11d88fSJerome Brunet */ 1130d3f01dcSJerome Brunet invert = axg_tdm_sclk_invert(ts->iface->fmt); 1140d3f01dcSJerome Brunet ret = clk_set_phase(formatter->sclk, invert ? 0 : 180); 1151a11d88fSJerome Brunet if (ret) 1161a11d88fSJerome Brunet return ret; 1171a11d88fSJerome Brunet 1181a11d88fSJerome Brunet /* Setup the stream parameter in the formatter */ 119f01bc67fSJerome Brunet ret = formatter->drv->ops->prepare(formatter->map, 120f01bc67fSJerome Brunet formatter->drv->quirks, 121f01bc67fSJerome Brunet formatter->stream); 1221a11d88fSJerome Brunet if (ret) 1231a11d88fSJerome Brunet return ret; 1241a11d88fSJerome Brunet 1251a11d88fSJerome Brunet /* Enable the signal clocks feeding the formatter */ 1261a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->sclk); 1271a11d88fSJerome Brunet if (ret) 1281a11d88fSJerome Brunet return ret; 1291a11d88fSJerome Brunet 1301a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->lrclk); 1311a11d88fSJerome Brunet if (ret) { 1321a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 1331a11d88fSJerome Brunet return ret; 1341a11d88fSJerome Brunet } 1351a11d88fSJerome Brunet 1361a11d88fSJerome Brunet /* Finally, actually enable the formatter */ 1371a11d88fSJerome Brunet formatter->drv->ops->enable(formatter->map); 1381a11d88fSJerome Brunet formatter->enabled = true; 1391a11d88fSJerome Brunet 1401a11d88fSJerome Brunet return 0; 1411a11d88fSJerome Brunet } 1421a11d88fSJerome Brunet 1431a11d88fSJerome Brunet static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) 1441a11d88fSJerome Brunet { 1451a11d88fSJerome Brunet /* Do nothing if the formatter is already disabled */ 1461a11d88fSJerome Brunet if (!formatter->enabled) 1471a11d88fSJerome Brunet return; 1481a11d88fSJerome Brunet 1491a11d88fSJerome Brunet formatter->drv->ops->disable(formatter->map); 1501a11d88fSJerome Brunet clk_disable_unprepare(formatter->lrclk); 1511a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 1521a11d88fSJerome Brunet formatter->enabled = false; 1531a11d88fSJerome Brunet } 1541a11d88fSJerome Brunet 1551a11d88fSJerome Brunet static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) 1561a11d88fSJerome Brunet { 1571a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 1581a11d88fSJerome Brunet int ret = 0; 1591a11d88fSJerome Brunet 1601a11d88fSJerome Brunet mutex_lock(&ts->lock); 1611a11d88fSJerome Brunet 1621a11d88fSJerome Brunet /* Catch up if the stream is already running when we attach */ 1631a11d88fSJerome Brunet if (ts->ready) { 1641a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 1651a11d88fSJerome Brunet if (ret) { 1661a11d88fSJerome Brunet pr_err("failed to enable formatter\n"); 1671a11d88fSJerome Brunet goto out; 1681a11d88fSJerome Brunet } 1691a11d88fSJerome Brunet } 1701a11d88fSJerome Brunet 1711a11d88fSJerome Brunet list_add_tail(&formatter->list, &ts->formatter_list); 1721a11d88fSJerome Brunet out: 1731a11d88fSJerome Brunet mutex_unlock(&ts->lock); 1741a11d88fSJerome Brunet return ret; 1751a11d88fSJerome Brunet } 1761a11d88fSJerome Brunet 1771a11d88fSJerome Brunet static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) 1781a11d88fSJerome Brunet { 1791a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 1801a11d88fSJerome Brunet 1811a11d88fSJerome Brunet mutex_lock(&ts->lock); 1821a11d88fSJerome Brunet list_del(&formatter->list); 1831a11d88fSJerome Brunet mutex_unlock(&ts->lock); 1841a11d88fSJerome Brunet 1851a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 1861a11d88fSJerome Brunet } 1871a11d88fSJerome Brunet 1881a11d88fSJerome Brunet static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, 1891a11d88fSJerome Brunet struct snd_soc_dapm_widget *w) 1901a11d88fSJerome Brunet { 1911a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); 1921a11d88fSJerome Brunet int ret; 1931a11d88fSJerome Brunet 1941a11d88fSJerome Brunet /* 1951a11d88fSJerome Brunet * If we don't get a stream at this stage, it would mean that the 1961a11d88fSJerome Brunet * widget is powering up but is not attached to any backend DAI. 1971a11d88fSJerome Brunet * It should not happen, ever ! 1981a11d88fSJerome Brunet */ 1991a11d88fSJerome Brunet if (WARN_ON(!ts)) 2001a11d88fSJerome Brunet return -ENODEV; 2011a11d88fSJerome Brunet 2021a11d88fSJerome Brunet /* Clock our device */ 2031a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->pclk); 2041a11d88fSJerome Brunet if (ret) 2051a11d88fSJerome Brunet return ret; 2061a11d88fSJerome Brunet 2071a11d88fSJerome Brunet /* Reparent the bit clock to the TDM interface */ 2081a11d88fSJerome Brunet ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); 2091a11d88fSJerome Brunet if (ret) 2101a11d88fSJerome Brunet goto disable_pclk; 2111a11d88fSJerome Brunet 2121a11d88fSJerome Brunet /* Reparent the sample clock to the TDM interface */ 2131a11d88fSJerome Brunet ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); 2141a11d88fSJerome Brunet if (ret) 2151a11d88fSJerome Brunet goto disable_pclk; 2161a11d88fSJerome Brunet 2171a11d88fSJerome Brunet formatter->stream = ts; 2181a11d88fSJerome Brunet ret = axg_tdm_formatter_attach(formatter); 2191a11d88fSJerome Brunet if (ret) 2201a11d88fSJerome Brunet goto disable_pclk; 2211a11d88fSJerome Brunet 2221a11d88fSJerome Brunet return 0; 2231a11d88fSJerome Brunet 2241a11d88fSJerome Brunet disable_pclk: 2251a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 2261a11d88fSJerome Brunet return ret; 2271a11d88fSJerome Brunet } 2281a11d88fSJerome Brunet 2291a11d88fSJerome Brunet static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) 2301a11d88fSJerome Brunet { 2311a11d88fSJerome Brunet axg_tdm_formatter_dettach(formatter); 2321a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 2331a11d88fSJerome Brunet formatter->stream = NULL; 2341a11d88fSJerome Brunet } 2351a11d88fSJerome Brunet 2361a11d88fSJerome Brunet int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, 2371a11d88fSJerome Brunet struct snd_kcontrol *control, 2381a11d88fSJerome Brunet int event) 2391a11d88fSJerome Brunet { 2401a11d88fSJerome Brunet struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); 2411a11d88fSJerome Brunet struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); 2421a11d88fSJerome Brunet int ret = 0; 2431a11d88fSJerome Brunet 2441a11d88fSJerome Brunet switch (event) { 2451a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMU: 2461a11d88fSJerome Brunet ret = axg_tdm_formatter_power_up(formatter, w); 2471a11d88fSJerome Brunet break; 2481a11d88fSJerome Brunet 2491a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMD: 2501a11d88fSJerome Brunet axg_tdm_formatter_power_down(formatter); 2511a11d88fSJerome Brunet break; 2521a11d88fSJerome Brunet 2531a11d88fSJerome Brunet default: 2541a11d88fSJerome Brunet dev_err(c->dev, "Unexpected event %d\n", event); 2551a11d88fSJerome Brunet return -EINVAL; 2561a11d88fSJerome Brunet } 2571a11d88fSJerome Brunet 2581a11d88fSJerome Brunet return ret; 2591a11d88fSJerome Brunet } 2601a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); 2611a11d88fSJerome Brunet 2621a11d88fSJerome Brunet int axg_tdm_formatter_probe(struct platform_device *pdev) 2631a11d88fSJerome Brunet { 2641a11d88fSJerome Brunet struct device *dev = &pdev->dev; 2651a11d88fSJerome Brunet const struct axg_tdm_formatter_driver *drv; 2661a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 2671a11d88fSJerome Brunet void __iomem *regs; 2681a11d88fSJerome Brunet 2691a11d88fSJerome Brunet drv = of_device_get_match_data(dev); 2701a11d88fSJerome Brunet if (!drv) { 2711a11d88fSJerome Brunet dev_err(dev, "failed to match device\n"); 2721a11d88fSJerome Brunet return -ENODEV; 2731a11d88fSJerome Brunet } 2741a11d88fSJerome Brunet 2751a11d88fSJerome Brunet formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); 2761a11d88fSJerome Brunet if (!formatter) 2771a11d88fSJerome Brunet return -ENOMEM; 2781a11d88fSJerome Brunet platform_set_drvdata(pdev, formatter); 2791a11d88fSJerome Brunet formatter->drv = drv; 2801a11d88fSJerome Brunet 28110392fcaSYueHaibing regs = devm_platform_ioremap_resource(pdev, 0); 2821a11d88fSJerome Brunet if (IS_ERR(regs)) 2831a11d88fSJerome Brunet return PTR_ERR(regs); 2841a11d88fSJerome Brunet 2851a11d88fSJerome Brunet formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); 2861a11d88fSJerome Brunet if (IS_ERR(formatter->map)) { 2871a11d88fSJerome Brunet dev_err(dev, "failed to init regmap: %ld\n", 2881a11d88fSJerome Brunet PTR_ERR(formatter->map)); 2891a11d88fSJerome Brunet return PTR_ERR(formatter->map); 2901a11d88fSJerome Brunet } 2911a11d88fSJerome Brunet 2921a11d88fSJerome Brunet /* Peripharal clock */ 2931a11d88fSJerome Brunet formatter->pclk = devm_clk_get(dev, "pclk"); 2942ff4e003SKuninori Morimoto if (IS_ERR(formatter->pclk)) 2952ff4e003SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n"); 2961a11d88fSJerome Brunet 2971a11d88fSJerome Brunet /* Formatter bit clock */ 2981a11d88fSJerome Brunet formatter->sclk = devm_clk_get(dev, "sclk"); 2992ff4e003SKuninori Morimoto if (IS_ERR(formatter->sclk)) 3002ff4e003SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n"); 3011a11d88fSJerome Brunet 3021a11d88fSJerome Brunet /* Formatter sample clock */ 3031a11d88fSJerome Brunet formatter->lrclk = devm_clk_get(dev, "lrclk"); 3042ff4e003SKuninori Morimoto if (IS_ERR(formatter->lrclk)) 3052ff4e003SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n"); 3061a11d88fSJerome Brunet 3071a11d88fSJerome Brunet /* Formatter bit clock input multiplexer */ 3081a11d88fSJerome Brunet formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); 3092ff4e003SKuninori Morimoto if (IS_ERR(formatter->sclk_sel)) 3102ff4e003SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n"); 3111a11d88fSJerome Brunet 3121a11d88fSJerome Brunet /* Formatter sample clock input multiplexer */ 3131a11d88fSJerome Brunet formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); 3142ff4e003SKuninori Morimoto if (IS_ERR(formatter->lrclk_sel)) 3152ff4e003SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel), 3162ff4e003SKuninori Morimoto "failed to get lrclk_sel\n"); 3171a11d88fSJerome Brunet 318751bd5dbSJerome Brunet /* Formatter dedicated reset line */ 31918dd62aeSJerome Brunet formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); 3202ff4e003SKuninori Morimoto if (IS_ERR(formatter->reset)) 3212ff4e003SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n"); 322751bd5dbSJerome Brunet 3231a11d88fSJerome Brunet return devm_snd_soc_register_component(dev, drv->component_drv, 3241a11d88fSJerome Brunet NULL, 0); 3251a11d88fSJerome Brunet } 3261a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); 3271a11d88fSJerome Brunet 3281a11d88fSJerome Brunet int axg_tdm_stream_start(struct axg_tdm_stream *ts) 3291a11d88fSJerome Brunet { 3301a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 3311a11d88fSJerome Brunet int ret = 0; 3321a11d88fSJerome Brunet 3331a11d88fSJerome Brunet mutex_lock(&ts->lock); 3341a11d88fSJerome Brunet ts->ready = true; 3351a11d88fSJerome Brunet 3361a11d88fSJerome Brunet /* Start all the formatters attached to the stream */ 3371a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 3381a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 3391a11d88fSJerome Brunet if (ret) { 3401a11d88fSJerome Brunet pr_err("failed to start tdm stream\n"); 3411a11d88fSJerome Brunet goto out; 3421a11d88fSJerome Brunet } 3431a11d88fSJerome Brunet } 3441a11d88fSJerome Brunet 3451a11d88fSJerome Brunet out: 3461a11d88fSJerome Brunet mutex_unlock(&ts->lock); 3471a11d88fSJerome Brunet return ret; 3481a11d88fSJerome Brunet } 3491a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_start); 3501a11d88fSJerome Brunet 3511a11d88fSJerome Brunet void axg_tdm_stream_stop(struct axg_tdm_stream *ts) 3521a11d88fSJerome Brunet { 3531a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 3541a11d88fSJerome Brunet 3551a11d88fSJerome Brunet mutex_lock(&ts->lock); 3561a11d88fSJerome Brunet ts->ready = false; 3571a11d88fSJerome Brunet 3581a11d88fSJerome Brunet /* Stop all the formatters attached to the stream */ 3591a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 3601a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 3611a11d88fSJerome Brunet } 3621a11d88fSJerome Brunet 3631a11d88fSJerome Brunet mutex_unlock(&ts->lock); 3641a11d88fSJerome Brunet } 3651a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); 3661a11d88fSJerome Brunet 3671a11d88fSJerome Brunet struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) 3681a11d88fSJerome Brunet { 3691a11d88fSJerome Brunet struct axg_tdm_stream *ts; 3701a11d88fSJerome Brunet 3711a11d88fSJerome Brunet ts = kzalloc(sizeof(*ts), GFP_KERNEL); 3721a11d88fSJerome Brunet if (ts) { 3731a11d88fSJerome Brunet INIT_LIST_HEAD(&ts->formatter_list); 3741a11d88fSJerome Brunet mutex_init(&ts->lock); 3751a11d88fSJerome Brunet ts->iface = iface; 3761a11d88fSJerome Brunet } 3771a11d88fSJerome Brunet 3781a11d88fSJerome Brunet return ts; 3791a11d88fSJerome Brunet } 3801a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); 3811a11d88fSJerome Brunet 3821a11d88fSJerome Brunet void axg_tdm_stream_free(struct axg_tdm_stream *ts) 3831a11d88fSJerome Brunet { 3841a11d88fSJerome Brunet /* 3851a11d88fSJerome Brunet * If the list is not empty, it would mean that one of the formatter 3861a11d88fSJerome Brunet * widget is still powered and attached to the interface while we 3870d8aa2ccSRandy Dunlap * are removing the TDM DAI. It should not be possible 3881a11d88fSJerome Brunet */ 3891a11d88fSJerome Brunet WARN_ON(!list_empty(&ts->formatter_list)); 3901a11d88fSJerome Brunet mutex_destroy(&ts->lock); 3911a11d88fSJerome Brunet kfree(ts); 3921a11d88fSJerome Brunet } 3931a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_free); 3941a11d88fSJerome Brunet 3951a11d88fSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); 3961a11d88fSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 3971a11d88fSJerome Brunet MODULE_LICENSE("GPL v2"); 398