Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
[linux-2.6-microblaze.git] / drivers / acpi / video_detect.c
1 /*
2  *  Copyright (C) 2015       Red Hat Inc.
3  *                           Hans de Goede <hdegoede@redhat.com>
4  *  Copyright (C) 2008       SuSE Linux Products GmbH
5  *                           Thomas Renninger <trenn@suse.de>
6  *
7  *  May be copied or modified under the terms of the GNU General Public License
8  *
9  * video_detect.c:
10  * After PCI devices are glued with ACPI devices
11  * acpi_get_pci_dev() can be called to identify ACPI graphics
12  * devices for which a real graphics card is plugged in
13  *
14  * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B)
15  * are available, video.ko should be used to handle the device.
16  *
17  * Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop,
18  * sony_acpi,... can take care about backlight brightness.
19  *
20  * Backlight drivers can use acpi_video_get_backlight_type() to determine
21  * which driver should handle the backlight.
22  *
23  * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
24  * this file will not be compiled and acpi_video_get_backlight_type() will
25  * always return acpi_backlight_vendor.
26  */
27
28 #include <linux/export.h>
29 #include <linux/acpi.h>
30 #include <linux/backlight.h>
31 #include <linux/dmi.h>
32 #include <linux/module.h>
33 #include <linux/pci.h>
34 #include <linux/types.h>
35 #include <linux/workqueue.h>
36 #include <acpi/video.h>
37
38 void acpi_video_unregister_backlight(void);
39
40 static bool backlight_notifier_registered;
41 static struct notifier_block backlight_nb;
42 static struct work_struct backlight_notify_work;
43
44 static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef;
45 static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef;
46
47 static void acpi_video_parse_cmdline(void)
48 {
49         if (!strcmp("vendor", acpi_video_backlight_string))
50                 acpi_backlight_cmdline = acpi_backlight_vendor;
51         if (!strcmp("video", acpi_video_backlight_string))
52                 acpi_backlight_cmdline = acpi_backlight_video;
53         if (!strcmp("native", acpi_video_backlight_string))
54                 acpi_backlight_cmdline = acpi_backlight_native;
55         if (!strcmp("none", acpi_video_backlight_string))
56                 acpi_backlight_cmdline = acpi_backlight_none;
57 }
58
59 static acpi_status
60 find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
61 {
62         long *cap = context;
63         struct pci_dev *dev;
64         struct acpi_device *acpi_dev;
65
66         static const struct acpi_device_id video_ids[] = {
67                 {ACPI_VIDEO_HID, 0},
68                 {"", 0},
69         };
70         if (acpi_bus_get_device(handle, &acpi_dev))
71                 return AE_OK;
72
73         if (!acpi_match_device_ids(acpi_dev, video_ids)) {
74                 dev = acpi_get_pci_dev(handle);
75                 if (!dev)
76                         return AE_OK;
77                 pci_dev_put(dev);
78                 *cap |= acpi_is_video_device(handle);
79         }
80         return AE_OK;
81 }
82
83 /* Force to use vendor driver when the ACPI device is known to be
84  * buggy */
85 static int video_detect_force_vendor(const struct dmi_system_id *d)
86 {
87         acpi_backlight_dmi = acpi_backlight_vendor;
88         return 0;
89 }
90
91 static int video_detect_force_video(const struct dmi_system_id *d)
92 {
93         acpi_backlight_dmi = acpi_backlight_video;
94         return 0;
95 }
96
97 static int video_detect_force_native(const struct dmi_system_id *d)
98 {
99         acpi_backlight_dmi = acpi_backlight_native;
100         return 0;
101 }
102
103 static int video_detect_force_none(const struct dmi_system_id *d)
104 {
105         acpi_backlight_dmi = acpi_backlight_none;
106         return 0;
107 }
108
109 static const struct dmi_system_id video_detect_dmi_table[] = {
110         /* On Samsung X360, the BIOS will set a flag (VDRV) if generic
111          * ACPI backlight device is used. This flag will definitively break
112          * the backlight interface (even the vendor interface) until next
113          * reboot. It's why we should prevent video.ko from being used here
114          * and we can't rely on a later call to acpi_video_unregister().
115          */
116         {
117          .callback = video_detect_force_vendor,
118          /* X360 */
119          .matches = {
120                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
121                 DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
122                 DMI_MATCH(DMI_BOARD_NAME, "X360"),
123                 },
124         },
125         {
126         .callback = video_detect_force_vendor,
127         /* Asus UL30VT */
128         .matches = {
129                 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
130                 DMI_MATCH(DMI_PRODUCT_NAME, "UL30VT"),
131                 },
132         },
133         {
134         .callback = video_detect_force_vendor,
135         /* Asus UL30A */
136         .matches = {
137                 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
138                 DMI_MATCH(DMI_PRODUCT_NAME, "UL30A"),
139                 },
140         },
141         {
142         .callback = video_detect_force_vendor,
143         /* GIGABYTE GB-BXBT-2807 */
144         .matches = {
145                 DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
146                 DMI_MATCH(DMI_PRODUCT_NAME, "GB-BXBT-2807"),
147                 },
148         },
149         {
150         .callback = video_detect_force_vendor,
151         /* Sony VPCEH3U1E */
152         .matches = {
153                 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
154                 DMI_MATCH(DMI_PRODUCT_NAME, "VPCEH3U1E"),
155                 },
156         },
157         {
158         .callback = video_detect_force_vendor,
159         /* Xiaomi Mi Pad 2 */
160         .matches = {
161                         DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
162                         DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
163                 },
164         },
165
166         /*
167          * These models have a working acpi_video backlight control, and using
168          * native backlight causes a regression where backlight does not work
169          * when userspace is not handling brightness key events. Disable
170          * native_backlight on these to fix this:
171          * https://bugzilla.kernel.org/show_bug.cgi?id=81691
172          */
173         {
174          .callback = video_detect_force_video,
175          /* ThinkPad T420 */
176          .matches = {
177                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
178                 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"),
179                 },
180         },
181         {
182          .callback = video_detect_force_video,
183          /* ThinkPad T520 */
184          .matches = {
185                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
186                 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
187                 },
188         },
189         {
190          .callback = video_detect_force_video,
191          /* ThinkPad X201s */
192          .matches = {
193                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
194                 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
195                 },
196         },
197         {
198          .callback = video_detect_force_video,
199          /* ThinkPad X201T */
200          .matches = {
201                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
202                 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201T"),
203                 },
204         },
205
206         /* The native backlight controls do not work on some older machines */
207         {
208          /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */
209          .callback = video_detect_force_video,
210          /* HP ENVY 15 Notebook */
211          .matches = {
212                 DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
213                 DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
214                 },
215         },
216         {
217          .callback = video_detect_force_video,
218          /* SAMSUNG 870Z5E/880Z5E/680Z5E */
219          .matches = {
220                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
221                 DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
222                 },
223         },
224         {
225          .callback = video_detect_force_video,
226          /* SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V */
227          .matches = {
228                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
229                 DMI_MATCH(DMI_PRODUCT_NAME,
230                           "370R4E/370R4V/370R5E/3570RE/370R5V"),
231                 },
232         },
233         {
234          /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */
235          .callback = video_detect_force_video,
236          /* SAMSUNG 3570R/370R/470R/450R/510R/4450RV */
237          .matches = {
238                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
239                 DMI_MATCH(DMI_PRODUCT_NAME,
240                           "3570R/370R/470R/450R/510R/4450RV"),
241                 },
242         },
243         {
244          /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */
245          .callback = video_detect_force_video,
246          /* SAMSUNG 670Z5E */
247          .matches = {
248                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
249                 DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"),
250                 },
251         },
252         {
253          /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
254          .callback = video_detect_force_video,
255          /* SAMSUNG 730U3E/740U3E */
256          .matches = {
257                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
258                 DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
259                 },
260         },
261         {
262          /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */
263          .callback = video_detect_force_video,
264          /* SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D */
265          .matches = {
266                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
267                 DMI_MATCH(DMI_PRODUCT_NAME,
268                           "900X3C/900X3D/900X3E/900X4C/900X4D"),
269                 },
270         },
271         {
272          /* https://bugzilla.redhat.com/show_bug.cgi?id=1272633 */
273          .callback = video_detect_force_video,
274          /* Dell XPS14 L421X */
275          .matches = {
276                 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
277                 DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
278                 },
279         },
280         {
281          /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */
282          .callback = video_detect_force_video,
283          /* Dell XPS15 L521X */
284          .matches = {
285                 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
286                 DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
287                 },
288         },
289         {
290          /* https://bugzilla.kernel.org/show_bug.cgi?id=108971 */
291          .callback = video_detect_force_video,
292          /* SAMSUNG 530U4E/540U4E */
293          .matches = {
294                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
295                 DMI_MATCH(DMI_PRODUCT_NAME, "530U4E/540U4E"),
296                 },
297         },
298         /* https://bugs.launchpad.net/bugs/1894667 */
299         {
300          .callback = video_detect_force_video,
301          /* HP 635 Notebook */
302          .matches = {
303                 DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
304                 DMI_MATCH(DMI_PRODUCT_NAME, "HP 635 Notebook PC"),
305                 },
306         },
307
308         /* Non win8 machines which need native backlight nevertheless */
309         {
310          /* https://bugzilla.redhat.com/show_bug.cgi?id=1201530 */
311          .callback = video_detect_force_native,
312          /* Lenovo Ideapad S405 */
313          .matches = {
314                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
315                 DMI_MATCH(DMI_BOARD_NAME, "Lenovo IdeaPad S405"),
316                 },
317         },
318         {
319          /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
320          .callback = video_detect_force_native,
321          /* Lenovo Ideapad Z570 */
322          .matches = {
323                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
324                 DMI_MATCH(DMI_PRODUCT_NAME, "102434U"),
325                 },
326         },
327         {
328          .callback = video_detect_force_native,
329          /* Lenovo E41-25 */
330          .matches = {
331                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
332                 DMI_MATCH(DMI_PRODUCT_NAME, "81FS"),
333                 },
334         },
335         {
336          .callback = video_detect_force_native,
337          /* Lenovo E41-45 */
338          .matches = {
339                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
340                 DMI_MATCH(DMI_PRODUCT_NAME, "82BK"),
341                 },
342         },
343         {
344          /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
345          .callback = video_detect_force_native,
346          /* Apple MacBook Pro 12,1 */
347          .matches = {
348                 DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
349                 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
350                 },
351         },
352         {
353          .callback = video_detect_force_native,
354          /* Dell Vostro V131 */
355          .matches = {
356                 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
357                 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
358                 },
359         },
360         {
361          /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */
362          .callback = video_detect_force_native,
363          /* Dell XPS 17 L702X */
364          .matches = {
365                 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
366                 DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"),
367                 },
368         },
369         {
370          .callback = video_detect_force_native,
371          /* Dell Precision 7510 */
372          .matches = {
373                 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
374                 DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7510"),
375                 },
376         },
377         {
378          .callback = video_detect_force_native,
379          /* Acer Aspire 5738z */
380          .matches = {
381                 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
382                 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"),
383                 DMI_MATCH(DMI_BOARD_NAME, "JV50"),
384                 },
385         },
386         {
387          /* https://bugzilla.kernel.org/show_bug.cgi?id=207835 */
388          .callback = video_detect_force_native,
389          /* Acer TravelMate 5735Z */
390          .matches = {
391                 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
392                 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5735Z"),
393                 DMI_MATCH(DMI_BOARD_NAME, "BA51_MV"),
394                 },
395         },
396         {
397         .callback = video_detect_force_native,
398         /* ASUSTeK COMPUTER INC. GA401 */
399         .matches = {
400                 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
401                 DMI_MATCH(DMI_PRODUCT_NAME, "GA401"),
402                 },
403         },
404         {
405         .callback = video_detect_force_native,
406         /* ASUSTeK COMPUTER INC. GA502 */
407         .matches = {
408                 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
409                 DMI_MATCH(DMI_PRODUCT_NAME, "GA502"),
410                 },
411         },
412         {
413         .callback = video_detect_force_native,
414         /* ASUSTeK COMPUTER INC. GA503 */
415         .matches = {
416                 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
417                 DMI_MATCH(DMI_PRODUCT_NAME, "GA503"),
418                 },
419         },
420
421         /*
422          * Desktops which falsely report a backlight and which our heuristics
423          * for this do not catch.
424          */
425         {
426          .callback = video_detect_force_none,
427          /* Dell OptiPlex 9020M */
428          .matches = {
429                 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
430                 DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 9020M"),
431                 },
432         },
433         {
434          .callback = video_detect_force_none,
435          /* MSI MS-7721 */
436          .matches = {
437                 DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
438                 DMI_MATCH(DMI_PRODUCT_NAME, "MS-7721"),
439                 },
440         },
441         { },
442 };
443
444 /* This uses a workqueue to avoid various locking ordering issues */
445 static void acpi_video_backlight_notify_work(struct work_struct *work)
446 {
447         if (acpi_video_get_backlight_type() != acpi_backlight_video)
448                 acpi_video_unregister_backlight();
449 }
450
451 static int acpi_video_backlight_notify(struct notifier_block *nb,
452                                        unsigned long val, void *bd)
453 {
454         struct backlight_device *backlight = bd;
455
456         /* A raw bl registering may change video -> native */
457         if (backlight->props.type == BACKLIGHT_RAW &&
458             val == BACKLIGHT_REGISTERED)
459                 schedule_work(&backlight_notify_work);
460
461         return NOTIFY_OK;
462 }
463
464 /*
465  * Determine which type of backlight interface to use on this system,
466  * First check cmdline, then dmi quirks, then do autodetect.
467  *
468  * The autodetect order is:
469  * 1) Is the acpi-video backlight interface supported ->
470  *  no, use a vendor interface
471  * 2) Is this a win8 "ready" BIOS and do we have a native interface ->
472  *  yes, use a native interface
473  * 3) Else use the acpi-video interface
474  *
475  * Arguably the native on win8 check should be done first, but that would
476  * be a behavior change, which may causes issues.
477  */
478 enum acpi_backlight_type acpi_video_get_backlight_type(void)
479 {
480         static DEFINE_MUTEX(init_mutex);
481         static bool init_done;
482         static long video_caps;
483
484         /* Parse cmdline, dmi and acpi only once */
485         mutex_lock(&init_mutex);
486         if (!init_done) {
487                 acpi_video_parse_cmdline();
488                 dmi_check_system(video_detect_dmi_table);
489                 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
490                                     ACPI_UINT32_MAX, find_video, NULL,
491                                     &video_caps, NULL);
492                 INIT_WORK(&backlight_notify_work,
493                           acpi_video_backlight_notify_work);
494                 backlight_nb.notifier_call = acpi_video_backlight_notify;
495                 backlight_nb.priority = 0;
496                 if (backlight_register_notifier(&backlight_nb) == 0)
497                         backlight_notifier_registered = true;
498                 init_done = true;
499         }
500         mutex_unlock(&init_mutex);
501
502         if (acpi_backlight_cmdline != acpi_backlight_undef)
503                 return acpi_backlight_cmdline;
504
505         if (acpi_backlight_dmi != acpi_backlight_undef)
506                 return acpi_backlight_dmi;
507
508         if (!(video_caps & ACPI_VIDEO_BACKLIGHT))
509                 return acpi_backlight_vendor;
510
511         if (acpi_osi_is_win8() && backlight_device_get_by_type(BACKLIGHT_RAW))
512                 return acpi_backlight_native;
513
514         return acpi_backlight_video;
515 }
516 EXPORT_SYMBOL(acpi_video_get_backlight_type);
517
518 /*
519  * Set the preferred backlight interface type based on DMI info.
520  * This function allows DMI blacklists to be implemented by external
521  * platform drivers instead of putting a big blacklist in video_detect.c
522  */
523 void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type)
524 {
525         acpi_backlight_dmi = type;
526         /* Remove acpi-video backlight interface if it is no longer desired */
527         if (acpi_video_get_backlight_type() != acpi_backlight_video)
528                 acpi_video_unregister_backlight();
529 }
530 EXPORT_SYMBOL(acpi_video_set_dmi_backlight_type);
531
532 void __exit acpi_video_detect_exit(void)
533 {
534         if (backlight_notifier_registered)
535                 backlight_unregister_notifier(&backlight_nb);
536 }