1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // rt1015p.c -- RT1015P ALSA SoC audio amplifier driver |
4 | // |
5 | // Copyright 2020 The Linux Foundation. All rights reserved. |
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/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <sound/pcm.h> |
17 | #include <sound/soc.h> |
18 | #include <sound/soc-dai.h> |
19 | #include <sound/soc-dapm.h> |
20 | |
21 | struct rt1015p_priv { |
22 | struct gpio_desc *sdb; |
23 | bool calib_done; |
24 | }; |
25 | |
26 | static int rt1015p_sdb_event(struct snd_soc_dapm_widget *w, |
27 | struct snd_kcontrol *kcontrol, int event) |
28 | { |
29 | struct snd_soc_component *component = |
30 | snd_soc_dapm_to_component(dapm: w->dapm); |
31 | struct rt1015p_priv *rt1015p = |
32 | snd_soc_component_get_drvdata(c: component); |
33 | |
34 | if (!rt1015p->sdb) |
35 | return 0; |
36 | |
37 | switch (event) { |
38 | case SND_SOC_DAPM_PRE_PMU: |
39 | gpiod_set_value_cansleep(desc: rt1015p->sdb, value: 1); |
40 | dev_dbg(component->dev, "set sdb to 1" ); |
41 | |
42 | if (!rt1015p->calib_done) { |
43 | msleep(msecs: 300); |
44 | rt1015p->calib_done = true; |
45 | } |
46 | break; |
47 | case SND_SOC_DAPM_POST_PMD: |
48 | gpiod_set_value_cansleep(desc: rt1015p->sdb, value: 0); |
49 | dev_dbg(component->dev, "set sdb to 0" ); |
50 | break; |
51 | default: |
52 | break; |
53 | } |
54 | |
55 | return 0; |
56 | } |
57 | |
58 | static const struct snd_soc_dapm_widget rt1015p_dapm_widgets[] = { |
59 | SND_SOC_DAPM_OUTPUT("Speaker" ), |
60 | SND_SOC_DAPM_OUT_DRV_E("SDB" , SND_SOC_NOPM, 0, 0, NULL, 0, |
61 | rt1015p_sdb_event, |
62 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
63 | }; |
64 | |
65 | static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = { |
66 | {"SDB" , NULL, "HiFi Playback" }, |
67 | {"Speaker" , NULL, "SDB" }, |
68 | }; |
69 | |
70 | #ifdef CONFIG_PM |
71 | static int rt1015p_suspend(struct snd_soc_component *component) |
72 | { |
73 | struct rt1015p_priv *rt1015p = snd_soc_component_get_drvdata(c: component); |
74 | |
75 | rt1015p->calib_done = false; |
76 | return 0; |
77 | } |
78 | #else |
79 | #define rt1015p_suspend NULL |
80 | #endif |
81 | |
82 | static const struct snd_soc_component_driver rt1015p_component_driver = { |
83 | .suspend = rt1015p_suspend, |
84 | .dapm_widgets = rt1015p_dapm_widgets, |
85 | .num_dapm_widgets = ARRAY_SIZE(rt1015p_dapm_widgets), |
86 | .dapm_routes = rt1015p_dapm_routes, |
87 | .num_dapm_routes = ARRAY_SIZE(rt1015p_dapm_routes), |
88 | .idle_bias_on = 1, |
89 | .use_pmdown_time = 1, |
90 | .endianness = 1, |
91 | }; |
92 | |
93 | static struct snd_soc_dai_driver rt1015p_dai_driver = { |
94 | .name = "HiFi" , |
95 | .playback = { |
96 | .stream_name = "HiFi Playback" , |
97 | .formats = SNDRV_PCM_FMTBIT_S24 | |
98 | SNDRV_PCM_FMTBIT_S32, |
99 | .rates = SNDRV_PCM_RATE_48000, |
100 | .channels_min = 1, |
101 | .channels_max = 2, |
102 | }, |
103 | }; |
104 | |
105 | static int rt1015p_platform_probe(struct platform_device *pdev) |
106 | { |
107 | struct rt1015p_priv *rt1015p; |
108 | |
109 | rt1015p = devm_kzalloc(dev: &pdev->dev, size: sizeof(*rt1015p), GFP_KERNEL); |
110 | if (!rt1015p) |
111 | return -ENOMEM; |
112 | |
113 | rt1015p->sdb = devm_gpiod_get_optional(dev: &pdev->dev, |
114 | con_id: "sdb" , flags: GPIOD_OUT_LOW); |
115 | if (IS_ERR(ptr: rt1015p->sdb)) |
116 | return PTR_ERR(ptr: rt1015p->sdb); |
117 | |
118 | dev_set_drvdata(dev: &pdev->dev, data: rt1015p); |
119 | |
120 | return devm_snd_soc_register_component(dev: &pdev->dev, |
121 | component_driver: &rt1015p_component_driver, |
122 | dai_drv: &rt1015p_dai_driver, num_dai: 1); |
123 | } |
124 | |
125 | #ifdef CONFIG_OF |
126 | static const struct of_device_id rt1015p_device_id[] = { |
127 | { .compatible = "realtek,rt1015p" }, |
128 | { .compatible = "realtek,rt1019p" }, |
129 | {} |
130 | }; |
131 | MODULE_DEVICE_TABLE(of, rt1015p_device_id); |
132 | #endif |
133 | |
134 | #ifdef CONFIG_ACPI |
135 | static const struct acpi_device_id rt1015p_acpi_match[] = { |
136 | { "RTL1015" , 0}, |
137 | { "RTL1019" , 0}, |
138 | { }, |
139 | }; |
140 | MODULE_DEVICE_TABLE(acpi, rt1015p_acpi_match); |
141 | #endif |
142 | |
143 | static struct platform_driver rt1015p_platform_driver = { |
144 | .driver = { |
145 | .name = "rt1015p" , |
146 | .of_match_table = of_match_ptr(rt1015p_device_id), |
147 | .acpi_match_table = ACPI_PTR(rt1015p_acpi_match), |
148 | }, |
149 | .probe = rt1015p_platform_probe, |
150 | }; |
151 | module_platform_driver(rt1015p_platform_driver); |
152 | |
153 | MODULE_DESCRIPTION("ASoC RT1015P driver" ); |
154 | MODULE_LICENSE("GPL v2" ); |
155 | |