Skip to content

Commit b8c87fc

Browse files
Peter Malkinpopcornmix
Peter Malkin
authored andcommitted
Driver support for Google voiceHAT soundcard.
1 parent f420dd4 commit b8c87fc

File tree

9 files changed

+394
-0
lines changed

9 files changed

+394
-0
lines changed

arch/arm/boot/dts/overlays/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
2323
enc28j60.dtbo \
2424
enc28j60-spi2.dtbo \
2525
fe-pi-audio.dtbo \
26+
googlevoicehat-soundcard.dtbo \
2627
gpio-ir.dtbo \
2728
gpio-poweroff.dtbo \
2829
hifiberry-amp.dtbo \

arch/arm/boot/dts/overlays/README

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,12 @@ Load: dtoverlay=fe-pi-audio
414414
Params: <None>
415415

416416

417+
Name: googlevoicehat-soundcard
418+
Info: Configures the Google voiceHAT soundcard
419+
Load: dtoverlay=googlevoicehat-soundcard
420+
Params: <None>
421+
422+
417423
Name: gpio-ir
418424
Info: Use GPIO pin as rc-core style infrared receiver input. The rc-core-
419425
based gpio_ir_recv driver maps received keys directly to a
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Definitions for Google voiceHAT v1 soundcard overlay
2+
/dts-v1/;
3+
/plugin/;
4+
5+
/ {
6+
compatible = "brcm,bcm2708";
7+
8+
fragment@0 {
9+
target = <&i2s>;
10+
__overlay__ {
11+
status = "okay";
12+
};
13+
};
14+
15+
fragment@1 {
16+
target = <&gpio>;
17+
__overlay__ {
18+
googlevoicehat_pins: googlevoicehat_pins {
19+
brcm,pins = <16>;
20+
brcm,function = <1>; /* out */
21+
brcm,pull = <0>; /* up */
22+
};
23+
};
24+
};
25+
26+
27+
fragment@2 {
28+
target-path = "/";
29+
__overlay__ {
30+
voicehat-codec {
31+
#sound-dai-cells = <0>;
32+
compatible = "google,voicehat";
33+
pinctrl-names = "default";
34+
pinctrl-0 = <&googlevoicehat_pins>;
35+
sdmode-gpios= <&gpio 16 0>;
36+
status = "okay";
37+
};
38+
};
39+
};
40+
41+
fragment@3 {
42+
target = <&sound>;
43+
__overlay__ {
44+
compatible = "googlevoicehat,googlevoicehat-soundcard";
45+
i2s-controller = <&i2s>;
46+
status = "okay";
47+
};
48+
};
49+
};

arch/arm/configs/bcm2709_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,7 @@ CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
884884
CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m
885885
CONFIG_SND_BCM2708_SOC_RASPIDAC3=m
886886
CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m
887+
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
887888
CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
888889
CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m
889890
CONFIG_SND_DIGIDAC1_SOUNDCARD=m

arch/arm/configs/bcmrpi_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
877877
CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m
878878
CONFIG_SND_BCM2708_SOC_RASPIDAC3=m
879879
CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m
880+
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
880881
CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
881882
CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m
882883
CONFIG_SND_DIGIDAC1_SOUNDCARD=m

sound/soc/bcm/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ config SND_SOC_CYGNUS
1818

1919
If you don't know what to do here, say N.
2020

21+
config SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD
22+
tristate "Support for Google voiceHAT soundcard"
23+
depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
24+
select SND_SOC_VOICEHAT
25+
help
26+
Say Y or M if you want to add support for voiceHAT soundcard.
27+
2128
config SND_BCM2708_SOC_HIFIBERRY_DAC
2229
tristate "Support for HifiBerry DAC"
2330
depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S

sound/soc/bcm/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ snd-soc-cygnus-objs := cygnus-pcm.o cygnus-ssp.o
88

99
obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
1010

11+
# Google voiceHAT custom codec support
12+
snd-soc-googlevoicehat-codec-objs := googlevoicehat-codec.o
13+
1114
# BCM2708 Machine Support
1215
snd-soc-adau1977-adc-objs := adau1977-adc.o
16+
snd-soc-googlevoicehat-soundcard-objs := googlevoicehat-soundcard.o
1317
snd-soc-hifiberry-amp-objs := hifiberry_amp.o
1418
snd-soc-hifiberry-dac-objs := hifiberry_dac.o
1519
snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o
@@ -32,6 +36,8 @@ snd-soc-pisound-objs := pisound.o
3236
snd-soc-fe-pi-audio-objs := fe-pi-audio.o
3337

3438
obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
39+
obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-soundcard.o
40+
obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-codec.o
3541
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o
3642
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
3743
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o

sound/soc/bcm/googlevoicehat-codec.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Driver for the Google voiceHAT audio codec for Raspberry Pi.
3+
*
4+
* Author: Peter Malkin <[email protected]>
5+
* Copyright 2016
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU General Public License
9+
* version 2 as published by the Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful, but
12+
* WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* General Public License for more details.
15+
*/
16+
17+
#include <linux/device.h>
18+
#include <linux/err.h>
19+
#include <linux/gpio.h>
20+
#include <linux/gpio/consumer.h>
21+
#include <linux/init.h>
22+
#include <linux/kernel.h>
23+
#include <linux/mod_devicetable.h>
24+
#include <linux/module.h>
25+
#include <linux/of.h>
26+
#include <linux/platform_device.h>
27+
#include <linux/version.h>
28+
#include <sound/pcm.h>
29+
#include <sound/soc.h>
30+
#include <sound/soc-dai.h>
31+
#include <sound/soc-dapm.h>
32+
33+
#define ICS43432_RATE_MIN_HZ 7190 /* from data sheet */
34+
#define ICS43432_RATE_MAX_HZ 52800 /* from data sheet */
35+
#define SDMODE_DELAY_MS \
36+
5 /* Delay in enabling SDMODE after clock settles to remove pop */
37+
38+
struct voicehat_priv {
39+
struct delayed_work enable_sdmode_work;
40+
struct gpio_desc *sdmode_gpio;
41+
unsigned int sdmode_delay;
42+
};
43+
44+
static void voicehat_enable_sdmode_work(struct work_struct *work) {
45+
struct voicehat_priv *voicehat =
46+
container_of(work, struct voicehat_priv, enable_sdmode_work.work);
47+
gpiod_set_value(voicehat->sdmode_gpio, 1);
48+
}
49+
50+
static int voicehat_codec_probe(struct snd_soc_codec *codec) {
51+
struct voicehat_priv *voicehat = snd_soc_codec_get_drvdata(codec);
52+
53+
voicehat->sdmode_gpio = devm_gpiod_get(codec->dev, "sdmode", GPIOD_OUT_LOW);
54+
if (IS_ERR(voicehat->sdmode_gpio)) {
55+
dev_err(codec->dev, "Unable to allocate GPIO pin\n");
56+
return PTR_ERR(voicehat->sdmode_gpio);
57+
}
58+
59+
INIT_DELAYED_WORK(&voicehat->enable_sdmode_work, voicehat_enable_sdmode_work);
60+
return 0;
61+
}
62+
63+
static int voicehat_codec_remove(struct snd_soc_codec *codec) {
64+
struct voicehat_priv *voicehat = snd_soc_codec_get_drvdata(codec);
65+
66+
cancel_delayed_work_sync(&voicehat->enable_sdmode_work);
67+
68+
return 0;
69+
}
70+
71+
static const struct snd_soc_dapm_widget voicehat_dapm_widgets[] = {
72+
SND_SOC_DAPM_OUTPUT("Speaker"),
73+
};
74+
75+
static const struct snd_soc_dapm_route voicehat_dapm_routes[] = {
76+
{"Speaker", NULL, "HiFi Playback"},
77+
};
78+
79+
static struct snd_soc_codec_driver voicehat_codec_driver = {
80+
.probe = voicehat_codec_probe,
81+
.remove = voicehat_codec_remove,
82+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
83+
.component_driver = {
84+
#endif
85+
.dapm_widgets = voicehat_dapm_widgets,
86+
.num_dapm_widgets = ARRAY_SIZE(voicehat_dapm_widgets),
87+
.dapm_routes = voicehat_dapm_routes,
88+
.num_dapm_routes = ARRAY_SIZE(voicehat_dapm_routes),
89+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
90+
},
91+
#endif
92+
};
93+
94+
static int voicehat_daiops_trigger(struct snd_pcm_substream *substream, int cmd,
95+
struct snd_soc_dai *dai) {
96+
struct snd_soc_codec *codec = dai->codec;
97+
struct voicehat_priv *voicehat = snd_soc_codec_get_drvdata(codec);
98+
99+
if (voicehat->sdmode_delay == 0) return 0;
100+
101+
dev_dbg(dai->dev, "CMD %d", cmd);
102+
dev_dbg(dai->dev, "Playback Active %d", dai->playback_active);
103+
dev_dbg(dai->dev, "Capture Active %d", dai->capture_active);
104+
105+
switch (cmd) {
106+
case SNDRV_PCM_TRIGGER_START:
107+
case SNDRV_PCM_TRIGGER_RESUME:
108+
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
109+
if (dai->playback_active) {
110+
dev_info(dai->dev, "Enabling audio amp...\n");
111+
queue_delayed_work(system_power_efficient_wq,
112+
&voicehat->enable_sdmode_work,
113+
msecs_to_jiffies(voicehat->sdmode_delay));
114+
}
115+
break;
116+
case SNDRV_PCM_TRIGGER_STOP:
117+
case SNDRV_PCM_TRIGGER_SUSPEND:
118+
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
119+
if (dai->playback_active) {
120+
cancel_delayed_work(&voicehat->enable_sdmode_work);
121+
dev_info(dai->dev, "Disabling audio amp...\n");
122+
gpiod_set_value(voicehat->sdmode_gpio, 0);
123+
}
124+
break;
125+
}
126+
return 0;
127+
}
128+
129+
static const struct snd_soc_dai_ops voicehat_dai_ops = {
130+
.trigger = voicehat_daiops_trigger,
131+
};
132+
133+
static struct snd_soc_dai_driver voicehat_dai = {
134+
.name = "voicehat-hifi",
135+
.capture = {.stream_name = "HiFi Capture",
136+
.channels_min = 2,
137+
.channels_max = 2,
138+
.rates = SNDRV_PCM_RATE_48000,
139+
.formats = SNDRV_PCM_FMTBIT_S32_LE},
140+
.playback = {.stream_name = "HiFi Playback",
141+
.channels_min = 2,
142+
.channels_max = 2,
143+
.rates = SNDRV_PCM_RATE_48000,
144+
.formats = SNDRV_PCM_FMTBIT_S32_LE},
145+
.ops = &voicehat_dai_ops,
146+
.symmetric_rates = 1};
147+
148+
#ifdef CONFIG_OF
149+
static const struct of_device_id voicehat_ids[] = {
150+
{
151+
.compatible = "google,voicehat",
152+
},
153+
{}};
154+
MODULE_DEVICE_TABLE(of, voicehat_ids);
155+
#endif
156+
157+
static int voicehat_platform_probe(struct platform_device *pdev) {
158+
struct voicehat_priv *voicehat;
159+
int ret;
160+
161+
voicehat = devm_kzalloc(&pdev->dev, sizeof(*voicehat), GFP_KERNEL);
162+
if (!voicehat) return -ENOMEM;
163+
164+
ret = device_property_read_u32(&pdev->dev, "voicehat_sdmode_delay",
165+
&voicehat->sdmode_delay);
166+
167+
if (ret) {
168+
voicehat->sdmode_delay = SDMODE_DELAY_MS;
169+
dev_info(&pdev->dev,
170+
"property 'voicehat_sdmode_delay' not found default 5 mS");
171+
} else {
172+
dev_info(&pdev->dev, "property 'voicehat_sdmode_delay' found delay= %d mS",
173+
voicehat->sdmode_delay);
174+
}
175+
176+
dev_set_drvdata(&pdev->dev, voicehat);
177+
178+
return snd_soc_register_codec(&pdev->dev, &voicehat_codec_driver, &voicehat_dai, 1);
179+
}
180+
181+
static int voicehat_platform_remove(struct platform_device *pdev) {
182+
snd_soc_unregister_codec(&pdev->dev);
183+
return 0;
184+
}
185+
186+
static struct platform_driver voicehat_driver = {
187+
.driver =
188+
{
189+
.name = "voicehat-codec", .of_match_table = of_match_ptr(voicehat_ids),
190+
},
191+
.probe = voicehat_platform_probe,
192+
.remove = voicehat_platform_remove,
193+
};
194+
195+
module_platform_driver(voicehat_driver);
196+
197+
MODULE_DESCRIPTION("Google voiceHAT Codec driver");
198+
MODULE_AUTHOR("Peter Malkin <[email protected]>");
199+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)