root/sound/soc/samsung/odroid.c

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

DEFINITIONS

This source file includes following definitions.
  1. odroid_card_fe_startup
  2. odroid_card_fe_hw_params
  3. odroid_card_be_hw_params
  4. odroid_card_be_trigger
  5. odroid_audio_probe
  6. odroid_audio_remove

   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 
  15 struct 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 
  26 static 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 
  35 static 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 = substream->private_data;
  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 
  51 static 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 
  56 static 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 = substream->private_data;
  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 = rtd->codec_dais[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 
 116 static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd)
 117 {
 118         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 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 
 143 static 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 */
 149 static 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 
 154 SND_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 
 159 SND_SOC_DAILINK_DEFS(mixer,
 160         DAILINK_COMP_ARRAY(COMP_DUMMY()),
 161         DAILINK_COMP_ARRAY(COMP_EMPTY()),
 162         DAILINK_COMP_ARRAY(COMP_DUMMY()));
 163 
 164 SND_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 
 169 static 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 
 199 static 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                 dev_err(dev, "snd_soc_register_card() failed: %d\n", ret);
 315                 goto err_put_clk_i2s;
 316         }
 317 
 318         of_node_put(cpu_dai);
 319         of_node_put(codec);
 320         return 0;
 321 
 322 err_put_clk_i2s:
 323         clk_put(priv->clk_i2s_bus);
 324 err_put_sclk:
 325         clk_put(priv->sclk_i2s);
 326 err_put_cpu_dai:
 327         of_node_put(cpu_dai);
 328         snd_soc_of_put_dai_link_codecs(codec_link);
 329 err_put_node:
 330         of_node_put(codec);
 331         return ret;
 332 }
 333 
 334 static int odroid_audio_remove(struct platform_device *pdev)
 335 {
 336         struct odroid_priv *priv = platform_get_drvdata(pdev);
 337 
 338         snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]);
 339         clk_put(priv->sclk_i2s);
 340         clk_put(priv->clk_i2s_bus);
 341 
 342         return 0;
 343 }
 344 
 345 static const struct of_device_id odroid_audio_of_match[] = {
 346         { .compatible   = "hardkernel,odroid-xu3-audio" },
 347         { .compatible   = "hardkernel,odroid-xu4-audio" },
 348         { .compatible   = "samsung,odroid-xu3-audio" },
 349         { .compatible   = "samsung,odroid-xu4-audio" },
 350         { },
 351 };
 352 MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
 353 
 354 static struct platform_driver odroid_audio_driver = {
 355         .driver = {
 356                 .name           = "odroid-audio",
 357                 .of_match_table = odroid_audio_of_match,
 358                 .pm             = &snd_soc_pm_ops,
 359         },
 360         .probe  = odroid_audio_probe,
 361         .remove = odroid_audio_remove,
 362 };
 363 module_platform_driver(odroid_audio_driver);
 364 
 365 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
 366 MODULE_DESCRIPTION("Odroid XU3/XU4 audio support");
 367 MODULE_LICENSE("GPL v2");

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