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