aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-1046-Enhances-the-Hifiberry-DAC-driver-for-Hifiberry-AMP1.patch
blob: 4e15285220472fe1cb653558cf8a6e5fb92749b4 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
From 0b584f3c2808dba8dc9e74a628b65008302804a5 Mon Sep 17 00:00:00 2001
From: Joerg Schambacher <joerg@i2audio.com>
Date: Fri, 29 Jan 2021 16:16:39 +0100
Subject: [PATCH] Enhances the Hifiberry DAC+ driver for Hifiberry
 AMP100 support

Adds the necessary GPIO handling and ALSA mixer extensions.
Also fixes a problem with the PLL/CLK control when switching sample rates.
Thanks to Clive Messer for the support!

Signed-off-by: Joerg Schambacher <joerg@hifiberry.com>
---
 sound/soc/bcm/hifiberry_dacplus.c | 124 ++++++++++++++++++++++++++----
 1 file changed, 111 insertions(+), 13 deletions(-)

--- a/sound/soc/bcm/hifiberry_dacplus.c
+++ b/sound/soc/bcm/hifiberry_dacplus.c
@@ -1,10 +1,10 @@
 /*
- * ASoC Driver for HiFiBerry DAC+ / DAC Pro
+ * ASoC Driver for HiFiBerry DAC+ / DAC Pro / AMP100
  *
  * Author:	Daniel Matuschek, Stuart MacLean <stuart@hifiberry.com>
  *		Copyright 2014-2015
  *		based on code by Florian Meier <florian.meier@koalo.de>
- *		Headphone added by Joerg Schambacher, joerg@i2audio.com
+ *		Headphone/AMP100 Joerg Schambacher <joerg@hifiberry.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -17,6 +17,8 @@
  */
 
 #include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <../drivers/gpio/gpiolib.h>
 #include <linux/platform_device.h>
 #include <linux/kernel.h>
 #include <linux/clk.h>
@@ -53,6 +55,47 @@ static bool slave;
 static bool snd_rpi_hifiberry_is_dacpro;
 static bool digital_gain_0db_limit = true;
 static bool leds_off;
+static bool auto_mute;
+static int mute_ext_ctl;
+static int mute_ext;
+static struct gpio_desc *snd_mute_gpio;
+static struct gpio_desc *snd_reset_gpio;
+static struct snd_soc_card snd_rpi_hifiberry_dacplus;
+
+static int snd_rpi_hifiberry_dacplus_mute_set(int mute)
+{
+	gpiod_set_value_cansleep(snd_mute_gpio, mute);
+	return 1;
+}
+
+static int snd_rpi_hifiberry_dacplus_mute_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = mute_ext;
+
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplus_mute_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	if (mute_ext == ucontrol->value.integer.value[0])
+		return 0;
+
+	mute_ext = ucontrol->value.integer.value[0];
+
+	return snd_rpi_hifiberry_dacplus_mute_set(mute_ext);
+}
+
+static const char * const mute_text[] = {"Play", "Mute"};
+static const struct soc_enum hb_dacplus_opt_mute_enum =
+	SOC_ENUM_SINGLE_EXT(2, mute_text);
+
+static const struct snd_kcontrol_new hb_dacplus_opt_mute_controls[] = {
+	SOC_ENUM_EXT("Mute(ext)", hb_dacplus_opt_mute_enum,
+			      snd_rpi_hifiberry_dacplus_mute_get,
+			      snd_rpi_hifiberry_dacplus_mute_put),
+};
 
 static void snd_rpi_hifiberry_dacplus_select_clk(struct snd_soc_component *component,
 	int clk_id)
@@ -68,6 +111,7 @@ static void snd_rpi_hifiberry_dacplus_se
 		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x04);
 		break;
 	}
+	usleep_range(2000, 2100);
 }
 
 static void snd_rpi_hifiberry_dacplus_clk_gpio(struct snd_soc_component *component)
@@ -85,13 +129,6 @@ static bool snd_rpi_hifiberry_dacplus_is
 	return (!(sck & 0x40));
 }
 
-static bool snd_rpi_hifiberry_dacplus_is_sclk_sleep(
-	struct snd_soc_component *component)
-{
-	msleep(2);
-	return snd_rpi_hifiberry_dacplus_is_sclk(component);
-}
-
 static bool snd_rpi_hifiberry_dacplus_is_pro_card(struct snd_soc_component *component)
 {
 	bool isClk44EN, isClk48En, isNoClk;
@@ -99,13 +136,13 @@ static bool snd_rpi_hifiberry_dacplus_is
 	snd_rpi_hifiberry_dacplus_clk_gpio(component);
 
 	snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_CLK44EN);
-	isClk44EN = snd_rpi_hifiberry_dacplus_is_sclk_sleep(component);
+	isClk44EN = snd_rpi_hifiberry_dacplus_is_sclk(component);
 
 	snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_NOCLOCK);
-	isNoClk = snd_rpi_hifiberry_dacplus_is_sclk_sleep(component);
+	isNoClk = snd_rpi_hifiberry_dacplus_is_sclk(component);
 
 	snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_CLK48EN);
-	isClk48En = snd_rpi_hifiberry_dacplus_is_sclk_sleep(component);
+	isClk48En = snd_rpi_hifiberry_dacplus_is_sclk(component);
 
 	return (isClk44EN && isClk48En && !isNoClk);
 }
@@ -149,6 +186,7 @@ static int snd_rpi_hifiberry_dacplus_ini
 {
 	struct snd_soc_component *component = rtd->codec_dai->component;
 	struct pcm512x_priv *priv;
+	struct snd_soc_card *card = &snd_rpi_hifiberry_dacplus;
 
 	if (slave)
 		snd_rpi_hifiberry_is_dacpro = false;
@@ -187,6 +225,20 @@ static int snd_rpi_hifiberry_dacplus_ini
 		if (ret < 0)
 			dev_warn(card->dev, "Failed to set volume limit: %d\n", ret);
 	}
+	if (snd_reset_gpio) {
+		gpiod_set_value_cansleep(snd_reset_gpio, 0);
+		msleep(1);
+		gpiod_set_value_cansleep(snd_reset_gpio, 1);
+		msleep(1);
+		gpiod_set_value_cansleep(snd_reset_gpio, 0);
+	}
+
+	if (mute_ext_ctl)
+		snd_soc_add_card_controls(card,	hb_dacplus_opt_mute_controls,
+				ARRAY_SIZE(hb_dacplus_opt_mute_controls));
+
+	if (snd_mute_gpio)
+		gpiod_set_value_cansleep(snd_mute_gpio,	mute_ext);
 
 	return 0;
 }
@@ -254,6 +306,8 @@ static int snd_rpi_hifiberry_dacplus_sta
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_component *component = rtd->codec_dai->component;
 
+	if (auto_mute)
+		gpiod_set_value_cansleep(snd_mute_gpio, 0);
 	if (leds_off)
 		return 0;
 	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
@@ -267,6 +321,8 @@ static void snd_rpi_hifiberry_dacplus_sh
 	struct snd_soc_component *component = rtd->codec_dai->component;
 
 	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+	if (auto_mute)
+		gpiod_set_value_cansleep(snd_mute_gpio, 1);
 }
 
 /* machine stream operations */
@@ -342,6 +398,8 @@ static int snd_rpi_hifiberry_dacplus_pro
 	struct device_node *tpa_node;
 	struct property *tpa_prop;
 	struct of_changeset ocs;
+	struct property *pp;
+	int tmp;
 
 	/* probe for head phone amp */
 	ret = hb_hp_detect();
@@ -396,6 +454,39 @@ static int snd_rpi_hifiberry_dacplus_pro
 						"hifiberry-dacplus,slave");
 		leds_off = of_property_read_bool(pdev->dev.of_node,
 						"hifiberry-dacplus,leds_off");
+		auto_mute = of_property_read_bool(pdev->dev.of_node,
+						"hifiberry-dacplus,auto_mute");
+
+		/*
+		 * check for HW MUTE as defined in DT-overlay
+		 * active high, therefore default to HIGH to MUTE
+		 */
+		snd_mute_gpio =	devm_gpiod_get_optional(&pdev->dev,
+						 "mute", GPIOD_OUT_HIGH);
+		if (IS_ERR(snd_mute_gpio)) {
+			dev_err(&pdev->dev, "Can't allocate GPIO (HW-MUTE)");
+			return PTR_ERR(snd_mute_gpio);
+		}
+
+		/* add ALSA control if requested in DT-overlay (AMP100) */
+		pp = of_find_property(pdev->dev.of_node,
+				"hifiberry-dacplus,mute_ext_ctl", &tmp);
+		if (pp) {
+			if (!of_property_read_u32(pdev->dev.of_node,
+				"hifiberry-dacplus,mute_ext_ctl", &mute_ext)) {
+				/* ALSA control will be used */
+				mute_ext_ctl = 1;
+			}
+		}
+
+		/* check for HW RESET (AMP100) */
+		snd_reset_gpio = devm_gpiod_get_optional(&pdev->dev,
+						"reset", GPIOD_OUT_HIGH);
+		if (IS_ERR(snd_reset_gpio)) {
+			dev_err(&pdev->dev, "Can't allocate GPIO (HW-RESET)");
+			return PTR_ERR(snd_reset_gpio);
+		}
+
 	}
 
 	ret = devm_snd_soc_register_card(&pdev->dev,
@@ -403,7 +494,14 @@ static int snd_rpi_hifiberry_dacplus_pro
 	if (ret && ret != -EPROBE_DEFER)
 		dev_err(&pdev->dev,
 			"snd_soc_register_card() failed: %d\n", ret);
-
+	if (!ret) {
+		if (snd_mute_gpio)
+			dev_info(&pdev->dev, "GPIO%i for HW-MUTE selected",
+					gpio_chip_hwgpio(snd_mute_gpio));
+		if (snd_reset_gpio)
+			dev_info(&pdev->dev, "GPIO%i for HW-RESET selected",
+					gpio_chip_hwgpio(snd_reset_gpio));
+	}
 	return ret;
 }