static __devinit int cs42l73_i2c_probe(struct i2c_client*i2c_client,
const struct i2c_device_id *id)
{
structcs42l73_private *cs42l73;
intret;
unsigned int devid = 0;
unsigned int reg;
//分配一块内存,这块内存不用显示删除。
cs42l73 =devm_kzalloc(&i2c_client->dev,sizeof(struct cs42l73_private),
GFP_KERNEL);
//保存CODEC的数据,这块数据在CODEC的probe中会被取出来
i2c_set_clientdata(i2c_client, cs42l73);
// Reg map, 在LINUX3.1之后被引入的对I2C和SPI接口读写的接口 ,详细分析见下节:
cs42l73->regmap = regmap_init_i2c(i2c_client,&cs42l73_regmap);
if(IS_ERR(cs42l73->regmap)) {
ret = PTR_ERR(cs42l73->regmap);
dev_err(&i2c_client->dev,"regmap_init() failed: %d\n", ret);
goto err;
}
// 读取CODEC的相关ID,确认是正确的CODEC.
ret =regmap_read(cs42l73->regmap, CS42L73_DEVID_AB,?);
devid =(reg & 0xFF) <<12;
// 把这个 register map设置为cache only mode,在这种模式下,写操作将仅更新CACHE值,不会真正设置到硬件中。主要是为了省电
regcache_cache_only(cs42l73->regmap, true);
// 注册对应的CODEC,
ret= snd_soc_register_codec(&i2c_client->dev,
&soc_codec_dev_cs42l73, cs42l73_dai,
ARRAY_SIZE(cs42l73_dai));
if (ret< 0)
goto err_regmap;
return0;
err_regmap:
regmap_exit(cs42l73->regmap);
err:
returnret;
}
struct regmap_config {
int reg_bits;// 定义寄存器地址的位数 必须
int pad_bits;//定义在寄存器和值之间的添加位,
int val_bits;//定义寄存器值的位数,必须
bool (*writeable_reg)(struct device *dev, unsigned intreg);//判断一个寄存器是否可读/可写/是否不能被CACHED/寄存器时候可在驱动之外被读
bool (*readable_reg)(struct device *dev, unsigned intreg);
bool (*volatile_reg)(struct device *dev, unsigned intreg);
bool (*precious_reg)(struct device *dev, unsigned intreg);
unsigned int max_register;//最大的寄存器地址索引,也就是定义的寄存器的最大值
const struct reg_default *reg_defaults;//定义寄存器的缺省值
unsigned int num_reg_defaults;//缺省值的数量
enum regcache_type cache_type;
const void *reg_defaults_raw;//power onreset的时候的缺省值???这和上边的缺省值的什么区别???
unsigned int num_reg_defaults_raw;
u8 read_flag_mask;//在读的时候,高字节的MASK
u8 write_flag_mask;//在写的时候,高字节的MASK
};
可以调用regmap_init_i2c 获得对应的regmap,之后可以regmap_read/regmap_write 读写对应的寄存器的值。
3. CODEC Driver
static struct snd_soc_codec_driver soc_codec_dev_cs42l73 ={
.probe= cs42l73_probe,
.remove= cs42l73_remove,
.set_bias_level = cs42l73_set_bias_level,
.dapm_widgets = cs42l73_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets),
.dapm_routes = cs42l73_audio_map,
.num_dapm_routes = ARRAY_SIZE(cs42l73_audio_map),
.controls = cs42l73_snd_controls,
.num_controls = ARRAY_SIZE(cs42l73_snd_controls),
};
在这个数据结构中,需要关注cs42l73_probe, widgets,controls. routes.
3.1 cs42l73_probe
// 这个函数怎么调用起来的???
static int cs42l73_probe(struct snd_soc_codec *codec)
{
intret;
//获取在I2C 驱动中保存的数据
structcs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
//把regmap保存在snd_soc_codec中
codec->control_data =cs42l73->regmap;
// 给codec设置标准的I/Ofunctions,比如读写
ret =snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
// 缺省设置codec的偏置电压为OFF
cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF);
codec->dapm.idle_bias_off = 1;
regcache_cache_only(cs42l73->regmap, true);
// 缺省设置codec的偏置电压为SND_SOC_BIAS_STANDBY??为什么需要一个OFF 到SND_SOC_BIAS_STANDBY的过程
cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
// 设置 cs42l73 使用的是哪个MCLK作为主时钟
cs42l73->mclksel =CS42L73_CLKID_MCLK1;
cs42l73->mclk = 0;
returnret;
3.2 widgets, controls. routes
见另外一篇DAPM的专门总结。
4. DAI
在每个CODEC中,都会提供对应的PCM数据处理接口,snd_soc_dai_driver结构就是来描述这种接口的。
static struct snd_soc_dai_driver cs42l73_dai[] = {
{
//DAI名字,machinedriver会根据这个名字,把DAI和平台驱动绑定在一起。
.name = "cs42l73-xsp",
.id = CS42L73_XSP,
//每个DAI都应以它支持的PLAYBACK 和CAPTURE功能。
.playback = {
.stream_name = "XSP Playback",//BT之类接在这个DAI上
.channels_min = 1,
.channels_max = 2,
.rates = CS42L73_RATES,
.formats = CS42L73_FORMATS,
},
.capture = {
.stream_name = "XSP Capture",
.channels_min = 1,
.channels_max = 2,
.rates = CS42L73_RATES,
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
.symmetric_rates = 1,
},
{
.name = "cs42l73-asp",//音乐播放通过这个DAI
.id = CS42L73_ASP,
.playback = {
.stream_name = "ASP Playback",
.channels_min = 2,
.channels_max = 2,
.rates = CS42L73_RATES,
.formats = CS42L73_FORMATS,
},
.capture = {
.stream_name = "ASP Capture",
.channels_min = 2,
.channels_max = 2,
.rates = CS42L73_RATES,
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
.symmetric_rates = 1,
},
{
.name = "cs42l73-vsp",//语音通话在这个DAI
.id = CS42L73_VSP,
.playback = {
.stream_name = "VSP Playback",
.channels_min = 1,
.channels_max = 2,
.rates = CS42L73_RATES,
.formats = CS42L73_FORMATS,//支持的PCM格式
},
.capture = {
.stream_name = "VSP Capture",
.channels_min = 1,
.channels_max = 2,
.rates = CS42L73_RATES,
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
.symmetric_rates = 1,
}
};
cs42l73_ops:
//用于实现该dai的控制盒参数配置,会被machine driver调用。这些函数会调用snd_soc_** 去实现各种的功能。
static const struct snd_soc_dai_ops cs42l73_ops = {
.startup = cs42l73_pcm_startup,
.hw_params = cs42l73_pcm_hw_params,
.set_fmt = cs42l73_set_dai_fmt,
.set_sysclk = cs42l73_set_sysclk,
.set_tristate = cs42l73_set_tristate,
};
// 这些函数需要根据硬件手册来编写 ,这需要去研究SOCCORE的接口。