1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for Samsung Q10 and related laptops: controls the backlight |
4 | * |
5 | * Copyright (c) 2011 Frederick van der Wyck <fvanderwyck@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/backlight.h> |
13 | #include <linux/dmi.h> |
14 | #include <linux/acpi.h> |
15 | |
16 | #define SAMSUNGQ10_BL_MAX_INTENSITY 7 |
17 | |
18 | static acpi_handle ec_handle; |
19 | |
20 | static bool force; |
21 | module_param(force, bool, 0); |
22 | MODULE_PARM_DESC(force, |
23 | "Disable the DMI check and force the driver to be loaded" ); |
24 | |
25 | static int samsungq10_bl_set_intensity(struct backlight_device *bd) |
26 | { |
27 | |
28 | acpi_status status; |
29 | int i; |
30 | |
31 | for (i = 0; i < SAMSUNGQ10_BL_MAX_INTENSITY; i++) { |
32 | status = acpi_evaluate_object(object: ec_handle, pathname: "_Q63" , NULL, NULL); |
33 | if (ACPI_FAILURE(status)) |
34 | return -EIO; |
35 | } |
36 | for (i = 0; i < bd->props.brightness; i++) { |
37 | status = acpi_evaluate_object(object: ec_handle, pathname: "_Q64" , NULL, NULL); |
38 | if (ACPI_FAILURE(status)) |
39 | return -EIO; |
40 | } |
41 | |
42 | return 0; |
43 | } |
44 | |
45 | static const struct backlight_ops samsungq10_bl_ops = { |
46 | .update_status = samsungq10_bl_set_intensity, |
47 | }; |
48 | |
49 | static int samsungq10_probe(struct platform_device *pdev) |
50 | { |
51 | |
52 | struct backlight_properties props; |
53 | struct backlight_device *bd; |
54 | |
55 | memset(&props, 0, sizeof(struct backlight_properties)); |
56 | props.type = BACKLIGHT_PLATFORM; |
57 | props.max_brightness = SAMSUNGQ10_BL_MAX_INTENSITY; |
58 | bd = backlight_device_register(name: "samsung" , dev: &pdev->dev, NULL, |
59 | ops: &samsungq10_bl_ops, props: &props); |
60 | if (IS_ERR(ptr: bd)) |
61 | return PTR_ERR(ptr: bd); |
62 | |
63 | platform_set_drvdata(pdev, data: bd); |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static void samsungq10_remove(struct platform_device *pdev) |
69 | { |
70 | |
71 | struct backlight_device *bd = platform_get_drvdata(pdev); |
72 | |
73 | backlight_device_unregister(bd); |
74 | } |
75 | |
76 | static struct platform_driver samsungq10_driver = { |
77 | .driver = { |
78 | .name = KBUILD_MODNAME, |
79 | }, |
80 | .probe = samsungq10_probe, |
81 | .remove_new = samsungq10_remove, |
82 | }; |
83 | |
84 | static struct platform_device *samsungq10_device; |
85 | |
86 | static int __init dmi_check_callback(const struct dmi_system_id *id) |
87 | { |
88 | printk(KERN_INFO KBUILD_MODNAME ": found model '%s'\n" , id->ident); |
89 | return 1; |
90 | } |
91 | |
92 | static const struct dmi_system_id samsungq10_dmi_table[] __initconst = { |
93 | { |
94 | .ident = "Samsung Q10" , |
95 | .matches = { |
96 | DMI_MATCH(DMI_SYS_VENDOR, "Samsung" ), |
97 | DMI_MATCH(DMI_PRODUCT_NAME, "SQ10" ), |
98 | }, |
99 | .callback = dmi_check_callback, |
100 | }, |
101 | { |
102 | .ident = "Samsung Q20" , |
103 | .matches = { |
104 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics" ), |
105 | DMI_MATCH(DMI_PRODUCT_NAME, "SENS Q20" ), |
106 | }, |
107 | .callback = dmi_check_callback, |
108 | }, |
109 | { |
110 | .ident = "Samsung Q25" , |
111 | .matches = { |
112 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics" ), |
113 | DMI_MATCH(DMI_PRODUCT_NAME, "NQ25" ), |
114 | }, |
115 | .callback = dmi_check_callback, |
116 | }, |
117 | { |
118 | .ident = "Dell Latitude X200" , |
119 | .matches = { |
120 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation" ), |
121 | DMI_MATCH(DMI_PRODUCT_NAME, "X200" ), |
122 | }, |
123 | .callback = dmi_check_callback, |
124 | }, |
125 | { }, |
126 | }; |
127 | MODULE_DEVICE_TABLE(dmi, samsungq10_dmi_table); |
128 | |
129 | static int __init samsungq10_init(void) |
130 | { |
131 | if (!force && !dmi_check_system(list: samsungq10_dmi_table)) |
132 | return -ENODEV; |
133 | |
134 | ec_handle = ec_get_handle(); |
135 | |
136 | if (!ec_handle) |
137 | return -ENODEV; |
138 | |
139 | samsungq10_device = platform_create_bundle(&samsungq10_driver, |
140 | samsungq10_probe, |
141 | NULL, 0, NULL, 0); |
142 | |
143 | return PTR_ERR_OR_ZERO(ptr: samsungq10_device); |
144 | } |
145 | |
146 | static void __exit samsungq10_exit(void) |
147 | { |
148 | platform_device_unregister(samsungq10_device); |
149 | platform_driver_unregister(&samsungq10_driver); |
150 | } |
151 | |
152 | module_init(samsungq10_init); |
153 | module_exit(samsungq10_exit); |
154 | |
155 | MODULE_AUTHOR("Frederick van der Wyck <fvanderwyck@gmail.com>" ); |
156 | MODULE_DESCRIPTION("Samsung Q10 Driver" ); |
157 | MODULE_LICENSE("GPL" ); |
158 | |