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 { 331a11d88fSJerome Brunet unsigned int val, ch = ts->channels; 341a11d88fSJerome Brunet unsigned long mask; 351a11d88fSJerome Brunet int i, j; 361a11d88fSJerome Brunet 371a11d88fSJerome Brunet /* 381a11d88fSJerome Brunet * Distribute the channels of the stream over the available slots 391a11d88fSJerome Brunet * of each TDM lane 401a11d88fSJerome Brunet */ 411a11d88fSJerome Brunet for (i = 0; i < AXG_TDM_NUM_LANES; i++) { 421a11d88fSJerome Brunet val = 0; 431a11d88fSJerome Brunet mask = ts->mask[i]; 441a11d88fSJerome Brunet 451a11d88fSJerome Brunet for (j = find_first_bit(&mask, 32); 461a11d88fSJerome Brunet (j < 32) && ch; 471a11d88fSJerome Brunet j = find_next_bit(&mask, 32, j + 1)) { 481a11d88fSJerome Brunet val |= 1 << j; 491a11d88fSJerome Brunet ch -= 1; 501a11d88fSJerome Brunet } 511a11d88fSJerome Brunet 521a11d88fSJerome Brunet regmap_write(map, offset, val); 531a11d88fSJerome Brunet offset += regmap_get_reg_stride(map); 541a11d88fSJerome Brunet } 551a11d88fSJerome Brunet 561a11d88fSJerome Brunet /* 571a11d88fSJerome Brunet * If we still have channel left at the end of the process, it means 581a11d88fSJerome Brunet * the stream has more channels than we can accommodate and we should 591a11d88fSJerome Brunet * have caught this earlier. 601a11d88fSJerome Brunet */ 611a11d88fSJerome Brunet if (WARN_ON(ch != 0)) { 621a11d88fSJerome Brunet pr_err("channel mask error\n"); 631a11d88fSJerome Brunet return -EINVAL; 641a11d88fSJerome Brunet } 651a11d88fSJerome Brunet 661a11d88fSJerome Brunet return 0; 671a11d88fSJerome Brunet } 681a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks); 691a11d88fSJerome Brunet 701a11d88fSJerome Brunet static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) 711a11d88fSJerome Brunet { 721a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 73f01bc67fSJerome Brunet bool invert = formatter->drv->quirks->invert_sclk; 741a11d88fSJerome Brunet int ret; 751a11d88fSJerome Brunet 761a11d88fSJerome Brunet /* Do nothing if the formatter is already enabled */ 771a11d88fSJerome Brunet if (formatter->enabled) 781a11d88fSJerome Brunet return 0; 791a11d88fSJerome Brunet 801a11d88fSJerome Brunet /* 81751bd5dbSJerome Brunet * On the g12a (and possibly other SoCs), when a stream using 82751bd5dbSJerome Brunet * multiple lanes is restarted, it will sometimes not start 83751bd5dbSJerome Brunet * from the first lane, but randomly from another used one. 84751bd5dbSJerome Brunet * The result is an unexpected and random channel shift. 85751bd5dbSJerome Brunet * 86751bd5dbSJerome Brunet * The hypothesis is that an HW counter is not properly reset 87751bd5dbSJerome Brunet * and the formatter simply starts on the lane it stopped 88751bd5dbSJerome Brunet * before. Unfortunately, there does not seems to be a way to 89751bd5dbSJerome Brunet * reset this through the registers of the block. 90751bd5dbSJerome Brunet * 91751bd5dbSJerome Brunet * However, the g12a has indenpendent reset lines for each audio 92751bd5dbSJerome Brunet * devices. Using this reset before each start solves the issue. 93751bd5dbSJerome Brunet */ 94751bd5dbSJerome Brunet ret = reset_control_reset(formatter->reset); 95751bd5dbSJerome Brunet if (ret) 96751bd5dbSJerome Brunet return ret; 97751bd5dbSJerome Brunet 98751bd5dbSJerome Brunet /* 991a11d88fSJerome Brunet * If sclk is inverted, invert it back and provide the inversion 1001a11d88fSJerome Brunet * required by the formatter 1011a11d88fSJerome Brunet */ 1021a11d88fSJerome Brunet invert ^= axg_tdm_sclk_invert(ts->iface->fmt); 1031a11d88fSJerome Brunet ret = clk_set_phase(formatter->sclk, invert ? 180 : 0); 1041a11d88fSJerome Brunet if (ret) 1051a11d88fSJerome Brunet return ret; 1061a11d88fSJerome Brunet 1071a11d88fSJerome Brunet /* Setup the stream parameter in the formatter */ 108f01bc67fSJerome Brunet ret = formatter->drv->ops->prepare(formatter->map, 109f01bc67fSJerome Brunet formatter->drv->quirks, 110f01bc67fSJerome Brunet formatter->stream); 1111a11d88fSJerome Brunet if (ret) 1121a11d88fSJerome Brunet return ret; 1131a11d88fSJerome Brunet 1141a11d88fSJerome Brunet /* Enable the signal clocks feeding the formatter */ 1151a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->sclk); 1161a11d88fSJerome Brunet if (ret) 1171a11d88fSJerome Brunet return ret; 1181a11d88fSJerome Brunet 1191a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->lrclk); 1201a11d88fSJerome Brunet if (ret) { 1211a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 1221a11d88fSJerome Brunet return ret; 1231a11d88fSJerome Brunet } 1241a11d88fSJerome Brunet 1251a11d88fSJerome Brunet /* Finally, actually enable the formatter */ 1261a11d88fSJerome Brunet formatter->drv->ops->enable(formatter->map); 1271a11d88fSJerome Brunet formatter->enabled = true; 1281a11d88fSJerome Brunet 1291a11d88fSJerome Brunet return 0; 1301a11d88fSJerome Brunet } 1311a11d88fSJerome Brunet 1321a11d88fSJerome Brunet static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) 1331a11d88fSJerome Brunet { 1341a11d88fSJerome Brunet /* Do nothing if the formatter is already disabled */ 1351a11d88fSJerome Brunet if (!formatter->enabled) 1361a11d88fSJerome Brunet return; 1371a11d88fSJerome Brunet 1381a11d88fSJerome Brunet formatter->drv->ops->disable(formatter->map); 1391a11d88fSJerome Brunet clk_disable_unprepare(formatter->lrclk); 1401a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 1411a11d88fSJerome Brunet formatter->enabled = false; 1421a11d88fSJerome Brunet } 1431a11d88fSJerome Brunet 1441a11d88fSJerome Brunet static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) 1451a11d88fSJerome Brunet { 1461a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 1471a11d88fSJerome Brunet int ret = 0; 1481a11d88fSJerome Brunet 1491a11d88fSJerome Brunet mutex_lock(&ts->lock); 1501a11d88fSJerome Brunet 1511a11d88fSJerome Brunet /* Catch up if the stream is already running when we attach */ 1521a11d88fSJerome Brunet if (ts->ready) { 1531a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 1541a11d88fSJerome Brunet if (ret) { 1551a11d88fSJerome Brunet pr_err("failed to enable formatter\n"); 1561a11d88fSJerome Brunet goto out; 1571a11d88fSJerome Brunet } 1581a11d88fSJerome Brunet } 1591a11d88fSJerome Brunet 1601a11d88fSJerome Brunet list_add_tail(&formatter->list, &ts->formatter_list); 1611a11d88fSJerome Brunet out: 1621a11d88fSJerome Brunet mutex_unlock(&ts->lock); 1631a11d88fSJerome Brunet return ret; 1641a11d88fSJerome Brunet } 1651a11d88fSJerome Brunet 1661a11d88fSJerome Brunet static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) 1671a11d88fSJerome Brunet { 1681a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 1691a11d88fSJerome Brunet 1701a11d88fSJerome Brunet mutex_lock(&ts->lock); 1711a11d88fSJerome Brunet list_del(&formatter->list); 1721a11d88fSJerome Brunet mutex_unlock(&ts->lock); 1731a11d88fSJerome Brunet 1741a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 1751a11d88fSJerome Brunet } 1761a11d88fSJerome Brunet 1771a11d88fSJerome Brunet static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, 1781a11d88fSJerome Brunet struct snd_soc_dapm_widget *w) 1791a11d88fSJerome Brunet { 1801a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); 1811a11d88fSJerome Brunet int ret; 1821a11d88fSJerome Brunet 1831a11d88fSJerome Brunet /* 1841a11d88fSJerome Brunet * If we don't get a stream at this stage, it would mean that the 1851a11d88fSJerome Brunet * widget is powering up but is not attached to any backend DAI. 1861a11d88fSJerome Brunet * It should not happen, ever ! 1871a11d88fSJerome Brunet */ 1881a11d88fSJerome Brunet if (WARN_ON(!ts)) 1891a11d88fSJerome Brunet return -ENODEV; 1901a11d88fSJerome Brunet 1911a11d88fSJerome Brunet /* Clock our device */ 1921a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->pclk); 1931a11d88fSJerome Brunet if (ret) 1941a11d88fSJerome Brunet return ret; 1951a11d88fSJerome Brunet 1961a11d88fSJerome Brunet /* Reparent the bit clock to the TDM interface */ 1971a11d88fSJerome Brunet ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); 1981a11d88fSJerome Brunet if (ret) 1991a11d88fSJerome Brunet goto disable_pclk; 2001a11d88fSJerome Brunet 2011a11d88fSJerome Brunet /* Reparent the sample clock to the TDM interface */ 2021a11d88fSJerome Brunet ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); 2031a11d88fSJerome Brunet if (ret) 2041a11d88fSJerome Brunet goto disable_pclk; 2051a11d88fSJerome Brunet 2061a11d88fSJerome Brunet formatter->stream = ts; 2071a11d88fSJerome Brunet ret = axg_tdm_formatter_attach(formatter); 2081a11d88fSJerome Brunet if (ret) 2091a11d88fSJerome Brunet goto disable_pclk; 2101a11d88fSJerome Brunet 2111a11d88fSJerome Brunet return 0; 2121a11d88fSJerome Brunet 2131a11d88fSJerome Brunet disable_pclk: 2141a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 2151a11d88fSJerome Brunet return ret; 2161a11d88fSJerome Brunet } 2171a11d88fSJerome Brunet 2181a11d88fSJerome Brunet static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) 2191a11d88fSJerome Brunet { 2201a11d88fSJerome Brunet axg_tdm_formatter_dettach(formatter); 2211a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 2221a11d88fSJerome Brunet formatter->stream = NULL; 2231a11d88fSJerome Brunet } 2241a11d88fSJerome Brunet 2251a11d88fSJerome Brunet int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, 2261a11d88fSJerome Brunet struct snd_kcontrol *control, 2271a11d88fSJerome Brunet int event) 2281a11d88fSJerome Brunet { 2291a11d88fSJerome Brunet struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); 2301a11d88fSJerome Brunet struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); 2311a11d88fSJerome Brunet int ret = 0; 2321a11d88fSJerome Brunet 2331a11d88fSJerome Brunet switch (event) { 2341a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMU: 2351a11d88fSJerome Brunet ret = axg_tdm_formatter_power_up(formatter, w); 2361a11d88fSJerome Brunet break; 2371a11d88fSJerome Brunet 2381a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMD: 2391a11d88fSJerome Brunet axg_tdm_formatter_power_down(formatter); 2401a11d88fSJerome Brunet break; 2411a11d88fSJerome Brunet 2421a11d88fSJerome Brunet default: 2431a11d88fSJerome Brunet dev_err(c->dev, "Unexpected event %d\n", event); 2441a11d88fSJerome Brunet return -EINVAL; 2451a11d88fSJerome Brunet } 2461a11d88fSJerome Brunet 2471a11d88fSJerome Brunet return ret; 2481a11d88fSJerome Brunet } 2491a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); 2501a11d88fSJerome Brunet 2511a11d88fSJerome Brunet int axg_tdm_formatter_probe(struct platform_device *pdev) 2521a11d88fSJerome Brunet { 2531a11d88fSJerome Brunet struct device *dev = &pdev->dev; 2541a11d88fSJerome Brunet const struct axg_tdm_formatter_driver *drv; 2551a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 2561a11d88fSJerome Brunet struct resource *res; 2571a11d88fSJerome Brunet void __iomem *regs; 2581a11d88fSJerome Brunet int ret; 2591a11d88fSJerome Brunet 2601a11d88fSJerome Brunet drv = of_device_get_match_data(dev); 2611a11d88fSJerome Brunet if (!drv) { 2621a11d88fSJerome Brunet dev_err(dev, "failed to match device\n"); 2631a11d88fSJerome Brunet return -ENODEV; 2641a11d88fSJerome Brunet } 2651a11d88fSJerome Brunet 2661a11d88fSJerome Brunet formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); 2671a11d88fSJerome Brunet if (!formatter) 2681a11d88fSJerome Brunet return -ENOMEM; 2691a11d88fSJerome Brunet platform_set_drvdata(pdev, formatter); 2701a11d88fSJerome Brunet formatter->drv = drv; 2711a11d88fSJerome Brunet 2721a11d88fSJerome Brunet res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2731a11d88fSJerome Brunet regs = devm_ioremap_resource(dev, res); 2741a11d88fSJerome Brunet if (IS_ERR(regs)) 2751a11d88fSJerome Brunet return PTR_ERR(regs); 2761a11d88fSJerome Brunet 2771a11d88fSJerome Brunet formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); 2781a11d88fSJerome Brunet if (IS_ERR(formatter->map)) { 2791a11d88fSJerome Brunet dev_err(dev, "failed to init regmap: %ld\n", 2801a11d88fSJerome Brunet PTR_ERR(formatter->map)); 2811a11d88fSJerome Brunet return PTR_ERR(formatter->map); 2821a11d88fSJerome Brunet } 2831a11d88fSJerome Brunet 2841a11d88fSJerome Brunet /* Peripharal clock */ 2851a11d88fSJerome Brunet formatter->pclk = devm_clk_get(dev, "pclk"); 2861a11d88fSJerome Brunet if (IS_ERR(formatter->pclk)) { 2871a11d88fSJerome Brunet ret = PTR_ERR(formatter->pclk); 2881a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 2891a11d88fSJerome Brunet dev_err(dev, "failed to get pclk: %d\n", ret); 2901a11d88fSJerome Brunet return ret; 2911a11d88fSJerome Brunet } 2921a11d88fSJerome Brunet 2931a11d88fSJerome Brunet /* Formatter bit clock */ 2941a11d88fSJerome Brunet formatter->sclk = devm_clk_get(dev, "sclk"); 2951a11d88fSJerome Brunet if (IS_ERR(formatter->sclk)) { 2961a11d88fSJerome Brunet ret = PTR_ERR(formatter->sclk); 2971a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 2981a11d88fSJerome Brunet dev_err(dev, "failed to get sclk: %d\n", ret); 2991a11d88fSJerome Brunet return ret; 3001a11d88fSJerome Brunet } 3011a11d88fSJerome Brunet 3021a11d88fSJerome Brunet /* Formatter sample clock */ 3031a11d88fSJerome Brunet formatter->lrclk = devm_clk_get(dev, "lrclk"); 3041a11d88fSJerome Brunet if (IS_ERR(formatter->lrclk)) { 3051a11d88fSJerome Brunet ret = PTR_ERR(formatter->lrclk); 3061a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 3071a11d88fSJerome Brunet dev_err(dev, "failed to get lrclk: %d\n", ret); 3081a11d88fSJerome Brunet return ret; 3091a11d88fSJerome Brunet } 3101a11d88fSJerome Brunet 3111a11d88fSJerome Brunet /* Formatter bit clock input multiplexer */ 3121a11d88fSJerome Brunet formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); 3131a11d88fSJerome Brunet if (IS_ERR(formatter->sclk_sel)) { 3141a11d88fSJerome Brunet ret = PTR_ERR(formatter->sclk_sel); 3151a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 3161a11d88fSJerome Brunet dev_err(dev, "failed to get sclk_sel: %d\n", ret); 3171a11d88fSJerome Brunet return ret; 3181a11d88fSJerome Brunet } 3191a11d88fSJerome Brunet 3201a11d88fSJerome Brunet /* Formatter sample clock input multiplexer */ 3211a11d88fSJerome Brunet formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); 3221a11d88fSJerome Brunet if (IS_ERR(formatter->lrclk_sel)) { 3231a11d88fSJerome Brunet ret = PTR_ERR(formatter->lrclk_sel); 3241a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 3251a11d88fSJerome Brunet dev_err(dev, "failed to get lrclk_sel: %d\n", ret); 3261a11d88fSJerome Brunet return ret; 3271a11d88fSJerome Brunet } 3281a11d88fSJerome Brunet 329751bd5dbSJerome Brunet /* Formatter dedicated reset line */ 330*18dd62aeSJerome Brunet formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); 331751bd5dbSJerome Brunet if (IS_ERR(formatter->reset)) { 332751bd5dbSJerome Brunet ret = PTR_ERR(formatter->reset); 333751bd5dbSJerome Brunet if (ret != -EPROBE_DEFER) 334751bd5dbSJerome Brunet dev_err(dev, "failed to get reset: %d\n", ret); 335751bd5dbSJerome Brunet return ret; 336751bd5dbSJerome Brunet } 337751bd5dbSJerome Brunet 3381a11d88fSJerome Brunet return devm_snd_soc_register_component(dev, drv->component_drv, 3391a11d88fSJerome Brunet NULL, 0); 3401a11d88fSJerome Brunet } 3411a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); 3421a11d88fSJerome Brunet 3431a11d88fSJerome Brunet int axg_tdm_stream_start(struct axg_tdm_stream *ts) 3441a11d88fSJerome Brunet { 3451a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 3461a11d88fSJerome Brunet int ret = 0; 3471a11d88fSJerome Brunet 3481a11d88fSJerome Brunet mutex_lock(&ts->lock); 3491a11d88fSJerome Brunet ts->ready = true; 3501a11d88fSJerome Brunet 3511a11d88fSJerome Brunet /* Start all the formatters attached to the stream */ 3521a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 3531a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 3541a11d88fSJerome Brunet if (ret) { 3551a11d88fSJerome Brunet pr_err("failed to start tdm stream\n"); 3561a11d88fSJerome Brunet goto out; 3571a11d88fSJerome Brunet } 3581a11d88fSJerome Brunet } 3591a11d88fSJerome Brunet 3601a11d88fSJerome Brunet out: 3611a11d88fSJerome Brunet mutex_unlock(&ts->lock); 3621a11d88fSJerome Brunet return ret; 3631a11d88fSJerome Brunet } 3641a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_start); 3651a11d88fSJerome Brunet 3661a11d88fSJerome Brunet void axg_tdm_stream_stop(struct axg_tdm_stream *ts) 3671a11d88fSJerome Brunet { 3681a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 3691a11d88fSJerome Brunet 3701a11d88fSJerome Brunet mutex_lock(&ts->lock); 3711a11d88fSJerome Brunet ts->ready = false; 3721a11d88fSJerome Brunet 3731a11d88fSJerome Brunet /* Stop all the formatters attached to the stream */ 3741a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 3751a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 3761a11d88fSJerome Brunet } 3771a11d88fSJerome Brunet 3781a11d88fSJerome Brunet mutex_unlock(&ts->lock); 3791a11d88fSJerome Brunet } 3801a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); 3811a11d88fSJerome Brunet 3821a11d88fSJerome Brunet struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) 3831a11d88fSJerome Brunet { 3841a11d88fSJerome Brunet struct axg_tdm_stream *ts; 3851a11d88fSJerome Brunet 3861a11d88fSJerome Brunet ts = kzalloc(sizeof(*ts), GFP_KERNEL); 3871a11d88fSJerome Brunet if (ts) { 3881a11d88fSJerome Brunet INIT_LIST_HEAD(&ts->formatter_list); 3891a11d88fSJerome Brunet mutex_init(&ts->lock); 3901a11d88fSJerome Brunet ts->iface = iface; 3911a11d88fSJerome Brunet } 3921a11d88fSJerome Brunet 3931a11d88fSJerome Brunet return ts; 3941a11d88fSJerome Brunet } 3951a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); 3961a11d88fSJerome Brunet 3971a11d88fSJerome Brunet void axg_tdm_stream_free(struct axg_tdm_stream *ts) 3981a11d88fSJerome Brunet { 3991a11d88fSJerome Brunet /* 4001a11d88fSJerome Brunet * If the list is not empty, it would mean that one of the formatter 4011a11d88fSJerome Brunet * widget is still powered and attached to the interface while we 4021a11d88fSJerome Brunet * we are removing the TDM DAI. It should not be possible 4031a11d88fSJerome Brunet */ 4041a11d88fSJerome Brunet WARN_ON(!list_empty(&ts->formatter_list)); 4051a11d88fSJerome Brunet mutex_destroy(&ts->lock); 4061a11d88fSJerome Brunet kfree(ts); 4071a11d88fSJerome Brunet } 4081a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_free); 4091a11d88fSJerome Brunet 4101a11d88fSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); 4111a11d88fSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 4121a11d88fSJerome Brunet MODULE_LICENSE("GPL v2"); 413