1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. |
3 | * |
4 | * max98357a.c -- MAX98357A ALSA SoC Codec driver |
5 | */ |
6 | |
7 | #include <linux/acpi.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/device.h> |
10 | #include <linux/err.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/mod_devicetable.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <sound/pcm.h> |
18 | #include <sound/soc.h> |
19 | #include <sound/soc-dai.h> |
20 | #include <sound/soc-dapm.h> |
21 | |
22 | struct max98357a_priv { |
23 | struct gpio_desc *sdmode; |
24 | unsigned int sdmode_delay; |
25 | int sdmode_switch; |
26 | }; |
27 | |
28 | static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, |
29 | int cmd, struct snd_soc_dai *dai) |
30 | { |
31 | struct snd_soc_component *component = dai->component; |
32 | struct max98357a_priv *max98357a = |
33 | snd_soc_component_get_drvdata(c: component); |
34 | |
35 | if (!max98357a->sdmode) |
36 | return 0; |
37 | |
38 | switch (cmd) { |
39 | case SNDRV_PCM_TRIGGER_START: |
40 | case SNDRV_PCM_TRIGGER_RESUME: |
41 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
42 | mdelay(max98357a->sdmode_delay); |
43 | if (max98357a->sdmode_switch) { |
44 | gpiod_set_value(desc: max98357a->sdmode, value: 1); |
45 | dev_dbg(component->dev, "set sdmode to 1" ); |
46 | } |
47 | break; |
48 | case SNDRV_PCM_TRIGGER_STOP: |
49 | case SNDRV_PCM_TRIGGER_SUSPEND: |
50 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
51 | gpiod_set_value(desc: max98357a->sdmode, value: 0); |
52 | dev_dbg(component->dev, "set sdmode to 0" ); |
53 | break; |
54 | } |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static int max98357a_sdmode_event(struct snd_soc_dapm_widget *w, |
60 | struct snd_kcontrol *kcontrol, int event) |
61 | { |
62 | struct snd_soc_component *component = |
63 | snd_soc_dapm_to_component(dapm: w->dapm); |
64 | struct max98357a_priv *max98357a = |
65 | snd_soc_component_get_drvdata(c: component); |
66 | |
67 | if (event & SND_SOC_DAPM_POST_PMU) |
68 | max98357a->sdmode_switch = 1; |
69 | else if (event & SND_SOC_DAPM_POST_PMD) |
70 | max98357a->sdmode_switch = 0; |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = { |
76 | SND_SOC_DAPM_OUTPUT("Speaker" ), |
77 | SND_SOC_DAPM_OUT_DRV_E("SD_MODE" , SND_SOC_NOPM, 0, 0, NULL, 0, |
78 | max98357a_sdmode_event, |
79 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
80 | }; |
81 | |
82 | static const struct snd_soc_dapm_route max98357a_dapm_routes[] = { |
83 | {"SD_MODE" , NULL, "HiFi Playback" }, |
84 | {"Speaker" , NULL, "SD_MODE" }, |
85 | }; |
86 | |
87 | static const struct snd_soc_component_driver max98357a_component_driver = { |
88 | .dapm_widgets = max98357a_dapm_widgets, |
89 | .num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets), |
90 | .dapm_routes = max98357a_dapm_routes, |
91 | .num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes), |
92 | .idle_bias_on = 1, |
93 | .use_pmdown_time = 1, |
94 | .endianness = 1, |
95 | }; |
96 | |
97 | static const struct snd_soc_dai_ops max98357a_dai_ops = { |
98 | .trigger = max98357a_daiops_trigger, |
99 | }; |
100 | |
101 | static struct snd_soc_dai_driver max98357a_dai_driver = { |
102 | .name = "HiFi" , |
103 | .playback = { |
104 | .stream_name = "HiFi Playback" , |
105 | .formats = SNDRV_PCM_FMTBIT_S16 | |
106 | SNDRV_PCM_FMTBIT_S24 | |
107 | SNDRV_PCM_FMTBIT_S32, |
108 | .rates = SNDRV_PCM_RATE_8000 | |
109 | SNDRV_PCM_RATE_16000 | |
110 | SNDRV_PCM_RATE_32000 | |
111 | SNDRV_PCM_RATE_44100 | |
112 | SNDRV_PCM_RATE_48000 | |
113 | SNDRV_PCM_RATE_88200 | |
114 | SNDRV_PCM_RATE_96000, |
115 | .rate_min = 8000, |
116 | .rate_max = 96000, |
117 | .channels_min = 1, |
118 | .channels_max = 2, |
119 | }, |
120 | .ops = &max98357a_dai_ops, |
121 | }; |
122 | |
123 | static int max98357a_platform_probe(struct platform_device *pdev) |
124 | { |
125 | struct max98357a_priv *max98357a; |
126 | int ret; |
127 | |
128 | max98357a = devm_kzalloc(dev: &pdev->dev, size: sizeof(*max98357a), GFP_KERNEL); |
129 | if (!max98357a) |
130 | return -ENOMEM; |
131 | |
132 | max98357a->sdmode = devm_gpiod_get_optional(dev: &pdev->dev, |
133 | con_id: "sdmode" , flags: GPIOD_OUT_LOW); |
134 | if (IS_ERR(ptr: max98357a->sdmode)) |
135 | return PTR_ERR(ptr: max98357a->sdmode); |
136 | |
137 | ret = device_property_read_u32(dev: &pdev->dev, propname: "sdmode-delay" , |
138 | val: &max98357a->sdmode_delay); |
139 | if (ret) { |
140 | max98357a->sdmode_delay = 0; |
141 | dev_dbg(&pdev->dev, |
142 | "no optional property 'sdmode-delay' found, " |
143 | "default: no delay\n" ); |
144 | } |
145 | |
146 | dev_set_drvdata(dev: &pdev->dev, data: max98357a); |
147 | |
148 | return devm_snd_soc_register_component(dev: &pdev->dev, |
149 | component_driver: &max98357a_component_driver, |
150 | dai_drv: &max98357a_dai_driver, num_dai: 1); |
151 | } |
152 | |
153 | #ifdef CONFIG_OF |
154 | static const struct of_device_id max98357a_device_id[] = { |
155 | { .compatible = "maxim,max98357a" }, |
156 | { .compatible = "maxim,max98360a" }, |
157 | {} |
158 | }; |
159 | MODULE_DEVICE_TABLE(of, max98357a_device_id); |
160 | #endif |
161 | |
162 | #ifdef CONFIG_ACPI |
163 | static const struct acpi_device_id max98357a_acpi_match[] = { |
164 | { "MX98357A" , 0 }, |
165 | { "MX98360A" , 0 }, |
166 | {}, |
167 | }; |
168 | MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match); |
169 | #endif |
170 | |
171 | static struct platform_driver max98357a_platform_driver = { |
172 | .driver = { |
173 | .name = "max98357a" , |
174 | .of_match_table = of_match_ptr(max98357a_device_id), |
175 | .acpi_match_table = ACPI_PTR(max98357a_acpi_match), |
176 | }, |
177 | .probe = max98357a_platform_probe, |
178 | }; |
179 | module_platform_driver(max98357a_platform_driver); |
180 | |
181 | MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver" ); |
182 | MODULE_LICENSE("GPL v2" ); |
183 | |