13fb57847SRajat Jain // SPDX-License-Identifier: GPL-2.0
23fb57847SRajat Jain
33fb57847SRajat Jain /*
43fb57847SRajat Jain * ChromeOS Privacy Screen support
53fb57847SRajat Jain *
63fb57847SRajat Jain * Copyright (C) 2022 Google LLC
73fb57847SRajat Jain *
83fb57847SRajat Jain * This is the Chromeos privacy screen provider, present on certain chromebooks,
93fb57847SRajat Jain * represented by a GOOG0010 device in the ACPI. This ACPI device, if present,
103fb57847SRajat Jain * will cause the i915 drm driver to probe defer until this driver registers
113fb57847SRajat Jain * the privacy-screen.
123fb57847SRajat Jain */
133fb57847SRajat Jain
143fb57847SRajat Jain #include <linux/acpi.h>
153fb57847SRajat Jain #include <drm/drm_privacy_screen_driver.h>
163fb57847SRajat Jain
173fb57847SRajat Jain /*
183fb57847SRajat Jain * The DSM (Device Specific Method) constants below are the agreed API with
193fb57847SRajat Jain * the firmware team, on how to control privacy screen using ACPI methods.
203fb57847SRajat Jain */
213fb57847SRajat Jain #define PRIV_SCRN_DSM_REVID 1 /* DSM version */
223fb57847SRajat Jain #define PRIV_SCRN_DSM_FN_GET_STATUS 1 /* Get privacy screen status */
233fb57847SRajat Jain #define PRIV_SCRN_DSM_FN_ENABLE 2 /* Enable privacy screen */
243fb57847SRajat Jain #define PRIV_SCRN_DSM_FN_DISABLE 3 /* Disable privacy screen */
253fb57847SRajat Jain
263fb57847SRajat Jain static const guid_t chromeos_privacy_screen_dsm_guid =
273fb57847SRajat Jain GUID_INIT(0xc7033113, 0x8720, 0x4ceb,
283fb57847SRajat Jain 0x90, 0x90, 0x9d, 0x52, 0xb3, 0xe5, 0x2d, 0x73);
293fb57847SRajat Jain
303fb57847SRajat Jain static void
chromeos_privacy_screen_get_hw_state(struct drm_privacy_screen * drm_privacy_screen)313fb57847SRajat Jain chromeos_privacy_screen_get_hw_state(struct drm_privacy_screen
323fb57847SRajat Jain *drm_privacy_screen)
333fb57847SRajat Jain {
343fb57847SRajat Jain union acpi_object *obj;
353fb57847SRajat Jain acpi_handle handle;
363fb57847SRajat Jain struct device *privacy_screen =
373fb57847SRajat Jain drm_privacy_screen_get_drvdata(drm_privacy_screen);
383fb57847SRajat Jain
393fb57847SRajat Jain handle = acpi_device_handle(to_acpi_device(privacy_screen));
403fb57847SRajat Jain obj = acpi_evaluate_dsm(handle, &chromeos_privacy_screen_dsm_guid,
413fb57847SRajat Jain PRIV_SCRN_DSM_REVID,
423fb57847SRajat Jain PRIV_SCRN_DSM_FN_GET_STATUS, NULL);
433fb57847SRajat Jain if (!obj) {
443fb57847SRajat Jain dev_err(privacy_screen,
453fb57847SRajat Jain "_DSM failed to get privacy-screen state\n");
463fb57847SRajat Jain return;
473fb57847SRajat Jain }
483fb57847SRajat Jain
493fb57847SRajat Jain if (obj->type != ACPI_TYPE_INTEGER)
503fb57847SRajat Jain dev_err(privacy_screen,
513fb57847SRajat Jain "Bad _DSM to get privacy-screen state\n");
523fb57847SRajat Jain else if (obj->integer.value == 1)
533fb57847SRajat Jain drm_privacy_screen->hw_state = drm_privacy_screen->sw_state =
543fb57847SRajat Jain PRIVACY_SCREEN_ENABLED;
553fb57847SRajat Jain else
563fb57847SRajat Jain drm_privacy_screen->hw_state = drm_privacy_screen->sw_state =
573fb57847SRajat Jain PRIVACY_SCREEN_DISABLED;
583fb57847SRajat Jain
593fb57847SRajat Jain ACPI_FREE(obj);
603fb57847SRajat Jain }
613fb57847SRajat Jain
623fb57847SRajat Jain static int
chromeos_privacy_screen_set_sw_state(struct drm_privacy_screen * drm_privacy_screen,enum drm_privacy_screen_status state)633fb57847SRajat Jain chromeos_privacy_screen_set_sw_state(struct drm_privacy_screen
643fb57847SRajat Jain *drm_privacy_screen,
653fb57847SRajat Jain enum drm_privacy_screen_status state)
663fb57847SRajat Jain {
673fb57847SRajat Jain union acpi_object *obj = NULL;
683fb57847SRajat Jain acpi_handle handle;
693fb57847SRajat Jain struct device *privacy_screen =
703fb57847SRajat Jain drm_privacy_screen_get_drvdata(drm_privacy_screen);
713fb57847SRajat Jain
723fb57847SRajat Jain handle = acpi_device_handle(to_acpi_device(privacy_screen));
733fb57847SRajat Jain
743fb57847SRajat Jain if (state == PRIVACY_SCREEN_DISABLED) {
753fb57847SRajat Jain obj = acpi_evaluate_dsm(handle,
763fb57847SRajat Jain &chromeos_privacy_screen_dsm_guid,
773fb57847SRajat Jain PRIV_SCRN_DSM_REVID,
783fb57847SRajat Jain PRIV_SCRN_DSM_FN_DISABLE, NULL);
793fb57847SRajat Jain } else if (state == PRIVACY_SCREEN_ENABLED) {
803fb57847SRajat Jain obj = acpi_evaluate_dsm(handle,
813fb57847SRajat Jain &chromeos_privacy_screen_dsm_guid,
823fb57847SRajat Jain PRIV_SCRN_DSM_REVID,
833fb57847SRajat Jain PRIV_SCRN_DSM_FN_ENABLE, NULL);
843fb57847SRajat Jain } else {
853fb57847SRajat Jain dev_err(privacy_screen,
863fb57847SRajat Jain "Bad attempt to set privacy-screen status to %u\n",
873fb57847SRajat Jain state);
883fb57847SRajat Jain return -EINVAL;
893fb57847SRajat Jain }
903fb57847SRajat Jain
913fb57847SRajat Jain if (!obj) {
923fb57847SRajat Jain dev_err(privacy_screen,
933fb57847SRajat Jain "_DSM failed to set privacy-screen state\n");
943fb57847SRajat Jain return -EIO;
953fb57847SRajat Jain }
963fb57847SRajat Jain
973fb57847SRajat Jain drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = state;
983fb57847SRajat Jain ACPI_FREE(obj);
993fb57847SRajat Jain return 0;
1003fb57847SRajat Jain }
1013fb57847SRajat Jain
1023fb57847SRajat Jain static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = {
1033fb57847SRajat Jain .get_hw_state = chromeos_privacy_screen_get_hw_state,
1043fb57847SRajat Jain .set_sw_state = chromeos_privacy_screen_set_sw_state,
1053fb57847SRajat Jain };
1063fb57847SRajat Jain
chromeos_privacy_screen_add(struct acpi_device * adev)1073fb57847SRajat Jain static int chromeos_privacy_screen_add(struct acpi_device *adev)
1083fb57847SRajat Jain {
1093fb57847SRajat Jain struct drm_privacy_screen *drm_privacy_screen =
1103fb57847SRajat Jain drm_privacy_screen_register(&adev->dev,
1113fb57847SRajat Jain &chromeos_privacy_screen_ops,
1123fb57847SRajat Jain &adev->dev);
1133fb57847SRajat Jain
1143fb57847SRajat Jain if (IS_ERR(drm_privacy_screen)) {
1153fb57847SRajat Jain dev_err(&adev->dev, "Error registering privacy-screen\n");
1163fb57847SRajat Jain return PTR_ERR(drm_privacy_screen);
1173fb57847SRajat Jain }
1183fb57847SRajat Jain
1193fb57847SRajat Jain adev->driver_data = drm_privacy_screen;
1203fb57847SRajat Jain dev_info(&adev->dev, "registered privacy-screen '%s'\n",
1213fb57847SRajat Jain dev_name(&drm_privacy_screen->dev));
1223fb57847SRajat Jain
1233fb57847SRajat Jain return 0;
1243fb57847SRajat Jain }
1253fb57847SRajat Jain
chromeos_privacy_screen_remove(struct acpi_device * adev)126*6c0eb5baSDawei Li static void chromeos_privacy_screen_remove(struct acpi_device *adev)
1273fb57847SRajat Jain {
1283fb57847SRajat Jain struct drm_privacy_screen *drm_privacy_screen = acpi_driver_data(adev);
1293fb57847SRajat Jain
1303fb57847SRajat Jain drm_privacy_screen_unregister(drm_privacy_screen);
1313fb57847SRajat Jain }
1323fb57847SRajat Jain
1333fb57847SRajat Jain static const struct acpi_device_id chromeos_privacy_screen_device_ids[] = {
1343fb57847SRajat Jain {"GOOG0010", 0}, /* Google's electronic privacy screen for eDP-1 */
1353fb57847SRajat Jain {}
1363fb57847SRajat Jain };
1373fb57847SRajat Jain MODULE_DEVICE_TABLE(acpi, chromeos_privacy_screen_device_ids);
1383fb57847SRajat Jain
1393fb57847SRajat Jain static struct acpi_driver chromeos_privacy_screen_driver = {
1403fb57847SRajat Jain .name = "chromeos_privacy_screen_driver",
1413fb57847SRajat Jain .class = "ChromeOS",
1423fb57847SRajat Jain .ids = chromeos_privacy_screen_device_ids,
1433fb57847SRajat Jain .ops = {
1443fb57847SRajat Jain .add = chromeos_privacy_screen_add,
1453fb57847SRajat Jain .remove = chromeos_privacy_screen_remove,
1463fb57847SRajat Jain },
1473fb57847SRajat Jain };
1483fb57847SRajat Jain
1493fb57847SRajat Jain module_acpi_driver(chromeos_privacy_screen_driver);
1503fb57847SRajat Jain MODULE_LICENSE("GPL v2");
1513fb57847SRajat Jain MODULE_DESCRIPTION("ChromeOS ACPI Privacy Screen driver");
1523fb57847SRajat Jain MODULE_AUTHOR("Rajat Jain <rajatja@google.com>");
153