1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Board info for Lenovo X86 tablets which ship with Android as the factory image |
4 | * and which have broken DSDT tables. The factory kernels shipped on these |
5 | * devices typically have a bunch of things hardcoded, rather than specified |
6 | * in their DSDT. |
7 | * |
8 | * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> |
9 | */ |
10 | |
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
12 | |
13 | #include <linux/efi.h> |
14 | #include <linux/gpio/machine.h> |
15 | #include <linux/mfd/arizona/pdata.h> |
16 | #include <linux/mfd/arizona/registers.h> |
17 | #include <linux/mfd/intel_soc_pmic.h> |
18 | #include <linux/pinctrl/consumer.h> |
19 | #include <linux/pinctrl/machine.h> |
20 | #include <linux/platform_data/lp855x.h> |
21 | #include <linux/platform_device.h> |
22 | #include <linux/reboot.h> |
23 | #include <linux/rmi.h> |
24 | #include <linux/spi/spi.h> |
25 | |
26 | #include "shared-psy-info.h" |
27 | #include "x86-android-tablets.h" |
28 | |
29 | /* |
30 | * Various Lenovo models use a TI LP8557 LED backlight controller with its PWM |
31 | * input connected to a PWM output coming from the LCD panel's controller. |
32 | * The Android kernels have a hack in the i915 driver to write a non-standard |
33 | * panel specific DSI register to set the duty-cycle of the LCD's PWM output. |
34 | * |
35 | * To avoid having to have a similar hack in the mainline kernel program the |
36 | * LP8557 to directly set the level and use the lp855x_bl driver for control. |
37 | * |
38 | * The LP8557 can either be configured to multiply its PWM input and |
39 | * the I2C register set level (requiring both to be at 100% for 100% output); |
40 | * or to only take the I2C register set level into account. |
41 | * |
42 | * Multiplying the 2 levels is useful because this will turn off the backlight |
43 | * when the panel goes off and turns off its PWM output. |
44 | * |
45 | * But on some models the panel's PWM output defaults to a duty-cycle of |
46 | * much less then 100%, severely limiting max brightness. In this case |
47 | * the LP8557 should be configured to only take the I2C register into |
48 | * account and the i915 driver must turn off the panel and the backlight |
49 | * separately using e.g. VBT MIPI sequences to turn off the backlight. |
50 | */ |
51 | static struct lp855x_platform_data lenovo_lp8557_pwm_and_reg_pdata = { |
52 | .device_control = 0x86, |
53 | .initial_brightness = 128, |
54 | }; |
55 | |
56 | static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = { |
57 | .device_control = 0x85, |
58 | .initial_brightness = 128, |
59 | }; |
60 | |
61 | /* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */ |
62 | |
63 | static const struct property_entry lenovo_yb1_x90_wacom_props[] = { |
64 | PROPERTY_ENTRY_U32("hid-descr-addr" , 0x0001), |
65 | PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms" , 150), |
66 | { } |
67 | }; |
68 | |
69 | static const struct software_node lenovo_yb1_x90_wacom_node = { |
70 | .properties = lenovo_yb1_x90_wacom_props, |
71 | }; |
72 | |
73 | /* |
74 | * The HiDeep IST940E touchscreen comes up in I2C-HID mode. The native protocol |
75 | * reports ABS_MT_PRESSURE and ABS_MT_TOUCH_MAJOR which are not reported in HID |
76 | * mode, so using native mode is preferred. |
77 | * It could alternatively be used in HID mode by changing the properties to: |
78 | * PROPERTY_ENTRY_U32("hid-descr-addr", 0x0020), |
79 | * PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120), |
80 | * and changing board_info.type to "hid-over-i2c". |
81 | */ |
82 | static const struct property_entry lenovo_yb1_x90_hideep_ts_props[] = { |
83 | PROPERTY_ENTRY_U32("touchscreen-size-x" , 1200), |
84 | PROPERTY_ENTRY_U32("touchscreen-size-y" , 1920), |
85 | PROPERTY_ENTRY_U32("touchscreen-max-pressure" , 16384), |
86 | PROPERTY_ENTRY_BOOL("hideep,force-native-protocol" ), |
87 | { } |
88 | }; |
89 | |
90 | static const struct software_node lenovo_yb1_x90_hideep_ts_node = { |
91 | .properties = lenovo_yb1_x90_hideep_ts_props, |
92 | }; |
93 | |
94 | static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst = { |
95 | { |
96 | /* BQ27542 fuel-gauge */ |
97 | .board_info = { |
98 | .type = "bq27542" , |
99 | .addr = 0x55, |
100 | .dev_name = "bq27542" , |
101 | .swnode = &fg_bq25890_supply_node, |
102 | }, |
103 | .adapter_path = "\\_SB_.PCI0.I2C1" , |
104 | }, { |
105 | /* Goodix Touchscreen in keyboard half */ |
106 | .board_info = { |
107 | .type = "GDIX1001:00" , |
108 | .addr = 0x14, |
109 | .dev_name = "goodix_ts" , |
110 | }, |
111 | .adapter_path = "\\_SB_.PCI0.I2C2" , |
112 | .irq_data = { |
113 | .type = X86_ACPI_IRQ_TYPE_GPIOINT, |
114 | .chip = "INT33FF:01" , |
115 | .index = 56, |
116 | .trigger = ACPI_EDGE_SENSITIVE, |
117 | .polarity = ACPI_ACTIVE_LOW, |
118 | .con_id = "goodix_ts_irq" , |
119 | .free_gpio = true, |
120 | }, |
121 | }, { |
122 | /* Wacom Digitizer in keyboard half */ |
123 | .board_info = { |
124 | .type = "hid-over-i2c" , |
125 | .addr = 0x09, |
126 | .dev_name = "wacom" , |
127 | .swnode = &lenovo_yb1_x90_wacom_node, |
128 | }, |
129 | .adapter_path = "\\_SB_.PCI0.I2C4" , |
130 | .irq_data = { |
131 | .type = X86_ACPI_IRQ_TYPE_GPIOINT, |
132 | .chip = "INT33FF:01" , |
133 | .index = 49, |
134 | .trigger = ACPI_LEVEL_SENSITIVE, |
135 | .polarity = ACPI_ACTIVE_LOW, |
136 | .con_id = "wacom_irq" , |
137 | }, |
138 | }, { |
139 | /* LP8557 Backlight controller */ |
140 | .board_info = { |
141 | .type = "lp8557" , |
142 | .addr = 0x2c, |
143 | .dev_name = "lp8557" , |
144 | .platform_data = &lenovo_lp8557_pwm_and_reg_pdata, |
145 | }, |
146 | .adapter_path = "\\_SB_.PCI0.I2C4" , |
147 | }, { |
148 | /* HiDeep IST940E Touchscreen in display half */ |
149 | .board_info = { |
150 | .type = "hideep_ts" , |
151 | .addr = 0x6c, |
152 | .dev_name = "hideep_ts" , |
153 | .swnode = &lenovo_yb1_x90_hideep_ts_node, |
154 | }, |
155 | .adapter_path = "\\_SB_.PCI0.I2C6" , |
156 | .irq_data = { |
157 | .type = X86_ACPI_IRQ_TYPE_GPIOINT, |
158 | .chip = "INT33FF:03" , |
159 | .index = 77, |
160 | .trigger = ACPI_LEVEL_SENSITIVE, |
161 | .polarity = ACPI_ACTIVE_LOW, |
162 | .con_id = "hideep_ts_irq" , |
163 | }, |
164 | }, |
165 | }; |
166 | |
167 | static const struct platform_device_info lenovo_yb1_x90_pdevs[] __initconst = { |
168 | { |
169 | .name = "yogabook-touch-kbd-digitizer-switch" , |
170 | .id = PLATFORM_DEVID_NONE, |
171 | }, |
172 | }; |
173 | |
174 | /* |
175 | * DSDT says UART path is "\\_SB.PCIO.URT1" with a letter 'O' instead of |
176 | * the number '0' add the link manually. |
177 | */ |
178 | static const struct x86_serdev_info lenovo_yb1_x90_serdevs[] __initconst = { |
179 | { |
180 | .ctrl_hid = "8086228A" , |
181 | .ctrl_uid = "1" , |
182 | .ctrl_devname = "serial0" , |
183 | .serdev_hid = "BCM2E1A" , |
184 | }, |
185 | }; |
186 | |
187 | static const struct x86_gpio_button lenovo_yb1_x90_lid __initconst = { |
188 | .button = { |
189 | .code = SW_LID, |
190 | .active_low = true, |
191 | .desc = "lid_sw" , |
192 | .type = EV_SW, |
193 | .wakeup = true, |
194 | .debounce_interval = 50, |
195 | }, |
196 | .chip = "INT33FF:02" , |
197 | .pin = 19, |
198 | }; |
199 | |
200 | static struct gpiod_lookup_table lenovo_yb1_x90_goodix_gpios = { |
201 | .dev_id = "i2c-goodix_ts" , |
202 | .table = { |
203 | GPIO_LOOKUP("INT33FF:01" , 53, "reset" , GPIO_ACTIVE_HIGH), |
204 | GPIO_LOOKUP("INT33FF:01" , 56, "irq" , GPIO_ACTIVE_HIGH), |
205 | { } |
206 | }, |
207 | }; |
208 | |
209 | static struct gpiod_lookup_table lenovo_yb1_x90_hideep_gpios = { |
210 | .dev_id = "i2c-hideep_ts" , |
211 | .table = { |
212 | GPIO_LOOKUP("INT33FF:00" , 7, "reset" , GPIO_ACTIVE_LOW), |
213 | { } |
214 | }, |
215 | }; |
216 | |
217 | static struct gpiod_lookup_table lenovo_yb1_x90_wacom_gpios = { |
218 | .dev_id = "i2c-wacom" , |
219 | .table = { |
220 | GPIO_LOOKUP("INT33FF:00" , 82, "reset" , GPIO_ACTIVE_LOW), |
221 | { } |
222 | }, |
223 | }; |
224 | |
225 | static struct gpiod_lookup_table * const lenovo_yb1_x90_gpios[] = { |
226 | &lenovo_yb1_x90_hideep_gpios, |
227 | &lenovo_yb1_x90_goodix_gpios, |
228 | &lenovo_yb1_x90_wacom_gpios, |
229 | NULL |
230 | }; |
231 | |
232 | static int __init lenovo_yb1_x90_init(void) |
233 | { |
234 | /* Enable the regulators used by the touchscreens */ |
235 | |
236 | /* Vprog3B 3.0V used by the goodix touchscreen in the keyboard half */ |
237 | intel_soc_pmic_exec_mipi_pmic_seq_element(i2c_address: 0x6e, reg_address: 0x9b, value: 0x02, mask: 0xff); |
238 | |
239 | /* Vprog4D 3.0V used by the HiDeep touchscreen in the display half */ |
240 | intel_soc_pmic_exec_mipi_pmic_seq_element(i2c_address: 0x6e, reg_address: 0x9f, value: 0x02, mask: 0xff); |
241 | |
242 | /* Vprog5A 1.8V used by the HiDeep touchscreen in the display half */ |
243 | intel_soc_pmic_exec_mipi_pmic_seq_element(i2c_address: 0x6e, reg_address: 0xa0, value: 0x02, mask: 0xff); |
244 | |
245 | /* Vprog5B 1.8V used by the goodix touchscreen in the keyboard half */ |
246 | intel_soc_pmic_exec_mipi_pmic_seq_element(i2c_address: 0x6e, reg_address: 0xa1, value: 0x02, mask: 0xff); |
247 | |
248 | return 0; |
249 | } |
250 | |
251 | const struct x86_dev_info lenovo_yogabook_x90_info __initconst = { |
252 | .i2c_client_info = lenovo_yb1_x90_i2c_clients, |
253 | .i2c_client_count = ARRAY_SIZE(lenovo_yb1_x90_i2c_clients), |
254 | .pdev_info = lenovo_yb1_x90_pdevs, |
255 | .pdev_count = ARRAY_SIZE(lenovo_yb1_x90_pdevs), |
256 | .serdev_info = lenovo_yb1_x90_serdevs, |
257 | .serdev_count = ARRAY_SIZE(lenovo_yb1_x90_serdevs), |
258 | .gpio_button = &lenovo_yb1_x90_lid, |
259 | .gpio_button_count = 1, |
260 | .gpiod_lookup_tables = lenovo_yb1_x90_gpios, |
261 | .init = lenovo_yb1_x90_init, |
262 | }; |
263 | |
264 | /* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */ |
265 | static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = { |
266 | { |
267 | /* BQ27542 fuel-gauge */ |
268 | .board_info = { |
269 | .type = "bq27542" , |
270 | .addr = 0x55, |
271 | .dev_name = "bq27542" , |
272 | .swnode = &fg_bq25890_supply_node, |
273 | }, |
274 | .adapter_path = "\\_SB_.PCI0.I2C1" , |
275 | }, |
276 | }; |
277 | |
278 | const struct x86_dev_info lenovo_yogabook_x91_info __initconst = { |
279 | .i2c_client_info = lenovo_yogabook_x91_i2c_clients, |
280 | .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x91_i2c_clients), |
281 | }; |
282 | |
283 | /* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ |
284 | static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { |
285 | PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from" , tusb1211_chg_det_psy, 1), |
286 | PROPERTY_ENTRY_REF("monitored-battery" , &generic_lipo_hv_4v35_battery_node), |
287 | PROPERTY_ENTRY_BOOL("omit-battery-class" ), |
288 | PROPERTY_ENTRY_BOOL("disable-reset" ), |
289 | { } |
290 | }; |
291 | |
292 | static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { |
293 | .properties = lenovo_yoga_tab2_830_1050_bq24190_props, |
294 | }; |
295 | |
296 | static const struct x86_gpio_button lenovo_yoga_tab2_830_1050_lid __initconst = { |
297 | .button = { |
298 | .code = SW_LID, |
299 | .active_low = true, |
300 | .desc = "lid_sw" , |
301 | .type = EV_SW, |
302 | .wakeup = true, |
303 | .debounce_interval = 50, |
304 | }, |
305 | .chip = "INT33FC:02" , |
306 | .pin = 26, |
307 | }; |
308 | |
309 | /* This gets filled by lenovo_yoga_tab2_830_1050_init() */ |
310 | static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; |
311 | |
312 | static struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initdata = { |
313 | { |
314 | /* |
315 | * This must be the first entry because lenovo_yoga_tab2_830_1050_init() |
316 | * may update its swnode. LSM303DA accelerometer + magnetometer. |
317 | */ |
318 | .board_info = { |
319 | .type = "lsm303d" , |
320 | .addr = 0x1d, |
321 | .dev_name = "lsm303d" , |
322 | }, |
323 | .adapter_path = "\\_SB_.I2C5" , |
324 | }, { |
325 | /* AL3320A ambient light sensor */ |
326 | .board_info = { |
327 | .type = "al3320a" , |
328 | .addr = 0x1c, |
329 | .dev_name = "al3320a" , |
330 | }, |
331 | .adapter_path = "\\_SB_.I2C5" , |
332 | }, { |
333 | /* bq24292i battery charger */ |
334 | .board_info = { |
335 | .type = "bq24190" , |
336 | .addr = 0x6b, |
337 | .dev_name = "bq24292i" , |
338 | .swnode = &lenovo_yoga_tab2_830_1050_bq24190_node, |
339 | .platform_data = &bq24190_pdata, |
340 | }, |
341 | .adapter_path = "\\_SB_.I2C1" , |
342 | .irq_data = { |
343 | .type = X86_ACPI_IRQ_TYPE_GPIOINT, |
344 | .chip = "INT33FC:02" , |
345 | .index = 2, |
346 | .trigger = ACPI_EDGE_SENSITIVE, |
347 | .polarity = ACPI_ACTIVE_HIGH, |
348 | .con_id = "bq24292i_irq" , |
349 | }, |
350 | }, { |
351 | /* BQ27541 fuel-gauge */ |
352 | .board_info = { |
353 | .type = "bq27541" , |
354 | .addr = 0x55, |
355 | .dev_name = "bq27541" , |
356 | .swnode = &fg_bq24190_supply_node, |
357 | }, |
358 | .adapter_path = "\\_SB_.I2C1" , |
359 | }, { |
360 | /* Synaptics RMI touchscreen */ |
361 | .board_info = { |
362 | .type = "rmi4_i2c" , |
363 | .addr = 0x38, |
364 | .dev_name = "rmi4_i2c" , |
365 | .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata, |
366 | }, |
367 | .adapter_path = "\\_SB_.I2C6" , |
368 | .irq_data = { |
369 | .type = X86_ACPI_IRQ_TYPE_APIC, |
370 | .index = 0x45, |
371 | .trigger = ACPI_EDGE_SENSITIVE, |
372 | .polarity = ACPI_ACTIVE_HIGH, |
373 | }, |
374 | }, { |
375 | /* LP8557 Backlight controller */ |
376 | .board_info = { |
377 | .type = "lp8557" , |
378 | .addr = 0x2c, |
379 | .dev_name = "lp8557" , |
380 | .platform_data = &lenovo_lp8557_pwm_and_reg_pdata, |
381 | }, |
382 | .adapter_path = "\\_SB_.I2C3" , |
383 | }, |
384 | }; |
385 | |
386 | static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = { |
387 | .dev_id = "intel-int3496" , |
388 | .table = { |
389 | GPIO_LOOKUP("INT33FC:02" , 1, "mux" , GPIO_ACTIVE_LOW), |
390 | GPIO_LOOKUP("INT33FC:02" , 24, "id" , GPIO_ACTIVE_HIGH), |
391 | { } |
392 | }, |
393 | }; |
394 | |
395 | #define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" |
396 | |
397 | static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = { |
398 | .dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, |
399 | .table = { |
400 | GPIO_LOOKUP("gpio_crystalcove" , 3, "reset" , GPIO_ACTIVE_HIGH), |
401 | GPIO_LOOKUP("INT33FC:01" , 23, "wlf,ldoena" , GPIO_ACTIVE_HIGH), |
402 | GPIO_LOOKUP("arizona" , 2, "wlf,spkvdd-ena" , GPIO_ACTIVE_HIGH), |
403 | GPIO_LOOKUP("arizona" , 4, "wlf,micd-pol" , GPIO_ACTIVE_LOW), |
404 | { } |
405 | }, |
406 | }; |
407 | |
408 | static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { |
409 | &lenovo_yoga_tab2_830_1050_int3496_gpios, |
410 | &lenovo_yoga_tab2_830_1050_codec_gpios, |
411 | NULL |
412 | }; |
413 | |
414 | static int __init lenovo_yoga_tab2_830_1050_init(void); |
415 | static void lenovo_yoga_tab2_830_1050_exit(void); |
416 | |
417 | const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = { |
418 | .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, |
419 | .i2c_client_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients), |
420 | .pdev_info = int3496_pdevs, |
421 | .pdev_count = 1, |
422 | .gpio_button = &lenovo_yoga_tab2_830_1050_lid, |
423 | .gpio_button_count = 1, |
424 | .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, |
425 | .bat_swnode = &generic_lipo_hv_4v35_battery_node, |
426 | .modules = bq24190_modules, |
427 | .init = lenovo_yoga_tab2_830_1050_init, |
428 | .exit = lenovo_yoga_tab2_830_1050_exit, |
429 | }; |
430 | |
431 | /* |
432 | * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same |
433 | * mainboard, but the 830 uses a portrait LCD panel with a landscape touchscreen, |
434 | * requiring the touchscreen driver to adjust the touch-coords to match the LCD. |
435 | * And requiring the accelerometer to have a mount-matrix set to correct for |
436 | * the 90° rotation of the LCD vs the frame. |
437 | */ |
438 | static const char * const lenovo_yoga_tab2_830_lms303d_mount_matrix[] = { |
439 | "0" , "1" , "0" , |
440 | "-1" , "0" , "0" , |
441 | "0" , "0" , "1" |
442 | }; |
443 | |
444 | static const struct property_entry lenovo_yoga_tab2_830_lms303d_props[] = { |
445 | PROPERTY_ENTRY_STRING_ARRAY("mount-matrix" , lenovo_yoga_tab2_830_lms303d_mount_matrix), |
446 | { } |
447 | }; |
448 | |
449 | static const struct software_node lenovo_yoga_tab2_830_lms303d_node = { |
450 | .properties = lenovo_yoga_tab2_830_lms303d_props, |
451 | }; |
452 | |
453 | static int __init lenovo_yoga_tab2_830_1050_init_touchscreen(void) |
454 | { |
455 | struct gpio_desc *gpiod; |
456 | int ret; |
457 | |
458 | /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ |
459 | ret = x86_android_tablet_get_gpiod(chip: "gpio_crystalcove" , pin: 10, con_id: "yoga_bootstrap" , |
460 | active_low: false, dflags: GPIOD_ASIS, desc: &gpiod); |
461 | if (ret) |
462 | return ret; |
463 | |
464 | ret = gpiod_get_value_cansleep(desc: gpiod); |
465 | if (ret) { |
466 | pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n" ); |
467 | } else { |
468 | pr_info("detected Lenovo Yoga Tablet 2 830F/L\n" ); |
469 | lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; |
470 | lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; |
471 | lenovo_yoga_tab2_830_1050_i2c_clients[0].board_info.swnode = |
472 | &lenovo_yoga_tab2_830_lms303d_node; |
473 | } |
474 | |
475 | return 0; |
476 | } |
477 | |
478 | /* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */ |
479 | static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map = |
480 | PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk" , |
481 | "INT33FC:02" , "pmu_clk2_grp" , "pmu_clk" ); |
482 | |
483 | static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl; |
484 | static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler; |
485 | |
486 | static int __init lenovo_yoga_tab2_830_1050_init_codec(void) |
487 | { |
488 | struct device *codec_dev; |
489 | struct pinctrl *pinctrl; |
490 | int ret; |
491 | |
492 | codec_dev = bus_find_device_by_name(bus: &spi_bus_type, NULL, |
493 | LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); |
494 | if (!codec_dev) { |
495 | pr_err("error cannot find %s device\n" , LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); |
496 | return -ENODEV; |
497 | } |
498 | |
499 | ret = pinctrl_register_mappings(map: &lenovo_yoga_tab2_830_1050_codec_pinctrl_map, num_maps: 1); |
500 | if (ret) |
501 | goto err_put_device; |
502 | |
503 | pinctrl = pinctrl_get_select(dev: codec_dev, name: "codec_32khz_clk" ); |
504 | if (IS_ERR(ptr: pinctrl)) { |
505 | ret = dev_err_probe(dev: codec_dev, err: PTR_ERR(ptr: pinctrl), fmt: "selecting codec_32khz_clk\n" ); |
506 | goto err_unregister_mappings; |
507 | } |
508 | |
509 | /* We're done with the codec_dev now */ |
510 | put_device(dev: codec_dev); |
511 | |
512 | lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl; |
513 | return 0; |
514 | |
515 | err_unregister_mappings: |
516 | pinctrl_unregister_mappings(map: &lenovo_yoga_tab2_830_1050_codec_pinctrl_map); |
517 | err_put_device: |
518 | put_device(dev: codec_dev); |
519 | return ret; |
520 | } |
521 | |
522 | /* |
523 | * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off |
524 | * gets used as pm_power_off handler. This causes "poweroff" on these tablets |
525 | * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* |
526 | * followed by a normal 3 second press to recover. Avoid this by doing an EFI |
527 | * poweroff instead. |
528 | */ |
529 | static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data) |
530 | { |
531 | efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); |
532 | |
533 | return NOTIFY_DONE; |
534 | } |
535 | |
536 | static int __init lenovo_yoga_tab2_830_1050_init(void) |
537 | { |
538 | int ret; |
539 | |
540 | ret = lenovo_yoga_tab2_830_1050_init_touchscreen(); |
541 | if (ret) |
542 | return ret; |
543 | |
544 | ret = lenovo_yoga_tab2_830_1050_init_codec(); |
545 | if (ret) |
546 | return ret; |
547 | |
548 | /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ |
549 | lenovo_yoga_tab2_830_1050_sys_off_handler = |
550 | register_sys_off_handler(mode: SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, |
551 | callback: lenovo_yoga_tab2_830_1050_power_off, NULL); |
552 | if (IS_ERR(ptr: lenovo_yoga_tab2_830_1050_sys_off_handler)) |
553 | return PTR_ERR(ptr: lenovo_yoga_tab2_830_1050_sys_off_handler); |
554 | |
555 | return 0; |
556 | } |
557 | |
558 | static void lenovo_yoga_tab2_830_1050_exit(void) |
559 | { |
560 | unregister_sys_off_handler(handler: lenovo_yoga_tab2_830_1050_sys_off_handler); |
561 | |
562 | if (lenovo_yoga_tab2_830_1050_codec_pinctrl) { |
563 | pinctrl_put(p: lenovo_yoga_tab2_830_1050_codec_pinctrl); |
564 | pinctrl_unregister_mappings(map: &lenovo_yoga_tab2_830_1050_codec_pinctrl_map); |
565 | } |
566 | } |
567 | |
568 | /* Lenovo Yoga Tab 3 Pro YT3-X90F */ |
569 | |
570 | /* |
571 | * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, |
572 | * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. |
573 | */ |
574 | static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; |
575 | static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; |
576 | |
577 | static const struct property_entry fg_bq25890_1_supply_props[] = { |
578 | PROPERTY_ENTRY_STRING_ARRAY("supplied-from" , bq25890_1_psy), |
579 | { } |
580 | }; |
581 | |
582 | static const struct software_node fg_bq25890_1_supply_node = { |
583 | .properties = fg_bq25890_1_supply_props, |
584 | }; |
585 | |
586 | /* bq25892 charger settings for the flat lipo battery behind the screen */ |
587 | static const struct property_entry lenovo_yt3_bq25892_0_props[] = { |
588 | PROPERTY_ENTRY_STRING_ARRAY("supplied-from" , lenovo_yt3_bq25892_0_suppliers), |
589 | PROPERTY_ENTRY_U32("linux,iinlim-percentage" , 40), |
590 | PROPERTY_ENTRY_BOOL("linux,skip-reset" ), |
591 | /* Values taken from Android Factory Image */ |
592 | PROPERTY_ENTRY_U32("ti,charge-current" , 2048000), |
593 | PROPERTY_ENTRY_U32("ti,battery-regulation-voltage" , 4352000), |
594 | PROPERTY_ENTRY_U32("ti,termination-current" , 128000), |
595 | PROPERTY_ENTRY_U32("ti,precharge-current" , 128000), |
596 | PROPERTY_ENTRY_U32("ti,minimum-sys-voltage" , 3700000), |
597 | PROPERTY_ENTRY_U32("ti,boost-voltage" , 4998000), |
598 | PROPERTY_ENTRY_U32("ti,boost-max-current" , 500000), |
599 | PROPERTY_ENTRY_BOOL("ti,use-ilim-pin" ), |
600 | { } |
601 | }; |
602 | |
603 | static const struct software_node lenovo_yt3_bq25892_0_node = { |
604 | .properties = lenovo_yt3_bq25892_0_props, |
605 | }; |
606 | |
607 | static const struct property_entry lenovo_yt3_hideep_ts_props[] = { |
608 | PROPERTY_ENTRY_U32("touchscreen-size-x" , 1600), |
609 | PROPERTY_ENTRY_U32("touchscreen-size-y" , 2560), |
610 | PROPERTY_ENTRY_U32("touchscreen-max-pressure" , 255), |
611 | { } |
612 | }; |
613 | |
614 | static const struct software_node lenovo_yt3_hideep_ts_node = { |
615 | .properties = lenovo_yt3_hideep_ts_props, |
616 | }; |
617 | |
618 | static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { |
619 | { |
620 | /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ |
621 | .board_info = { |
622 | .type = "bq27500" , |
623 | .addr = 0x55, |
624 | .dev_name = "bq27500_0" , |
625 | .swnode = &fg_bq25890_supply_node, |
626 | }, |
627 | .adapter_path = "\\_SB_.PCI0.I2C1" , |
628 | }, { |
629 | /* bq25892 charger for the flat lipo battery behind the screen */ |
630 | .board_info = { |
631 | .type = "bq25892" , |
632 | .addr = 0x6b, |
633 | .dev_name = "bq25892_0" , |
634 | .swnode = &lenovo_yt3_bq25892_0_node, |
635 | }, |
636 | .adapter_path = "\\_SB_.PCI0.I2C1" , |
637 | .irq_data = { |
638 | .type = X86_ACPI_IRQ_TYPE_GPIOINT, |
639 | .chip = "INT33FF:01" , |
640 | .index = 5, |
641 | .trigger = ACPI_EDGE_SENSITIVE, |
642 | .polarity = ACPI_ACTIVE_LOW, |
643 | .con_id = "bq25892_0_irq" , |
644 | }, |
645 | }, { |
646 | /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ |
647 | .board_info = { |
648 | .type = "bq27500" , |
649 | .addr = 0x55, |
650 | .dev_name = "bq27500_1" , |
651 | .swnode = &fg_bq25890_1_supply_node, |
652 | }, |
653 | .adapter_path = "\\_SB_.PCI0.I2C2" , |
654 | }, { |
655 | /* HiDeep IST520E Touchscreen */ |
656 | .board_info = { |
657 | .type = "hideep_ts" , |
658 | .addr = 0x6c, |
659 | .dev_name = "hideep_ts" , |
660 | .swnode = &lenovo_yt3_hideep_ts_node, |
661 | }, |
662 | .adapter_path = "\\_SB_.PCI0.I2C6" , |
663 | .irq_data = { |
664 | .type = X86_ACPI_IRQ_TYPE_GPIOINT, |
665 | .chip = "INT33FF:03" , |
666 | .index = 77, |
667 | .trigger = ACPI_LEVEL_SENSITIVE, |
668 | .polarity = ACPI_ACTIVE_LOW, |
669 | .con_id = "hideep_ts_irq" , |
670 | }, |
671 | }, { |
672 | /* LP8557 Backlight controller */ |
673 | .board_info = { |
674 | .type = "lp8557" , |
675 | .addr = 0x2c, |
676 | .dev_name = "lp8557" , |
677 | .platform_data = &lenovo_lp8557_reg_only_pdata, |
678 | }, |
679 | .adapter_path = "\\_SB_.PCI0.I2C1" , |
680 | } |
681 | }; |
682 | |
683 | /* |
684 | * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: |
685 | * Function A Play/Pause: 0 ohm |
686 | * Function D Voice assistant: 135 ohm |
687 | * Function B Volume Up 240 ohm |
688 | * Function C Volume Down 470 ohm |
689 | * Minimum Mic DC resistance 1000 ohm |
690 | * Minimum Ear speaker impedance 16 ohm |
691 | * Note the first max value below must be less then the min. speaker impedance, |
692 | * to allow CTIA/OMTP detection to work. The other max values are the closest |
693 | * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. |
694 | */ |
695 | static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { |
696 | { .max = 11, .key = KEY_PLAYPAUSE }, |
697 | { .max = 186, .key = KEY_VOICECOMMAND }, |
698 | { .max = 348, .key = KEY_VOLUMEUP }, |
699 | { .max = 752, .key = KEY_VOLUMEDOWN }, |
700 | }; |
701 | |
702 | /* YT3 WM5102 arizona_micd_config comes from Android kernel sources */ |
703 | static struct arizona_micd_config lenovo_yt3_wm5102_micd_config[] = { |
704 | { 0, 1, 0 }, |
705 | { ARIZONA_ACCDET_SRC, 2, 1 }, |
706 | }; |
707 | |
708 | static struct arizona_pdata lenovo_yt3_wm5102_pdata = { |
709 | .irq_flags = IRQF_TRIGGER_LOW, |
710 | .micd_detect_debounce = 200, |
711 | .micd_ranges = arizona_micd_aosp_ranges, |
712 | .num_micd_ranges = ARRAY_SIZE(arizona_micd_aosp_ranges), |
713 | .hpdet_channel = ARIZONA_ACCDET_MODE_HPL, |
714 | |
715 | /* Below settings come from Android kernel sources */ |
716 | .micd_bias_start_time = 1, |
717 | .micd_rate = 6, |
718 | .micd_configs = lenovo_yt3_wm5102_micd_config, |
719 | .num_micd_configs = ARRAY_SIZE(lenovo_yt3_wm5102_micd_config), |
720 | .micbias = { |
721 | [0] = { /* MICBIAS1 */ |
722 | .mV = 2800, |
723 | .ext_cap = 1, |
724 | .discharge = 1, |
725 | .soft_start = 0, |
726 | .bypass = 0, |
727 | }, |
728 | [1] = { /* MICBIAS2 */ |
729 | .mV = 2800, |
730 | .ext_cap = 1, |
731 | .discharge = 1, |
732 | .soft_start = 0, |
733 | .bypass = 0, |
734 | }, |
735 | [2] = { /* MICBIAS2 */ |
736 | .mV = 2800, |
737 | .ext_cap = 1, |
738 | .discharge = 1, |
739 | .soft_start = 0, |
740 | .bypass = 0, |
741 | }, |
742 | }, |
743 | }; |
744 | |
745 | static const struct x86_spi_dev_info lenovo_yt3_spi_devs[] __initconst = { |
746 | { |
747 | /* WM5102 codec */ |
748 | .board_info = { |
749 | .modalias = "wm5102" , |
750 | .platform_data = &lenovo_yt3_wm5102_pdata, |
751 | .max_speed_hz = 5000000, |
752 | }, |
753 | .ctrl_path = "\\_SB_.PCI0.SPI1" , |
754 | .irq_data = { |
755 | .type = X86_ACPI_IRQ_TYPE_GPIOINT, |
756 | .chip = "INT33FF:00" , |
757 | .index = 91, |
758 | .trigger = ACPI_LEVEL_SENSITIVE, |
759 | .polarity = ACPI_ACTIVE_LOW, |
760 | .con_id = "wm5102_irq" , |
761 | }, |
762 | } |
763 | }; |
764 | |
765 | static int __init lenovo_yt3_init(void) |
766 | { |
767 | int ret; |
768 | |
769 | /* |
770 | * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins |
771 | * connected to GPIOs, rather then having them hardwired to the correct |
772 | * values as is normally done. |
773 | * |
774 | * The bq25890_charger driver controls these through I2C, but this only |
775 | * works if not overridden by the pins. Set these pins here: |
776 | * 1. Set /CE to 1 to allow charging. |
777 | * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of |
778 | * the main "bq25892_1" charger is used when necessary. |
779 | */ |
780 | |
781 | /* /CE pin */ |
782 | ret = x86_android_tablet_get_gpiod(chip: "INT33FF:02" , pin: 22, con_id: "bq25892_0_ce" , |
783 | active_low: true, dflags: GPIOD_OUT_HIGH, NULL); |
784 | if (ret < 0) |
785 | return ret; |
786 | |
787 | /* OTG pin */ |
788 | ret = x86_android_tablet_get_gpiod(chip: "INT33FF:03" , pin: 19, con_id: "bq25892_0_otg" , |
789 | active_low: false, dflags: GPIOD_OUT_LOW, NULL); |
790 | if (ret < 0) |
791 | return ret; |
792 | |
793 | /* Enable the regulators used by the touchscreen */ |
794 | intel_soc_pmic_exec_mipi_pmic_seq_element(i2c_address: 0x6e, reg_address: 0x9b, value: 0x02, mask: 0xff); |
795 | intel_soc_pmic_exec_mipi_pmic_seq_element(i2c_address: 0x6e, reg_address: 0xa0, value: 0x02, mask: 0xff); |
796 | |
797 | return 0; |
798 | } |
799 | |
800 | static struct gpiod_lookup_table lenovo_yt3_hideep_gpios = { |
801 | .dev_id = "i2c-hideep_ts" , |
802 | .table = { |
803 | GPIO_LOOKUP("INT33FF:00" , 7, "reset" , GPIO_ACTIVE_LOW), |
804 | { } |
805 | }, |
806 | }; |
807 | |
808 | static struct gpiod_lookup_table lenovo_yt3_wm5102_gpios = { |
809 | .dev_id = "spi1.0" , |
810 | .table = { |
811 | GPIO_LOOKUP("INT33FF:00" , 75, "wlf,spkvdd-ena" , GPIO_ACTIVE_HIGH), |
812 | GPIO_LOOKUP("INT33FF:00" , 81, "wlf,ldoena" , GPIO_ACTIVE_HIGH), |
813 | GPIO_LOOKUP("INT33FF:00" , 82, "reset" , GPIO_ACTIVE_HIGH), |
814 | GPIO_LOOKUP("arizona" , 2, "wlf,micd-pol" , GPIO_ACTIVE_HIGH), |
815 | { } |
816 | }, |
817 | }; |
818 | |
819 | static struct gpiod_lookup_table * const lenovo_yt3_gpios[] = { |
820 | &lenovo_yt3_hideep_gpios, |
821 | &lenovo_yt3_wm5102_gpios, |
822 | NULL |
823 | }; |
824 | |
825 | const struct x86_dev_info lenovo_yt3_info __initconst = { |
826 | .i2c_client_info = lenovo_yt3_i2c_clients, |
827 | .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), |
828 | .spi_dev_info = lenovo_yt3_spi_devs, |
829 | .spi_dev_count = ARRAY_SIZE(lenovo_yt3_spi_devs), |
830 | .gpiod_lookup_tables = lenovo_yt3_gpios, |
831 | .init = lenovo_yt3_init, |
832 | }; |
833 | |