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