162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MAX9759 Amplifier Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2017 BayLibre, SAS. 662306a36Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <sound/soc.h> 1262306a36Sopenharmony_ci#include <sound/soc-dapm.h> 1362306a36Sopenharmony_ci#include <sound/tlv.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define DRV_NAME "max9759" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct max9759 { 1862306a36Sopenharmony_ci struct gpio_desc *gpiod_shutdown; 1962306a36Sopenharmony_ci struct gpio_desc *gpiod_mute; 2062306a36Sopenharmony_ci struct gpio_descs *gpiod_gain; 2162306a36Sopenharmony_ci bool is_mute; 2262306a36Sopenharmony_ci unsigned int gain; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int pga_event(struct snd_soc_dapm_widget *w, 2662306a36Sopenharmony_ci struct snd_kcontrol *control, int event) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); 2962306a36Sopenharmony_ci struct max9759 *priv = snd_soc_component_get_drvdata(c); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) 3262306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->gpiod_shutdown, 0); 3362306a36Sopenharmony_ci else 3462306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->gpiod_shutdown, 1); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* From 6dB to 24dB in steps of 6dB */ 4062306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(speaker_gain_tlv, 600, 600, 0); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int speaker_gain_control_get(struct snd_kcontrol *kcontrol, 4362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); 4662306a36Sopenharmony_ci struct max9759 *priv = snd_soc_component_get_drvdata(c); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = priv->gain; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const bool speaker_gain_table[4][2] = { 5462306a36Sopenharmony_ci /* G1, G2 */ 5562306a36Sopenharmony_ci {true, true}, /* +6dB */ 5662306a36Sopenharmony_ci {false, true}, /* +12dB */ 5762306a36Sopenharmony_ci {true, false}, /* +18dB */ 5862306a36Sopenharmony_ci {false, false}, /* +24dB */ 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int speaker_gain_control_put(struct snd_kcontrol *kcontrol, 6262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); 6562306a36Sopenharmony_ci struct max9759 *priv = snd_soc_component_get_drvdata(c); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (ucontrol->value.integer.value[0] < 0 || 6862306a36Sopenharmony_ci ucontrol->value.integer.value[0] > 3) 6962306a36Sopenharmony_ci return -EINVAL; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci priv->gain = ucontrol->value.integer.value[0]; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* G1 */ 7462306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->gpiod_gain->desc[0], 7562306a36Sopenharmony_ci speaker_gain_table[priv->gain][0]); 7662306a36Sopenharmony_ci /* G2 */ 7762306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->gpiod_gain->desc[1], 7862306a36Sopenharmony_ci speaker_gain_table[priv->gain][1]); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 1; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int speaker_mute_get(struct snd_kcontrol *kcontrol, 8462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); 8762306a36Sopenharmony_ci struct max9759 *priv = snd_soc_component_get_drvdata(c); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !priv->is_mute; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int speaker_mute_put(struct snd_kcontrol *kcontrol, 9562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); 9862306a36Sopenharmony_ci struct max9759 *priv = snd_soc_component_get_drvdata(c); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci priv->is_mute = !ucontrol->value.integer.value[0]; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->gpiod_mute, priv->is_mute); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 1; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic const struct snd_kcontrol_new max9759_dapm_controls[] = { 10862306a36Sopenharmony_ci SOC_SINGLE_EXT_TLV("Speaker Gain Volume", 0, 0, 3, 0, 10962306a36Sopenharmony_ci speaker_gain_control_get, speaker_gain_control_put, 11062306a36Sopenharmony_ci speaker_gain_tlv), 11162306a36Sopenharmony_ci SOC_SINGLE_BOOL_EXT("Playback Switch", 0, 11262306a36Sopenharmony_ci speaker_mute_get, speaker_mute_put), 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget max9759_dapm_widgets[] = { 11662306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("INL"), 11762306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("INR"), 11862306a36Sopenharmony_ci SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, pga_event, 11962306a36Sopenharmony_ci (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)), 12062306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("OUTL"), 12162306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("OUTR"), 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic const struct snd_soc_dapm_route max9759_dapm_routes[] = { 12562306a36Sopenharmony_ci { "PGA", NULL, "INL" }, 12662306a36Sopenharmony_ci { "PGA", NULL, "INR" }, 12762306a36Sopenharmony_ci { "OUTL", NULL, "PGA" }, 12862306a36Sopenharmony_ci { "OUTR", NULL, "PGA" }, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const struct snd_soc_component_driver max9759_component_driver = { 13262306a36Sopenharmony_ci .controls = max9759_dapm_controls, 13362306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(max9759_dapm_controls), 13462306a36Sopenharmony_ci .dapm_widgets = max9759_dapm_widgets, 13562306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(max9759_dapm_widgets), 13662306a36Sopenharmony_ci .dapm_routes = max9759_dapm_routes, 13762306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(max9759_dapm_routes), 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int max9759_probe(struct platform_device *pdev) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 14362306a36Sopenharmony_ci struct max9759 *priv; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 14662306a36Sopenharmony_ci if (!priv) 14762306a36Sopenharmony_ci return -ENOMEM; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH); 15262306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_shutdown)) 15362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), 15462306a36Sopenharmony_ci "Failed to get 'shutdown' gpio"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH); 15762306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_mute)) 15862306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute), 15962306a36Sopenharmony_ci "Failed to get 'mute' gpio"); 16062306a36Sopenharmony_ci priv->is_mute = true; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH); 16362306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_gain)) 16462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain), 16562306a36Sopenharmony_ci "Failed to get 'gain' gpios"); 16662306a36Sopenharmony_ci priv->gain = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (priv->gpiod_gain->ndescs != 2) { 16962306a36Sopenharmony_ci dev_err(dev, "Invalid 'gain' gpios count: %d", 17062306a36Sopenharmony_ci priv->gpiod_gain->ndescs); 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return devm_snd_soc_register_component(dev, &max9759_component_driver, 17562306a36Sopenharmony_ci NULL, 0); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#ifdef CONFIG_OF 17962306a36Sopenharmony_cistatic const struct of_device_id max9759_ids[] = { 18062306a36Sopenharmony_ci { .compatible = "maxim,max9759", }, 18162306a36Sopenharmony_ci { } 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max9759_ids); 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct platform_driver max9759_driver = { 18762306a36Sopenharmony_ci .driver = { 18862306a36Sopenharmony_ci .name = DRV_NAME, 18962306a36Sopenharmony_ci .of_match_table = of_match_ptr(max9759_ids), 19062306a36Sopenharmony_ci }, 19162306a36Sopenharmony_ci .probe = max9759_probe, 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cimodule_platform_driver(max9759_driver); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC MAX9759 amplifier driver"); 19762306a36Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 19862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 199