Sabresd板子上WM8962的耳机和功放的关系

本文档主要讨论i.mx6_sabresd板子WM8962 machine driver中耳机(HP)和喇叭(Ext Spk)之间的关系。
主要包括三个部分:
1
Linux 3.10.17版本内核中HPExt Spk的关系。
2
Linux 3.0.35版本内核中HPExt Spk的关系,以及改进。
3
:如何增加kcontrol接口,通过amixer来控制HPExt Spk
上面三种方法基本上包含了目前ASoC中的主流做法。
本文档配套两个patch
0001-Add-kcontrol-API-for-Headphone-Jack-and-Spk-from-thi.patch
0001-New-hp-Jack-driver.patch
文档里的做法,在这两个patch中都有体现。

1:Kernel 3.10.17中HP和Ext SPK的关系:
286 static int imx_wm8962_gpio_init(struct snd_soc_card *card) {
...
294         if (gpio_is_valid(priv->hp_gpio)) {
295                 imx_hp_jack_gpio.gpio = priv->hp_gpio;
296                 imx_hp_jack_gpio.jack_status_check = hpjack_status_check;// 相当于耳机插拔的中断回调函数。
297        
298                 snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, &imx_hp_jack);
299                 snd_soc_jack_add_pins(&imx_hp_jack,
300                                 ARRAY_SIZE(imx_hp_jack_pins), imx_hp_jack_pins);
301                 snd_soc_jack_add_gpios(&imx_hp_jack, 1, &imx_hp_jack_gpio);//耳机插拔的中断操作,实际上是在这个函数里进行的。
302         }
...
}

87 static int hpjack_status_check(void) {
...
97         hp_status = gpio_get_value(priv->hp_gpio) ? 1 : 0;
99         buf = kmalloc(32, GFP_ATOMIC); //buf 是给android用的
105         if (hp_status != priv->hp_active_low) { //耳机插入
106                 snprintf(buf, 32, "STATE=%d", 2);
107                 snd_soc_dapm_disable_pin(&priv->codec->dapm, "Ext Spk");
/*************************************************************************************
Ext  Spk被定义在imx6qdl-sabresd.dtsi中:
134                 audio-routing =
135                         "Headphone Jack", "HPOUTL",
136                         "Headphone Jack", "HPOUTR",
137                         "Ext Spk", "SPKOUTL",
138                         "Ext Spk", "SPKOUTR",
139                         "MICBIAS", "AMIC",
140                         "IN3R", "MICBIAS",
141                         "DMIC", "MICBIAS",
142                         "DMICDAT", "DMIC";
 snd_soc_dapm_disable_pin(&priv->codec->dapm, "Ext Spk")
的意思是将wm8962代码中:
"SPKOUTL",
"SPKOUTR",
通路的dapm widget disable 掉。
按道理讲此时应该还要将headphone通路的widget全部打开:
"HPOUTL",
"HPOUTR",
实际上,headphone相关通路的打开和关闭,是在回调函数的另一个地方进行的,下面会讲到。
*************************************************************************************/
108                 ret = imx_hp_jack_gpio.report; //ret 返回,用于打开、关闭headphone对应的dapm widget
109                 snd_kctl_jack_report(priv->snd_card, priv->headphone_kctl, 1);
110         } else {//耳机拔出
111                 snprintf(buf, 32, "STATE=%d", 0);
112                 snd_soc_dapm_enable_pin(&priv->codec->dapm, "Ext Spk");//打开spk对应的dapm widget
113                 ret = 0; //ret 返回,用于打开、关闭headphone对应的dapm widget
114                 snd_kctl_jack_report(priv->snd_card, priv->headphone_kctl, 0);
115         }

117         envp[0] = "NAME=headphone"; //往下是给android用的
118         envp[1] = buf;
119         envp[2] = NULL;
120         kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);

return ret; //ret 返回,用于打开、关闭headphone对应的dapm widget
}
 snd_soc_jack_add_gpios//这个函数里主要用来申请中断相关的东西
       ---》INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
              ---》gpio_work
                     ---》snd_soc_jack_gpio_detect(gpio);
       ---》snd_soc_jack_gpio_detect
              ---》report = gpio->jack_status_check();//此处的report就是上面返回的ret
              ---》snd_soc_jack_report(jack, report, gpio->report);
snd_soc_jack_report: {
86                 enable = pin->mask & jack->status;
 87
 88                 if (pin->invert)
 89                         enable = !enable;
 90        
 91                 if (enable)
 92                         snd_soc_dapm_enable_pin(dapm, pin->pin);//打开headphone对应的dapm router
 93                 else
 94                         snd_soc_dapm_disable_pin(dapm, pin->pin);//关闭headphone对应的dapm router
...
}

我们8962的machine driver里面,还有一些比较奇怪的东西:
实际上是为了对付开机之后内核里hp的切换已经发生了,上层文件系统收不到底层hp的状态,于是在内核将状态保存在/sys/bus/platform/drivers/imx-wm8962/headphone中,上层可以cat这个headphone来得到底层hp的状态:
插入耳机时
root@freescale /sys/bus/platform/drivers/imx-wm8962$ cat headphone
in show headphone, priv->hp_status = 0
headphone

拔出耳机时
root@freescale /sys/bus/platform/drivers/imx-wm8962$ cat headphone
in show headphone, priv->hp_status = 1
speaker

534         if (gpio_is_valid(priv->hp_gpio)) {
535                 ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
536                 if (ret) {
537                         dev_err(&pdev->dev, "create hp attr failed (%d)\n", ret);
538                         goto fail_hp;
539                 }
540         }

324 static ssize_t show_headphone(struct device_driver *dev, char *buf)
325 {
326         struct imx_priv *priv = &card_priv;
327         int hp_status;
328
329         if (!gpio_is_valid(priv->hp_gpio)) {
330                 strcpy(buf, "no detect gpio connected\n");
331                 return strlen(buf);
332         }
333
334         /* Check if headphone is plugged in */
335         hp_status = gpio_get_value(priv->hp_gpio) ? 1 : 0;
336
337         if (hp_status != priv->hp_active_low)
338                 strcpy(buf, "headphone\n");
339         else
340                 strcpy(buf, "speaker\n");
341
342         return strlen(buf);
343 }
344

345 static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);

 

2: Kernel 3.0.35代码中HP和Ext Spk的关系
FSL基础的3.0.35代码中,HP Router是一直连通的,只不过是拔掉HP时,从HP出来的声音听不到。代码中故意将HP pin.pin写成"Ext Spk"
 70 static struct snd_soc_jack_pin imx_hp_jack_pins[] = {
 71         {
 72                 .pin = "Ext Spk",
 74                 .mask = SND_JACK_HEADPHONE,
 75         },
 76 };

731                 snd_soc_jack_new(codec, "Ext Spk", SND_JACK_LINEOUT,
732                                &imx_hp_jack);
HP
插拔时,本来要处理名字为"Headphone Jack"的事件,这里改成了"Ext Spk"在耳机插入的时候关掉Ext SpkRouter,耳机拔出时使能Ext SpkRouter
这样做导致:
1
:代码理解起来有些混淆。
2
HP Router一直开着,不利于系统低功耗。

所以,可以用3.10.17内核的做法,将上述代码做以上改动:
 70 static struct snd_soc_jack_pin imx_hp_jack_pins[] = {
 71         {
 72 //              .pin = "Ext Spk"
 73                 .pin = "Headphone Jack",
 74                 .mask = SND_JACK_HEADPHONE,
 75         },
 76 };

729                 imx_hp_jack_gpio.jack_status_check = hpjack_status_check;

731 //              snd_soc_jack_new(codec, "Ext Spk", SND_JACK_LINEOUT,
732 //                              &imx_hp_jack);
733                 snd_soc_jack_new(codec, "Headphone Jack",    
                                             SND_JACK_HEADPHONE,
734                                               &imx_hp_jack);

hpjack_status_check
函数的写法和3.10.17代码的写法一样。但是,我刚开始调试的时候发现,下面两个ret的值要调换,功能才能正常。也就是说插着耳机时,report 0, 拔掉耳机report 1功能才正常。原因是imx_hp_jack_pins.pin我忘了将其由"Ext Spk"改成Headphone Jack了。
613         if (hp_status != plat->hp_active_low)
614                 snprintf(buf, 32, "STATE=%d", 2)

615                 snd_soc_dapm_disable_pin(&priv->codec->dapm, "Ext Spk")

616                 ret = imx_hp_jack_gpio.report;
617         } else {
618                 snprintf(buf, 32, "STATE=%d", 0);
619                 snd_soc_dapm_enable_pin(&priv->codec->dapm, "Ext Spk");
620                 ret = 0;
621         }

注意:
后来我尝试将hpjack_status_check里的操作,移到w->event中,也就是下面的imx_event_hp中,但是效果不怎么稳定,因为snd_soc_dapm_disable/enable_pin之后要加snd_soc_dapm_sync操作,而w->event中不能加入snd_soc_dapm_sync
因为w->event是由snd_soc_dapm_sync调用的,系统进入了类似于死锁的状态:
snd_soc_dapm_sync
         --> dapm_power_widgets
                            --> dapm_seq_run
                                               --> dapm_seq_run_coalesced
                                                                 --> dapm_seq_check_event
                                                                           --> imx_event_hp ( w->event )

516 static const struct snd_soc_dapm_widget imx_dapm_widgets[] = {
517         SND_SOC_DAPM_HP("Headphone Jack", imx_event_hp),
518         SND_SOC_DAPM_SPK("Ext Spk", NULL),
519         SND_SOC_DAPM_MIC("AMIC", NULL),
520         SND_SOC_DAPM_MIC("DMIC", imx_event_mic),
521 };
所以snd_soc_dapm_disable/enable_pin的操作,是不能放到w->event中。

 

3:通过amixer来控制HP和Ext Spk
马维尔和intel,以及我们sgtl5000audio machine driver中,加入了通过amixer来控制HPExt Spk的接口。
WM8962
也可以使用这样的做法:
1
:增加相关的control接口:
712         ret = snd_soc_add_controls(codec, wm8962_machine_controls,
713                         ARRAY_SIZE(wm8962_machine_controls));
714         if (ret)
715                 return ret;

2
:增加kcontrol.get.put操作。
694 static const struct snd_kcontrol_new wm8962_machine_controls[] = {
695         SOC_ENUM_EXT("HP Function", wm8962_enum[0], wm8962_get_jack,
696                      wm8962_set_jack),
697         SOC_ENUM_EXT("SPK Function", wm8962_enum[1], wm8962_get_spk,
698                      wm8962_set_spk),
699 };

167 #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
168 {       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
169         .info = snd_soc_info_enum_ext, \
170         .get = xhandler_get, .put = xhandler_put, \
171         .private_value = (unsigned long)&xenum }
上面.get.put的操作,对应的是amixer cgetamixer cset。需要注意的是,在.put会判断需要写入的值如果和前一次的值一样时,会放弃这次操作,不一样时,会继续写入操作。.put里做完snd_soc_dapm_disable/enable_pin之后,要加上snd_soc_dapm_sync操作。

两个名字为"HP Function""SPK Function"mixer kcontrol,可以设置其为off/on(0/1)状态。
635 static const char *jack_function[] = { "off", "on"};
637 static const char *spk_function[] = { "off", "on" };
639 static const struct soc_enum wm8962_enum[] = {
640         SOC_ENUM_SINGLE_EXT(2, jack_function),
641         SOC_ENUM_SINGLE_EXT(2, spk_function),
642 };

做完上述的操作后,可以通过下面的方法来使用:
1
:获取刚刚加入的两个kcontrolnumid
    amixer controls:
    numid=62,iface=MIXER,name='HP Function'
    numid=63,iface=MIXER,name='SPK Function'
2
:根据刚刚获得的numid来操作HPSPK
    How to enable HeadPhone using this Kcontrol:
    amixer cget numid=62
    numid=62,iface=MIXER,name='HP Function'
      ; type=ENUMERATED,access=rw------,values=1,items=2
      ; Item #0 'off'
      ; Item #1 'on'
      : values=0
  
    amixer cset numid=62 1 //enable HP
    numid=62,iface=MIXER,name='HP Function'
      ; type=ENUMERATED,access=rw------,values=1,items=2
      ; Item #0 'off'
      ; Item #1 'on'
      : values=1
   
    How to enable Speaker using this Kcontrol:
    amixer cget numid=63
    numid=63,iface=MIXER,name='SPK Function'
      ; type=ENUMERATED,access=rw------,values=1,items=2
      ; Item #0 'off'
      ; Item #1 'on'
      : values=0
   
    amixer cset numid=63 1 //SPK enable
    numid=63,iface=MIXER,name='SPK Function'
      ; type=ENUMERATED,access=rw------,values=1,items=2
      ; Item #0 'off'
      ; Item #1 'on'
      : values=1

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 PCB(Printed Circuit Board,印刷电路板)中,控制线和信号线是两种不同的线路。 控制线通常用于控制电路中的开关、传感器和其他组件,以及控制电源和地线的分配。控制线的作用是控制电路中的各个部分,使其按照预期的方式运作。 信号线用于传输数据或信号,例如从传感器或其他设备到微控制器或其他电子设备。信号线的作用是传输数据和信号,确保电路中各个组件之间的正确通信。 在 PCB 上,通常使用不同的线宽、距离和轨迹布局来区分控制线和信号线。控制线通常需要更宽的线宽和更紧密的轨迹布局,以便更好地承受高电流和高频率的噪声。信号线则通常需要更细的线宽和更大的距离,以避免干扰和交叉耦合。此外,信号线也可能需要使用阻抗匹配和终端电阻等技术来确保信号质量和稳定性。 ### 回答2: PCB板上的控制线和信号线是两种不同的导线,它们在电子设备中发挥不同的作用。 控制线是用于传输信号控制的导线,它主要用于连接控制器和被控制设备之间,传输各种控制信号。这些信号可以是电平信号、脉冲信号、时钟信号等,用于控制相关设备的工作状态、工作时间、工作方式等。控制线的作用是实现设备之间的协同工作,通过控制信号的传递实现设备的启动、停止、调节等功能。控制线通常需要较高的稳定性和抗干扰能力,以确保控制信号的准确传输和可靠的工作。 信号线是用于传输信号的导线,它主要用于传输信息和数据。信号线可以是模拟信号线,用于传输模拟信号,如音频、视频信号等;也可以是数字信号线,用于传输数字信号,如数据信号、时序信号等。信号线的作用是保证信号的传输质量和准确性,以确保接收端能够正确解读和处理信号。信号线通常需要考虑信号的带宽、传输速率、抗干扰能力等因素,以满足不同信号的传输要求。 总的来说,控制线主要用于传输控制信号,而信号线主要用于传输信息和数据。它们在PCB板的设计和布线中有不同的要求和考虑因素,但都是非常重要的组成部分,对电子设备的正常运行起着至关重要的作用。 ### 回答3: PCB板是印刷电路板的英文缩写,是电子产品的核心部件之一。在PCB板上,控制线和信号线起着不同的作用,具有以下区别: 1. 功能不同:控制线主要用于传输控制信号,控制电路的开关与状态。信号线主要用于传输各种信号,如数据、声音、图像等。 2. 传输方式不同:控制线通常是单向传输的,只能从控制单元发送控制信号给被控制单元,用于控制硬件的工作。而信号线则是双向传输的,可以进行双向数据传输和通信。 3. 电压和电流要求不同:控制线一般只需要传输较低的电压和电流,因为控制信号只需要对目标设备进行控制开关即可。而信号线可能需要传输更高的电压和电流,以保证传输的信号质量与稳定性。 4. 布局位置不同:控制线通常布局在电路板上的边缘区域,以便于连接控制单元和被控制单元。信号线则会根据不同的传输需求,在电路板上的其他位置进行布局。 需要注意的是,控制线和信号线的区别是相对的,具体的应用中会根据不同的设计需求和电路特性而有所变化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值