Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

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