242 lines · c
1// SPDX-License-Identifier: GPL-2.02//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 316 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");242