linux/sound/soc/samsung/s3c24xx_uda134x.c
<<
>>
Prefs
   1/*
   2 * Modifications by Christian Pellegrin <chripell@evolware.org>
   3 *
   4 * s3c24xx_uda134x.c  --  S3C24XX_UDA134X ALSA SoC Audio board driver
   5 *
   6 * Copyright 2007 Dension Audio Systems Ltd.
   7 * Author: Zoltan Devai
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/gpio.h>
  16
  17#include <sound/soc.h>
  18#include <sound/s3c24xx_uda134x.h>
  19
  20#include <plat/regs-iis.h>
  21
  22#include "s3c24xx-i2s.h"
  23
  24/* #define ENFORCE_RATES 1 */
  25/*
  26  Unfortunately the S3C24XX in master mode has a limited capacity of
  27  generating the clock for the codec. If you define this only rates
  28  that are really available will be enforced. But be careful, most
  29  user level application just want the usual sampling frequencies (8,
  30  11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
  31  operation for embedded systems. So if you aren't very lucky or your
  32  hardware engineer wasn't very forward-looking it's better to leave
  33  this undefined. If you do so an approximate value for the requested
  34  sampling rate in the range -/+ 5% will be chosen. If this in not
  35  possible an error will be returned.
  36*/
  37
  38static struct clk *xtal;
  39static struct clk *pclk;
  40/* this is need because we don't have a place where to keep the
  41 * pointers to the clocks in each substream. We get the clocks only
  42 * when we are actually using them so we don't block stuff like
  43 * frequency change or oscillator power-off */
  44static int clk_users;
  45static DEFINE_MUTEX(clk_lock);
  46
  47static unsigned int rates[33 * 2];
  48#ifdef ENFORCE_RATES
  49static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
  50        .count  = ARRAY_SIZE(rates),
  51        .list   = rates,
  52        .mask   = 0,
  53};
  54#endif
  55
  56static struct platform_device *s3c24xx_uda134x_snd_device;
  57
  58static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
  59{
  60        int ret = 0;
  61#ifdef ENFORCE_RATES
  62        struct snd_pcm_runtime *runtime = substream->runtime;
  63#endif
  64
  65        mutex_lock(&clk_lock);
  66        pr_debug("%s %d\n", __func__, clk_users);
  67        if (clk_users == 0) {
  68                xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
  69                if (!xtal) {
  70                        printk(KERN_ERR "%s cannot get xtal\n", __func__);
  71                        ret = -EBUSY;
  72                } else {
  73                        pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
  74                                       "pclk");
  75                        if (!pclk) {
  76                                printk(KERN_ERR "%s cannot get pclk\n",
  77                                       __func__);
  78                                clk_put(xtal);
  79                                ret = -EBUSY;
  80                        }
  81                }
  82                if (!ret) {
  83                        int i, j;
  84
  85                        for (i = 0; i < 2; i++) {
  86                                int fs = i ? 256 : 384;
  87
  88                                rates[i*33] = clk_get_rate(xtal) / fs;
  89                                for (j = 1; j < 33; j++)
  90                                        rates[i*33 + j] = clk_get_rate(pclk) /
  91                                                (j * fs);
  92                        }
  93                }
  94        }
  95        clk_users += 1;
  96        mutex_unlock(&clk_lock);
  97        if (!ret) {
  98#ifdef ENFORCE_RATES
  99                ret = snd_pcm_hw_constraint_list(runtime, 0,
 100                                                 SNDRV_PCM_HW_PARAM_RATE,
 101                                                 &hw_constraints_rates);
 102                if (ret < 0)
 103                        printk(KERN_ERR "%s cannot set constraints\n",
 104                               __func__);
 105#endif
 106        }
 107        return ret;
 108}
 109
 110static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
 111{
 112        mutex_lock(&clk_lock);
 113        pr_debug("%s %d\n", __func__, clk_users);
 114        clk_users -= 1;
 115        if (clk_users == 0) {
 116                clk_put(xtal);
 117                xtal = NULL;
 118                clk_put(pclk);
 119                pclk = NULL;
 120        }
 121        mutex_unlock(&clk_lock);
 122}
 123
 124static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
 125                                        struct snd_pcm_hw_params *params)
 126{
 127        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 128        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 129        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 130        unsigned int clk = 0;
 131        int ret = 0;
 132        int clk_source, fs_mode;
 133        unsigned long rate = params_rate(params);
 134        long err, cerr;
 135        unsigned int div;
 136        int i, bi;
 137
 138        err = 999999;
 139        bi = 0;
 140        for (i = 0; i < 2*33; i++) {
 141                cerr = rates[i] - rate;
 142                if (cerr < 0)
 143                        cerr = -cerr;
 144                if (cerr < err) {
 145                        err = cerr;
 146                        bi = i;
 147                }
 148        }
 149        if (bi / 33 == 1)
 150                fs_mode = S3C2410_IISMOD_256FS;
 151        else
 152                fs_mode = S3C2410_IISMOD_384FS;
 153        if (bi % 33 == 0) {
 154                clk_source = S3C24XX_CLKSRC_MPLL;
 155                div = 1;
 156        } else {
 157                clk_source = S3C24XX_CLKSRC_PCLK;
 158                div = bi % 33;
 159        }
 160        pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
 161
 162        clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
 163        pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
 164                 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
 165                 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
 166                 div, clk, err);
 167
 168        if ((err * 100 / rate) > 5) {
 169                printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
 170                       "too different from desired (%ld%%)\n",
 171                       err * 100 / rate);
 172                return -EINVAL;
 173        }
 174
 175        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 176                        SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 177        if (ret < 0)
 178                return ret;
 179
 180        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 181                        SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 182        if (ret < 0)
 183                return ret;
 184
 185        ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
 186                        SND_SOC_CLOCK_IN);
 187        if (ret < 0)
 188                return ret;
 189
 190        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
 191        if (ret < 0)
 192                return ret;
 193
 194        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
 195                        S3C2410_IISMOD_32FS);
 196        if (ret < 0)
 197                return ret;
 198
 199        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 200                        S3C24XX_PRESCALE(div, div));
 201        if (ret < 0)
 202                return ret;
 203
 204        /* set the codec system clock for DAC and ADC */
 205        ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
 206                        SND_SOC_CLOCK_OUT);
 207        if (ret < 0)
 208                return ret;
 209
 210        return 0;
 211}
 212
 213static struct snd_soc_ops s3c24xx_uda134x_ops = {
 214        .startup = s3c24xx_uda134x_startup,
 215        .shutdown = s3c24xx_uda134x_shutdown,
 216        .hw_params = s3c24xx_uda134x_hw_params,
 217};
 218
 219static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
 220        .name = "UDA134X",
 221        .stream_name = "UDA134X",
 222        .codec_name = "uda134x-codec",
 223        .codec_dai_name = "uda134x-hifi",
 224        .cpu_dai_name = "s3c24xx-iis",
 225        .ops = &s3c24xx_uda134x_ops,
 226        .platform_name  = "samsung-audio",
 227};
 228
 229static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
 230        .name = "S3C24XX_UDA134X",
 231        .dai_link = &s3c24xx_uda134x_dai_link,
 232        .num_links = 1,
 233};
 234
 235static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
 236
 237static void setdat(int v)
 238{
 239        gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
 240}
 241
 242static void setclk(int v)
 243{
 244        gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
 245}
 246
 247static void setmode(int v)
 248{
 249        gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
 250}
 251
 252/* FIXME - This must be codec platform data but in which board file ?? */
 253static struct uda134x_platform_data s3c24xx_uda134x = {
 254        .l3 = {
 255                .setdat = setdat,
 256                .setclk = setclk,
 257                .setmode = setmode,
 258                .data_hold = 1,
 259                .data_setup = 1,
 260                .clock_high = 1,
 261                .mode_hold = 1,
 262                .mode = 1,
 263                .mode_setup = 1,
 264        },
 265};
 266
 267static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
 268{
 269        if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
 270                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
 271                       "l3 %s pin already in use", fun);
 272                return -EBUSY;
 273        }
 274        gpio_direction_output(pin, 0);
 275        return 0;
 276}
 277
 278static int s3c24xx_uda134x_probe(struct platform_device *pdev)
 279{
 280        int ret;
 281
 282        printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
 283
 284        s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
 285        if (s3c24xx_uda134x_l3_pins == NULL) {
 286                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
 287                       "unable to find platform data\n");
 288                return -ENODEV;
 289        }
 290        s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
 291        s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
 292
 293        if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
 294                                      "data") < 0)
 295                return -EBUSY;
 296        if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
 297                                      "clk") < 0) {
 298                gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
 299                return -EBUSY;
 300        }
 301        if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
 302                                      "mode") < 0) {
 303                gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
 304                gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
 305                return -EBUSY;
 306        }
 307
 308        s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
 309        if (!s3c24xx_uda134x_snd_device) {
 310                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
 311                       "Unable to register\n");
 312                return -ENOMEM;
 313        }
 314
 315        platform_set_drvdata(s3c24xx_uda134x_snd_device,
 316                             &snd_soc_s3c24xx_uda134x);
 317        platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
 318        ret = platform_device_add(s3c24xx_uda134x_snd_device);
 319        if (ret) {
 320                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
 321                platform_device_put(s3c24xx_uda134x_snd_device);
 322        }
 323
 324        return ret;
 325}
 326
 327static int s3c24xx_uda134x_remove(struct platform_device *pdev)
 328{
 329        platform_device_unregister(s3c24xx_uda134x_snd_device);
 330        gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
 331        gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
 332        gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
 333        return 0;
 334}
 335
 336static struct platform_driver s3c24xx_uda134x_driver = {
 337        .probe  = s3c24xx_uda134x_probe,
 338        .remove = s3c24xx_uda134x_remove,
 339        .driver = {
 340                .name = "s3c24xx_uda134x",
 341                .owner = THIS_MODULE,
 342        },
 343};
 344
 345static int __init s3c24xx_uda134x_init(void)
 346{
 347        return platform_driver_register(&s3c24xx_uda134x_driver);
 348}
 349
 350static void __exit s3c24xx_uda134x_exit(void)
 351{
 352        platform_driver_unregister(&s3c24xx_uda134x_driver);
 353}
 354
 355
 356module_init(s3c24xx_uda134x_init);
 357module_exit(s3c24xx_uda134x_exit);
 358
 359MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
 360MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
 361MODULE_LICENSE("GPL");
 362