[Android6.0][RK3399] 实现耳机和喇叭自动切换功能

需求分析

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,并打开声卡驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts b/arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts
index b9002f7..039c95a 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-orangepi.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 结构体添加相关成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);

添加驱动代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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);
您的支持将鼓励我继续创作!