1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // ROHM BD28623MUV class D speaker amplifier codec driver.
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun // Copyright (c) 2018 Socionext Inc.
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/delay.h>
8*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/of.h>
11*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
12*4882a593Smuzhiyun #include <sound/pcm.h>
13*4882a593Smuzhiyun #include <sound/soc.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #define BD28623_NUM_SUPPLIES 3
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun static const char *const bd28623_supply_names[BD28623_NUM_SUPPLIES] = {
18*4882a593Smuzhiyun "VCCA",
19*4882a593Smuzhiyun "VCCP1",
20*4882a593Smuzhiyun "VCCP2",
21*4882a593Smuzhiyun };
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun struct bd28623_priv {
24*4882a593Smuzhiyun struct device *dev;
25*4882a593Smuzhiyun struct regulator_bulk_data supplies[BD28623_NUM_SUPPLIES];
26*4882a593Smuzhiyun struct gpio_desc *reset_gpio;
27*4882a593Smuzhiyun struct gpio_desc *mute_gpio;
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun int switch_spk;
30*4882a593Smuzhiyun };
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun static const struct snd_soc_dapm_widget bd28623_widgets[] = {
33*4882a593Smuzhiyun SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
34*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("OUT1P"),
35*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("OUT1N"),
36*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("OUT2P"),
37*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("OUT2N"),
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun static const struct snd_soc_dapm_route bd28623_routes[] = {
41*4882a593Smuzhiyun { "OUT1P", NULL, "DAC" },
42*4882a593Smuzhiyun { "OUT1N", NULL, "DAC" },
43*4882a593Smuzhiyun { "OUT2P", NULL, "DAC" },
44*4882a593Smuzhiyun { "OUT2N", NULL, "DAC" },
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun
bd28623_power_on(struct bd28623_priv * bd)47*4882a593Smuzhiyun static int bd28623_power_on(struct bd28623_priv *bd)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun int ret;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun ret = regulator_bulk_enable(ARRAY_SIZE(bd->supplies), bd->supplies);
52*4882a593Smuzhiyun if (ret) {
53*4882a593Smuzhiyun dev_err(bd->dev, "Failed to enable supplies: %d\n", ret);
54*4882a593Smuzhiyun return ret;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun gpiod_set_value_cansleep(bd->reset_gpio, 0);
58*4882a593Smuzhiyun usleep_range(300000, 400000);
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun return 0;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
bd28623_power_off(struct bd28623_priv * bd)63*4882a593Smuzhiyun static void bd28623_power_off(struct bd28623_priv *bd)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun gpiod_set_value_cansleep(bd->reset_gpio, 1);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun regulator_bulk_disable(ARRAY_SIZE(bd->supplies), bd->supplies);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
bd28623_get_switch_spk(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)70*4882a593Smuzhiyun static int bd28623_get_switch_spk(struct snd_kcontrol *kcontrol,
71*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun struct snd_soc_component *component =
74*4882a593Smuzhiyun snd_soc_kcontrol_component(kcontrol);
75*4882a593Smuzhiyun struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun ucontrol->value.integer.value[0] = bd->switch_spk;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun return 0;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
bd28623_set_switch_spk(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)82*4882a593Smuzhiyun static int bd28623_set_switch_spk(struct snd_kcontrol *kcontrol,
83*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun struct snd_soc_component *component =
86*4882a593Smuzhiyun snd_soc_kcontrol_component(kcontrol);
87*4882a593Smuzhiyun struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun if (bd->switch_spk == ucontrol->value.integer.value[0])
90*4882a593Smuzhiyun return 0;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun bd->switch_spk = ucontrol->value.integer.value[0];
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun static const struct snd_kcontrol_new bd28623_controls[] = {
100*4882a593Smuzhiyun SOC_SINGLE_BOOL_EXT("Speaker Switch", 0,
101*4882a593Smuzhiyun bd28623_get_switch_spk, bd28623_set_switch_spk),
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun
bd28623_codec_probe(struct snd_soc_component * component)104*4882a593Smuzhiyun static int bd28623_codec_probe(struct snd_soc_component *component)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
107*4882a593Smuzhiyun int ret;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun bd->switch_spk = 1;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun ret = bd28623_power_on(bd);
112*4882a593Smuzhiyun if (ret)
113*4882a593Smuzhiyun return ret;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun return 0;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
bd28623_codec_remove(struct snd_soc_component * component)120*4882a593Smuzhiyun static void bd28623_codec_remove(struct snd_soc_component *component)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun bd28623_power_off(bd);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
bd28623_codec_suspend(struct snd_soc_component * component)127*4882a593Smuzhiyun static int bd28623_codec_suspend(struct snd_soc_component *component)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun bd28623_power_off(bd);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun return 0;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
bd28623_codec_resume(struct snd_soc_component * component)136*4882a593Smuzhiyun static int bd28623_codec_resume(struct snd_soc_component *component)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
139*4882a593Smuzhiyun int ret;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun ret = bd28623_power_on(bd);
142*4882a593Smuzhiyun if (ret)
143*4882a593Smuzhiyun return ret;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun return 0;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun static const struct snd_soc_component_driver soc_codec_bd = {
151*4882a593Smuzhiyun .probe = bd28623_codec_probe,
152*4882a593Smuzhiyun .remove = bd28623_codec_remove,
153*4882a593Smuzhiyun .suspend = bd28623_codec_suspend,
154*4882a593Smuzhiyun .resume = bd28623_codec_resume,
155*4882a593Smuzhiyun .dapm_widgets = bd28623_widgets,
156*4882a593Smuzhiyun .num_dapm_widgets = ARRAY_SIZE(bd28623_widgets),
157*4882a593Smuzhiyun .dapm_routes = bd28623_routes,
158*4882a593Smuzhiyun .num_dapm_routes = ARRAY_SIZE(bd28623_routes),
159*4882a593Smuzhiyun .controls = bd28623_controls,
160*4882a593Smuzhiyun .num_controls = ARRAY_SIZE(bd28623_controls),
161*4882a593Smuzhiyun .idle_bias_on = 1,
162*4882a593Smuzhiyun .use_pmdown_time = 1,
163*4882a593Smuzhiyun .endianness = 1,
164*4882a593Smuzhiyun .non_legacy_dai_naming = 1,
165*4882a593Smuzhiyun };
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun static struct snd_soc_dai_driver soc_dai_bd = {
168*4882a593Smuzhiyun .name = "bd28623-speaker",
169*4882a593Smuzhiyun .playback = {
170*4882a593Smuzhiyun .stream_name = "Playback",
171*4882a593Smuzhiyun .formats = SNDRV_PCM_FMTBIT_S32_LE |
172*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S24_LE |
173*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S16_LE,
174*4882a593Smuzhiyun .rates = SNDRV_PCM_RATE_48000 |
175*4882a593Smuzhiyun SNDRV_PCM_RATE_44100 |
176*4882a593Smuzhiyun SNDRV_PCM_RATE_32000,
177*4882a593Smuzhiyun .channels_min = 2,
178*4882a593Smuzhiyun .channels_max = 2,
179*4882a593Smuzhiyun },
180*4882a593Smuzhiyun };
181*4882a593Smuzhiyun
bd28623_probe(struct platform_device * pdev)182*4882a593Smuzhiyun static int bd28623_probe(struct platform_device *pdev)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun struct bd28623_priv *bd;
185*4882a593Smuzhiyun struct device *dev = &pdev->dev;
186*4882a593Smuzhiyun int i, ret;
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun bd = devm_kzalloc(&pdev->dev, sizeof(struct bd28623_priv), GFP_KERNEL);
189*4882a593Smuzhiyun if (!bd)
190*4882a593Smuzhiyun return -ENOMEM;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(bd->supplies); i++)
193*4882a593Smuzhiyun bd->supplies[i].supply = bd28623_supply_names[i];
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(bd->supplies),
196*4882a593Smuzhiyun bd->supplies);
197*4882a593Smuzhiyun if (ret) {
198*4882a593Smuzhiyun dev_err(dev, "Failed to get supplies: %d\n", ret);
199*4882a593Smuzhiyun return ret;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun bd->reset_gpio = devm_gpiod_get_optional(dev, "reset",
203*4882a593Smuzhiyun GPIOD_OUT_HIGH);
204*4882a593Smuzhiyun if (IS_ERR(bd->reset_gpio)) {
205*4882a593Smuzhiyun dev_err(dev, "Failed to request reset_gpio: %ld\n",
206*4882a593Smuzhiyun PTR_ERR(bd->reset_gpio));
207*4882a593Smuzhiyun return PTR_ERR(bd->reset_gpio);
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun bd->mute_gpio = devm_gpiod_get_optional(dev, "mute",
211*4882a593Smuzhiyun GPIOD_OUT_HIGH);
212*4882a593Smuzhiyun if (IS_ERR(bd->mute_gpio)) {
213*4882a593Smuzhiyun dev_err(dev, "Failed to request mute_gpio: %ld\n",
214*4882a593Smuzhiyun PTR_ERR(bd->mute_gpio));
215*4882a593Smuzhiyun return PTR_ERR(bd->mute_gpio);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun platform_set_drvdata(pdev, bd);
219*4882a593Smuzhiyun bd->dev = dev;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun return devm_snd_soc_register_component(dev, &soc_codec_bd,
222*4882a593Smuzhiyun &soc_dai_bd, 1);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun static const struct of_device_id bd28623_of_match[] = {
226*4882a593Smuzhiyun { .compatible = "rohm,bd28623", },
227*4882a593Smuzhiyun {}
228*4882a593Smuzhiyun };
229*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, bd28623_of_match);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun static struct platform_driver bd28623_codec_driver = {
232*4882a593Smuzhiyun .driver = {
233*4882a593Smuzhiyun .name = "bd28623",
234*4882a593Smuzhiyun .of_match_table = of_match_ptr(bd28623_of_match),
235*4882a593Smuzhiyun },
236*4882a593Smuzhiyun .probe = bd28623_probe,
237*4882a593Smuzhiyun };
238*4882a593Smuzhiyun module_platform_driver(bd28623_codec_driver);
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
241*4882a593Smuzhiyun MODULE_DESCRIPTION("ROHM BD28623 speaker amplifier driver");
242*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
243