linux/sound/soc/meson/axg-tdm-formatter.c
<<
>>
Prefs
   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
  15struct 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
  29int 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}
  68EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
  69
  70static 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
 132static 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
 144static 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);
 161out:
 162        mutex_unlock(&ts->lock);
 163        return ret;
 164}
 165
 166static 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
 177static 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
 213disable_pclk:
 214        clk_disable_unprepare(formatter->pclk);
 215        return ret;
 216}
 217
 218static 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
 225int 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}
 249EXPORT_SYMBOL_GPL(axg_tdm_formatter_event);
 250
 251int 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}
 339EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe);
 340
 341int 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
 358out:
 359        mutex_unlock(&ts->lock);
 360        return ret;
 361}
 362EXPORT_SYMBOL_GPL(axg_tdm_stream_start);
 363
 364void 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}
 378EXPORT_SYMBOL_GPL(axg_tdm_stream_stop);
 379
 380struct 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}
 393EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc);
 394
 395void 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}
 406EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
 407
 408MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
 409MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 410MODULE_LICENSE("GPL v2");
 411