1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * arizona-spi.c -- Arizona SPI bus interface |
4 | * |
5 | * Copyright 2012 Wolfson Microelectronics plc |
6 | * |
7 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
8 | */ |
9 | |
10 | #include <linux/acpi.h> |
11 | #include <linux/err.h> |
12 | #include <linux/gpio/consumer.h> |
13 | #include <linux/gpio/machine.h> |
14 | #include <linux/module.h> |
15 | #include <linux/pm_runtime.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/regulator/consumer.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/spi/spi.h> |
20 | #include <linux/of.h> |
21 | #include <uapi/linux/input-event-codes.h> |
22 | |
23 | #include <linux/mfd/arizona/core.h> |
24 | |
25 | #include "arizona.h" |
26 | |
27 | #ifdef CONFIG_ACPI |
28 | static const struct acpi_gpio_params reset_gpios = { 1, 0, false }; |
29 | static const struct acpi_gpio_params ldoena_gpios = { 2, 0, false }; |
30 | |
31 | static const struct acpi_gpio_mapping arizona_acpi_gpios[] = { |
32 | { "reset-gpios" , &reset_gpios, 1, }, |
33 | { "wlf,ldoena-gpios" , &ldoena_gpios, 1 }, |
34 | { } |
35 | }; |
36 | |
37 | /* |
38 | * The ACPI resources for the device only describe external GPIO-s. They do |
39 | * not provide mappings for the GPIO-s coming from the Arizona codec itself. |
40 | */ |
41 | static const struct gpiod_lookup arizona_soc_gpios[] = { |
42 | { "arizona" , 2, "wlf,spkvdd-ena" , 0, GPIO_ACTIVE_HIGH }, |
43 | { "arizona" , 4, "wlf,micd-pol" , 0, GPIO_ACTIVE_LOW }, |
44 | }; |
45 | |
46 | static void arizona_spi_acpi_remove_lookup(void *lookup) |
47 | { |
48 | gpiod_remove_lookup_table(table: lookup); |
49 | } |
50 | |
51 | /* For ACPI tables from boards which ship with Windows as factory OS */ |
52 | static int arizona_spi_acpi_windows_probe(struct arizona *arizona) |
53 | { |
54 | struct gpiod_lookup_table *lookup; |
55 | acpi_status status; |
56 | int ret; |
57 | |
58 | /* Add mappings for the 2 ACPI declared GPIOs used for reset and ldo-ena */ |
59 | devm_acpi_dev_add_driver_gpios(dev: arizona->dev, gpios: arizona_acpi_gpios); |
60 | |
61 | /* Add lookups for the SoCs own GPIOs used for micdet-polarity and spkVDD-enable */ |
62 | lookup = devm_kzalloc(dev: arizona->dev, |
63 | struct_size(lookup, table, ARRAY_SIZE(arizona_soc_gpios) + 1), |
64 | GFP_KERNEL); |
65 | if (!lookup) |
66 | return -ENOMEM; |
67 | |
68 | lookup->dev_id = dev_name(dev: arizona->dev); |
69 | memcpy(lookup->table, arizona_soc_gpios, sizeof(arizona_soc_gpios)); |
70 | |
71 | gpiod_add_lookup_table(table: lookup); |
72 | ret = devm_add_action_or_reset(arizona->dev, arizona_spi_acpi_remove_lookup, lookup); |
73 | if (ret) |
74 | return ret; |
75 | |
76 | /* Enable 32KHz clock from SoC to codec for jack-detect */ |
77 | status = acpi_evaluate_object(ACPI_HANDLE(arizona->dev), pathname: "CLKE" , NULL, NULL); |
78 | if (ACPI_FAILURE(status)) |
79 | dev_warn(arizona->dev, "Failed to enable 32KHz clk ACPI error %d\n" , status); |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | /* For ACPI tables from boards which ship with Android as factory OS */ |
85 | static int arizona_spi_acpi_android_probe(struct arizona *arizona) |
86 | { |
87 | int ret; |
88 | |
89 | /* |
90 | * Get the reset GPIO, treating -ENOENT as -EPROBE_DEFER to wait for |
91 | * the x86-android-tablets module to register the board specific GPIO |
92 | * lookup table. |
93 | */ |
94 | arizona->pdata.reset = devm_gpiod_get(dev: arizona->dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
95 | if (IS_ERR(ptr: arizona->pdata.reset)) { |
96 | ret = PTR_ERR(ptr: arizona->pdata.reset); |
97 | if (ret == -ENOENT) { |
98 | dev_info_once(arizona->dev, |
99 | "Deferring probe till GPIO lookup is registered\n" ); |
100 | ret = -EPROBE_DEFER; |
101 | } |
102 | return dev_err_probe(dev: arizona->dev, err: ret, fmt: "getting reset GPIO\n" ); |
103 | } |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | /* |
109 | * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: |
110 | * Function A Play/Pause: 0 ohm |
111 | * Function D Voice assistant: 135 ohm |
112 | * Function B Volume Up 240 ohm |
113 | * Function C Volume Down 470 ohm |
114 | * Minimum Mic DC resistance 1000 ohm |
115 | * Minimum Ear speaker impedance 16 ohm |
116 | * Note the first max value below must be less then the min. speaker impedance, |
117 | * to allow CTIA/OMTP detection to work. The other max values are the closest |
118 | * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. |
119 | */ |
120 | static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { |
121 | { .max = 11, .key = KEY_PLAYPAUSE }, |
122 | { .max = 186, .key = KEY_VOICECOMMAND }, |
123 | { .max = 348, .key = KEY_VOLUMEUP }, |
124 | { .max = 752, .key = KEY_VOLUMEDOWN }, |
125 | }; |
126 | |
127 | static int arizona_spi_acpi_probe(struct arizona *arizona) |
128 | { |
129 | struct acpi_device *adev = ACPI_COMPANION(arizona->dev); |
130 | int ret; |
131 | |
132 | if (acpi_dev_hid_uid_match(adev, hid2: "10WM5102" , NULL)) |
133 | ret = arizona_spi_acpi_android_probe(arizona); |
134 | else |
135 | ret = arizona_spi_acpi_windows_probe(arizona); |
136 | |
137 | if (ret) |
138 | return ret; |
139 | |
140 | /* |
141 | * Some DSDTs wrongly declare the IRQ trigger-type as IRQF_TRIGGER_FALLING |
142 | * The IRQ line will stay low when a new IRQ event happens between reading |
143 | * the IRQ status flags and acknowledging them. When the IRQ line stays |
144 | * low like this the IRQ will never trigger again when its type is set |
145 | * to IRQF_TRIGGER_FALLING. Correct the IRQ trigger-type to fix this. |
146 | * |
147 | * Note theoretically it is possible that some boards are not capable |
148 | * of handling active low level interrupts. In that case setting the |
149 | * flag to IRQF_TRIGGER_FALLING would not be a bug (and we would need |
150 | * to work around this) but so far all known usages of IRQF_TRIGGER_FALLING |
151 | * are a bug in the board's DSDT. |
152 | */ |
153 | arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; |
154 | |
155 | /* Wait 200 ms after jack insertion */ |
156 | arizona->pdata.micd_detect_debounce = 200; |
157 | |
158 | /* Use standard AOSP values for headset-button mappings */ |
159 | arizona->pdata.micd_ranges = arizona_micd_aosp_ranges; |
160 | arizona->pdata.num_micd_ranges = ARRAY_SIZE(arizona_micd_aosp_ranges); |
161 | |
162 | /* Use left headphone speaker for HP vs line-out detection */ |
163 | arizona->pdata.hpdet_channel = ARIZONA_ACCDET_MODE_HPL; |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static const struct acpi_device_id arizona_acpi_match[] = { |
169 | { |
170 | .id = "WM510204" , |
171 | .driver_data = WM5102, |
172 | }, |
173 | { |
174 | .id = "WM510205" , |
175 | .driver_data = WM5102, |
176 | }, |
177 | { |
178 | .id = "10WM5102" , |
179 | .driver_data = WM5102, |
180 | }, |
181 | { } |
182 | }; |
183 | MODULE_DEVICE_TABLE(acpi, arizona_acpi_match); |
184 | #else |
185 | static int arizona_spi_acpi_probe(struct arizona *arizona) |
186 | { |
187 | return -ENODEV; |
188 | } |
189 | #endif |
190 | |
191 | static int arizona_spi_probe(struct spi_device *spi) |
192 | { |
193 | const struct spi_device_id *id = spi_get_device_id(sdev: spi); |
194 | const void *match_data; |
195 | struct arizona *arizona; |
196 | const struct regmap_config *regmap_config = NULL; |
197 | unsigned long type = 0; |
198 | int ret; |
199 | |
200 | match_data = device_get_match_data(dev: &spi->dev); |
201 | if (match_data) |
202 | type = (unsigned long)match_data; |
203 | else if (id) |
204 | type = id->driver_data; |
205 | |
206 | switch (type) { |
207 | case WM5102: |
208 | if (IS_ENABLED(CONFIG_MFD_WM5102)) |
209 | regmap_config = &wm5102_spi_regmap; |
210 | break; |
211 | case WM5110: |
212 | case WM8280: |
213 | if (IS_ENABLED(CONFIG_MFD_WM5110)) |
214 | regmap_config = &wm5110_spi_regmap; |
215 | break; |
216 | case WM1831: |
217 | case CS47L24: |
218 | if (IS_ENABLED(CONFIG_MFD_CS47L24)) |
219 | regmap_config = &cs47l24_spi_regmap; |
220 | break; |
221 | default: |
222 | dev_err(&spi->dev, "Unknown device type %ld\n" , type); |
223 | return -EINVAL; |
224 | } |
225 | |
226 | if (!regmap_config) { |
227 | dev_err(&spi->dev, |
228 | "No kernel support for device type %ld\n" , type); |
229 | return -EINVAL; |
230 | } |
231 | |
232 | arizona = devm_kzalloc(dev: &spi->dev, size: sizeof(*arizona), GFP_KERNEL); |
233 | if (arizona == NULL) |
234 | return -ENOMEM; |
235 | |
236 | arizona->regmap = devm_regmap_init_spi(spi, regmap_config); |
237 | if (IS_ERR(ptr: arizona->regmap)) { |
238 | ret = PTR_ERR(ptr: arizona->regmap); |
239 | dev_err(&spi->dev, "Failed to allocate register map: %d\n" , |
240 | ret); |
241 | return ret; |
242 | } |
243 | |
244 | arizona->type = type; |
245 | arizona->dev = &spi->dev; |
246 | arizona->irq = spi->irq; |
247 | |
248 | if (has_acpi_companion(dev: &spi->dev)) { |
249 | ret = arizona_spi_acpi_probe(arizona); |
250 | if (ret) |
251 | return ret; |
252 | } |
253 | |
254 | return arizona_dev_init(arizona); |
255 | } |
256 | |
257 | static void arizona_spi_remove(struct spi_device *spi) |
258 | { |
259 | struct arizona *arizona = spi_get_drvdata(spi); |
260 | |
261 | arizona_dev_exit(arizona); |
262 | } |
263 | |
264 | static const struct spi_device_id arizona_spi_ids[] = { |
265 | { "wm5102" , WM5102 }, |
266 | { "wm5110" , WM5110 }, |
267 | { "wm8280" , WM8280 }, |
268 | { "wm1831" , WM1831 }, |
269 | { "cs47l24" , CS47L24 }, |
270 | { }, |
271 | }; |
272 | MODULE_DEVICE_TABLE(spi, arizona_spi_ids); |
273 | |
274 | #ifdef CONFIG_OF |
275 | static const struct of_device_id arizona_spi_of_match[] = { |
276 | { .compatible = "wlf,wm5102" , .data = (void *)WM5102 }, |
277 | { .compatible = "wlf,wm5110" , .data = (void *)WM5110 }, |
278 | { .compatible = "wlf,wm8280" , .data = (void *)WM8280 }, |
279 | { .compatible = "wlf,wm1831" , .data = (void *)WM1831 }, |
280 | { .compatible = "cirrus,cs47l24" , .data = (void *)CS47L24 }, |
281 | {}, |
282 | }; |
283 | MODULE_DEVICE_TABLE(of, arizona_spi_of_match); |
284 | #endif |
285 | |
286 | static struct spi_driver arizona_spi_driver = { |
287 | .driver = { |
288 | .name = "arizona" , |
289 | .pm = pm_ptr(&arizona_pm_ops), |
290 | .of_match_table = of_match_ptr(arizona_spi_of_match), |
291 | .acpi_match_table = ACPI_PTR(arizona_acpi_match), |
292 | }, |
293 | .probe = arizona_spi_probe, |
294 | .remove = arizona_spi_remove, |
295 | .id_table = arizona_spi_ids, |
296 | }; |
297 | |
298 | module_spi_driver(arizona_spi_driver); |
299 | |
300 | MODULE_SOFTDEP("pre: arizona_ldo1" ); |
301 | MODULE_DESCRIPTION("Arizona SPI bus interface" ); |
302 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>" ); |
303 | MODULE_LICENSE("GPL" ); |
304 | |