static int __init s3c24xx_uda134x_init(void)
{
return platform_driver_register(&s3c24xx_uda134x_driver);
}
static struct platform_driver s3c24xx_uda134x_driver = {
.probe = s3c24xx_uda134x_probe,
.remove = s3c24xx_uda134x_remove,
.driver = {
.name = "s3c24xx_uda134x",
.owner = THIS_MODULE,
},
};
这里就是模块初始化函数。我们都知道当注册成功的时候也就是.name和平台设备的.name匹配成功的时候将调用.probe,这里就是s3c24xx_uda134x_probe。那么平台的设备是什么时候加入的呢。看linux-2.6.32.2\arch\arm\mach-s3c2440\mach-mini2440.中的
mini2440_machine_init()
{...
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
.....
}
static struct platform_device *mini2440_devices[] __initdata = {
...
&s3c24xx_uda134x,
...
};
static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = {
.l3_clk = S3C2410_GPB(4),
.l3_data = S3C2410_GPB(3),
.l3_mode = S3C2410_GPB(2),
.model = UDA134X_UDA1341,
};
static struct platform_device s3c24xx_uda134x = {
.name = "s3c24xx_uda134x",
.dev = {
.platform_data = &s3c24xx_uda134x_data,
}
};
所以s3c24xx_uda134x_probe会得到运行。讲这个函数之前先看看一些结构体,变量
static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins用来指向&s3c24xx_uda134x_data
tatic struct uda134x_platform_data s3c24xx_uda134x = {
.l3 = {
.setdat = setdat,
.setclk = setclk,
.setmode = setmode,
.data_hold = 1,
.data_setup = 1,
.clock_high = 1,
.mode_hold = 1,
.mode = 1,
.mode_setup = 1,
},
};与L3MODE,L3CLOCK,L3DATA有关,也就是编码相关的数据。它会赋值给static struct snd_soc_device s3c24xx_uda134x_snd_devdata
snd_soc_device s3c24xx_uda134x_snd_devdata非常重要。是对ASoc设备的整体封装。包括封装ASoc Codec设备用的snd_soc_codec_device(.codec_dev 成员),板和平台设备用的snd_soc_card组件(.card成员)。
--------------------------------------------------------------------------------------
static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
.card = &snd_soc_s3c24xx_uda134x,
.codec_dev = &soc_codec_dev_uda134x,
.codec_data = &s3c24xx_uda134x,
};
---------------------------------------------------------------------------------------
封装的ASoc Codec设备用的snd_soc_codec_device(.codec_dev 成员)定义如下:
struct snd_soc_codec_device soc_codec_dev_uda134x = {
.probe = uda134x_soc_probe,
.remove = uda134x_soc_remove,
.suspend = uda134x_soc_suspend,
.resume = uda134x_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);
在snd_soc_card组件(.card成员)中封装了板和平台的定义如下:
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
.name = "S3C24XX_UDA134X",
.platform = &s3c24xx_soc_platform,平台,在s3c24xx-pcm.c中注册了平台
.dai_link = &s3c24xx_uda134x_dai_link,板
.num_links = 1,
};
板&s3c24xx_uda134x_dai_link起绑定codec驱动和平台I2C驱动的作用定义:
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
.name = "UDA134X",
.stream_name = "UDA134X",
.codec_dai = &uda134x_dai,//codec驱动
.cpu_dai = &s3c24xx_i2s_dai,//平台I2S驱动
.ops = &s3c24xx_uda134x_ops,//操作板驱动的函数
};
static struct snd_soc_ops s3c24xx_uda134x_ops = {
.startup = s3c24xx_uda134x_startup,
.shutdown = s3c24xx_uda134x_shutdown,
.hw_params = s3c24xx_uda134x_hw_params,
};
最后看看s3c24xx_uda134x_probe()做了什么:
获得设备资源赋值给s3c24xx_uda134x_l3_pins。设置s3c24xx_uda134x的power,model.
通过函数s3c24xx_uda134x_setup_pin()获得音频口的clk,data,model引脚使用,设置输出为0;
申请s3c24xx_uda134x_snd_device内存并通过platform_set_drvdata()设置ASoc设备的整体封装s3c24xx_uda134x_snd_devdata 为其私有数据。然后设置s3c24xx_uda134x_snd_devdata的dev指向s3c24xx_uda134x_snd_device的dev.最后添加s3c24xx_uda134x_snd_device平台设备。
添加这的这个平台设备.name为“soc-audio”,在soc-core.c有个平台驱动的.name也为“soc-audio”。
所以会引起匹配调用在soc-core.c文件中的.probe,所以s3c24xx_uda134x_snd_device会传递给soc-core.c文件中的.probe函数作为参数。也就完成了ASoc设备的整体封装s3c24xx_uda134x_snd_devdata的传递。
下面是板驱动的源代码:
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/s3c24xx_uda134x.h>
#include <sound/uda134x.h>
#include <plat/regs-iis.h>
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
#include "../codecs/uda134x.h"
static struct clk *xtal;
static struct clk *pclk;
static int clk_users;
static DEFINE_MUTEX(clk_lock);
static unsigned int rates[33 * 2];
#ifdef ENFORCE_RATES
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
#endif
static struct platform_device *s3c24xx_uda134x_snd_device;
static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
{
int ret = 0;
#ifdef ENFORCE_RATES
struct snd_pcm_runtime *runtime = substream->runtime;
#endif
mutex_lock(&clk_lock);
pr_debug("%s %d\n", __func__, clk_users);
if (clk_users == 0) {
xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
if (!xtal) {
printk(KERN_ERR "%s cannot get xtal\n", __func__);
ret = -EBUSY;
} else {
pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
"pclk");
if (!pclk) {
printk(KERN_ERR "%s cannot get pclk\n",
__func__);
clk_put(xtal);
ret = -EBUSY;
}
}
if (!ret) {
int i, j;
for (i = 0; i < 2; i++) {
int fs = i ? 256 : 384;
rates[i*33] = clk_get_rate(xtal) / fs;
for (j = 1; j < 33; j++)
rates[i*33 + j] = clk_get_rate(pclk) /
(j * fs);
}
}
}
clk_users += 1;
mutex_unlock(&clk_lock);
if (!ret) {
#ifdef ENFORCE_RATES
ret = snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_rates);
if (ret < 0)
printk(KERN_ERR "%s cannot set constraints\n",
__func__);
#endif
}
return ret;
}
static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
{
mutex_lock(&clk_lock);
pr_debug("%s %d\n", __func__, clk_users);
clk_users -= 1;
if (clk_users == 0) {
clk_put(xtal);
xtal = NULL;
clk_put(pclk);
pclk = NULL;
}
mutex_unlock(&clk_lock);
}
static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int clk = 0;
int ret = 0;
int clk_source, fs_mode;
unsigned long rate = params_rate(params);
long err, cerr;
unsigned int div;
int i, bi;
err = 999999;
bi = 0;
for (i = 0; i < 2*33; i++) {
cerr = rates[i] - rate;
if (cerr < 0)
cerr = -cerr;
if (cerr < err) {
err = cerr;
bi = i;
}
}
if (bi / 33 == 1)
fs_mode = S3C2410_IISMOD_256FS;
else
fs_mode = S3C2410_IISMOD_384FS;
if (bi % 33 == 0) {
clk_source = S3C24XX_CLKSRC_MPLL;
div = 1;
} else {
clk_source = S3C24XX_CLKSRC_PCLK;
div = bi % 33;
}
pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
div, clk, err);
if ((err * 100 / rate) > 5) {
printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
"too different from desired (%ld%%)\n",
err * 100 / rate);
return -EINVAL;
}
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
S3C2410_IISMOD_32FS);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
S3C24XX_PRESCALE(div, div));
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops s3c24xx_uda134x_ops = {
.startup = s3c24xx_uda134x_startup,
.shutdown = s3c24xx_uda134x_shutdown,
.hw_params = s3c24xx_uda134x_hw_params,
};
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
.name = "UDA134X",
.stream_name = "UDA134X",
.codec_dai = &uda134x_dai,
.cpu_dai = &s3c24xx_i2s_dai,
.ops = &s3c24xx_uda134x_ops,
};
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
.name = "S3C24XX_UDA134X",
.platform = &s3c24xx_soc_platform,
.dai_link = &s3c24xx_uda134x_dai_link,
.num_links = 1,
};
static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
static void setdat(int v)
{
gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
}
static void setclk(int v)
{
gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
}
static void setmode(int v)
{
gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
}
static struct uda134x_platform_data s3c24xx_uda134x = {
.l3 = {
.setdat = setdat,
.setclk = setclk,
.setmode = setmode,
.data_hold = 1,
.data_setup = 1,
.clock_high = 1,
.mode_hold = 1,
.mode = 1,
.mode_setup = 1,
},
};
static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
.card = &snd_soc_s3c24xx_uda134x,
.codec_dev = &soc_codec_dev_uda134x,
.codec_data = &s3c24xx_uda134x,
};
static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
{
if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
"l3 %s pin already in use", fun);
return -EBUSY;
}
gpio_direction_output(pin, 0);
return 0;
}
static int s3c24xx_uda134x_probe(struct platform_device *pdev)
{
int ret;
printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
if (s3c24xx_uda134x_l3_pins == NULL) {
printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
"unable to find platform data\n");
return -ENODEV;
}
s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
"data") < 0)
return -EBUSY;
if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
"clk") < 0) {
gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
return -EBUSY;
}
if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
"mode") < 0) {
gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
return -EBUSY;
}
s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
if (!s3c24xx_uda134x_snd_device) {
printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
"Unable to register\n");
return -ENOMEM;
}
platform_set_drvdata(s3c24xx_uda134x_snd_device,
&s3c24xx_uda134x_snd_devdata);
s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
ret = platform_device_add(s3c24xx_uda134x_snd_device);
if (ret) {
printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
platform_device_put(s3c24xx_uda134x_snd_device);
}
return ret;
}
static int s3c24xx_uda134x_remove(struct platform_device *pdev)
{
platform_device_unregister(s3c24xx_uda134x_snd_device);
gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
return 0;
}
static struct platform_driver s3c24xx_uda134x_driver = {
.probe = s3c24xx_uda134x_probe,
.remove = s3c24xx_uda134x_remove,
.driver = {
.name = "s3c24xx_uda134x",
.owner = THIS_MODULE,
},
};
static int __init s3c24xx_uda134x_init(void)
{
return platform_driver_register(&s3c24xx_uda134x_driver);
}
static void __exit s3c24xx_uda134x_exit(void)
{
platform_driver_unregister(&s3c24xx_uda134x_driver);
}
module_init(s3c24xx_uda134x_init);
module_exit(s3c24xx_uda134x_exit);
MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <[email protected]>");
MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
MODULE_LICENSE("GPL");