1*1a11d88fSJerome Brunet // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2*1a11d88fSJerome Brunet // 3*1a11d88fSJerome Brunet // Copyright (c) 2018 BayLibre, SAS. 4*1a11d88fSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com> 5*1a11d88fSJerome Brunet 6*1a11d88fSJerome Brunet #include <linux/clk.h> 7*1a11d88fSJerome Brunet #include <linux/module.h> 8*1a11d88fSJerome Brunet #include <linux/of_platform.h> 9*1a11d88fSJerome Brunet #include <linux/regmap.h> 10*1a11d88fSJerome Brunet #include <sound/soc.h> 11*1a11d88fSJerome Brunet 12*1a11d88fSJerome Brunet #include "axg-tdm-formatter.h" 13*1a11d88fSJerome Brunet 14*1a11d88fSJerome Brunet struct axg_tdm_formatter { 15*1a11d88fSJerome Brunet struct list_head list; 16*1a11d88fSJerome Brunet struct axg_tdm_stream *stream; 17*1a11d88fSJerome Brunet const struct axg_tdm_formatter_driver *drv; 18*1a11d88fSJerome Brunet struct clk *pclk; 19*1a11d88fSJerome Brunet struct clk *sclk; 20*1a11d88fSJerome Brunet struct clk *lrclk; 21*1a11d88fSJerome Brunet struct clk *sclk_sel; 22*1a11d88fSJerome Brunet struct clk *lrclk_sel; 23*1a11d88fSJerome Brunet bool enabled; 24*1a11d88fSJerome Brunet struct regmap *map; 25*1a11d88fSJerome Brunet }; 26*1a11d88fSJerome Brunet 27*1a11d88fSJerome Brunet int axg_tdm_formatter_set_channel_masks(struct regmap *map, 28*1a11d88fSJerome Brunet struct axg_tdm_stream *ts, 29*1a11d88fSJerome Brunet unsigned int offset) 30*1a11d88fSJerome Brunet { 31*1a11d88fSJerome Brunet unsigned int val, ch = ts->channels; 32*1a11d88fSJerome Brunet unsigned long mask; 33*1a11d88fSJerome Brunet int i, j; 34*1a11d88fSJerome Brunet 35*1a11d88fSJerome Brunet /* 36*1a11d88fSJerome Brunet * Distribute the channels of the stream over the available slots 37*1a11d88fSJerome Brunet * of each TDM lane 38*1a11d88fSJerome Brunet */ 39*1a11d88fSJerome Brunet for (i = 0; i < AXG_TDM_NUM_LANES; i++) { 40*1a11d88fSJerome Brunet val = 0; 41*1a11d88fSJerome Brunet mask = ts->mask[i]; 42*1a11d88fSJerome Brunet 43*1a11d88fSJerome Brunet for (j = find_first_bit(&mask, 32); 44*1a11d88fSJerome Brunet (j < 32) && ch; 45*1a11d88fSJerome Brunet j = find_next_bit(&mask, 32, j + 1)) { 46*1a11d88fSJerome Brunet val |= 1 << j; 47*1a11d88fSJerome Brunet ch -= 1; 48*1a11d88fSJerome Brunet } 49*1a11d88fSJerome Brunet 50*1a11d88fSJerome Brunet regmap_write(map, offset, val); 51*1a11d88fSJerome Brunet offset += regmap_get_reg_stride(map); 52*1a11d88fSJerome Brunet } 53*1a11d88fSJerome Brunet 54*1a11d88fSJerome Brunet /* 55*1a11d88fSJerome Brunet * If we still have channel left at the end of the process, it means 56*1a11d88fSJerome Brunet * the stream has more channels than we can accommodate and we should 57*1a11d88fSJerome Brunet * have caught this earlier. 58*1a11d88fSJerome Brunet */ 59*1a11d88fSJerome Brunet if (WARN_ON(ch != 0)) { 60*1a11d88fSJerome Brunet pr_err("channel mask error\n"); 61*1a11d88fSJerome Brunet return -EINVAL; 62*1a11d88fSJerome Brunet } 63*1a11d88fSJerome Brunet 64*1a11d88fSJerome Brunet return 0; 65*1a11d88fSJerome Brunet } 66*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks); 67*1a11d88fSJerome Brunet 68*1a11d88fSJerome Brunet static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) 69*1a11d88fSJerome Brunet { 70*1a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 71*1a11d88fSJerome Brunet bool invert = formatter->drv->invert_sclk; 72*1a11d88fSJerome Brunet int ret; 73*1a11d88fSJerome Brunet 74*1a11d88fSJerome Brunet /* Do nothing if the formatter is already enabled */ 75*1a11d88fSJerome Brunet if (formatter->enabled) 76*1a11d88fSJerome Brunet return 0; 77*1a11d88fSJerome Brunet 78*1a11d88fSJerome Brunet /* 79*1a11d88fSJerome Brunet * If sclk is inverted, invert it back and provide the inversion 80*1a11d88fSJerome Brunet * required by the formatter 81*1a11d88fSJerome Brunet */ 82*1a11d88fSJerome Brunet invert ^= axg_tdm_sclk_invert(ts->iface->fmt); 83*1a11d88fSJerome Brunet ret = clk_set_phase(formatter->sclk, invert ? 180 : 0); 84*1a11d88fSJerome Brunet if (ret) 85*1a11d88fSJerome Brunet return ret; 86*1a11d88fSJerome Brunet 87*1a11d88fSJerome Brunet /* Setup the stream parameter in the formatter */ 88*1a11d88fSJerome Brunet ret = formatter->drv->ops->prepare(formatter->map, formatter->stream); 89*1a11d88fSJerome Brunet if (ret) 90*1a11d88fSJerome Brunet return ret; 91*1a11d88fSJerome Brunet 92*1a11d88fSJerome Brunet /* Enable the signal clocks feeding the formatter */ 93*1a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->sclk); 94*1a11d88fSJerome Brunet if (ret) 95*1a11d88fSJerome Brunet return ret; 96*1a11d88fSJerome Brunet 97*1a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->lrclk); 98*1a11d88fSJerome Brunet if (ret) { 99*1a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 100*1a11d88fSJerome Brunet return ret; 101*1a11d88fSJerome Brunet } 102*1a11d88fSJerome Brunet 103*1a11d88fSJerome Brunet /* Finally, actually enable the formatter */ 104*1a11d88fSJerome Brunet formatter->drv->ops->enable(formatter->map); 105*1a11d88fSJerome Brunet formatter->enabled = true; 106*1a11d88fSJerome Brunet 107*1a11d88fSJerome Brunet return 0; 108*1a11d88fSJerome Brunet } 109*1a11d88fSJerome Brunet 110*1a11d88fSJerome Brunet static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) 111*1a11d88fSJerome Brunet { 112*1a11d88fSJerome Brunet /* Do nothing if the formatter is already disabled */ 113*1a11d88fSJerome Brunet if (!formatter->enabled) 114*1a11d88fSJerome Brunet return; 115*1a11d88fSJerome Brunet 116*1a11d88fSJerome Brunet formatter->drv->ops->disable(formatter->map); 117*1a11d88fSJerome Brunet clk_disable_unprepare(formatter->lrclk); 118*1a11d88fSJerome Brunet clk_disable_unprepare(formatter->sclk); 119*1a11d88fSJerome Brunet formatter->enabled = false; 120*1a11d88fSJerome Brunet } 121*1a11d88fSJerome Brunet 122*1a11d88fSJerome Brunet static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) 123*1a11d88fSJerome Brunet { 124*1a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 125*1a11d88fSJerome Brunet int ret = 0; 126*1a11d88fSJerome Brunet 127*1a11d88fSJerome Brunet mutex_lock(&ts->lock); 128*1a11d88fSJerome Brunet 129*1a11d88fSJerome Brunet /* Catch up if the stream is already running when we attach */ 130*1a11d88fSJerome Brunet if (ts->ready) { 131*1a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 132*1a11d88fSJerome Brunet if (ret) { 133*1a11d88fSJerome Brunet pr_err("failed to enable formatter\n"); 134*1a11d88fSJerome Brunet goto out; 135*1a11d88fSJerome Brunet } 136*1a11d88fSJerome Brunet } 137*1a11d88fSJerome Brunet 138*1a11d88fSJerome Brunet list_add_tail(&formatter->list, &ts->formatter_list); 139*1a11d88fSJerome Brunet out: 140*1a11d88fSJerome Brunet mutex_unlock(&ts->lock); 141*1a11d88fSJerome Brunet return ret; 142*1a11d88fSJerome Brunet } 143*1a11d88fSJerome Brunet 144*1a11d88fSJerome Brunet static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) 145*1a11d88fSJerome Brunet { 146*1a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->stream; 147*1a11d88fSJerome Brunet 148*1a11d88fSJerome Brunet mutex_lock(&ts->lock); 149*1a11d88fSJerome Brunet list_del(&formatter->list); 150*1a11d88fSJerome Brunet mutex_unlock(&ts->lock); 151*1a11d88fSJerome Brunet 152*1a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 153*1a11d88fSJerome Brunet } 154*1a11d88fSJerome Brunet 155*1a11d88fSJerome Brunet static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, 156*1a11d88fSJerome Brunet struct snd_soc_dapm_widget *w) 157*1a11d88fSJerome Brunet { 158*1a11d88fSJerome Brunet struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); 159*1a11d88fSJerome Brunet int ret; 160*1a11d88fSJerome Brunet 161*1a11d88fSJerome Brunet /* 162*1a11d88fSJerome Brunet * If we don't get a stream at this stage, it would mean that the 163*1a11d88fSJerome Brunet * widget is powering up but is not attached to any backend DAI. 164*1a11d88fSJerome Brunet * It should not happen, ever ! 165*1a11d88fSJerome Brunet */ 166*1a11d88fSJerome Brunet if (WARN_ON(!ts)) 167*1a11d88fSJerome Brunet return -ENODEV; 168*1a11d88fSJerome Brunet 169*1a11d88fSJerome Brunet /* Clock our device */ 170*1a11d88fSJerome Brunet ret = clk_prepare_enable(formatter->pclk); 171*1a11d88fSJerome Brunet if (ret) 172*1a11d88fSJerome Brunet return ret; 173*1a11d88fSJerome Brunet 174*1a11d88fSJerome Brunet /* Reparent the bit clock to the TDM interface */ 175*1a11d88fSJerome Brunet ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); 176*1a11d88fSJerome Brunet if (ret) 177*1a11d88fSJerome Brunet goto disable_pclk; 178*1a11d88fSJerome Brunet 179*1a11d88fSJerome Brunet /* Reparent the sample clock to the TDM interface */ 180*1a11d88fSJerome Brunet ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); 181*1a11d88fSJerome Brunet if (ret) 182*1a11d88fSJerome Brunet goto disable_pclk; 183*1a11d88fSJerome Brunet 184*1a11d88fSJerome Brunet formatter->stream = ts; 185*1a11d88fSJerome Brunet ret = axg_tdm_formatter_attach(formatter); 186*1a11d88fSJerome Brunet if (ret) 187*1a11d88fSJerome Brunet goto disable_pclk; 188*1a11d88fSJerome Brunet 189*1a11d88fSJerome Brunet return 0; 190*1a11d88fSJerome Brunet 191*1a11d88fSJerome Brunet disable_pclk: 192*1a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 193*1a11d88fSJerome Brunet return ret; 194*1a11d88fSJerome Brunet } 195*1a11d88fSJerome Brunet 196*1a11d88fSJerome Brunet static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) 197*1a11d88fSJerome Brunet { 198*1a11d88fSJerome Brunet axg_tdm_formatter_dettach(formatter); 199*1a11d88fSJerome Brunet clk_disable_unprepare(formatter->pclk); 200*1a11d88fSJerome Brunet formatter->stream = NULL; 201*1a11d88fSJerome Brunet } 202*1a11d88fSJerome Brunet 203*1a11d88fSJerome Brunet int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, 204*1a11d88fSJerome Brunet struct snd_kcontrol *control, 205*1a11d88fSJerome Brunet int event) 206*1a11d88fSJerome Brunet { 207*1a11d88fSJerome Brunet struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); 208*1a11d88fSJerome Brunet struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); 209*1a11d88fSJerome Brunet int ret = 0; 210*1a11d88fSJerome Brunet 211*1a11d88fSJerome Brunet switch (event) { 212*1a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMU: 213*1a11d88fSJerome Brunet ret = axg_tdm_formatter_power_up(formatter, w); 214*1a11d88fSJerome Brunet break; 215*1a11d88fSJerome Brunet 216*1a11d88fSJerome Brunet case SND_SOC_DAPM_PRE_PMD: 217*1a11d88fSJerome Brunet axg_tdm_formatter_power_down(formatter); 218*1a11d88fSJerome Brunet break; 219*1a11d88fSJerome Brunet 220*1a11d88fSJerome Brunet default: 221*1a11d88fSJerome Brunet dev_err(c->dev, "Unexpected event %d\n", event); 222*1a11d88fSJerome Brunet return -EINVAL; 223*1a11d88fSJerome Brunet } 224*1a11d88fSJerome Brunet 225*1a11d88fSJerome Brunet return ret; 226*1a11d88fSJerome Brunet } 227*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); 228*1a11d88fSJerome Brunet 229*1a11d88fSJerome Brunet int axg_tdm_formatter_probe(struct platform_device *pdev) 230*1a11d88fSJerome Brunet { 231*1a11d88fSJerome Brunet struct device *dev = &pdev->dev; 232*1a11d88fSJerome Brunet const struct axg_tdm_formatter_driver *drv; 233*1a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 234*1a11d88fSJerome Brunet struct resource *res; 235*1a11d88fSJerome Brunet void __iomem *regs; 236*1a11d88fSJerome Brunet int ret; 237*1a11d88fSJerome Brunet 238*1a11d88fSJerome Brunet drv = of_device_get_match_data(dev); 239*1a11d88fSJerome Brunet if (!drv) { 240*1a11d88fSJerome Brunet dev_err(dev, "failed to match device\n"); 241*1a11d88fSJerome Brunet return -ENODEV; 242*1a11d88fSJerome Brunet } 243*1a11d88fSJerome Brunet 244*1a11d88fSJerome Brunet formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); 245*1a11d88fSJerome Brunet if (!formatter) 246*1a11d88fSJerome Brunet return -ENOMEM; 247*1a11d88fSJerome Brunet platform_set_drvdata(pdev, formatter); 248*1a11d88fSJerome Brunet formatter->drv = drv; 249*1a11d88fSJerome Brunet 250*1a11d88fSJerome Brunet res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 251*1a11d88fSJerome Brunet regs = devm_ioremap_resource(dev, res); 252*1a11d88fSJerome Brunet if (IS_ERR(regs)) 253*1a11d88fSJerome Brunet return PTR_ERR(regs); 254*1a11d88fSJerome Brunet 255*1a11d88fSJerome Brunet formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); 256*1a11d88fSJerome Brunet if (IS_ERR(formatter->map)) { 257*1a11d88fSJerome Brunet dev_err(dev, "failed to init regmap: %ld\n", 258*1a11d88fSJerome Brunet PTR_ERR(formatter->map)); 259*1a11d88fSJerome Brunet return PTR_ERR(formatter->map); 260*1a11d88fSJerome Brunet } 261*1a11d88fSJerome Brunet 262*1a11d88fSJerome Brunet /* Peripharal clock */ 263*1a11d88fSJerome Brunet formatter->pclk = devm_clk_get(dev, "pclk"); 264*1a11d88fSJerome Brunet if (IS_ERR(formatter->pclk)) { 265*1a11d88fSJerome Brunet ret = PTR_ERR(formatter->pclk); 266*1a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 267*1a11d88fSJerome Brunet dev_err(dev, "failed to get pclk: %d\n", ret); 268*1a11d88fSJerome Brunet return ret; 269*1a11d88fSJerome Brunet } 270*1a11d88fSJerome Brunet 271*1a11d88fSJerome Brunet /* Formatter bit clock */ 272*1a11d88fSJerome Brunet formatter->sclk = devm_clk_get(dev, "sclk"); 273*1a11d88fSJerome Brunet if (IS_ERR(formatter->sclk)) { 274*1a11d88fSJerome Brunet ret = PTR_ERR(formatter->sclk); 275*1a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 276*1a11d88fSJerome Brunet dev_err(dev, "failed to get sclk: %d\n", ret); 277*1a11d88fSJerome Brunet return ret; 278*1a11d88fSJerome Brunet } 279*1a11d88fSJerome Brunet 280*1a11d88fSJerome Brunet /* Formatter sample clock */ 281*1a11d88fSJerome Brunet formatter->lrclk = devm_clk_get(dev, "lrclk"); 282*1a11d88fSJerome Brunet if (IS_ERR(formatter->lrclk)) { 283*1a11d88fSJerome Brunet ret = PTR_ERR(formatter->lrclk); 284*1a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 285*1a11d88fSJerome Brunet dev_err(dev, "failed to get lrclk: %d\n", ret); 286*1a11d88fSJerome Brunet return ret; 287*1a11d88fSJerome Brunet } 288*1a11d88fSJerome Brunet 289*1a11d88fSJerome Brunet /* Formatter bit clock input multiplexer */ 290*1a11d88fSJerome Brunet formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); 291*1a11d88fSJerome Brunet if (IS_ERR(formatter->sclk_sel)) { 292*1a11d88fSJerome Brunet ret = PTR_ERR(formatter->sclk_sel); 293*1a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 294*1a11d88fSJerome Brunet dev_err(dev, "failed to get sclk_sel: %d\n", ret); 295*1a11d88fSJerome Brunet return ret; 296*1a11d88fSJerome Brunet } 297*1a11d88fSJerome Brunet 298*1a11d88fSJerome Brunet /* Formatter sample clock input multiplexer */ 299*1a11d88fSJerome Brunet formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); 300*1a11d88fSJerome Brunet if (IS_ERR(formatter->lrclk_sel)) { 301*1a11d88fSJerome Brunet ret = PTR_ERR(formatter->lrclk_sel); 302*1a11d88fSJerome Brunet if (ret != -EPROBE_DEFER) 303*1a11d88fSJerome Brunet dev_err(dev, "failed to get lrclk_sel: %d\n", ret); 304*1a11d88fSJerome Brunet return ret; 305*1a11d88fSJerome Brunet } 306*1a11d88fSJerome Brunet 307*1a11d88fSJerome Brunet return devm_snd_soc_register_component(dev, drv->component_drv, 308*1a11d88fSJerome Brunet NULL, 0); 309*1a11d88fSJerome Brunet } 310*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); 311*1a11d88fSJerome Brunet 312*1a11d88fSJerome Brunet int axg_tdm_stream_start(struct axg_tdm_stream *ts) 313*1a11d88fSJerome Brunet { 314*1a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 315*1a11d88fSJerome Brunet int ret = 0; 316*1a11d88fSJerome Brunet 317*1a11d88fSJerome Brunet mutex_lock(&ts->lock); 318*1a11d88fSJerome Brunet ts->ready = true; 319*1a11d88fSJerome Brunet 320*1a11d88fSJerome Brunet /* Start all the formatters attached to the stream */ 321*1a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 322*1a11d88fSJerome Brunet ret = axg_tdm_formatter_enable(formatter); 323*1a11d88fSJerome Brunet if (ret) { 324*1a11d88fSJerome Brunet pr_err("failed to start tdm stream\n"); 325*1a11d88fSJerome Brunet goto out; 326*1a11d88fSJerome Brunet } 327*1a11d88fSJerome Brunet } 328*1a11d88fSJerome Brunet 329*1a11d88fSJerome Brunet out: 330*1a11d88fSJerome Brunet mutex_unlock(&ts->lock); 331*1a11d88fSJerome Brunet return ret; 332*1a11d88fSJerome Brunet } 333*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_start); 334*1a11d88fSJerome Brunet 335*1a11d88fSJerome Brunet void axg_tdm_stream_stop(struct axg_tdm_stream *ts) 336*1a11d88fSJerome Brunet { 337*1a11d88fSJerome Brunet struct axg_tdm_formatter *formatter; 338*1a11d88fSJerome Brunet 339*1a11d88fSJerome Brunet mutex_lock(&ts->lock); 340*1a11d88fSJerome Brunet ts->ready = false; 341*1a11d88fSJerome Brunet 342*1a11d88fSJerome Brunet /* Stop all the formatters attached to the stream */ 343*1a11d88fSJerome Brunet list_for_each_entry(formatter, &ts->formatter_list, list) { 344*1a11d88fSJerome Brunet axg_tdm_formatter_disable(formatter); 345*1a11d88fSJerome Brunet } 346*1a11d88fSJerome Brunet 347*1a11d88fSJerome Brunet mutex_unlock(&ts->lock); 348*1a11d88fSJerome Brunet } 349*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); 350*1a11d88fSJerome Brunet 351*1a11d88fSJerome Brunet struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) 352*1a11d88fSJerome Brunet { 353*1a11d88fSJerome Brunet struct axg_tdm_stream *ts; 354*1a11d88fSJerome Brunet 355*1a11d88fSJerome Brunet ts = kzalloc(sizeof(*ts), GFP_KERNEL); 356*1a11d88fSJerome Brunet if (ts) { 357*1a11d88fSJerome Brunet INIT_LIST_HEAD(&ts->formatter_list); 358*1a11d88fSJerome Brunet mutex_init(&ts->lock); 359*1a11d88fSJerome Brunet ts->iface = iface; 360*1a11d88fSJerome Brunet } 361*1a11d88fSJerome Brunet 362*1a11d88fSJerome Brunet return ts; 363*1a11d88fSJerome Brunet } 364*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); 365*1a11d88fSJerome Brunet 366*1a11d88fSJerome Brunet void axg_tdm_stream_free(struct axg_tdm_stream *ts) 367*1a11d88fSJerome Brunet { 368*1a11d88fSJerome Brunet /* 369*1a11d88fSJerome Brunet * If the list is not empty, it would mean that one of the formatter 370*1a11d88fSJerome Brunet * widget is still powered and attached to the interface while we 371*1a11d88fSJerome Brunet * we are removing the TDM DAI. It should not be possible 372*1a11d88fSJerome Brunet */ 373*1a11d88fSJerome Brunet WARN_ON(!list_empty(&ts->formatter_list)); 374*1a11d88fSJerome Brunet mutex_destroy(&ts->lock); 375*1a11d88fSJerome Brunet kfree(ts); 376*1a11d88fSJerome Brunet } 377*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_free); 378*1a11d88fSJerome Brunet 379*1a11d88fSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); 380*1a11d88fSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 381*1a11d88fSJerome Brunet MODULE_LICENSE("GPL v2"); 382