1*4d1a9952SDavid Lin // SPDX-License-Identifier: GPL-2.0-only 2*4d1a9952SDavid Lin // 3*4d1a9952SDavid Lin // nau8315.c -- NAU8315 ALSA SoC Audio Amplifier Driver 4*4d1a9952SDavid Lin // 5*4d1a9952SDavid Lin // Copyright 2020 Nuvoton Technology Crop. 6*4d1a9952SDavid Lin // 7*4d1a9952SDavid Lin // Author: David Lin <ctlin0@nuvoton.com> 8*4d1a9952SDavid Lin // 9*4d1a9952SDavid Lin // Based on MAX98357A.c 10*4d1a9952SDavid Lin 11*4d1a9952SDavid Lin #include <linux/acpi.h> 12*4d1a9952SDavid Lin #include <linux/device.h> 13*4d1a9952SDavid Lin #include <linux/err.h> 14*4d1a9952SDavid Lin #include <linux/gpio.h> 15*4d1a9952SDavid Lin #include <linux/gpio/consumer.h> 16*4d1a9952SDavid Lin #include <linux/kernel.h> 17*4d1a9952SDavid Lin #include <linux/module.h> 18*4d1a9952SDavid Lin #include <linux/of.h> 19*4d1a9952SDavid Lin #include <linux/platform_device.h> 20*4d1a9952SDavid Lin #include <sound/pcm.h> 21*4d1a9952SDavid Lin #include <sound/soc.h> 22*4d1a9952SDavid Lin #include <sound/soc-dai.h> 23*4d1a9952SDavid Lin #include <sound/soc-dapm.h> 24*4d1a9952SDavid Lin 25*4d1a9952SDavid Lin struct nau8315_priv { 26*4d1a9952SDavid Lin struct gpio_desc *enable; 27*4d1a9952SDavid Lin int enpin_switch; 28*4d1a9952SDavid Lin }; 29*4d1a9952SDavid Lin 30*4d1a9952SDavid Lin static int nau8315_daiops_trigger(struct snd_pcm_substream *substream, 31*4d1a9952SDavid Lin int cmd, struct snd_soc_dai *dai) 32*4d1a9952SDavid Lin { 33*4d1a9952SDavid Lin struct snd_soc_component *component = dai->component; 34*4d1a9952SDavid Lin struct nau8315_priv *nau8315 = 35*4d1a9952SDavid Lin snd_soc_component_get_drvdata(component); 36*4d1a9952SDavid Lin 37*4d1a9952SDavid Lin if (!nau8315->enable) 38*4d1a9952SDavid Lin return 0; 39*4d1a9952SDavid Lin 40*4d1a9952SDavid Lin switch (cmd) { 41*4d1a9952SDavid Lin case SNDRV_PCM_TRIGGER_START: 42*4d1a9952SDavid Lin case SNDRV_PCM_TRIGGER_RESUME: 43*4d1a9952SDavid Lin case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 44*4d1a9952SDavid Lin if (nau8315->enpin_switch) { 45*4d1a9952SDavid Lin gpiod_set_value(nau8315->enable, 1); 46*4d1a9952SDavid Lin dev_dbg(component->dev, "set enable to 1"); 47*4d1a9952SDavid Lin } 48*4d1a9952SDavid Lin break; 49*4d1a9952SDavid Lin case SNDRV_PCM_TRIGGER_STOP: 50*4d1a9952SDavid Lin case SNDRV_PCM_TRIGGER_SUSPEND: 51*4d1a9952SDavid Lin case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 52*4d1a9952SDavid Lin gpiod_set_value(nau8315->enable, 0); 53*4d1a9952SDavid Lin dev_dbg(component->dev, "set enable to 0"); 54*4d1a9952SDavid Lin break; 55*4d1a9952SDavid Lin } 56*4d1a9952SDavid Lin 57*4d1a9952SDavid Lin return 0; 58*4d1a9952SDavid Lin } 59*4d1a9952SDavid Lin 60*4d1a9952SDavid Lin static int nau8315_enpin_event(struct snd_soc_dapm_widget *w, 61*4d1a9952SDavid Lin struct snd_kcontrol *kcontrol, int event) 62*4d1a9952SDavid Lin { 63*4d1a9952SDavid Lin struct snd_soc_component *component = 64*4d1a9952SDavid Lin snd_soc_dapm_to_component(w->dapm); 65*4d1a9952SDavid Lin struct nau8315_priv *nau8315 = 66*4d1a9952SDavid Lin snd_soc_component_get_drvdata(component); 67*4d1a9952SDavid Lin 68*4d1a9952SDavid Lin if (event & SND_SOC_DAPM_POST_PMU) 69*4d1a9952SDavid Lin nau8315->enpin_switch = 1; 70*4d1a9952SDavid Lin else if (event & SND_SOC_DAPM_POST_PMD) 71*4d1a9952SDavid Lin nau8315->enpin_switch = 0; 72*4d1a9952SDavid Lin 73*4d1a9952SDavid Lin return 0; 74*4d1a9952SDavid Lin } 75*4d1a9952SDavid Lin 76*4d1a9952SDavid Lin static const struct snd_soc_dapm_widget nau8315_dapm_widgets[] = { 77*4d1a9952SDavid Lin SND_SOC_DAPM_OUTPUT("Speaker"), 78*4d1a9952SDavid Lin SND_SOC_DAPM_OUT_DRV_E("EN_Pin", SND_SOC_NOPM, 0, 0, NULL, 0, 79*4d1a9952SDavid Lin nau8315_enpin_event, 80*4d1a9952SDavid Lin SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 81*4d1a9952SDavid Lin }; 82*4d1a9952SDavid Lin 83*4d1a9952SDavid Lin static const struct snd_soc_dapm_route nau8315_dapm_routes[] = { 84*4d1a9952SDavid Lin {"EN_Pin", NULL, "HiFi Playback"}, 85*4d1a9952SDavid Lin {"Speaker", NULL, "EN_Pin"}, 86*4d1a9952SDavid Lin }; 87*4d1a9952SDavid Lin 88*4d1a9952SDavid Lin static const struct snd_soc_component_driver nau8315_component_driver = { 89*4d1a9952SDavid Lin .dapm_widgets = nau8315_dapm_widgets, 90*4d1a9952SDavid Lin .num_dapm_widgets = ARRAY_SIZE(nau8315_dapm_widgets), 91*4d1a9952SDavid Lin .dapm_routes = nau8315_dapm_routes, 92*4d1a9952SDavid Lin .num_dapm_routes = ARRAY_SIZE(nau8315_dapm_routes), 93*4d1a9952SDavid Lin .idle_bias_on = 1, 94*4d1a9952SDavid Lin .use_pmdown_time = 1, 95*4d1a9952SDavid Lin .endianness = 1, 96*4d1a9952SDavid Lin .non_legacy_dai_naming = 1, 97*4d1a9952SDavid Lin }; 98*4d1a9952SDavid Lin 99*4d1a9952SDavid Lin static const struct snd_soc_dai_ops nau8315_dai_ops = { 100*4d1a9952SDavid Lin .trigger = nau8315_daiops_trigger, 101*4d1a9952SDavid Lin }; 102*4d1a9952SDavid Lin 103*4d1a9952SDavid Lin #define NAU8315_RATES SNDRV_PCM_RATE_8000_96000 104*4d1a9952SDavid Lin #define NAU8315_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE) 105*4d1a9952SDavid Lin 106*4d1a9952SDavid Lin static struct snd_soc_dai_driver nau8315_dai_driver = { 107*4d1a9952SDavid Lin .name = "nau8315-hifi", 108*4d1a9952SDavid Lin .playback = { 109*4d1a9952SDavid Lin .stream_name = "HiFi Playback", 110*4d1a9952SDavid Lin .formats = NAU8315_FORMATS, 111*4d1a9952SDavid Lin .rates = NAU8315_RATES, 112*4d1a9952SDavid Lin .channels_min = 1, 113*4d1a9952SDavid Lin .channels_max = 2, 114*4d1a9952SDavid Lin }, 115*4d1a9952SDavid Lin .ops = &nau8315_dai_ops, 116*4d1a9952SDavid Lin }; 117*4d1a9952SDavid Lin 118*4d1a9952SDavid Lin static int nau8315_platform_probe(struct platform_device *pdev) 119*4d1a9952SDavid Lin { 120*4d1a9952SDavid Lin struct nau8315_priv *nau8315; 121*4d1a9952SDavid Lin 122*4d1a9952SDavid Lin nau8315 = devm_kzalloc(&pdev->dev, sizeof(*nau8315), GFP_KERNEL); 123*4d1a9952SDavid Lin if (!nau8315) 124*4d1a9952SDavid Lin return -ENOMEM; 125*4d1a9952SDavid Lin 126*4d1a9952SDavid Lin nau8315->enable = devm_gpiod_get_optional(&pdev->dev, 127*4d1a9952SDavid Lin "enable", GPIOD_OUT_LOW); 128*4d1a9952SDavid Lin if (IS_ERR(nau8315->enable)) 129*4d1a9952SDavid Lin return PTR_ERR(nau8315->enable); 130*4d1a9952SDavid Lin 131*4d1a9952SDavid Lin dev_set_drvdata(&pdev->dev, nau8315); 132*4d1a9952SDavid Lin 133*4d1a9952SDavid Lin return devm_snd_soc_register_component(&pdev->dev, 134*4d1a9952SDavid Lin &nau8315_component_driver, 135*4d1a9952SDavid Lin &nau8315_dai_driver, 1); 136*4d1a9952SDavid Lin } 137*4d1a9952SDavid Lin 138*4d1a9952SDavid Lin #ifdef CONFIG_OF 139*4d1a9952SDavid Lin static const struct of_device_id nau8315_device_id[] = { 140*4d1a9952SDavid Lin { .compatible = "nuvoton,nau8315" }, 141*4d1a9952SDavid Lin {} 142*4d1a9952SDavid Lin }; 143*4d1a9952SDavid Lin MODULE_DEVICE_TABLE(of, nau8315_device_id); 144*4d1a9952SDavid Lin #endif 145*4d1a9952SDavid Lin 146*4d1a9952SDavid Lin #ifdef CONFIG_ACPI 147*4d1a9952SDavid Lin static const struct acpi_device_id nau8315_acpi_match[] = { 148*4d1a9952SDavid Lin { "NVTN2010", 0 }, 149*4d1a9952SDavid Lin {}, 150*4d1a9952SDavid Lin }; 151*4d1a9952SDavid Lin MODULE_DEVICE_TABLE(acpi, nau8315_acpi_match); 152*4d1a9952SDavid Lin #endif 153*4d1a9952SDavid Lin 154*4d1a9952SDavid Lin static struct platform_driver nau8315_platform_driver = { 155*4d1a9952SDavid Lin .driver = { 156*4d1a9952SDavid Lin .name = "nau8315", 157*4d1a9952SDavid Lin .of_match_table = of_match_ptr(nau8315_device_id), 158*4d1a9952SDavid Lin .acpi_match_table = ACPI_PTR(nau8315_acpi_match), 159*4d1a9952SDavid Lin }, 160*4d1a9952SDavid Lin .probe = nau8315_platform_probe, 161*4d1a9952SDavid Lin }; 162*4d1a9952SDavid Lin module_platform_driver(nau8315_platform_driver); 163*4d1a9952SDavid Lin 164*4d1a9952SDavid Lin MODULE_DESCRIPTION("ASoC NAU8315 Mono Class-D Amplifier Driver"); 165*4d1a9952SDavid Lin MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>"); 166*4d1a9952SDavid Lin MODULE_LICENSE("GPL v2"); 167