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 <sound/soc.h>
  11
  12#include "axg-tdm-formatter.h"
  13
  14struct 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
  27int 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}
  66EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
  67
  68static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
  69{
  70        struct axg_tdm_stream *ts = formatter->stream;
  71        bool invert = formatter->drv->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, formatter->stream);
  89        if (ret)
  90                return ret;
  91
  92        /* Enable the signal clocks feeding the formatter */
  93        ret = clk_prepare_enable(formatter->sclk);
  94        if (ret)
  95                return ret;
  96
  97        ret = clk_prepare_enable(formatter->lrclk);
  98        if (ret) {
  99                clk_disable_unprepare(formatter->sclk);
 100                return ret;
 101        }
 102
 103        /* Finally, actually enable the formatter */
 104        formatter->drv->ops->enable(formatter->map);
 105        formatter->enabled = true;
 106
 107        return 0;
 108}
 109
 110static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter)
 111{
 112        /* Do nothing if the formatter is already disabled */
 113        if (!formatter->enabled)
 114                return;
 115
 116        formatter->drv->ops->disable(formatter->map);
 117        clk_disable_unprepare(formatter->lrclk);
 118        clk_disable_unprepare(formatter->sclk);
 119        formatter->enabled = false;
 120}
 121
 122static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter)
 123{
 124        struct axg_tdm_stream *ts = formatter->stream;
 125        int ret = 0;
 126
 127        mutex_lock(&ts->lock);
 128
 129        /* Catch up if the stream is already running when we attach */
 130        if (ts->ready) {
 131                ret = axg_tdm_formatter_enable(formatter);
 132                if (ret) {
 133                        pr_err("failed to enable formatter\n");
 134                        goto out;
 135                }
 136        }
 137
 138        list_add_tail(&formatter->list, &ts->formatter_list);
 139out:
 140        mutex_unlock(&ts->lock);
 141        return ret;
 142}
 143
 144static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter)
 145{
 146        struct axg_tdm_stream *ts = formatter->stream;
 147
 148        mutex_lock(&ts->lock);
 149        list_del(&formatter->list);
 150        mutex_unlock(&ts->lock);
 151
 152        axg_tdm_formatter_disable(formatter);
 153}
 154
 155static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter,
 156                                      struct snd_soc_dapm_widget *w)
 157{
 158        struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w);
 159        int ret;
 160
 161        /*
 162         * If we don't get a stream at this stage, it would mean that the
 163         * widget is powering up but is not attached to any backend DAI.
 164         * It should not happen, ever !
 165         */
 166        if (WARN_ON(!ts))
 167                return -ENODEV;
 168
 169        /* Clock our device */
 170        ret = clk_prepare_enable(formatter->pclk);
 171        if (ret)
 172                return ret;
 173
 174        /* Reparent the bit clock to the TDM interface */
 175        ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk);
 176        if (ret)
 177                goto disable_pclk;
 178
 179        /* Reparent the sample clock to the TDM interface */
 180        ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk);
 181        if (ret)
 182                goto disable_pclk;
 183
 184        formatter->stream = ts;
 185        ret = axg_tdm_formatter_attach(formatter);
 186        if (ret)
 187                goto disable_pclk;
 188
 189        return 0;
 190
 191disable_pclk:
 192        clk_disable_unprepare(formatter->pclk);
 193        return ret;
 194}
 195
 196static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter)
 197{
 198        axg_tdm_formatter_dettach(formatter);
 199        clk_disable_unprepare(formatter->pclk);
 200        formatter->stream = NULL;
 201}
 202
 203int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w,
 204                            struct snd_kcontrol *control,
 205                            int event)
 206{
 207        struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
 208        struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c);
 209        int ret = 0;
 210
 211        switch (event) {
 212        case SND_SOC_DAPM_PRE_PMU:
 213                ret = axg_tdm_formatter_power_up(formatter, w);
 214                break;
 215
 216        case SND_SOC_DAPM_PRE_PMD:
 217                axg_tdm_formatter_power_down(formatter);
 218                break;
 219
 220        default:
 221                dev_err(c->dev, "Unexpected event %d\n", event);
 222                return -EINVAL;
 223        }
 224
 225        return ret;
 226}
 227EXPORT_SYMBOL_GPL(axg_tdm_formatter_event);
 228
 229int axg_tdm_formatter_probe(struct platform_device *pdev)
 230{
 231        struct device *dev = &pdev->dev;
 232        const struct axg_tdm_formatter_driver *drv;
 233        struct axg_tdm_formatter *formatter;
 234        struct resource *res;
 235        void __iomem *regs;
 236        int ret;
 237
 238        drv = of_device_get_match_data(dev);
 239        if (!drv) {
 240                dev_err(dev, "failed to match device\n");
 241                return -ENODEV;
 242        }
 243
 244        formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
 245        if (!formatter)
 246                return -ENOMEM;
 247        platform_set_drvdata(pdev, formatter);
 248        formatter->drv = drv;
 249
 250        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 251        regs = devm_ioremap_resource(dev, res);
 252        if (IS_ERR(regs))
 253                return PTR_ERR(regs);
 254
 255        formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg);
 256        if (IS_ERR(formatter->map)) {
 257                dev_err(dev, "failed to init regmap: %ld\n",
 258                        PTR_ERR(formatter->map));
 259                return PTR_ERR(formatter->map);
 260        }
 261
 262        /* Peripharal clock */
 263        formatter->pclk = devm_clk_get(dev, "pclk");
 264        if (IS_ERR(formatter->pclk)) {
 265                ret = PTR_ERR(formatter->pclk);
 266                if (ret != -EPROBE_DEFER)
 267                        dev_err(dev, "failed to get pclk: %d\n", ret);
 268                return ret;
 269        }
 270
 271        /* Formatter bit clock */
 272        formatter->sclk = devm_clk_get(dev, "sclk");
 273        if (IS_ERR(formatter->sclk)) {
 274                ret = PTR_ERR(formatter->sclk);
 275                if (ret != -EPROBE_DEFER)
 276                        dev_err(dev, "failed to get sclk: %d\n", ret);
 277                return ret;
 278        }
 279
 280        /* Formatter sample clock */
 281        formatter->lrclk = devm_clk_get(dev, "lrclk");
 282        if (IS_ERR(formatter->lrclk)) {
 283                ret = PTR_ERR(formatter->lrclk);
 284                if (ret != -EPROBE_DEFER)
 285                        dev_err(dev, "failed to get lrclk: %d\n", ret);
 286                return ret;
 287        }
 288
 289        /* Formatter bit clock input multiplexer */
 290        formatter->sclk_sel = devm_clk_get(dev, "sclk_sel");
 291        if (IS_ERR(formatter->sclk_sel)) {
 292                ret = PTR_ERR(formatter->sclk_sel);
 293                if (ret != -EPROBE_DEFER)
 294                        dev_err(dev, "failed to get sclk_sel: %d\n", ret);
 295                return ret;
 296        }
 297
 298        /* Formatter sample clock input multiplexer */
 299        formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel");
 300        if (IS_ERR(formatter->lrclk_sel)) {
 301                ret = PTR_ERR(formatter->lrclk_sel);
 302                if (ret != -EPROBE_DEFER)
 303                        dev_err(dev, "failed to get lrclk_sel: %d\n", ret);
 304                return ret;
 305        }
 306
 307        return devm_snd_soc_register_component(dev, drv->component_drv,
 308                                               NULL, 0);
 309}
 310EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe);
 311
 312int axg_tdm_stream_start(struct axg_tdm_stream *ts)
 313{
 314        struct axg_tdm_formatter *formatter;
 315        int ret = 0;
 316
 317        mutex_lock(&ts->lock);
 318        ts->ready = true;
 319
 320        /* Start all the formatters attached to the stream */
 321        list_for_each_entry(formatter, &ts->formatter_list, list) {
 322                ret = axg_tdm_formatter_enable(formatter);
 323                if (ret) {
 324                        pr_err("failed to start tdm stream\n");
 325                        goto out;
 326                }
 327        }
 328
 329out:
 330        mutex_unlock(&ts->lock);
 331        return ret;
 332}
 333EXPORT_SYMBOL_GPL(axg_tdm_stream_start);
 334
 335void axg_tdm_stream_stop(struct axg_tdm_stream *ts)
 336{
 337        struct axg_tdm_formatter *formatter;
 338
 339        mutex_lock(&ts->lock);
 340        ts->ready = false;
 341
 342        /* Stop all the formatters attached to the stream */
 343        list_for_each_entry(formatter, &ts->formatter_list, list) {
 344                axg_tdm_formatter_disable(formatter);
 345        }
 346
 347        mutex_unlock(&ts->lock);
 348}
 349EXPORT_SYMBOL_GPL(axg_tdm_stream_stop);
 350
 351struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface)
 352{
 353        struct axg_tdm_stream *ts;
 354
 355        ts = kzalloc(sizeof(*ts), GFP_KERNEL);
 356        if (ts) {
 357                INIT_LIST_HEAD(&ts->formatter_list);
 358                mutex_init(&ts->lock);
 359                ts->iface = iface;
 360        }
 361
 362        return ts;
 363}
 364EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc);
 365
 366void axg_tdm_stream_free(struct axg_tdm_stream *ts)
 367{
 368        /*
 369         * If the list is not empty, it would mean that one of the formatter
 370         * widget is still powered and attached to the interface while we
 371         * we are removing the TDM DAI. It should not be possible
 372         */
 373        WARN_ON(!list_empty(&ts->formatter_list));
 374        mutex_destroy(&ts->lock);
 375        kfree(ts);
 376}
 377EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
 378
 379MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
 380MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 381MODULE_LICENSE("GPL v2");
 382