[Android6.0][RK3399] 實現耳機和喇叭自動切換功能
阿新 • • 發佈:2018-12-30
Platform: RK3399
OS: Android 6.0
Kernel: Linux4.4
Version: v2017.03
需求分析
RK 預設的音效卡 RT5651(Card 0)是從耳機(device 0)輸出。
但是我們的產品上同時具有 Speaker 和 Headphone,預設驅動中沒有自動切換兩者的邏輯。
所以需要實現預設從喇叭輸出,插上耳機的情況下從耳機輸出的功能。
實現方式
查閱原理圖
HP_DET_H 為耳機狀態檢測腳。
SPK_CTL_H 為控制 Speaker 使能管腳。
所以整個的邏輯很簡單即
HP_DET_H 檢測耳機狀態
————> 為高 耳機插入,拉底 SPK_CTL_H 禁能喇叭
————> 為低 耳機拔出,拉高 SPK_CTL_H 使能喇叭
進行編碼
dts 中新增相應 GPIO,並開啟音效卡驅動
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-xxx.dts b/arch/arm64/boot/dts/rockchip/rk3399-xxx.dts
index b9002f7..039c95a 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-xxx.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-xxx.dts
@@ -63,6 +63,8 @@
compatible = "rockchip,rockchip-rt5651-tc358749x-sound";
rockchip,cpu = <&i2s0 &i2s0>;
rockchip,codec = <&rt5651 &rt5651 &tc358749x>;
+ spk-con-gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+ hp-det-gpio = <&gpio4 28 GPIO_ACTIVE_HIGH>;
status = "okay";
};
@@ -147,7 +149,7 @@
int-gpios = <&gpio2 12 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&hdmiin_gpios>;
- status = "disabled";
+ status = "okay";
};
};
修改 snd_soc_card 結構體新增相關成員變數
diff --git a/include/sound/soc.h b/include/sound/soc.h
index fb955e6..e6b3ac7 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1080,8 +1080,16 @@ struct snd_soc_card {
struct mutex mutex;
struct mutex dapm_mutex;
+ int debounce_time;
+ int hp_det_invert;
+ struct delayed_work work;
+ bool spk_active_level;
+ bool hp_inserted;
+
bool instantiated;
+ int spk_ctl_gpio;
+ int hp_det_gpio;
int (*probe)(struct snd_soc_card *card);
int (*late_probe)(struct snd_soc_card *card);
int (*remove)(struct snd_soc_card *card);
新增驅動程式碼
diff --git a/sound/soc/rockchip/rockchip_rt5651_tc358749x.c b/sound/soc/rockchip/rockchip_rt5651_tc358749x.c
index 21f8ee2..14acebd 100644
--- a/sound/soc/rockchip/rockchip_rt5651_tc358749x.c
+++ b/sound/soc/rockchip/rockchip_rt5651_tc358749x.c
@@ -18,12 +18,18 @@
#include <linux/module.h>
#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+
#include "rockchip_i2s.h"
#include "../codecs/rt5651.h"
#include "../codecs/tc358749x.h"
#define DRV_NAME "rk3399-rt5651-tc358749x"
+#define INVALID_GPIO -1
+
static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Lineout", NULL),
@@ -184,11 +190,58 @@ static struct snd_soc_card rockchip_sound_card = {
.num_controls = ARRAY_SIZE(rockchip_controls),
};
+static irqreturn_t rt5651_irq_handler(int irq, void *data)
+{
+ struct snd_soc_card *card = data;
+ queue_delayed_work(system_power_efficient_wq, &card->work,
+ msecs_to_jiffies(card->debounce_time));
+
+ return IRQ_HANDLED;
+}
+
+static void rt5651_enable_spk(struct snd_soc_card *card, bool enable)
+{
+ bool level;
+
+ level = enable ? card->spk_active_level : !card->spk_active_level;
+ gpio_set_value(card->spk_ctl_gpio, level);
+}
+
+static void hp_work(struct work_struct *work)
+{
+ struct snd_soc_card *card;
+ int enable;
+
+ card = container_of(work, struct snd_soc_card, work.work);
+ enable = gpio_get_value(card->hp_det_gpio);
+ if(card->hp_det_invert)
+ enable = !enable;
+
+ card->hp_inserted = enable ? true : false;
+ if(card->hp_inserted){
+ //printk("hp_work rt5651_enable_spk false\n");
+ rt5651_enable_spk(card, false);
+ } else {
+ //printk("hp_work rt5651_enable_spk true\n");
+ rt5651_enable_spk(card,true);
+ }
+}
+
static int rockchip_sound_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &rockchip_sound_card;
struct device_node *cpu_node;
- int i, ret;
+ int i;
+ int ret = -1;
+
+ int hp_irq;
+ enum of_gpio_flags flags;
+
+ card->debounce_time = 200;
+ card->hp_det_invert = 0;
+ card->hp_inserted = false;
+ card->spk_ctl_gpio = INVALID_GPIO;
+ card->hp_det_gpio = INVALID_GPIO;
dev_info(&pdev->dev, "%s\n", __func__);
@@ -213,6 +266,47 @@ static int rockchip_sound_probe(struct platform_device *pdev)
}
}
+ card->spk_ctl_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "spk-con-gpio", 0, &flags);
+ if(!gpio_is_valid(card->spk_ctl_gpio)) {
+ dev_err(&pdev->dev,"spk-ctl-gpio: %d is invalid\n", card->spk_ctl_gpio);
+ card->spk_ctl_gpio = INVALID_GPIO;
+ }else {
+ dev_info(&pdev->dev,"spk-ctl-gpio: %d is arrivable\n", card->spk_ctl_gpio);
+ card->spk_active_level = !(flags & OF_GPIO_ACTIVE_LOW);
+ ret = devm_gpio_request_one(&pdev->dev, card->spk_ctl_gpio,
+ GPIOF_DIR_OUT,NULL);
+ if(ret) {
+ dev_err(&pdev->dev,"spk_ctl_gpio: request failed!\n");
+ }
+ rt5651_enable_spk(card, true);
+ }
+
+ card->hp_det_gpio = of_get_named_gpio_flags(pdev->dev.of_node,"hp-det-gpio", 0, &flags);
+ if(!gpio_is_valid(card->hp_det_gpio)) {
+ printk("hp-det-gpio: %d is invalid\n",card->hp_det_gpio);
+ card->hp_det_gpio = INVALID_GPIO;
+ }else {
+ INIT_DELAYED_WORK(&card->work, hp_work);
+ card->hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
+ ret = devm_gpio_request_one (&pdev->dev, card->hp_det_gpio,GPIOF_IN,"hp det");
+ if( ret < 0)
+ return ret;
+ hp_irq = gpio_to_irq(card->hp_det_gpio);
+ ret = devm_request_threaded_irq(&pdev->dev, hp_irq, NULL,
+ rt5651_irq_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ "rt5651_interrupt", card);
+ if( ret < 0) {
+ dev_err(&pdev->dev, "request_irq: failed %d\n", ret);
+ return ret;
+ }
+
+ schedule_delayed_work(&card->work,
+ msecs_to_jiffies(card->debounce_time));
+ }
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
ret = devm_snd_soc_register_card(&pdev->dev, card);