ALSA 驱动配置外部HP MIC SPK

作者: 郑俊飞 | 来源:发表于2018-02-01 14:11 被阅读42次

    程序总体结构

    以sound/soc/pxa/corgi.c为例来分析

    • 为了描述声卡,定义snd_soc_card实例,填充dai_link成员。
    • 为了能在ALSA上层控制SPK,HP,MIC的开启关闭,定义kcontrol实例。
    • 为了能让ALSA自动控制SPK,HP,MIC的功耗,定义widget,route实例。
    • 在snd_soc_card--dai_link--init里,注册上面声明的kcontrol,widget,route实例。

    下面以SPK的kcontrol,widget,route为例,分析三种数据结构的定义

    kcontrol的实现

    定义kcontrol数组,提供向上的参数和向下的实际控制函数 。

    • 向上的参数,用于上层amixer程序设置控件为不同的功能。
    • 向下的实际控制函数,用于底层驱动程序控制HP MIC SPK的开关。
    static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", "Off"};
    static const char *spk_function[] = {"On", "Off"};
    static const struct soc_enum corgi_enum[] = {
        SOC_ENUM_SINGLE_EXT(5, jack_function),//控件参数1-4
        SOC_ENUM_SINGLE_EXT(2, spk_function),//控件参数0-1
    };
    static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
        SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
            corgi_set_jack),//get,set分别读取和设置硬件工作模式
        SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
            corgi_set_spk),
    };
    

    get,set函数的实现

    static int corgi_get_spk(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
    {
        ucontrol->value.integer.value[0] = corgi_spk_func;  //全局变量为0-1表示ON OFF
        return 0;
    }
    
    static int corgi_set_spk(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
    {
        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
    
        if (corgi_spk_func == ucontrol->value.integer.value[0])
            return 0;
    
        corgi_spk_func = ucontrol->value.integer.value[0];//保存上层设置的参数
        corgi_ext_control(codec);//设置SPK为ON OFF模式
        return 1;
    }
    //corgi_ext_control函数的实现
    static void corgi_ext_control(struct snd_soc_codec *codec)
    {
        if (corgi_spk_func == CORGI_SPK_ON)
            snd_soc_dapm_enable_pin(dapm, "Ext Spk");  
            //通过enable widget,开启电源,widget的实现在后面描述
        else
            snd_soc_dapm_disable_pin(dapm, "Ext Spk");//通过disable widget,关闭电源
    
        /* signal a DAPM event */
        snd_soc_dapm_sync(dapm);
    }
    

    snd_soc_card->dai_link->init里,通过snd_soc_add_controls注册control。

    widget的实现

    //定义widget
    /* corgi machine dapm widgets */
    static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
    SND_SOC_DAPM_HP("Headphone Jack", NULL),
    SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
    SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),//定义widget名字,设置电源控制接口
    };
    //电源控制接口的实现
    static int corgi_amp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *k, int event)
    {
        //通过GPIO开关SPK的前置放大器
        gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
        return 0;
    }
    

    snd_soc_card->dai_link->init里,通过snd_soc_dapm_new_controls注册widget。

    route的实现

    //定义route
    /* Corgi machine audio map (connections to the codec pins) */
    static const struct snd_soc_dapm_route audio_map[] = {
        /* speaker connected to LOUT, ROUT */
        {"Ext Spk", NULL, "ROUT"},//设置SPK直连codec的ROUT和LOUT
        {"Ext Spk", NULL, "LOUT"},
    };
    

    snd_soc_card->dai_link->init里,通过snd_soc_dapm_add_routes注册route。

    从datesheet上,可以看到ROUT和LOUT是用来外接SPK的。


    image.png

    从codec驱动里,可以看到ROUT,LOUT的widget定义。

    static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
    SND_SOC_DAPM_OUTPUT("LOUT"),
    SND_SOC_DAPM_OUTPUT("LHPOUT"),
    SND_SOC_DAPM_OUTPUT("ROUT"),
    };
    

    简书文章上没有添加附件选项,将machine驱动,codec驱动和手册添加到了链接:
    http://download.csdn.net/download/a903265446/10235150
    一般产品开发,codec驱动和platform驱动由芯片厂商提供,只需要开发machine驱动。

    本文由头条号“嵌入式FM106点1”发布,各种原创技术干货,欢迎关注。

    相关文章

      网友评论

        本文标题:ALSA 驱动配置外部HP MIC SPK

        本文链接:https://www.haomeiwen.com/subject/cmzrzxtx.html