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