Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christian Pellegrin and committed by
Mark Brown
7ad933d7 1cad1de1

+395
+14
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
+5
sound/soc/s3c24xx/Kconfig
··· 44 44 Say Y if you want to add support for SoC audio on ln2440sbc 45 45 with the ALC650. 46 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
+2
sound/soc/s3c24xx/Makefile
··· 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
+374
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");