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