Commit 7ad933d7a6677c20ce1bdb17425e732cf1ebee8a

Authored by Christian Pellegrin
Committed by Mark Brown
1 parent 1cad1de1b2

ASoC: Machine driver for for s3c24xx with uda134x

Signed-off-by: Christian Pellegrin <chripell@fsfe.org>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

Showing 4 changed files with 395 additions and 0 deletions Side-by-side Diff

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