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