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 int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
  58{
  59        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  60        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  61#ifdef ENFORCE_RATES
  62        struct snd_pcm_runtime *runtime = substream->runtime;
  63#endif
  64        int ret = 0;
  65
  66        mutex_lock(&clk_lock);
  67
  68        if (clk_users == 0) {
  69                xtal = clk_get(rtd->dev, "xtal");
  70                if (IS_ERR(xtal)) {
  71                        dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
  72                        ret = PTR_ERR(xtal);
  73                } else {
  74                        pclk = clk_get(cpu_dai->dev, "iis");
  75                        if (IS_ERR(pclk)) {
  76                                dev_err(rtd->dev, "%s cannot get pclk\n",
  77                                        __func__);
  78                                clk_put(xtal);
  79                                ret = PTR_ERR(pclk);
  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                        dev_err(rtd->dev, "%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        clk_users -= 1;
 114        if (clk_users == 0) {
 115                clk_put(xtal);
 116                xtal = NULL;
 117                clk_put(pclk);
 118                pclk = NULL;
 119        }
 120        mutex_unlock(&clk_lock);
 121}
 122
 123static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
 124                                        struct snd_pcm_hw_params *params)
 125{
 126        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 127        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 128        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 129        unsigned int clk = 0;
 130        int ret = 0;
 131        int clk_source, fs_mode;
 132        unsigned long rate = params_rate(params);
 133        long err, cerr;
 134        unsigned int div;
 135        int i, bi;
 136
 137        err = 999999;
 138        bi = 0;
 139        for (i = 0; i < 2*33; i++) {
 140                cerr = rates[i] - rate;
 141                if (cerr < 0)
 142                        cerr = -cerr;
 143                if (cerr < err) {
 144                        err = cerr;
 145                        bi = i;
 146                }
 147        }
 148        if (bi / 33 == 1)
 149                fs_mode = S3C2410_IISMOD_256FS;
 150        else
 151                fs_mode = S3C2410_IISMOD_384FS;
 152        if (bi % 33 == 0) {
 153                clk_source = S3C24XX_CLKSRC_MPLL;
 154                div = 1;
 155        } else {
 156                clk_source = S3C24XX_CLKSRC_PCLK;
 157                div = bi % 33;
 158        }
 159
 160        dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
 161
 162        clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
 163
 164        dev_dbg(rtd->dev, "%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                dev_err(rtd->dev, "effective frequency too different "
 171                                  "from desired (%ld%%)\n", err * 100 / rate);
 172                return -EINVAL;
 173        }
 174
 175        ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
 176                        SND_SOC_CLOCK_IN);
 177        if (ret < 0)
 178                return ret;
 179
 180        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
 181        if (ret < 0)
 182                return ret;
 183
 184        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
 185                        S3C2410_IISMOD_32FS);
 186        if (ret < 0)
 187                return ret;
 188
 189        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 190                        S3C24XX_PRESCALE(div, div));
 191        if (ret < 0)
 192                return ret;
 193
 194        /* set the codec system clock for DAC and ADC */
 195        ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
 196                        SND_SOC_CLOCK_OUT);
 197        if (ret < 0)
 198                return ret;
 199
 200        return 0;
 201}
 202
 203static struct snd_soc_ops s3c24xx_uda134x_ops = {
 204        .startup = s3c24xx_uda134x_startup,
 205        .shutdown = s3c24xx_uda134x_shutdown,
 206        .hw_params = s3c24xx_uda134x_hw_params,
 207};
 208
 209static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
 210        .name = "UDA134X",
 211        .stream_name = "UDA134X",
 212        .codec_name = "uda134x-codec",
 213        .codec_dai_name = "uda134x-hifi",
 214        .cpu_dai_name = "s3c24xx-iis",
 215        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 216                   SND_SOC_DAIFMT_CBS_CFS,
 217        .ops = &s3c24xx_uda134x_ops,
 218        .platform_name  = "s3c24xx-iis",
 219};
 220
 221static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
 222        .name = "S3C24XX_UDA134X",
 223        .owner = THIS_MODULE,
 224        .dai_link = &s3c24xx_uda134x_dai_link,
 225        .num_links = 1,
 226};
 227
 228static int s3c24xx_uda134x_probe(struct platform_device *pdev)
 229{
 230        struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
 231        int ret;
 232
 233        platform_set_drvdata(pdev, card);
 234        card->dev = &pdev->dev;
 235
 236        ret = devm_snd_soc_register_card(&pdev->dev, card);
 237        if (ret)
 238                dev_err(&pdev->dev, "failed to register card: %d\n", ret);
 239
 240        return ret;
 241}
 242
 243static struct platform_driver s3c24xx_uda134x_driver = {
 244        .probe  = s3c24xx_uda134x_probe,
 245        .driver = {
 246                .name = "s3c24xx_uda134x",
 247        },
 248};
 249module_platform_driver(s3c24xx_uda134x_driver);
 250
 251MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
 252MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
 253MODULE_LICENSE("GPL");
 254