root/sound/soc/meson/axg-tdm-formatter.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. axg_tdm_formatter_set_channel_masks
  2. axg_tdm_formatter_enable
  3. axg_tdm_formatter_disable
  4. axg_tdm_formatter_attach
  5. axg_tdm_formatter_dettach
  6. axg_tdm_formatter_power_up
  7. axg_tdm_formatter_power_down
  8. axg_tdm_formatter_event
  9. axg_tdm_formatter_probe
  10. axg_tdm_stream_start
  11. axg_tdm_stream_stop
  12. axg_tdm_stream_alloc
  13. axg_tdm_stream_free

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

/* [<][>][^][v][top][bottom][index][help] */