Path: blob/master/sound/soc/samsung/s3c24xx_uda134x.c
10817 views
/*1* Modifications by Christian Pellegrin <[email protected]>2*3* s3c24xx_uda134x.c -- S3C24XX_UDA134X ALSA SoC Audio board driver4*5* Copyright 2007 Dension Audio Systems Ltd.6* Author: Zoltan Devai7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License version 2 as10* published by the Free Software Foundation.11*/1213#include <linux/clk.h>14#include <linux/gpio.h>1516#include <sound/soc.h>17#include <sound/s3c24xx_uda134x.h>1819#include <plat/regs-iis.h>2021#include "s3c24xx-i2s.h"2223/* #define ENFORCE_RATES 1 */24/*25Unfortunately the S3C24XX in master mode has a limited capacity of26generating the clock for the codec. If you define this only rates27that are really available will be enforced. But be careful, most28user level application just want the usual sampling frequencies (8,2911.025, 22.050, 44.1 kHz) and anyway resampling is a costly30operation for embedded systems. So if you aren't very lucky or your31hardware engineer wasn't very forward-looking it's better to leave32this undefined. If you do so an approximate value for the requested33sampling rate in the range -/+ 5% will be chosen. If this in not34possible an error will be returned.35*/3637static struct clk *xtal;38static struct clk *pclk;39/* this is need because we don't have a place where to keep the40* pointers to the clocks in each substream. We get the clocks only41* when we are actually using them so we don't block stuff like42* frequency change or oscillator power-off */43static int clk_users;44static DEFINE_MUTEX(clk_lock);4546static unsigned int rates[33 * 2];47#ifdef ENFORCE_RATES48static struct snd_pcm_hw_constraint_list hw_constraints_rates = {49.count = ARRAY_SIZE(rates),50.list = rates,51.mask = 0,52};53#endif5455static struct platform_device *s3c24xx_uda134x_snd_device;5657static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)58{59int ret = 0;60#ifdef ENFORCE_RATES61struct snd_pcm_runtime *runtime = substream->runtime;62#endif6364mutex_lock(&clk_lock);65pr_debug("%s %d\n", __func__, clk_users);66if (clk_users == 0) {67xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");68if (!xtal) {69printk(KERN_ERR "%s cannot get xtal\n", __func__);70ret = -EBUSY;71} else {72pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,73"pclk");74if (!pclk) {75printk(KERN_ERR "%s cannot get pclk\n",76__func__);77clk_put(xtal);78ret = -EBUSY;79}80}81if (!ret) {82int i, j;8384for (i = 0; i < 2; i++) {85int fs = i ? 256 : 384;8687rates[i*33] = clk_get_rate(xtal) / fs;88for (j = 1; j < 33; j++)89rates[i*33 + j] = clk_get_rate(pclk) /90(j * fs);91}92}93}94clk_users += 1;95mutex_unlock(&clk_lock);96if (!ret) {97#ifdef ENFORCE_RATES98ret = snd_pcm_hw_constraint_list(runtime, 0,99SNDRV_PCM_HW_PARAM_RATE,100&hw_constraints_rates);101if (ret < 0)102printk(KERN_ERR "%s cannot set constraints\n",103__func__);104#endif105}106return ret;107}108109static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)110{111mutex_lock(&clk_lock);112pr_debug("%s %d\n", __func__, clk_users);113clk_users -= 1;114if (clk_users == 0) {115clk_put(xtal);116xtal = NULL;117clk_put(pclk);118pclk = NULL;119}120mutex_unlock(&clk_lock);121}122123static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,124struct snd_pcm_hw_params *params)125{126struct snd_soc_pcm_runtime *rtd = substream->private_data;127struct snd_soc_dai *codec_dai = rtd->codec_dai;128struct snd_soc_dai *cpu_dai = rtd->cpu_dai;129unsigned int clk = 0;130int ret = 0;131int clk_source, fs_mode;132unsigned long rate = params_rate(params);133long err, cerr;134unsigned int div;135int i, bi;136137err = 999999;138bi = 0;139for (i = 0; i < 2*33; i++) {140cerr = rates[i] - rate;141if (cerr < 0)142cerr = -cerr;143if (cerr < err) {144err = cerr;145bi = i;146}147}148if (bi / 33 == 1)149fs_mode = S3C2410_IISMOD_256FS;150else151fs_mode = S3C2410_IISMOD_384FS;152if (bi % 33 == 0) {153clk_source = S3C24XX_CLKSRC_MPLL;154div = 1;155} else {156clk_source = S3C24XX_CLKSRC_PCLK;157div = bi % 33;158}159pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);160161clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;162pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,163fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",164clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",165div, clk, err);166167if ((err * 100 / rate) > 5) {168printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "169"too different from desired (%ld%%)\n",170err * 100 / rate);171return -EINVAL;172}173174ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |175SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);176if (ret < 0)177return ret;178179ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |180SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);181if (ret < 0)182return ret;183184ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,185SND_SOC_CLOCK_IN);186if (ret < 0)187return ret;188189ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);190if (ret < 0)191return ret;192193ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,194S3C2410_IISMOD_32FS);195if (ret < 0)196return ret;197198ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,199S3C24XX_PRESCALE(div, div));200if (ret < 0)201return ret;202203/* set the codec system clock for DAC and ADC */204ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,205SND_SOC_CLOCK_OUT);206if (ret < 0)207return ret;208209return 0;210}211212static struct snd_soc_ops s3c24xx_uda134x_ops = {213.startup = s3c24xx_uda134x_startup,214.shutdown = s3c24xx_uda134x_shutdown,215.hw_params = s3c24xx_uda134x_hw_params,216};217218static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {219.name = "UDA134X",220.stream_name = "UDA134X",221.codec_name = "uda134x-codec",222.codec_dai_name = "uda134x-hifi",223.cpu_dai_name = "s3c24xx-iis",224.ops = &s3c24xx_uda134x_ops,225.platform_name = "samsung-audio",226};227228static struct snd_soc_card snd_soc_s3c24xx_uda134x = {229.name = "S3C24XX_UDA134X",230.dai_link = &s3c24xx_uda134x_dai_link,231.num_links = 1,232};233234static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;235236static void setdat(int v)237{238gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);239}240241static void setclk(int v)242{243gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);244}245246static void setmode(int v)247{248gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);249}250251/* FIXME - This must be codec platform data but in which board file ?? */252static struct uda134x_platform_data s3c24xx_uda134x = {253.l3 = {254.setdat = setdat,255.setclk = setclk,256.setmode = setmode,257.data_hold = 1,258.data_setup = 1,259.clock_high = 1,260.mode_hold = 1,261.mode = 1,262.mode_setup = 1,263},264};265266static int s3c24xx_uda134x_setup_pin(int pin, char *fun)267{268if (gpio_request(pin, "s3c24xx_uda134x") < 0) {269printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "270"l3 %s pin already in use", fun);271return -EBUSY;272}273gpio_direction_output(pin, 0);274return 0;275}276277static int s3c24xx_uda134x_probe(struct platform_device *pdev)278{279int ret;280281printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");282283s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;284if (s3c24xx_uda134x_l3_pins == NULL) {285printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "286"unable to find platform data\n");287return -ENODEV;288}289s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;290s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;291292if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,293"data") < 0)294return -EBUSY;295if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,296"clk") < 0) {297gpio_free(s3c24xx_uda134x_l3_pins->l3_data);298return -EBUSY;299}300if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,301"mode") < 0) {302gpio_free(s3c24xx_uda134x_l3_pins->l3_data);303gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);304return -EBUSY;305}306307s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);308if (!s3c24xx_uda134x_snd_device) {309printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "310"Unable to register\n");311return -ENOMEM;312}313314platform_set_drvdata(s3c24xx_uda134x_snd_device,315&snd_soc_s3c24xx_uda134x);316platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));317ret = platform_device_add(s3c24xx_uda134x_snd_device);318if (ret) {319printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");320platform_device_put(s3c24xx_uda134x_snd_device);321}322323return ret;324}325326static int s3c24xx_uda134x_remove(struct platform_device *pdev)327{328platform_device_unregister(s3c24xx_uda134x_snd_device);329gpio_free(s3c24xx_uda134x_l3_pins->l3_data);330gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);331gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);332return 0;333}334335static struct platform_driver s3c24xx_uda134x_driver = {336.probe = s3c24xx_uda134x_probe,337.remove = s3c24xx_uda134x_remove,338.driver = {339.name = "s3c24xx_uda134x",340.owner = THIS_MODULE,341},342};343344static int __init s3c24xx_uda134x_init(void)345{346return platform_driver_register(&s3c24xx_uda134x_driver);347}348349static void __exit s3c24xx_uda134x_exit(void)350{351platform_driver_unregister(&s3c24xx_uda134x_driver);352}353354355module_init(s3c24xx_uda134x_init);356module_exit(s3c24xx_uda134x_exit);357358MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <[email protected]>");359MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");360MODULE_LICENSE("GPL");361362363