kernel\sound\soc\s3c24xx\Valar_wm89xx.c

来源:互联网 发布:电子风水罗盘软件 编辑:程序博客网 时间:2024/06/05 08:00

android audio driver

 

kernel\sound\soc\s3c24xx\Valar_wm89xx.c

 

module_init(smdk64xx_audio_init);

static int __init smdk64xx_audio_init(void)

{

     smdk64xx_snd_device = platform_device_alloc("soc-audio", 0); //模块初始化时,注册了一个名为soc-audio的Platform设备
     platform_set_drvdata(smdk64xx_snd_device, &smdk64xx_rt5631_snd_devdata);//把smdk64xx_rt5631_snd_devdata设到platform_device结构的dev.drvdata字段中
     smdk64xx_rt5631_snd_devdata.dev = &smdk64xx_snd_device->dev;//这里引出了第一个数据结构snd_soc_card的实例smdk64xx_rt5631
     ret = platform_device_add(smdk64xx_snd_device);//把这个platform_device加入到内核中去
     init_hw_params(smdk64xx_snd_device);    //设置codec相关的clock,reset IIS

}

 

其中static struct platform_device *smdk64xx_snd_device;

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;
};

 

static struct snd_soc_device smdk64xx_rt5631_snd_devdata = {
 .card = &smdk64xx_rt5631,   

 .codec_dev = &soc_codec_dev_rt5631,
};

 

//这里引出了第一个数据结构snd_soc_card的实例smdk64xx_rt5631
它的定义如下:

static struct snd_soc_card smdk64xx_rt5631 = {
 .name = "smdk",
 .platform = &s3c_dma_wrapper,
 .dai_link = smdk64xx_rt5631_dai, //这里引出了数据结构snd_soc_dai_link的实例smdk64xx_rt5631_dai,它的定义如下:

 .num_links = ARRAY_SIZE(smdk64xx_rt5631_dai),
 
 .suspend_pre = smdk64xx_suspend_pre,
 .resume_post  = smdk64xx_resume_post,
 .suspend_post = smdk64xx_wm8961_suspend_post,
 .resume_pre = smdk64xx_wm8961_resume_pre,
};

 

static struct snd_soc_dai_link smdk64xx_rt5631_dai[] = {
{ /* Primary Playback i/f */
 .name = "RT5631 PAIF RX",
 .stream_name = "Playback",
 .cpu_dai = &s3c64xx_i2s_v4_dai[S3C24XX_DAI_I2S],
 .codec_dai = &rt5631_dai[0],
 .init = smdkv2xx_rt5631_init,
 .ops = &smdk64xx_ops,
},
{ /* Primary Capture i/f */
 .name = "RT5631 PAIF TX",
 .stream_name = "Capture",
 .cpu_dai = &s3c64xx_i2s_v4_dai[S3C24XX_DAI_I2S],
 .codec_dai = &rt5631_dai[0],
 .init = smdkv2xx_rt5631_tx_init,
 .ops = &smdk64xx_ops,
},
};

 

通过snd_soc_card结构,又引出了Machine驱动的另外两个数据结构:

  • snd_soc_dai_link(实例:smdk64xx_rt5631_dai) 

  • snd_soc_ops(实例:smdk64xx_ops)

其中,snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择合适Platform和Codec以及dai,用他们填充以上几个数据结构,然后注册Platform设备即可。当然还要实现连接Platform和Codec的dai_link对应的ops实现,本例就是smdk64xx_ops,它只实现了hw_params函数:smdk_hw_params。

 

/* SoC Device - the audio subsystem */
struct snd_soc_device{
 struct device *dev;
 struct snd_soc_card *card; //.card = &smdk64xx_rt5631
 struct snd_soc_codec_device *codec_dev; //.codec_dev = &soc_codec_dev_rt5631,

 void *codec_data;
};

 

/* SoC card */
struct snd_soc_card {
 char *name; //.name = "smdk",
 struct device *dev;

 struct list_head list;

 int instantiated;

 int (*probe)(struct platform_device *pdev);
 int (*remove)(struct platform_device *pdev);

 /* the pre and post PM functions are used to do any PM work before and
  * after the codec and DAI's do any PM work. */
 int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);   //.suspend_pre = smdk64xx_suspend_pre,
 int (*suspend_post)(struct platform_device *pdev, pm_message_t state);   //.suspend_post = smdk64xx_wm8961_suspend_post,
 int (*resume_pre)(struct platform_device *pdev);   //.resume_pre = smdk64xx_wm8961_resume_pre,
 int (*resume_post)(struct platform_device *pdev);     //.resume_post  = smdk64xx_resume_post,

 /* callbacks */
 int (*set_bias_level)(struct snd_soc_card *,
         enum snd_soc_bias_level level);

 long pmdown_time;

 /* CPU <--> Codec DAI links  */
 struct snd_soc_dai_link *dai_link;  //.dai_link = smdk64xx_rt5631_dai,
 int num_links;

 struct snd_soc_device *socdev;

 struct snd_soc_codec *codec;

 struct snd_soc_platform *platform;   //.platform = &s3c_dma_wrapper,
 struct delayed_work delayed_work;
 struct work_struct deferred_resume_work;
};

 

/* codec device */
struct snd_soc_codec_device {
 int (*probe)(struct platform_device *pdev);     //.probe = rt5631_probe,
 int (*remove)(struct platform_device *pdev);    //.remove = rt5631_remove,
 int (*suspend)(struct platform_device *pdev, pm_message_t state);    //.suspend = rt5631_suspend,
 int (*resume)(struct platform_device *pdev);    //.resume = rt5631_resume,
};

 

 ret = platform_device_add(smdk64xx_snd_device);//具体实现在(kernel\drviers\base\platform.c)
-> device_add(&pdev->dev); //(kernel\drviers\base\core.c)

-> bus_probe_device(dev);  //(kernel\drviers\base\bus.c)

-> device_attach(dev);  //(kernel\drviers\base\db.c)

-> bus_for_each_drv(dev->bus, NULL, dev, __device_attach); ( )

->__device_attach ->driver_probe_device(drv, dev);

-> really_probe(dev, drv);

-> drv->probe(dev); <=> rt5631_probe  ???? 因为struct snd_soc_codec_device { int (*probe)(struct platform_device *pdev); //.probe = rt5631_probe
-> static int rt5631_probe(struct platform_device *pdev) (kernel\sound\soc\codecs\rt5631.c)

{

1. -> snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); //register PCMs  (kernel\sound\soc\soc-core.c)

    -> soc_new_pcm(socdev, &card->dai_link[i], i); 

        -> 1. snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,capture, &pcm); 

        -> 2. platform->pcm_new(codec->card, codec_dai, pcm);

 

因为struct snd_soc_platform idma_soc_platform = {
 .name = "s5p-lp-audio",
 .pcm_ops = &s3c_idma_ops,
 .pcm_new = s3c_idma_pcm_new,
 .pcm_free = s3c_idma_pcm_free,
};

 

    所以 platform->pcm_new <=> s3c_idma_pcm_new

            -> s3c_idma_preallocate_buffer(pcm,SNDRV_PCM_STREAM_PLAYBACK); -> ioremap(buf->addr, buf->bytes)

  2. -> snd_soc_add_controls(socdev->card->codec, rt5631_snd_controls,ARRAY_SIZE(rt5631_snd_controls));
  3. -> rt5631_add_widgets(socdev->card->codec);

}

static const struct snd_kcontrol_new rt5631_snd_controls[] = {
SOC_ENUM("MIC1 Mode Control",  rt5631_enum[3]),   
SOC_ENUM("MIC1 Boost", rt5631_enum[6]),

SOC_ENUM("MIC2 Mode Control", rt5631_enum[4]),
SOC_ENUM("MIC2 Boost", rt5631_enum[7]),
SOC_ENUM("MONOIN Mode Control", rt5631_enum[5]),

SOC_DOUBLE("PCM Playback Volume", RT5631_STEREO_DAC_VOL_2, 8, 0, 255, 1),
SOC_DOUBLE("PCM Playback Switch", RT5631_STEREO_DAC_VOL_1,15, 7, 1, 1),

SOC_DOUBLE("MONOIN_RX Capture Volume", RT5631_MONO_INPUT_VOL, 8, 0, 31, 1),

SOC_DOUBLE("AXI Capture Volume", RT5631_AUX_IN_VOL, 8, 0, 31, 1),

SOC_SINGLE("AXO1 Playback Switch", RT5631_MONO_AXO_1_2_VOL, 15, 1, 1),
SOC_SINGLE("AXO2 Playback Switch", RT5631_MONO_AXO_1_2_VOL, 7, 1, 1),
SOC_DOUBLE("OUTVOL Playback Volume", RT5631_MONO_AXO_1_2_VOL, 8, 0, 31, 1),

SOC_DOUBLE("Speaker Playback Switch", RT5631_SPK_OUT_VOL,15, 7, 1, 1),
SOC_DOUBLE("Speaker Playback Volume", RT5631_SPK_OUT_VOL, 8, 0, 63, 1),

SOC_SINGLE("MONO Playback Switch", RT5631_MONO_AXO_1_2_VOL, 13, 1, 1),

SOC_DOUBLE("HP Playback Switch", RT5631_HP_OUT_VOL,15, 7, 1, 1),
SOC_DOUBLE("HP Playback Volume", RT5631_HP_OUT_VOL, 8, 0, 63, 1),

SOC_SINGLE_EXT("DMIC Capture Switch", VIRTUAL_REG_FOR_MISC_FUNC, 2, 1, 0, 
 rt5631_dmic_get, rt5631_dmic_put),
 
SOC_ENUM_EXT("EQ Mode", rt5631_enum[10], snd_soc_get_enum_double, rt5631_eq_sel_put),  
};

 

static const struct snd_soc_dapm_widget rt5631_dapm_widgets_pbk[] = {
 SND_SOC_DAPM_HP("HP-L/R", NULL),
 SND_SOC_DAPM_SPK("SPK-OUT",NULL),
 SND_SOC_DAPM_MIC("MicIn", NULL),
 SND_SOC_DAPM_MIC("HpMic", NULL),
};

 

static const struct snd_soc_dapm_route rt5631_audio_map[] = {
//RX(play) path
 {"HP-L/R", NULL, "HPOL"},
 {"HP-L/R", NULL, "HPOR"},
 
 {"SPK-OUT", NULL, "SPOL"},
 {"SPK-OUT", NULL, "SPOR"},

//TX(record) path
 {"MIC1", NULL, "MicIn"},
 {"MIC2", NULL, "HpMic"},
};

 

static int smdkv2xx_rt5631_init(struct snd_soc_codec *codec)
{

     enable_mclk(true);    
     enable_audio_i2c(1);

 

     snd_soc_dapm_new_controls(codec, rt5631_dapm_widgets_pbk, ARRAY_SIZE(rt5631_dapm_widgets_pbk));   //create new dapm controls for every widget
     snd_soc_dapm_add_routes(codec, rt5631_audio_map, ARRAY_SIZE(rt5631_audio_map));  //Add routes between DAPM widgets

     snd_soc_dapm_disable_pin(codec, "SPK-OUT");//将snd_soc_dapm_widget.connected置为0;
     snd_soc_dapm_disable_pin(codec, "HP-L/R"); //snd_soc_dapm_widget.connected = 0;

     snd_soc_dapm_enable_pin(codec, "MicIn"); //snd_soc_dapm_widget.connected = 1;
     snd_soc_dapm_disable_pin(codec, "HpMic"); //snd_soc_dapm_widget.connected = 0;
 
     snd_soc_dapm_sync(codec); //scan and power dapm paths
 
     //add valar audio controls
     snd_soc_add_controls(codec, valar_audio_controls_rt5631, ARRAY_SIZE(valar_audio_controls_rt5631));

}

 

 snd_soc_dapm_sync(codec); 

 -> dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);

 -> snd_soc_dapm_set_bias_level(socdev,SND_SOC_BIAS_ON);

 -> card->set_bias_level(card, level); <=> static int rt5631_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) //因为codec->set_bias_level = rt5631_set_bias_level;

//设置codec power reg 0x3A,0x3B,0x3C,0x3D;

 

static const struct snd_kcontrol_new valar_audio_controls_rt5631[] = {
 SOC_SINGLE_BOOL_EXT("Headphone Out Switch",
   (unsigned long)&valar_hp_switch,
   valar_get_output, valar_set_output),
 SOC_SINGLE_BOOL_EXT("Speaker Out Switch",
   (unsigned long)&valar_spk_switch,
   valar_get_output, valar_set_output),
 SOC_SINGLE_BOOL_EXT("HDMI Audio Switch",
   (unsigned long)&valar_hdmi_switch,
   valar_get_hdmi, valar_set_hdmi),
   
 SOC_ENUM_EXT("Jack Function", jack_func_enum, valar_get_jack, valar_set_jack),  
};

 

#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 .info = snd_soc_info_bool_ext, \
 .get = xhandler_get, .put = xhandler_put, \
 .private_value = xdata }

原创粉丝点击