1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* |
4 | * ChromeOS Privacy Screen support |
5 | * |
6 | * Copyright (C) 2022 Google LLC |
7 | * |
8 | * This is the Chromeos privacy screen provider, present on certain chromebooks, |
9 | * represented by a GOOG0010 device in the ACPI. This ACPI device, if present, |
10 | * will cause the i915 drm driver to probe defer until this driver registers |
11 | * the privacy-screen. |
12 | */ |
13 | |
14 | #include <linux/acpi.h> |
15 | #include <drm/drm_privacy_screen_driver.h> |
16 | |
17 | /* |
18 | * The DSM (Device Specific Method) constants below are the agreed API with |
19 | * the firmware team, on how to control privacy screen using ACPI methods. |
20 | */ |
21 | #define PRIV_SCRN_DSM_REVID 1 /* DSM version */ |
22 | #define PRIV_SCRN_DSM_FN_GET_STATUS 1 /* Get privacy screen status */ |
23 | #define PRIV_SCRN_DSM_FN_ENABLE 2 /* Enable privacy screen */ |
24 | #define PRIV_SCRN_DSM_FN_DISABLE 3 /* Disable privacy screen */ |
25 | |
26 | static const guid_t chromeos_privacy_screen_dsm_guid = |
27 | GUID_INIT(0xc7033113, 0x8720, 0x4ceb, |
28 | 0x90, 0x90, 0x9d, 0x52, 0xb3, 0xe5, 0x2d, 0x73); |
29 | |
30 | static void |
31 | chromeos_privacy_screen_get_hw_state(struct drm_privacy_screen |
32 | *drm_privacy_screen) |
33 | { |
34 | union acpi_object *obj; |
35 | acpi_handle handle; |
36 | struct device *privacy_screen = |
37 | drm_privacy_screen_get_drvdata(priv: drm_privacy_screen); |
38 | |
39 | handle = acpi_device_handle(to_acpi_device(privacy_screen)); |
40 | obj = acpi_evaluate_dsm(handle, guid: &chromeos_privacy_screen_dsm_guid, |
41 | PRIV_SCRN_DSM_REVID, |
42 | PRIV_SCRN_DSM_FN_GET_STATUS, NULL); |
43 | if (!obj) { |
44 | dev_err(privacy_screen, |
45 | "_DSM failed to get privacy-screen state\n" ); |
46 | return; |
47 | } |
48 | |
49 | if (obj->type != ACPI_TYPE_INTEGER) |
50 | dev_err(privacy_screen, |
51 | "Bad _DSM to get privacy-screen state\n" ); |
52 | else if (obj->integer.value == 1) |
53 | drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = |
54 | PRIVACY_SCREEN_ENABLED; |
55 | else |
56 | drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = |
57 | PRIVACY_SCREEN_DISABLED; |
58 | |
59 | ACPI_FREE(obj); |
60 | } |
61 | |
62 | static int |
63 | chromeos_privacy_screen_set_sw_state(struct drm_privacy_screen |
64 | *drm_privacy_screen, |
65 | enum drm_privacy_screen_status state) |
66 | { |
67 | union acpi_object *obj = NULL; |
68 | acpi_handle handle; |
69 | struct device *privacy_screen = |
70 | drm_privacy_screen_get_drvdata(priv: drm_privacy_screen); |
71 | |
72 | handle = acpi_device_handle(to_acpi_device(privacy_screen)); |
73 | |
74 | if (state == PRIVACY_SCREEN_DISABLED) { |
75 | obj = acpi_evaluate_dsm(handle, |
76 | guid: &chromeos_privacy_screen_dsm_guid, |
77 | PRIV_SCRN_DSM_REVID, |
78 | PRIV_SCRN_DSM_FN_DISABLE, NULL); |
79 | } else if (state == PRIVACY_SCREEN_ENABLED) { |
80 | obj = acpi_evaluate_dsm(handle, |
81 | guid: &chromeos_privacy_screen_dsm_guid, |
82 | PRIV_SCRN_DSM_REVID, |
83 | PRIV_SCRN_DSM_FN_ENABLE, NULL); |
84 | } else { |
85 | dev_err(privacy_screen, |
86 | "Bad attempt to set privacy-screen status to %u\n" , |
87 | state); |
88 | return -EINVAL; |
89 | } |
90 | |
91 | if (!obj) { |
92 | dev_err(privacy_screen, |
93 | "_DSM failed to set privacy-screen state\n" ); |
94 | return -EIO; |
95 | } |
96 | |
97 | drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = state; |
98 | ACPI_FREE(obj); |
99 | return 0; |
100 | } |
101 | |
102 | static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = { |
103 | .get_hw_state = chromeos_privacy_screen_get_hw_state, |
104 | .set_sw_state = chromeos_privacy_screen_set_sw_state, |
105 | }; |
106 | |
107 | static int chromeos_privacy_screen_add(struct acpi_device *adev) |
108 | { |
109 | struct drm_privacy_screen *drm_privacy_screen = |
110 | drm_privacy_screen_register(parent: &adev->dev, |
111 | ops: &chromeos_privacy_screen_ops, |
112 | data: &adev->dev); |
113 | |
114 | if (IS_ERR(ptr: drm_privacy_screen)) { |
115 | dev_err(&adev->dev, "Error registering privacy-screen\n" ); |
116 | return PTR_ERR(ptr: drm_privacy_screen); |
117 | } |
118 | |
119 | adev->driver_data = drm_privacy_screen; |
120 | dev_info(&adev->dev, "registered privacy-screen '%s'\n" , |
121 | dev_name(&drm_privacy_screen->dev)); |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static void chromeos_privacy_screen_remove(struct acpi_device *adev) |
127 | { |
128 | struct drm_privacy_screen *drm_privacy_screen = acpi_driver_data(d: adev); |
129 | |
130 | drm_privacy_screen_unregister(priv: drm_privacy_screen); |
131 | } |
132 | |
133 | static const struct acpi_device_id chromeos_privacy_screen_device_ids[] = { |
134 | {"GOOG0010" , 0}, /* Google's electronic privacy screen for eDP-1 */ |
135 | {} |
136 | }; |
137 | MODULE_DEVICE_TABLE(acpi, chromeos_privacy_screen_device_ids); |
138 | |
139 | static struct acpi_driver chromeos_privacy_screen_driver = { |
140 | .name = "chromeos_privacy_screen_driver" , |
141 | .class = "ChromeOS" , |
142 | .ids = chromeos_privacy_screen_device_ids, |
143 | .ops = { |
144 | .add = chromeos_privacy_screen_add, |
145 | .remove = chromeos_privacy_screen_remove, |
146 | }, |
147 | }; |
148 | |
149 | module_acpi_driver(chromeos_privacy_screen_driver); |
150 | MODULE_LICENSE("GPL v2" ); |
151 | MODULE_DESCRIPTION("ChromeOS ACPI Privacy Screen driver" ); |
152 | MODULE_AUTHOR("Rajat Jain <rajatja@google.com>" ); |
153 | |