打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Audio驱动之codecdriver
一  CODEC 驱动简介
在LINUX ASOC架构中,CODEC驱动是与平台无关的。它主要主要功能包括:
  1. 定义CODEC 侧的AUDIO 接口
  2. 定义CODEC里的CONTROLS
  3. 定义CODEC DAPM
  4. 定义CODEC IO
二 CODEC的代码实现
在实现中,CODEC 驱动包括两部分(以cs42l73为例)
  1. I2C驱动: 因为cs42l73使用I2C进行内部寄存器的存取,它首先注册了一个I2C驱动,在初始化的时候,去注册这个驱动。
//I2C的地址在哪里定义的
static struct i2c_driver cs42l73_i2c_driver ={
    .driver ={
          .name = "cs42l73",
          .owner = THIS_MODULE,
          },
    .id_table =cs42l73_id,
    .probe =cs42l73_i2c_probe,
    .remove =__devexit_p(cs42l73_i2c_remove),
 
};
通过name 和id_table (cs42l73)查询,可以知道在系统中board.c中去定义这个设备。{"cs42l73", SFI_DEV_TYPE_I2C,1, &no_platform_data, NULL},
这样在初始化的时候,在i2c_add_driver的时候就会调用cs42l73_i2c_probe
static int __init cs42l73_modinit(void)
{
    intret;
    ret =i2c_add_driver(&cs42l73_i2c_driver);//去添加这个cs42l73_i2c_driver ,在匹配ID和NAME之后,去调用对应的PROBE函数。
    if (ret!= 0) {
       pr_err("Failed to register CS42L73 I2C driver: %d\n", ret);
       return ret;
    }
    return0;
}
 
在退出函数中去删除这个i2c_driver 。
2.  I2C驱动中,比较重要的两个函数:probe/remove 分别去注册/去注册这个CODEC。下边主要分析probe函数:
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;
}
Register MAP
在CODEC 驱动中会定义一个regmap_config 的变量,定义CODEC寄存器的信息和接口
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的接口。
 
 
 
 
 
 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Linux ALSA声卡驱动之七:ASoC架构中的Codec
浅析linux 2.6.30.4内核中uda134x声卡驱动源码 - audio和bluetooth - gliethttp
转载:ALSA Soc音频驱动分析
Android4.4 耳机检测分析
Android 音频系统
[转]alsa声卡/dev/snd/pcmC0D0p的open打开流程
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服