linux/sound/soc/samsung/odroid.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (C) 2017 Samsung Electronics Co., Ltd.
   4
   5#include <linux/clk.h>
   6#include <linux/clk-provider.h>
   7#include <linux/of.h>
   8#include <linux/of_device.h>
   9#include <linux/module.h>
  10#include <sound/soc.h>
  11#include <sound/pcm_params.h>
  12#include "i2s.h"
  13#include "i2s-regs.h"
  14
  15struct odroid_priv {
  16        struct snd_soc_card card;
  17        struct clk *clk_i2s_bus;
  18        struct clk *sclk_i2s;
  19
  20        /* Spinlock protecting fields below */
  21        spinlock_t lock;
  22        unsigned int be_sample_rate;
  23        bool be_active;
  24};
  25
  26static int odroid_card_fe_startup(struct snd_pcm_substream *substream)
  27{
  28        struct snd_pcm_runtime *runtime = substream->runtime;
  29
  30        snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
  31
  32        return 0;
  33}
  34
  35static int odroid_card_fe_hw_params(struct snd_pcm_substream *substream,
  36                                      struct snd_pcm_hw_params *params)
  37{
  38        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  39        struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
  40        unsigned long flags;
  41        int ret = 0;
  42
  43        spin_lock_irqsave(&priv->lock, flags);
  44        if (priv->be_active && priv->be_sample_rate != params_rate(params))
  45                ret = -EINVAL;
  46        spin_unlock_irqrestore(&priv->lock, flags);
  47
  48        return ret;
  49}
  50
  51static const struct snd_soc_ops odroid_card_fe_ops = {
  52        .startup = odroid_card_fe_startup,
  53        .hw_params = odroid_card_fe_hw_params,
  54};
  55
  56static int odroid_card_be_hw_params(struct snd_pcm_substream *substream,
  57                                      struct snd_pcm_hw_params *params)
  58{
  59        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  60        struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
  61        unsigned int pll_freq, rclk_freq, rfs;
  62        unsigned long flags;
  63        int ret;
  64
  65        switch (params_rate(params)) {
  66        case 64000:
  67                pll_freq = 196608001U;
  68                rfs = 384;
  69                break;
  70        case 44100:
  71        case 88200:
  72                pll_freq = 180633609U;
  73                rfs = 512;
  74                break;
  75        case 32000:
  76        case 48000:
  77        case 96000:
  78                pll_freq = 196608001U;
  79                rfs = 512;
  80                break;
  81        default:
  82                return -EINVAL;
  83        }
  84
  85        ret = clk_set_rate(priv->clk_i2s_bus, pll_freq / 2 + 1);
  86        if (ret < 0)
  87                return ret;
  88
  89        /*
  90         *  We add 2 to the rclk_freq value in order to avoid too low clock
  91         *  frequency values due to the EPLL output frequency not being exact
  92         *  multiple of the audio sampling rate.
  93         */
  94        rclk_freq = params_rate(params) * rfs + 2;
  95
  96        ret = clk_set_rate(priv->sclk_i2s, rclk_freq);
  97        if (ret < 0)
  98                return ret;
  99
 100        if (rtd->num_codecs > 1) {
 101                struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 1);
 102
 103                ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq,
 104                                             SND_SOC_CLOCK_IN);
 105                if (ret < 0)
 106                        return ret;
 107        }
 108
 109        spin_lock_irqsave(&priv->lock, flags);
 110        priv->be_sample_rate = params_rate(params);
 111        spin_unlock_irqrestore(&priv->lock, flags);
 112
 113        return 0;
 114}
 115
 116static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd)
 117{
 118        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 119        struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
 120        unsigned long flags;
 121
 122        spin_lock_irqsave(&priv->lock, flags);
 123
 124        switch (cmd) {
 125        case SNDRV_PCM_TRIGGER_START:
 126        case SNDRV_PCM_TRIGGER_RESUME:
 127        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 128                priv->be_active = true;
 129                break;
 130
 131        case SNDRV_PCM_TRIGGER_STOP:
 132        case SNDRV_PCM_TRIGGER_SUSPEND:
 133        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 134                priv->be_active = false;
 135                break;
 136        }
 137
 138        spin_unlock_irqrestore(&priv->lock, flags);
 139
 140        return 0;
 141}
 142
 143static const struct snd_soc_ops odroid_card_be_ops = {
 144        .hw_params = odroid_card_be_hw_params,
 145        .trigger = odroid_card_be_trigger,
 146};
 147
 148/* DAPM routes for backward compatibility with old DTS */
 149static const struct snd_soc_dapm_route odroid_dapm_routes[] = {
 150        { "I2S Playback", NULL, "Mixer DAI TX" },
 151        { "HiFi Playback", NULL, "Mixer DAI TX" },
 152};
 153
 154SND_SOC_DAILINK_DEFS(primary,
 155        DAILINK_COMP_ARRAY(COMP_EMPTY()),
 156        DAILINK_COMP_ARRAY(COMP_DUMMY()),
 157        DAILINK_COMP_ARRAY(COMP_PLATFORM("3830000.i2s")));
 158
 159SND_SOC_DAILINK_DEFS(mixer,
 160        DAILINK_COMP_ARRAY(COMP_DUMMY()),
 161        DAILINK_COMP_ARRAY(COMP_EMPTY()),
 162        DAILINK_COMP_ARRAY(COMP_DUMMY()));
 163
 164SND_SOC_DAILINK_DEFS(secondary,
 165        DAILINK_COMP_ARRAY(COMP_EMPTY()),
 166        DAILINK_COMP_ARRAY(COMP_DUMMY()),
 167        DAILINK_COMP_ARRAY(COMP_PLATFORM("3830000.i2s-sec")));
 168
 169static struct snd_soc_dai_link odroid_card_dais[] = {
 170        {
 171                /* Primary FE <-> BE link */
 172                .ops = &odroid_card_fe_ops,
 173                .name = "Primary",
 174                .stream_name = "Primary",
 175                .dynamic = 1,
 176                .dpcm_playback = 1,
 177                SND_SOC_DAILINK_REG(primary),
 178        }, {
 179                /* BE <-> CODECs link */
 180                .name = "I2S Mixer",
 181                .ops = &odroid_card_be_ops,
 182                .no_pcm = 1,
 183                .dpcm_playback = 1,
 184                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 185                                SND_SOC_DAIFMT_CBS_CFS,
 186                SND_SOC_DAILINK_REG(mixer),
 187        }, {
 188                /* Secondary FE <-> BE link */
 189                .playback_only = 1,
 190                .ops = &odroid_card_fe_ops,
 191                .name = "Secondary",
 192                .stream_name = "Secondary",
 193                .dynamic = 1,
 194                .dpcm_playback = 1,
 195                SND_SOC_DAILINK_REG(secondary),
 196        }
 197};
 198
 199static int odroid_audio_probe(struct platform_device *pdev)
 200{
 201        struct device *dev = &pdev->dev;
 202        struct device_node *cpu_dai = NULL;
 203        struct device_node *cpu, *codec;
 204        struct odroid_priv *priv;
 205        struct snd_soc_card *card;
 206        struct snd_soc_dai_link *link, *codec_link;
 207        int num_pcms, ret, i;
 208        struct of_phandle_args args = {};
 209
 210        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 211        if (!priv)
 212                return -ENOMEM;
 213
 214        card = &priv->card;
 215        card->dev = dev;
 216
 217        card->owner = THIS_MODULE;
 218        card->fully_routed = true;
 219
 220        spin_lock_init(&priv->lock);
 221        snd_soc_card_set_drvdata(card, priv);
 222
 223        ret = snd_soc_of_parse_card_name(card, "model");
 224        if (ret < 0)
 225                return ret;
 226
 227        if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) {
 228                ret = snd_soc_of_parse_audio_simple_widgets(card,
 229                                                "samsung,audio-widgets");
 230                if (ret < 0)
 231                        return ret;
 232        }
 233
 234        if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) {
 235                ret = snd_soc_of_parse_audio_routing(card,
 236                                                "samsung,audio-routing");
 237                if (ret < 0)
 238                        return ret;
 239        }
 240
 241        card->dai_link = odroid_card_dais;
 242        card->num_links = ARRAY_SIZE(odroid_card_dais);
 243
 244        cpu = of_get_child_by_name(dev->of_node, "cpu");
 245        codec = of_get_child_by_name(dev->of_node, "codec");
 246        link = card->dai_link;
 247        codec_link = &card->dai_link[1];
 248
 249        /*
 250         * For backwards compatibility create the secondary CPU DAI link only
 251         * if there are 2 CPU DAI entries in the cpu sound-dai property in DT.
 252         * Also add required DAPM routes not available in old DTS.
 253         */
 254        num_pcms = of_count_phandle_with_args(cpu, "sound-dai",
 255                                              "#sound-dai-cells");
 256        if (num_pcms == 1) {
 257                card->dapm_routes = odroid_dapm_routes;
 258                card->num_dapm_routes = ARRAY_SIZE(odroid_dapm_routes);
 259                card->num_links--;
 260        }
 261
 262        for (i = 0; i < num_pcms; i++, link += 2) {
 263                ret = of_parse_phandle_with_args(cpu, "sound-dai",
 264                                                 "#sound-dai-cells", i, &args);
 265                if (ret < 0)
 266                        break;
 267
 268                if (!args.np) {
 269                        dev_err(dev, "sound-dai property parse error: %d\n", ret);
 270                        ret = -EINVAL;
 271                        break;
 272                }
 273
 274                ret = snd_soc_get_dai_name(&args, &link->cpus->dai_name);
 275                of_node_put(args.np);
 276
 277                if (ret < 0)
 278                        break;
 279        }
 280        if (ret == 0) {
 281                cpu_dai = of_parse_phandle(cpu, "sound-dai", 0);
 282                if (!cpu_dai)
 283                        ret = -EINVAL;
 284        }
 285
 286        of_node_put(cpu);
 287        if (ret < 0)
 288                goto err_put_node;
 289
 290        ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link);
 291        if (ret < 0)
 292                goto err_put_cpu_dai;
 293
 294        /* Set capture capability only for boards with the MAX98090 CODEC */
 295        if (codec_link->num_codecs > 1) {
 296                card->dai_link[0].dpcm_capture = 1;
 297                card->dai_link[1].dpcm_capture = 1;
 298        }
 299
 300        priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1");
 301        if (IS_ERR(priv->sclk_i2s)) {
 302                ret = PTR_ERR(priv->sclk_i2s);
 303                goto err_put_cpu_dai;
 304        }
 305
 306        priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis");
 307        if (IS_ERR(priv->clk_i2s_bus)) {
 308                ret = PTR_ERR(priv->clk_i2s_bus);
 309                goto err_put_sclk;
 310        }
 311
 312        ret = devm_snd_soc_register_card(dev, card);
 313        if (ret < 0) {
 314                if (ret != -EPROBE_DEFER)
 315                        dev_err(dev, "snd_soc_register_card() failed: %d\n",
 316                                ret);
 317                goto err_put_clk_i2s;
 318        }
 319
 320        of_node_put(cpu_dai);
 321        of_node_put(codec);
 322        return 0;
 323
 324err_put_clk_i2s:
 325        clk_put(priv->clk_i2s_bus);
 326err_put_sclk:
 327        clk_put(priv->sclk_i2s);
 328err_put_cpu_dai:
 329        of_node_put(cpu_dai);
 330        snd_soc_of_put_dai_link_codecs(codec_link);
 331err_put_node:
 332        of_node_put(codec);
 333        return ret;
 334}
 335
 336static int odroid_audio_remove(struct platform_device *pdev)
 337{
 338        struct odroid_priv *priv = platform_get_drvdata(pdev);
 339
 340        snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]);
 341        clk_put(priv->sclk_i2s);
 342        clk_put(priv->clk_i2s_bus);
 343
 344        return 0;
 345}
 346
 347static const struct of_device_id odroid_audio_of_match[] = {
 348        { .compatible   = "hardkernel,odroid-xu3-audio" },
 349        { .compatible   = "hardkernel,odroid-xu4-audio" },
 350        { .compatible   = "samsung,odroid-xu3-audio" },
 351        { .compatible   = "samsung,odroid-xu4-audio" },
 352        { },
 353};
 354MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
 355
 356static struct platform_driver odroid_audio_driver = {
 357        .driver = {
 358                .name           = "odroid-audio",
 359                .of_match_table = odroid_audio_of_match,
 360                .pm             = &snd_soc_pm_ops,
 361        },
 362        .probe  = odroid_audio_probe,
 363        .remove = odroid_audio_remove,
 364};
 365module_platform_driver(odroid_audio_driver);
 366
 367MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
 368MODULE_DESCRIPTION("Odroid XU3/XU4 audio support");
 369MODULE_LICENSE("GPL v2");
 370