Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Surface System Aggregator Module (SSAM) client device registry.
  4 *
  5 * Registry for non-platform/non-ACPI SSAM client devices, i.e. devices that
  6 * cannot be auto-detected. Provides device-hubs and performs instantiation
  7 * for these devices.
  8 *
  9 * Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
 10 */
 11
 12#include <linux/acpi.h>
 13#include <linux/kernel.h>
 14#include <linux/module.h>
 15#include <linux/of.h>
 16#include <linux/platform_device.h>
 17#include <linux/property.h>
 18#include <linux/types.h>
 19
 20#include <linux/surface_aggregator/device.h>
 21
 22
 23/* -- Device registry. ------------------------------------------------------ */
 24
 25/*
 26 * SSAM device names follow the SSAM module alias, meaning they are prefixed
 27 * with 'ssam:', followed by domain, category, target ID, instance ID, and
 28 * function, each encoded as two-digit hexadecimal, separated by ':'. In other
 29 * words, it follows the scheme
 30 *
 31 *      ssam:dd:cc:tt:ii:ff
 32 *
 33 * Where, 'dd', 'cc', 'tt', 'ii', and 'ff' are the two-digit hexadecimal
 34 * values mentioned above, respectively.
 35 */
 36
 37/* Root node. */
 38static const struct software_node ssam_node_root = {
 39	.name = "ssam_platform_hub",
 40};
 41
 42/* KIP device hub (connects keyboard cover devices on Surface Pro 8). */
 43static const struct software_node ssam_node_hub_kip = {
 44	.name = "ssam:00:00:01:0e:00",
 45	.parent = &ssam_node_root,
 46};
 47
 48/* Base device hub (devices attached to Surface Book 3 base). */
 49static const struct software_node ssam_node_hub_base = {
 50	.name = "ssam:00:00:01:11:00",
 51	.parent = &ssam_node_root,
 52};
 53
 54/* AC adapter. */
 55static const struct software_node ssam_node_bat_ac = {
 56	.name = "ssam:01:02:01:01:01",
 57	.parent = &ssam_node_root,
 58};
 59
 60/* Primary battery. */
 61static const struct software_node ssam_node_bat_main = {
 62	.name = "ssam:01:02:01:01:00",
 63	.parent = &ssam_node_root,
 64};
 65
 66/* Secondary battery (Surface Book 3). */
 67static const struct software_node ssam_node_bat_sb3base = {
 68	.name = "ssam:01:02:02:01:00",
 69	.parent = &ssam_node_hub_base,
 70};
 71
 72/* Platform profile / performance-mode device without a fan. */
 73static const struct software_node ssam_node_tmp_perf_profile = {
 74	.name = "ssam:01:03:01:00:01",
 75	.parent = &ssam_node_root,
 76};
 77
 78/* Platform profile / performance-mode device with a fan, such that
 79 * the fan controller profile can also be switched.
 80 */
 81static const struct property_entry ssam_node_tmp_perf_profile_has_fan[] = {
 82	PROPERTY_ENTRY_BOOL("has_fan"),
 83	{ }
 84};
 85
 86static const struct software_node ssam_node_tmp_perf_profile_with_fan = {
 87	.name = "ssam:01:03:01:00:01",
 88	.parent = &ssam_node_root,
 89	.properties = ssam_node_tmp_perf_profile_has_fan,
 90};
 91
 92/* Thermal sensors. */
 93static const struct software_node ssam_node_tmp_sensors = {
 94	.name = "ssam:01:03:01:00:02",
 95	.parent = &ssam_node_root,
 96};
 97
 98/* Fan speed function. */
 99static const struct software_node ssam_node_fan_speed = {
100	.name = "ssam:01:05:01:01:01",
101	.parent = &ssam_node_root,
102};
103
104/* Tablet-mode switch via KIP subsystem. */
105static const struct software_node ssam_node_kip_tablet_switch = {
106	.name = "ssam:01:0e:01:00:01",
107	.parent = &ssam_node_root,
108};
109
110/* DTX / detachment-system device (Surface Book 3). */
111static const struct software_node ssam_node_bas_dtx = {
112	.name = "ssam:01:11:01:00:00",
113	.parent = &ssam_node_root,
114};
115
116/* HID keyboard (SAM, TID=1). */
117static const struct software_node ssam_node_hid_sam_keyboard = {
118	.name = "ssam:01:15:01:01:00",
119	.parent = &ssam_node_root,
120};
121
122/* HID pen stash (SAM, TID=1; pen taken / stashed away evens). */
123static const struct software_node ssam_node_hid_sam_penstash = {
124	.name = "ssam:01:15:01:02:00",
125	.parent = &ssam_node_root,
126};
127
128/* HID touchpad (SAM, TID=1). */
129static const struct software_node ssam_node_hid_sam_touchpad = {
130	.name = "ssam:01:15:01:03:00",
131	.parent = &ssam_node_root,
132};
133
134/* HID device instance 6 (SAM, TID=1, HID sensor collection). */
135static const struct software_node ssam_node_hid_sam_sensors = {
136	.name = "ssam:01:15:01:06:00",
137	.parent = &ssam_node_root,
138};
139
140/* HID device instance 7 (SAM, TID=1, UCM UCSI HID client). */
141static const struct software_node ssam_node_hid_sam_ucm_ucsi = {
142	.name = "ssam:01:15:01:07:00",
143	.parent = &ssam_node_root,
144};
145
146/* HID system controls (SAM, TID=1). */
147static const struct software_node ssam_node_hid_sam_sysctrl = {
148	.name = "ssam:01:15:01:08:00",
149	.parent = &ssam_node_root,
150};
151
152/* HID keyboard. */
153static const struct software_node ssam_node_hid_main_keyboard = {
154	.name = "ssam:01:15:02:01:00",
155	.parent = &ssam_node_root,
156};
157
158/* HID touchpad. */
159static const struct software_node ssam_node_hid_main_touchpad = {
160	.name = "ssam:01:15:02:03:00",
161	.parent = &ssam_node_root,
162};
163
164/* HID device instance 5 (unknown HID device). */
165static const struct software_node ssam_node_hid_main_iid5 = {
166	.name = "ssam:01:15:02:05:00",
167	.parent = &ssam_node_root,
168};
169
170/* HID keyboard (base hub). */
171static const struct software_node ssam_node_hid_base_keyboard = {
172	.name = "ssam:01:15:02:01:00",
173	.parent = &ssam_node_hub_base,
174};
175
176/* HID touchpad (base hub). */
177static const struct software_node ssam_node_hid_base_touchpad = {
178	.name = "ssam:01:15:02:03:00",
179	.parent = &ssam_node_hub_base,
180};
181
182/* HID device instance 5 (unknown HID device, base hub). */
183static const struct software_node ssam_node_hid_base_iid5 = {
184	.name = "ssam:01:15:02:05:00",
185	.parent = &ssam_node_hub_base,
186};
187
188/* HID device instance 6 (unknown HID device, base hub). */
189static const struct software_node ssam_node_hid_base_iid6 = {
190	.name = "ssam:01:15:02:06:00",
191	.parent = &ssam_node_hub_base,
192};
193
194/* HID keyboard (KIP hub). */
195static const struct software_node ssam_node_hid_kip_keyboard = {
196	.name = "ssam:01:15:02:01:00",
197	.parent = &ssam_node_hub_kip,
198};
199
200/* HID pen stash (KIP hub; pen taken / stashed away evens). */
201static const struct software_node ssam_node_hid_kip_penstash = {
202	.name = "ssam:01:15:02:02:00",
203	.parent = &ssam_node_hub_kip,
204};
205
206/* HID touchpad (KIP hub). */
207static const struct software_node ssam_node_hid_kip_touchpad = {
208	.name = "ssam:01:15:02:03:00",
209	.parent = &ssam_node_hub_kip,
210};
211
212/* HID device instance 5 (KIP hub, type-cover firmware update). */
213static const struct software_node ssam_node_hid_kip_fwupd = {
214	.name = "ssam:01:15:02:05:00",
215	.parent = &ssam_node_hub_kip,
216};
217
218/* Tablet-mode switch via POS subsystem. */
219static const struct software_node ssam_node_pos_tablet_switch = {
220	.name = "ssam:01:26:01:00:01",
221	.parent = &ssam_node_root,
222};
223
224/*
225 * Devices for 5th- and 6th-generations models:
226 * - Surface Book 2,
227 * - Surface Laptop 1 and 2,
228 * - Surface Pro 5 and 6.
229 */
230static const struct software_node *ssam_node_group_gen5[] = {
231	&ssam_node_root,
232	&ssam_node_tmp_perf_profile,
233	NULL,
234};
235
236/* Devices for Surface Book 3. */
237static const struct software_node *ssam_node_group_sb3[] = {
238	&ssam_node_root,
239	&ssam_node_hub_base,
240	&ssam_node_bat_ac,
241	&ssam_node_bat_main,
242	&ssam_node_bat_sb3base,
243	&ssam_node_tmp_perf_profile,
244	&ssam_node_bas_dtx,
245	&ssam_node_hid_base_keyboard,
246	&ssam_node_hid_base_touchpad,
247	&ssam_node_hid_base_iid5,
248	&ssam_node_hid_base_iid6,
249	NULL,
250};
251
252/* Devices for Surface Laptop 3 and 4. */
253static const struct software_node *ssam_node_group_sl3[] = {
254	&ssam_node_root,
255	&ssam_node_bat_ac,
256	&ssam_node_bat_main,
257	&ssam_node_tmp_perf_profile,
258	&ssam_node_hid_main_keyboard,
259	&ssam_node_hid_main_touchpad,
260	&ssam_node_hid_main_iid5,
261	NULL,
262};
263
264/* Devices for Surface Laptop 5. */
265static const struct software_node *ssam_node_group_sl5[] = {
266	&ssam_node_root,
267	&ssam_node_bat_ac,
268	&ssam_node_bat_main,
269	&ssam_node_tmp_perf_profile_with_fan,
270	&ssam_node_tmp_sensors,
271	&ssam_node_fan_speed,
272	&ssam_node_hid_main_keyboard,
273	&ssam_node_hid_main_touchpad,
274	&ssam_node_hid_main_iid5,
275	&ssam_node_hid_sam_ucm_ucsi,
276	NULL,
277};
278
279/* Devices for Surface Laptop 6. */
280static const struct software_node *ssam_node_group_sl6[] = {
281	&ssam_node_root,
282	&ssam_node_bat_ac,
283	&ssam_node_bat_main,
284	&ssam_node_tmp_perf_profile_with_fan,
285	&ssam_node_tmp_sensors,
286	&ssam_node_fan_speed,
287	&ssam_node_hid_main_keyboard,
288	&ssam_node_hid_main_touchpad,
289	&ssam_node_hid_main_iid5,
290	&ssam_node_hid_sam_sensors,
291	&ssam_node_hid_sam_ucm_ucsi,
292	NULL,
293};
294
295/* Devices for Surface Laptop 7. */
296static const struct software_node *ssam_node_group_sl7[] = {
297	&ssam_node_root,
298	&ssam_node_bat_ac,
299	&ssam_node_bat_main,
300	&ssam_node_tmp_perf_profile_with_fan,
301	&ssam_node_fan_speed,
302	&ssam_node_hid_sam_keyboard,
303	/* TODO: evaluate thermal sensors devices when we get a driver for that */
304	NULL,
305};
306
307/* Devices for Surface Laptop Studio 1. */
308static const struct software_node *ssam_node_group_sls1[] = {
309	&ssam_node_root,
310	&ssam_node_bat_ac,
311	&ssam_node_bat_main,
312	&ssam_node_tmp_perf_profile,
313	&ssam_node_pos_tablet_switch,
314	&ssam_node_hid_sam_keyboard,
315	&ssam_node_hid_sam_penstash,
316	&ssam_node_hid_sam_touchpad,
317	&ssam_node_hid_sam_sensors,
318	&ssam_node_hid_sam_ucm_ucsi,
319	&ssam_node_hid_sam_sysctrl,
320	NULL,
321};
322
323/* Devices for Surface Laptop Studio 2. */
324static const struct software_node *ssam_node_group_sls2[] = {
325	&ssam_node_root,
326	&ssam_node_bat_ac,
327	&ssam_node_bat_main,
328	&ssam_node_tmp_perf_profile_with_fan,
329	&ssam_node_tmp_sensors,
330	&ssam_node_fan_speed,
331	&ssam_node_pos_tablet_switch,
332	&ssam_node_hid_sam_keyboard,
333	&ssam_node_hid_sam_penstash,
334	&ssam_node_hid_sam_sensors,
335	&ssam_node_hid_sam_ucm_ucsi,
336	NULL,
337};
338
339/* Devices for Surface Laptop Go. */
340static const struct software_node *ssam_node_group_slg1[] = {
341	&ssam_node_root,
342	&ssam_node_bat_ac,
343	&ssam_node_bat_main,
344	&ssam_node_tmp_perf_profile,
345	NULL,
346};
347
348/* Devices for Surface Pro 7 and Surface Pro 7+. */
349static const struct software_node *ssam_node_group_sp7[] = {
350	&ssam_node_root,
351	&ssam_node_bat_ac,
352	&ssam_node_bat_main,
353	&ssam_node_tmp_perf_profile,
354	NULL,
355};
356
357/* Devices for Surface Pro 8 */
358static const struct software_node *ssam_node_group_sp8[] = {
359	&ssam_node_root,
360	&ssam_node_hub_kip,
361	&ssam_node_bat_ac,
362	&ssam_node_bat_main,
363	&ssam_node_tmp_perf_profile,
364	&ssam_node_kip_tablet_switch,
365	&ssam_node_hid_kip_keyboard,
366	&ssam_node_hid_kip_penstash,
367	&ssam_node_hid_kip_touchpad,
368	&ssam_node_hid_kip_fwupd,
369	&ssam_node_hid_sam_sensors,
370	&ssam_node_hid_sam_ucm_ucsi,
371	NULL,
372};
373
374/* Devices for Surface Pro 9 (Intel/x86) and 10 */
375static const struct software_node *ssam_node_group_sp9[] = {
376	&ssam_node_root,
377	&ssam_node_hub_kip,
378	&ssam_node_bat_ac,
379	&ssam_node_bat_main,
380	&ssam_node_tmp_perf_profile_with_fan,
381	&ssam_node_tmp_sensors,
382	&ssam_node_fan_speed,
383	&ssam_node_pos_tablet_switch,
384	&ssam_node_hid_kip_keyboard,
385	&ssam_node_hid_kip_penstash,
386	&ssam_node_hid_kip_touchpad,
387	&ssam_node_hid_kip_fwupd,
388	&ssam_node_hid_sam_sensors,
389	&ssam_node_hid_sam_ucm_ucsi,
390	NULL,
391};
392
393/* Devices for Surface Pro 9 5G (ARM/QCOM) */
394static const struct software_node *ssam_node_group_sp9_5g[] = {
395	&ssam_node_root,
396	&ssam_node_hub_kip,
397	&ssam_node_bat_ac,
398	&ssam_node_bat_main,
399	&ssam_node_tmp_sensors,
400	&ssam_node_hid_kip_keyboard,
401	&ssam_node_hid_kip_penstash,
402	&ssam_node_hid_kip_touchpad,
403	&ssam_node_hid_kip_fwupd,
404	&ssam_node_hid_sam_sensors,
405	&ssam_node_kip_tablet_switch,
406	NULL,
407};
408
409/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
410
411static const struct acpi_device_id ssam_platform_hub_acpi_match[] = {
412	/* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
413	{ "MSHW0081", (unsigned long)ssam_node_group_gen5 },
414
415	/* Surface Pro 6 (OMBR >= 0x10) */
416	{ "MSHW0111", (unsigned long)ssam_node_group_gen5 },
417
418	/* Surface Pro 7 */
419	{ "MSHW0116", (unsigned long)ssam_node_group_sp7 },
420
421	/* Surface Pro 7+ */
422	{ "MSHW0119", (unsigned long)ssam_node_group_sp7 },
423
424	/* Surface Pro 8 */
425	{ "MSHW0263", (unsigned long)ssam_node_group_sp8 },
426
427	/* Surface Pro 9 */
428	{ "MSHW0343", (unsigned long)ssam_node_group_sp9 },
429
430	/* Surface Pro 10 */
431	{ "MSHW0510", (unsigned long)ssam_node_group_sp9 },
432
433	/* Surface Book 2 */
434	{ "MSHW0107", (unsigned long)ssam_node_group_gen5 },
435
436	/* Surface Book 3 */
437	{ "MSHW0117", (unsigned long)ssam_node_group_sb3 },
438
439	/* Surface Laptop 1 */
440	{ "MSHW0086", (unsigned long)ssam_node_group_gen5 },
441
442	/* Surface Laptop 2 */
443	{ "MSHW0112", (unsigned long)ssam_node_group_gen5 },
444
445	/* Surface Laptop 3 (13", Intel) */
446	{ "MSHW0114", (unsigned long)ssam_node_group_sl3 },
447
448	/* Surface Laptop 3 (15", AMD) and 4 (15", AMD) */
449	{ "MSHW0110", (unsigned long)ssam_node_group_sl3 },
450
451	/* Surface Laptop 4 (13", Intel) */
452	{ "MSHW0250", (unsigned long)ssam_node_group_sl3 },
453
454	/* Surface Laptop 5 */
455	{ "MSHW0350", (unsigned long)ssam_node_group_sl5 },
456
457	/* Surface Laptop 6 */
458	{ "MSHW0530", (unsigned long)ssam_node_group_sl6 },
459
460	/* Surface Laptop Go 1 */
461	{ "MSHW0118", (unsigned long)ssam_node_group_slg1 },
462
463	/* Surface Laptop Go 2 */
464	{ "MSHW0290", (unsigned long)ssam_node_group_slg1 },
465
466	/* Surface Laptop Go 3 */
467	{ "MSHW0440", (unsigned long)ssam_node_group_slg1 },
468
469	/* Surface Laptop Studio 1 */
470	{ "MSHW0123", (unsigned long)ssam_node_group_sls1 },
471
472	/* Surface Laptop Studio 2 */
473	{ "MSHW0360", (unsigned long)ssam_node_group_sls2 },
474
475	{ },
476};
477MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_acpi_match);
478
479static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = {
480	/* Surface Pro 9 5G (ARM/QCOM) */
481	{ .compatible = "microsoft,arcata", (void *)ssam_node_group_sp9_5g },
482	/* Surface Laptop 7 */
483	{ .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 },
484	{ .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 },
485	{ },
486};
487
488static int ssam_platform_hub_probe(struct platform_device *pdev)
489{
490	const struct software_node **nodes;
491	const struct of_device_id *match;
492	struct device_node *fdt_root;
493	struct ssam_controller *ctrl;
494	struct fwnode_handle *root;
495	int status;
496
497	nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev);
498	if (!nodes) {
499		fdt_root = of_find_node_by_path("/");
500		if (!fdt_root)
501			return -ENODEV;
502
503		match = of_match_node(ssam_platform_hub_of_match, fdt_root);
504		of_node_put(fdt_root);
505		if (!match)
506			return -ENODEV;
507
508		nodes = (const struct software_node **)match->data;
509		if (!nodes)
510			return -ENODEV;
511	}
512
513	/*
514	 * As we're adding the SSAM client devices as children under this device
515	 * and not the SSAM controller, we need to add a device link to the
516	 * controller to ensure that we remove all of our devices before the
517	 * controller is removed. This also guarantees proper ordering for
518	 * suspend/resume of the devices on this hub.
519	 */
520	ctrl = ssam_client_bind(&pdev->dev);
521	if (IS_ERR(ctrl))
522		return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
523
524	status = software_node_register_node_group(nodes);
525	if (status)
526		return status;
527
528	root = software_node_fwnode(&ssam_node_root);
529	if (!root) {
530		software_node_unregister_node_group(nodes);
531		return -ENOENT;
532	}
533
534	set_secondary_fwnode(&pdev->dev, root);
535
536	status = __ssam_register_clients(&pdev->dev, ctrl, root);
537	if (status) {
538		set_secondary_fwnode(&pdev->dev, NULL);
539		software_node_unregister_node_group(nodes);
540	}
541
542	platform_set_drvdata(pdev, nodes);
543	return status;
544}
545
546static void ssam_platform_hub_remove(struct platform_device *pdev)
547{
548	const struct software_node **nodes = platform_get_drvdata(pdev);
549
550	ssam_remove_clients(&pdev->dev);
551	set_secondary_fwnode(&pdev->dev, NULL);
552	software_node_unregister_node_group(nodes);
553}
554
555static struct platform_driver ssam_platform_hub_driver = {
556	.probe = ssam_platform_hub_probe,
557	.remove = ssam_platform_hub_remove,
558	.driver = {
559		.name = "surface_aggregator_platform_hub",
560		.acpi_match_table = ssam_platform_hub_acpi_match,
561		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
562	},
563};
564module_platform_driver(ssam_platform_hub_driver);
565
566MODULE_ALIAS("platform:surface_aggregator_platform_hub");
567MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
568MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");
569MODULE_LICENSE("GPL");