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; 730d3f01dcSJerome Brunet bool invert; 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 /* 990d3f01dcSJerome Brunet * If sclk is inverted, it means the bit should latched on the 1000d3f01dcSJerome Brunet * rising edge which is what our HW expects. If not, we need to 1010d3f01dcSJerome Brunet * invert it before the formatter. 1021a11d88fSJerome Brunet */ 1030d3f01dcSJerome Brunet invert = axg_tdm_sclk_invert(ts->iface->fmt); 1040d3f01dcSJerome Brunet ret = clk_set_phase(formatter->sclk, invert ? 0 : 180); 1051a11d88fSJerome Brunet if (ret) 1061a11d88fSJerome Brunet return ret; 1071a11d88fSJerome Brunet 1081a11d88fSJerome Brunet /* Setup the stream parameter in the formatter */ 109f01bc67fSJerome Brunet ret = formatter->drv->ops->prepare(formatter->map, 110f01bc67fSJerome Brunet formatter->drv->quirks, 111f01bc67fSJerome Brunet formatter->stream); 1121a11d88fSJerome Brunet if (ret) 1131a11d88fSJerome Brunet return ret; 1141a11d88fSJerome Brunet 1151a11d88fSJerome Brunet /* Enable the signal clocks feeding the formatter */ 1161a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->sclk); 1171a11d88fSJerome Brunet if (ret) 1181a11d88fSJerome Brunet return ret; 1191a11d88fSJerome Brunet 1201a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->lrclk); 1211a11d88fSJerome Brunet if (ret) { 1221a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 1231a11d88fSJerome Brunet return ret; 1241a11d88fSJerome Brunet } 1251a11d88fSJerome Brunet 1261a11d88fSJerome Brunet /* Finally, actually enable the formatter */ 1271a11d88fSJerome Brunet formatter->drv->ops->enable(formatter->map); 1281a11d88fSJerome Brunet formatter->enabled = true; 1291a11d88fSJerome Brunet 1301a11d88fSJerome Brunet return 0; 1311a11d88fSJerome Brunet } 1321a11d88fSJerome Brunet 1331a11d88fSJerome Brunet static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) 1341a11d88fSJerome Brunet { 1351a11d88fSJerome Brunet /* Do nothing if the formatter is already disabled */ 1361a11d88fSJerome Brunet if (!formatter->enabled) 1371a11d88fSJerome Brunet return; 1381a11d88fSJerome Brunet 1391a11d88fSJerome Brunet formatter->drv->ops->disable(formatter->map); 1401a11d88fSJerome Brunet clk_disable_unprepare(formatter->lrclk); 1411a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 1421a11d88fSJerome Brunet formatter->enabled = false; 1431a11d88fSJerome Brunet } 1441a11d88fSJerome Brunet 1451a11d88fSJerome Brunet static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) 1461a11d88fSJerome Brunet { 1471a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 1481a11d88fSJerome Brunet int ret = 0; 1491a11d88fSJerome Brunet 1501a11d88fSJerome Brunet mutex_lock(&ts->lock); 1511a11d88fSJerome Brunet 1521a11d88fSJerome Brunet /* Catch up if the stream is already running when we attach */ 1531a11d88fSJerome Brunet if (ts->ready) { 1541a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 1551a11d88fSJerome Brunet if (ret) { 1561a11d88fSJerome Brunet pr_err("failed to enable formatter\n"); 1571a11d88fSJerome Brunet goto out; 1581a11d88fSJerome Brunet } 1591a11d88fSJerome Brunet } 1601a11d88fSJerome Brunet 1611a11d88fSJerome Brunet list_add_tail(&formatter->list, &ts->formatter_list); 1621a11d88fSJerome Brunet out: 1631a11d88fSJerome Brunet mutex_unlock(&ts->lock); 1641a11d88fSJerome Brunet return ret; 1651a11d88fSJerome Brunet } 1661a11d88fSJerome Brunet 1671a11d88fSJerome Brunet static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) 1681a11d88fSJerome Brunet { 1691a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 1701a11d88fSJerome Brunet 1711a11d88fSJerome Brunet mutex_lock(&ts->lock); 1721a11d88fSJerome Brunet list_del(&formatter->list); 1731a11d88fSJerome Brunet mutex_unlock(&ts->lock); 1741a11d88fSJerome Brunet 1751a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 1761a11d88fSJerome Brunet } 1771a11d88fSJerome Brunet 1781a11d88fSJerome Brunet static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, 1791a11d88fSJerome Brunet struct snd_soc_dapm_widget *w) 1801a11d88fSJerome Brunet { 1811a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); 1821a11d88fSJerome Brunet int ret; 1831a11d88fSJerome Brunet 1841a11d88fSJerome Brunet /* 1851a11d88fSJerome Brunet * If we don't get a stream at this stage, it would mean that the 1861a11d88fSJerome Brunet * widget is powering up but is not attached to any backend DAI. 1871a11d88fSJerome Brunet * It should not happen, ever ! 1881a11d88fSJerome Brunet */ 1891a11d88fSJerome Brunet if (WARN_ON(!ts)) 1901a11d88fSJerome Brunet return -ENODEV; 1911a11d88fSJerome Brunet 1921a11d88fSJerome Brunet /* Clock our device */ 1931a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->pclk); 1941a11d88fSJerome Brunet if (ret) 1951a11d88fSJerome Brunet return ret; 1961a11d88fSJerome Brunet 1971a11d88fSJerome Brunet /* Reparent the bit clock to the TDM interface */ 1981a11d88fSJerome Brunet ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); 1991a11d88fSJerome Brunet if (ret) 2001a11d88fSJerome Brunet goto disable_pclk; 2011a11d88fSJerome Brunet 2021a11d88fSJerome Brunet /* Reparent the sample clock to the TDM interface */ 2031a11d88fSJerome Brunet ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); 2041a11d88fSJerome Brunet if (ret) 2051a11d88fSJerome Brunet goto disable_pclk; 2061a11d88fSJerome Brunet 2071a11d88fSJerome Brunet formatter->stream = ts; 2081a11d88fSJerome Brunet ret = axg_tdm_formatter_attach(formatter); 2091a11d88fSJerome Brunet if (ret) 2101a11d88fSJerome Brunet goto disable_pclk; 2111a11d88fSJerome Brunet 2121a11d88fSJerome Brunet return 0; 2131a11d88fSJerome Brunet 2141a11d88fSJerome Brunet disable_pclk: 2151a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 2161a11d88fSJerome Brunet return ret; 2171a11d88fSJerome Brunet } 2181a11d88fSJerome Brunet 2191a11d88fSJerome Brunet static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) 2201a11d88fSJerome Brunet { 2211a11d88fSJerome Brunet axg_tdm_formatter_dettach(formatter); 2221a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 2231a11d88fSJerome Brunet formatter->stream = NULL; 2241a11d88fSJerome Brunet } 2251a11d88fSJerome Brunet 2261a11d88fSJerome Brunet int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, 2271a11d88fSJerome Brunet struct snd_kcontrol *control, 2281a11d88fSJerome Brunet int event) 2291a11d88fSJerome Brunet { 2301a11d88fSJerome Brunet struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); 2311a11d88fSJerome Brunet struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); 2321a11d88fSJerome Brunet int ret = 0; 2331a11d88fSJerome Brunet 2341a11d88fSJerome Brunet switch (event) { 2351a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMU: 2361a11d88fSJerome Brunet ret = axg_tdm_formatter_power_up(formatter, w); 2371a11d88fSJerome Brunet break; 2381a11d88fSJerome Brunet 2391a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMD: 2401a11d88fSJerome Brunet axg_tdm_formatter_power_down(formatter); 2411a11d88fSJerome Brunet break; 2421a11d88fSJerome Brunet 2431a11d88fSJerome Brunet default: 2441a11d88fSJerome Brunet dev_err(c->dev, "Unexpected event %d\n", event); 2451a11d88fSJerome Brunet return -EINVAL; 2461a11d88fSJerome Brunet } 2471a11d88fSJerome Brunet 2481a11d88fSJerome Brunet return ret; 2491a11d88fSJerome Brunet } 2501a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); 2511a11d88fSJerome Brunet 2521a11d88fSJerome Brunet int axg_tdm_formatter_probe(struct platform_device *pdev) 2531a11d88fSJerome Brunet { 2541a11d88fSJerome Brunet struct device *dev = &pdev->dev; 2551a11d88fSJerome Brunet const struct axg_tdm_formatter_driver *drv; 2561a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 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 27210392fcaSYueHaibing regs = devm_platform_ioremap_resource(pdev, 0); 2731a11d88fSJerome Brunet if (IS_ERR(regs)) 2741a11d88fSJerome Brunet return PTR_ERR(regs); 2751a11d88fSJerome Brunet 2761a11d88fSJerome Brunet formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); 2771a11d88fSJerome Brunet if (IS_ERR(formatter->map)) { 2781a11d88fSJerome Brunet dev_err(dev, "failed to init regmap: %ld\n", 2791a11d88fSJerome Brunet PTR_ERR(formatter->map)); 2801a11d88fSJerome Brunet return PTR_ERR(formatter->map); 2811a11d88fSJerome Brunet } 2821a11d88fSJerome Brunet 2831a11d88fSJerome Brunet /* Peripharal clock */ 2841a11d88fSJerome Brunet formatter->pclk = devm_clk_get(dev, "pclk"); 2851a11d88fSJerome Brunet if (IS_ERR(formatter->pclk)) { 2861a11d88fSJerome Brunet ret = PTR_ERR(formatter->pclk); 2871a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 2881a11d88fSJerome Brunet dev_err(dev, "failed to get pclk: %d\n", ret); 2891a11d88fSJerome Brunet return ret; 2901a11d88fSJerome Brunet } 2911a11d88fSJerome Brunet 2921a11d88fSJerome Brunet /* Formatter bit clock */ 2931a11d88fSJerome Brunet formatter->sclk = devm_clk_get(dev, "sclk"); 2941a11d88fSJerome Brunet if (IS_ERR(formatter->sclk)) { 2951a11d88fSJerome Brunet ret = PTR_ERR(formatter->sclk); 2961a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 2971a11d88fSJerome Brunet dev_err(dev, "failed to get sclk: %d\n", ret); 2981a11d88fSJerome Brunet return ret; 2991a11d88fSJerome Brunet } 3001a11d88fSJerome Brunet 3011a11d88fSJerome Brunet /* Formatter sample clock */ 3021a11d88fSJerome Brunet formatter->lrclk = devm_clk_get(dev, "lrclk"); 3031a11d88fSJerome Brunet if (IS_ERR(formatter->lrclk)) { 3041a11d88fSJerome Brunet ret = PTR_ERR(formatter->lrclk); 3051a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 3061a11d88fSJerome Brunet dev_err(dev, "failed to get lrclk: %d\n", ret); 3071a11d88fSJerome Brunet return ret; 3081a11d88fSJerome Brunet } 3091a11d88fSJerome Brunet 3101a11d88fSJerome Brunet /* Formatter bit clock input multiplexer */ 3111a11d88fSJerome Brunet formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); 3121a11d88fSJerome Brunet if (IS_ERR(formatter->sclk_sel)) { 3131a11d88fSJerome Brunet ret = PTR_ERR(formatter->sclk_sel); 3141a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 3151a11d88fSJerome Brunet dev_err(dev, "failed to get sclk_sel: %d\n", ret); 3161a11d88fSJerome Brunet return ret; 3171a11d88fSJerome Brunet } 3181a11d88fSJerome Brunet 3191a11d88fSJerome Brunet /* Formatter sample clock input multiplexer */ 3201a11d88fSJerome Brunet formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); 3211a11d88fSJerome Brunet if (IS_ERR(formatter->lrclk_sel)) { 3221a11d88fSJerome Brunet ret = PTR_ERR(formatter->lrclk_sel); 3231a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 3241a11d88fSJerome Brunet dev_err(dev, "failed to get lrclk_sel: %d\n", ret); 3251a11d88fSJerome Brunet return ret; 3261a11d88fSJerome Brunet } 3271a11d88fSJerome Brunet 328751bd5dbSJerome Brunet /* Formatter dedicated reset line */ 32918dd62aeSJerome Brunet formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); 330751bd5dbSJerome Brunet if (IS_ERR(formatter->reset)) { 331751bd5dbSJerome Brunet ret = PTR_ERR(formatter->reset); 332751bd5dbSJerome Brunet if (ret != -EPROBE_DEFER) 333751bd5dbSJerome Brunet dev_err(dev, "failed to get reset: %d\n", ret); 334751bd5dbSJerome Brunet return ret; 335751bd5dbSJerome Brunet } 336751bd5dbSJerome Brunet 3371a11d88fSJerome Brunet return devm_snd_soc_register_component(dev, drv->component_drv, 3381a11d88fSJerome Brunet NULL, 0); 3391a11d88fSJerome Brunet } 3401a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); 3411a11d88fSJerome Brunet 3421a11d88fSJerome Brunet int axg_tdm_stream_start(struct axg_tdm_stream *ts) 3431a11d88fSJerome Brunet { 3441a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 3451a11d88fSJerome Brunet int ret = 0; 3461a11d88fSJerome Brunet 3471a11d88fSJerome Brunet mutex_lock(&ts->lock); 3481a11d88fSJerome Brunet ts->ready = true; 3491a11d88fSJerome Brunet 3501a11d88fSJerome Brunet /* Start all the formatters attached to the stream */ 3511a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 3521a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 3531a11d88fSJerome Brunet if (ret) { 3541a11d88fSJerome Brunet pr_err("failed to start tdm stream\n"); 3551a11d88fSJerome Brunet goto out; 3561a11d88fSJerome Brunet } 3571a11d88fSJerome Brunet } 3581a11d88fSJerome Brunet 3591a11d88fSJerome Brunet out: 3601a11d88fSJerome Brunet mutex_unlock(&ts->lock); 3611a11d88fSJerome Brunet return ret; 3621a11d88fSJerome Brunet } 3631a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_start); 3641a11d88fSJerome Brunet 3651a11d88fSJerome Brunet void axg_tdm_stream_stop(struct axg_tdm_stream *ts) 3661a11d88fSJerome Brunet { 3671a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 3681a11d88fSJerome Brunet 3691a11d88fSJerome Brunet mutex_lock(&ts->lock); 3701a11d88fSJerome Brunet ts->ready = false; 3711a11d88fSJerome Brunet 3721a11d88fSJerome Brunet /* Stop all the formatters attached to the stream */ 3731a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 3741a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 3751a11d88fSJerome Brunet } 3761a11d88fSJerome Brunet 3771a11d88fSJerome Brunet mutex_unlock(&ts->lock); 3781a11d88fSJerome Brunet } 3791a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); 3801a11d88fSJerome Brunet 3811a11d88fSJerome Brunet struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) 3821a11d88fSJerome Brunet { 3831a11d88fSJerome Brunet struct axg_tdm_stream *ts; 3841a11d88fSJerome Brunet 3851a11d88fSJerome Brunet ts = kzalloc(sizeof(*ts), GFP_KERNEL); 3861a11d88fSJerome Brunet if (ts) { 3871a11d88fSJerome Brunet INIT_LIST_HEAD(&ts->formatter_list); 3881a11d88fSJerome Brunet mutex_init(&ts->lock); 3891a11d88fSJerome Brunet ts->iface = iface; 3901a11d88fSJerome Brunet } 3911a11d88fSJerome Brunet 3921a11d88fSJerome Brunet return ts; 3931a11d88fSJerome Brunet } 3941a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); 3951a11d88fSJerome Brunet 3961a11d88fSJerome Brunet void axg_tdm_stream_free(struct axg_tdm_stream *ts) 3971a11d88fSJerome Brunet { 3981a11d88fSJerome Brunet /* 3991a11d88fSJerome Brunet * If the list is not empty, it would mean that one of the formatter 4001a11d88fSJerome Brunet * widget is still powered and attached to the interface while we 401*0d8aa2ccSRandy Dunlap * are removing the TDM DAI. It should not be possible 4021a11d88fSJerome Brunet */ 4031a11d88fSJerome Brunet WARN_ON(!list_empty(&ts->formatter_list)); 4041a11d88fSJerome Brunet mutex_destroy(&ts->lock); 4051a11d88fSJerome Brunet kfree(ts); 4061a11d88fSJerome Brunet } 4071a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_free); 4081a11d88fSJerome Brunet 4091a11d88fSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); 4101a11d88fSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 4111a11d88fSJerome Brunet MODULE_LICENSE("GPL v2"); 412