一. 在/sound/soc/codec/lc1120.c中的初始化代码:
1.init/exit lc1120_i2c_driver:
static const struct i2c_device_id lc1120_i2c_id[] = {
{ "lc1120", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lc1120_i2c_id);
/* machine i2c codec control layer */
static struct i2c_driver lc1120_i2c_driver = {
.driver = {
.name = "lc1120",
.owner = THIS_MODULE,
},
.probe = lc1120_i2c_probe, //lc1120_i2c_driver 添加完毕,就会自动运行probe.
.remove = lc1120_i2c_remove,
.id_table = lc1120_i2c_id,
};
static int __init lc1120_i2c_init(void)
{
int ret;
D_ENTER;
ret = i2c_add_driver(&lc1120_i2c_driver); //向I2C总线,添加一个i2c_driver,lc1120_i2c_driver,就是一个i2c_driver.
if (ret)
{
printk(KERN_ERR "%s: error when regsiter i2c, %d\n",
__func__, ret);
}
return ret;
}
static void __exit lc1120_i2c_exit(void)
{
D_ENTER;
i2c_del_driver(&lc1120_i2c_driver);
}
module_init(lc1120_i2c_init);
module_exit(lc1120_i2c_exit);
2.lc1120_i2c_probe and lc1120_i2c_remove:
/* codec private data */
struct lc1120_priv
{
struct snd_soc_codec codec; //只包含了一个codec 和一个原子变量
atomic_t count;
};
/*
* If the i2c layer weren't so broken, we could pass this kind of data
* around
*/
static int lc1120_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct snd_soc_codec *codec;
struct lc1120_priv *lc1120; //lc1120_priv 是一个自定义的表示lc1120设备的结构体。
D_ENTER;
if (device_create_file(&client->dev, &dev_attr_reg)) //在sys中建立reg和val文件,用来与用户控件交互。
printk(KERN_ERR "%s create sysfs reg failed\n", __func__);
if (device_create_file(&client->dev, &dev_attr_val))
printk(KERN_ERR "%s create sysfs val failed\n", __func__);
lc1120 = kzalloc(sizeof(struct lc1120_priv), GFP_KERNEL); //给lc1120_priv 申请内存.
if (lc1120 == NULL) {
dev_err(&client->dev, "failed to create private data\n");
return -ENOMEM;
}
atomic_set(&lc1120->count, 0); //初始化原子变量为0。
codec = &lc1120->codec;
codec->dev = &client->dev; //client设备 指向 codec 设备。
snd_soc_codec_set_drvdata(codec, lc1120); //把lc1120 指向 codec->drvdata.
codec->control_data = client; //client 指向 codec->control_data
i2c_set_clientdata(client, lc1120); //把lc1120 指向 client->dev->device_private->driver_data
return lc1120_register(codec); //注册codec。
}
static int lc1120_i2c_remove(struct i2c_client *client)
{
struct lc1120_priv *lc1120 = i2c_get_clientdata(client);
D_ENTER;
lc1120_unregister(lc1120); 注销codec
kfree(lc1120);
return 0;
}
3. lc1120_register(codec) /lc1120_unregister(codec)
static DEVICE_ATTR(reg, S_IRUGO | S_IWUGO, sys_reg_show, sys_reg_store);
static DEVICE_ATTR(val, S_IRUGO | S_IWUGO, sys_val_show, sys_val_store); //在sysfs中建立reg和val变量。
static int lc1120_register(struct snd_soc_codec *codec)
{
int ret;
D_ENTER;
lc1120_codec = codec;
codec->name = "lc1120";
codec->owner = THIS_MODULE;
codec->read = lc1120_read;
codec->write = lc1120_write;
/* TODO:
codec->set_bias_level = lc1120_set_bias_level;
*/
codec->dai = &lc1120_dai; //dai 就是soc与codec通信的链接。
codec->num_dai = 1;
ret = snd_soc_register_codec(codec); //标准注册codec函数。
if (ret) {
dev_err(codec->dev, "Failed to register codec\n");
return ret;
}
ret = snd_soc_register_dai(&lc1120_dai); //标准注册dai函数。
if (ret) {
dev_err(codec->dev, "Failed to register dai\n");
snd_soc_unregister_codec(codec);
return ret;
}
printk(KERN_ERR "yzt %s exited\n", __func__);
return 0;
}
static int lc1120_unregister(struct lc1120_priv *lc1120)
{
D_ENTER;
// lc1120_set_bias_level(&lc1120->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&lc1120_dai); //先注销dai
snd_soc_unregister_codec(&lc1120->codec); //在注销codec
lc1120_codec = NULL;
return 0;
}
二.在sound/soc/comip/snd_soc_comip_lc1120.c中的初始化代码:
1.comip_lc1120_init()/comip_lc1120_exit()
static struct clk *codec_mclk;
static struct platform_device *snd_soc_comip_lc1120_device; //两个全局变量,下面会用到。
static int __init comip_lc1120_init(void)
{
int ret;
D_ENTER;
codec_mclk = snd_soc_codec_set_mclk(); //在soc中配置寄存器,使之输出13M I2S main_clk
snd_soc_comip_lc1120_device = platform_device_alloc("soc-audio", -1); //申请platform device 空间。
if (!snd_soc_comip_lc1120_device) {
printk(KERN_ERR "Platform device allocation failed\n");
return -ENOMEM;
}
//把snd_soc_device 指向 platform -> dev
//snd_soc_comip_lc1120_devdata 是snd_soc_device,snd_soc_comip_lc1120_device 是platform
platform_set_drvdata(snd_soc_comip_lc1120_device, &snd_soc_comip_lc1120_devdata);
snd_soc_comip_lc1120_devdata.dev = &snd_soc_comip_lc1120_device->dev;
ret = platform_device_add(snd_soc_comip_lc1120_device); 添加一个platform设备。
if (ret) {
printk(KERN_ERR "Soc Audio: Unable to add lc1120\n");
platform_device_put(snd_soc_comip_lc1120_device);
}
return ret;
}
module_init(comip_lc1120_init);
static void __exit comip_lc1120_exit(void)
{
D_ENTER;
if (codec_mclk)
clk_put(codec_mclk);
codec_mclk = NULL; //时钟清零
platform_device_unregister(snd_soc_comip_lc1120_device);
}
module_exit(comip_lc1120_exit);
2. 几个结构体之间的关系:
a.一个platform_device 包含一个device 和一个resource。resource很有用。
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
b.一个snd_soc_device 包括一个snd_soc_card 和一个 snd_soc_codec_device./* SoC Device - the audio subsystem */
struct snd_soc_device {
struct device *dev;
struct snd_soc_card *card;
struct snd_soc_codec_device *codec_dev;
void *codec_data;
};
具体如下:
/* Audio subsystem */
static struct snd_soc_device snd_soc_comip_lc1120_devdata = //snd_soc_device包含了两项
{
.card = &snd_soc_comip_lc1120_card_data, //snd_soc_card.
.codec_dev = &soc_codec_dev_lc1120, //snd_soc_codec_device.
};
c.一
个snd_soc_card 包含一个
snd_soc_platform 和一个
snd_soc_dai_link :
/* Audio machine driver */
static struct snd_soc_card snd_soc_comip_lc1120_card_data =
{
.name = "comip_lc1120",
.platform = &comip_soc_platform, //snd_soc_platform,是指Soc端的音频操作
.dai_link = snd_soc_comip_lc1120_dai_link, //snd_soc_dai_link,Soc和codec 两个dai的 链接
.num_links = ARRAY_SIZE(snd_soc_comip_lc1120_dai_link),
};
d. 一个
snd_soc_platform 表示Soc端的pcm码流操作,包含:
struct snd_soc_platform comip_soc_platform = {
.name = "comip-audio",
.pcm_ops = &comip_pcm_ops,
.pcm_new = comip_pcm_new,
.pcm_free = comip_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(comip_soc_platform);
e. 一个snd_soc_dai_link 包含两个snd_soc_dai,一个cpu_dai和一个codec_dai;还有一个snd_soc_ops
static struct snd_soc_dai_link snd_soc_comip_lc1120_dai_link[] =
{
{
.name = "comip_i2s_lc1120",
.stream_name = "comip_i2s_lc1120",
.cpu_dai = &comip_i2s_dai,
.codec_dai = &lc1120_dai,
.ops = &snd_soc_comip_lc1120_ops,
},
};
f:snd_soc_ops snd_soc_comip_lc1120_ops
,包含如下;音频播放的时候,会调用trigger,startup,hw_param等等。/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_ops snd_soc_comip_lc1120_ops =
{
.startup = comip_lc1120_startup,
.shutdown = comip_lc1120_shutdown,
.hw_params = comip_lc1120_i2s_hw_params,
.hw_free = comip_lc1120_hw_free,
.prepare = comip_lc1120_prepare,
.trigger = comip_lc1120_trigger,
};
g. comip_lc1120_i2s_hw_params内容如下,其他基本都是直接return 0;
static int comip_lc1120_i2s_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;
int ret = 0;
// unsigned long rate = params_rate(params);
D_ENTER;
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
return ret;
}
h. struct snd_soc_codec_device soc_codec_dev_lc1120定义如下:soc_codec_dev_lc1120
struct snd_soc_codec_device soc_codec_dev_lc1120 = {
.probe = lc1120_probe, //在注册了soc_codec_dev_lc1120之后,就自动运行lc1120_probe
.remove = lc1120_remove,
.suspend = lc1120_suspend,
.resume = lc1120_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_lc1120);
三. lc1120 功能部分,注册完了之后,就具备了一系列功能。
1. lc1120_probe():
static struct snd_soc_codec *lc1120_codec; //定义了一个全局变量
static int lc1120_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
D_ENTER;
codec = lc1120_codec;
if (!codec) {
dev_err(&pdev->dev, "Codec not registered\n");
return -ENODEV;
}
if (lc1120_write_regs(codec, lc1120_init_data, //初始化一系列寄存器
ARRAY_SIZE(lc1120_init_data)))
{
printk(KERN_ERR "%s: initialize lc1120 failed\n", __func__);
return -ENODEV;
}
socdev->card->codec = codec;
/* register pcms */
mutex_init(&codec->mutex);
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); //create new sound card and pcms。
if (ret < 0) {
printk(KERN_ERR "%s: snd_soc_new_pcms failed for %d\n",
__func__, ret);
goto pcm_err;
}
ret = snd_soc_add_controls(codec, lc1120_snd_controls, //add an array of controls to a codec.
ARRAY_SIZE(lc1120_snd_controls));
if (ret < 0)
{
printk(KERN_ERR "%s: snd_soc_add controls failed for %d\n",
__func__, ret);
goto pcm_err;
}
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
ret = snd_soc_dapm_new_controls(codec, lc1120_dapm_widgets, //create new dapm controls
ARRAY_SIZE(lc1120_dapm_widgets));
if (ret < 0)
{
printk(KERN_ERR "%s: snd_soc_dapm_new_controls failed for %d\n",
__func__, ret);
goto pcm_err;
}
ret = snd_soc_dapm_add_routes(codec, lc1120_route, //Add routes between DAPM widgets
ARRAY_SIZE(lc1120_route));
if (ret < 0)
{
printk(KERN_ERR "%s: snd_soc_dapm_add_routes failed for %d\n",
__func__, ret);
goto pcm_err;
}
ret = snd_soc_dapm_new_widgets(codec); //add new dapm widgets
if (ret < 0)
{
printk(KERN_ERR "%s: snd_soc_dapm_new_widgets failed for %d\n",
__func__, ret);
goto pcm_err;
}
printk(KERN_ERR "yzt: %s exited\n", __func__);
pcm_err:
return ret;
}