板子:rk3568
平台:android12
1.耳机功能添加
1.1 设备树配置
rk_headset: rk-headset { compatible = "rockchip_headset"; headset_gpio = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; //hs_select = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; pinctrl-names = "default"; pinctrl-0 = <&hp_det &hs_select_pin>; status = "okay"; };
紧接着是rk809的配置:
rk809_sound: rk809-sound { status = "okay"; compatible = "simple-audio-card"; simple-audio-card,format = "i2s"; simple-audio-card,name = "rockchip,rk809-codec"; simple-audio-card,mclk-fs = <256>; simple-audio-card,widgets = "Microphone", "Mic Jack", "Headphone", "Headphone Jack"; simple-audio-card,routing = "Mic Jack", "MICBIAS1", "IN1R", "Mic Jack", "Headphone Jack", "HPOL", "Headphone Jack", "HPOR"; simple-audio-card,cpu { sound-dai = <&i2s1_8ch>; }; simple-audio-card,codec { sound-dai = <&rk809_codec>; }; }; ... /* 注意codec要放在rk809pmic节点下 */ rk809_codec: codec { #sound-dai-cells = <0>; compatible = "rockchip,rk809-codec", "rockchip,rk817-codec"; clocks = <&cru I2S1_MCLKOUT>; clock-names = "mclk"; assigned-clocks = <&cru I2S1_MCLKOUT>, <&cru I2S1_MCLK_TX_IOE>; assigned-clock-rates = <12288000>; assigned-clock-parents = <&cru I2S1_MCLKOUT_TX>, <&cru I2S1_MCLKOUT_TX>; pinctrl-names = "default"; pinctrl-0 = <&i2s1m0_mclk>; hp-volume = <20>; spk-volume = <3>; spk-ctl-gpios = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; mic-in-differential; status = "okay"; }; ... &i2s1_8ch { status = "okay"; rockchip,clk-trcm = <1>; pinctrl-names = "default"; pinctrl-0 = <&i2s1m0_sclktx &i2s1m0_lrcktx &i2s1m0_sdi0 &i2s1m0_sdo0>; };
由于我的板子还接了一个SPEAKER所以添加了一个SPEAKER控制引脚,spk-ctl-gpios属性在sound/soc/codecs/rk817_codec.c里面会去获取这个属性,并且会去自动控制并切换,相关源码如下:
rk817->spk_ctl_gpio = devm_gpiod_get_optional(dev, "spk-ctl", GPIOD_OUT_LOW); if (!IS_ERR_OR_NULL(rk817->spk_ctl_gpio)) { DBG("%s : spk-ctl-gpio %d\n", __func__, desc_to_gpio(rk817->spk_ctl_gpio)); }
相应的被调用函数:
static int rk817_codec_ctl_gpio(struct rk817_codec_priv *rk817, int gpio, int level) { if ((gpio & CODEC_SET_SPK) && rk817->spk_ctl_gpio) { gpiod_set_value(rk817->spk_ctl_gpio, level); DBG("%s set spk clt %d\n", __func__, level); msleep(rk817->spk_mute_delay); } if ((gpio & CODEC_SET_HP) && rk817->hp_ctl_gpio) { gpiod_set_value(rk817->hp_ctl_gpio, level); DBG("%s set hp clt %d\n", __func__, level); msleep(rk817->hp_mute_delay); } return 0; }
当然也可以加在head_set下面,我是采用的上面这种,下面是我的patch:
diff --git a/drivers/headset_observe/rk_headset.c b/drivers/headset_observe/rk_headset.c index 5b16db723317..14f69afe6022 100644 --- a/drivers/headset_observe/rk_headset.c +++ b/drivers/headset_observe/rk_headset.c @@ -159,6 +159,22 @@ static void headsetobserve_work(struct work_struct *work) printk("---headsetobserve_work---\n"); mutex_lock(&headset_info->mutex_lock[HEADSET]); level = read_gpio(pdata->headset_gpio); + if(pdata->hs_select_flg == 1) //judge support headphone speaker select + { + if(pdata->hs_select_pin) + { + if (!level) { //headset out + gpio_direction_output(pdata->hs_select_pin, pdata->hs_select_sta); + printk("%s-----------------------1\r\n",__func__); + } + else { //headset in + gpio_direction_output(pdata->hs_select_pin, !pdata->hs_select_sta); + printk("%s-----------------------2\r\n",__func__); + } + printk("%s: hs_select_pin->%d hs_select_sta->%d----------",__func__,pdata->hs_select_pin,pdata->hs_select_sta); + } + } + if (level < 0) goto out; msleep(100); diff --git a/drivers/headset_observe/rk_headset.h b/drivers/headset_observe/rk_headset.h index c10961ce66f8..9d911a082f9e 100644 --- a/drivers/headset_observe/rk_headset.h +++ b/drivers/headset_observe/rk_headset.h @@ -17,6 +17,12 @@ struct rk_headset_pdata { unsigned int hook_gpio; /* Hook key down status */ unsigned int hook_down_type; + + unsigned int hs_select_pin; //headphone/speaker select + unsigned int hs_select_sta; //headphone/speaker select state + unsigned int hs_select_flg; //headphone/speaker ping dts flag 1:suport headphone/speaker select 0:not support + + #ifdef CONFIG_MODEM_MIC_SWITCH /* mic about */ unsigned int mic_switch_gpio; diff --git a/drivers/headset_observe/rockchip_headset_core.c b/drivers/headset_observe/rockchip_headset_core.c index 3c2eeb7ac213..f283a22303bf 100644 --- a/drivers/headset_observe/rockchip_headset_core.c +++ b/drivers/headset_observe/rockchip_headset_core.c @@ -107,6 +107,29 @@ static int rockchip_headset_probe(struct platform_device *pdev) } } + //headphone/speaker select gpio + ret = of_get_named_gpio_flags(node, "hs_select", 0, &pdata->hs_select_sta); + if(ret < 0) + { + pdata->hs_select_flg = 0; + printk("%s:can not get hs_select gpio property---------- %d",__func__,pdata->hs_select_flg); + + } + else + { + pdata->hs_select_pin = ret; + ret = devm_gpio_request(&pdev->dev, pdata->hs_select_pin, + "hs_select"); + if(ret < 0) + { + printk("%s:can not request hs_select gpio ----------",__func__); + goto err; + } + printk("%s: hs_select_pin->%d hs_select_sta->%d----------",__func__,pdata->hs_select_pin,pdata->hs_select_sta); + + pdata->hs_select_flg = 1; + } + #ifdef CONFIG_MODEM_MIC_SWITCH /* mic */ ret = of_get_named_gpio_flags(node, "mic_switch_gpio", 0, &flags);
但是我按照上面的添加了发现耳机是有声音的,不插耳机SPEAKER无声音输出,并且我尝试在插入耳机的同时也将SPEAKER控制脚打开SPEAKER就能有声音,所以确定上层应该是没有问题的,所以将问题锁定在驱动上,参考瑞星微文档解析:
Documentation/devicetree/bindings/sound/rockchip,rk817-codec.txt - compatible: "rockchip,rk817-codec" - - clocks: a list of phandle + clock-specifer pairs, one for each entry inclock-names. - - clock-names: should be "mclk". - - spk-ctl-gpios: spk mute enable/disable 用于外置功放使能脚,如果产品上有使用外置功放,请配置对应的使能脚 - - hp-ctl-gpios: hp mute enable/disable - - spk-mute-delay-ms: spk mute delay time - - hp-mute-delay-ms: hp mute delay time - - spk-volume: DAC L/R volume digital setting for Speaker 配置喇叭输出音量,0 最大,255 最小 - - hp-volume: DAC L/R volume digital setting for Headphone 配置耳机输出音量,0 最大,255 最小 * * DDAC L/R volume setting * 0db~-95db,0.375db/step,for example: * 0: 0dB * 10: -3.75dB * 125: -46dB * 255: -95dB * - capture-volume: ADC L/R volume digital setting for Microphone 配置录音输入音量,0 最大,255 最小 * * DADC L/R volume setting * 0db~-95db,0.375db/step,for example: * 0: 0dB * 10: -3.75dB * 125: -46dB * 255: -95dB * - mic-in-differential: Boolean. Indicate MIC input is differential, rather than single-ended. 指定使用差分 MIC,否则为单端 MIC,所以使用差分 MIC 时加上此 property。 - pdmdata-out-enable: Boolean. Indicate pdmdata output is enabled or disabled. 录音数据使用 PDM 数字接口。 - use-ext-amplifier: Boolean. Indicate use external amplifier or not. 指定是否使用外部功放。 - adc-for-loopback: Boolean. Indicate adc use for loopback or not. 指定 ADC 用于回采,一般音箱类产品使用。
从这里我们看出要使用外放功能设备树里面就要添加 use-ext-amplifier: 这个属性
所以更改设备树:
rk809_codec: codec { #sound-dai-cells = <0>; compatible = "rockchip,rk809-codec", "rockchip,rk817-codec"; clocks = <&cru I2S1_MCLKOUT>; clock-names = "mclk"; assigned-clocks = <&cru I2S1_MCLKOUT>, <&cru I2S1_MCLK_TX_IOE>; assigned-clock-rates = <12288000>; assigned-clock-parents = <&cru I2S1_MCLKOUT_TX>, <&cru I2S1_MCLKOUT_TX>; pinctrl-names = "default"; pinctrl-0 = <&i2s1m0_mclk>; hp-volume = <20>; spk-volume = <3>; use-ext-amplifier; spk-ctl-gpios = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; mic-in-differential; status = "okay"; };
use-ext-amplifier;这个属性指定是否使用外部功放,改过之后就可以正常放音了,下面是adb shellz中使用tinymix看到的,输出Playback Path是speaker:
sbc_rk3568:/ # tinymix Mixer name: 'rockchip,rk809-codec' Number of controls: 2 ctl type num name value 0 ENUM 1 Playback Path SPK 1 ENUM 1 Capture MIC Path MIC OFF
插上耳机后显示的Playback Path是HP_NO_MIC:
sbc_rk3568:/ # tinymix Mixer name: 'rockchip,rk809-codec' Number of controls: 2 ctl type num name value 0 ENUM 1 Playback Path HP_NO_MIC 1 ENUM 1 Capture MIC Path MIC OFF
下面是瑞芯微给出的思路:
播放无声
- 确认音频源为 非静音文件
- 使用 aplay 或者 tinyplay 播放,定位问题是发生在用户态还是内核态
- 播放等待10秒以上确认是否为 I/O error 问题
- 使用 amixer 或者 tinymix 检查 CODEC 内部 DAC 通路是否打开,音量是否静音
- 查看 寄存器 配置,配合芯片手册或者 CODEC 手册确认配置是否正确:IOMUX,DAI,CODEC。
- 使用 万用表 和 示波器 测量电压,时钟,数据。确认电压,时钟正常,数据线上有波形;测量
- CODEC 近端 模拟输出信号是否正常,测量 PA 使能 gpio 电平,逐级定位问题点