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_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 177                        SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 178        if (ret < 0)
 179                return ret;
 180
 181        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 182                        SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 183        if (ret < 0)
 184                return ret;
 185
 186        ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
 187                        SND_SOC_CLOCK_IN);
 188        if (ret < 0)
 189                return ret;
 190
 191        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
 192        if (ret < 0)
 193                return ret;
 194
 195        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
 196                        S3C2410_IISMOD_32FS);
 197        if (ret < 0)
 198                return ret;
 199
 200        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 201                        S3C24XX_PRESCALE(div, div));
 202        if (ret < 0)
 203                return ret;
 204
 205        /* set the codec system clock for DAC and ADC */
 206        ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
 207                        SND_SOC_CLOCK_OUT);
 208        if (ret < 0)
 209                return ret;
 210
 211        return 0;
 212}
 213
 214static struct snd_soc_ops s3c24xx_uda134x_ops = {
 215        .startup = s3c24xx_uda134x_startup,
 216        .shutdown = s3c24xx_uda134x_shutdown,
 217        .hw_params = s3c24xx_uda134x_hw_params,
 218};
 219
 220static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
 221        .name = "UDA134X",
 222        .stream_name = "UDA134X",
 223        .codec_name = "uda134x-codec",
 224        .codec_dai_name = "uda134x-hifi",
 225        .cpu_dai_name = "s3c24xx-iis",
 226        .ops = &s3c24xx_uda134x_ops,
 227        .platform_name  = "s3c24xx-iis",
 228};
 229
 230static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
 231        .name = "S3C24XX_UDA134X",
 232        .owner = THIS_MODULE,
 233        .dai_link = &s3c24xx_uda134x_dai_link,
 234        .num_links = 1,
 235};
 236
 237static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
 238
 239static void setdat(int v)
 240{
 241        gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
 242}
 243
 244static void setclk(int v)
 245{
 246        gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
 247}
 248
 249static void setmode(int v)
 250{
 251        gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
 252}
 253
 254/* FIXME - This must be codec platform data but in which board file ?? */
 255static struct uda134x_platform_data s3c24xx_uda134x = {
 256        .l3 = {
 257                .setdat = setdat,
 258                .setclk = setclk,
 259                .setmode = setmode,
 260                .data_hold = 1,
 261                .data_setup = 1,
 262                .clock_high = 1,
 263                .mode_hold = 1,
 264                .mode = 1,
 265                .mode_setup = 1,
 266        },
 267};
 268
 269static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
 270{
 271        if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
 272                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
 273                       "l3 %s pin already in use", fun);
 274                return -EBUSY;
 275        }
 276        gpio_direction_output(pin, 0);
 277        return 0;
 278}
 279
 280static int s3c24xx_uda134x_probe(struct platform_device *pdev)
 281{
 282        int ret;
 283
 284        printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
 285
 286        s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
 287        if (s3c24xx_uda134x_l3_pins == NULL) {
 288                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
 289                       "unable to find platform data\n");
 290                return -ENODEV;
 291        }
 292        s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
 293        s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
 294
 295        if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
 296                                      "data") < 0)
 297                return -EBUSY;
 298        if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
 299                                      "clk") < 0) {
 300                gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
 301                return -EBUSY;
 302        }
 303        if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
 304                                      "mode") < 0) {
 305                gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
 306                gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
 307                return -EBUSY;
 308        }
 309
 310        s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
 311        if (!s3c24xx_uda134x_snd_device) {
 312                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
 313                       "Unable to register\n");
 314                return -ENOMEM;
 315        }
 316
 317        platform_set_drvdata(s3c24xx_uda134x_snd_device,
 318                             &snd_soc_s3c24xx_uda134x);
 319        platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
 320        ret = platform_device_add(s3c24xx_uda134x_snd_device);
 321        if (ret) {
 322                printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
 323                platform_device_put(s3c24xx_uda134x_snd_device);
 324        }
 325
 326        return ret;
 327}
 328
 329static int s3c24xx_uda134x_remove(struct platform_device *pdev)
 330{
 331        platform_device_unregister(s3c24xx_uda134x_snd_device);
 332        gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
 333        gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
 334        gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
 335        return 0;
 336}
 337
 338static struct platform_driver s3c24xx_uda134x_driver = {
 339        .probe  = s3c24xx_uda134x_probe,
 340        .remove = s3c24xx_uda134x_remove,
 341        .driver = {
 342                .name = "s3c24xx_uda134x",
 343                .owner = THIS_MODULE,
 344        },
 345};
 346
 347module_platform_driver(s3c24xx_uda134x_driver);
 348
 349MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
 350MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
 351MODULE_LICENSE("GPL");
 352