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 which
21 * driver should handle the backlight. RAW/GPU-driver backlight drivers must
22 * use the acpi_video_backlight_use_native() helper for this.
23 *
24 * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
25 * this file will not be compiled and acpi_video_get_backlight_type() will
26 * always return acpi_backlight_vendor.
27 */
28
29#include <linux/export.h>
30#include <linux/acpi.h>
31#include <linux/apple-gmux.h>
32#include <linux/backlight.h>
33#include <linux/dmi.h>
34#include <linux/module.h>
35#include <linux/pci.h>
36#include <linux/platform_data/x86/nvidia-wmi-ec-backlight.h>
37#include <linux/pnp.h>
38#include <linux/types.h>
39#include <linux/workqueue.h>
40#include <acpi/video.h>
41
42static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef;
43static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef;
44
45static void acpi_video_parse_cmdline(void)
46{
47	if (!strcmp("vendor", acpi_video_backlight_string))
48		acpi_backlight_cmdline = acpi_backlight_vendor;
49	if (!strcmp("video", acpi_video_backlight_string))
50		acpi_backlight_cmdline = acpi_backlight_video;
51	if (!strcmp("native", acpi_video_backlight_string))
52		acpi_backlight_cmdline = acpi_backlight_native;
53	if (!strcmp("nvidia_wmi_ec", acpi_video_backlight_string))
54		acpi_backlight_cmdline = acpi_backlight_nvidia_wmi_ec;
55	if (!strcmp("apple_gmux", acpi_video_backlight_string))
56		acpi_backlight_cmdline = acpi_backlight_apple_gmux;
57	if (!strcmp("none", acpi_video_backlight_string))
58		acpi_backlight_cmdline = acpi_backlight_none;
59}
60
61static acpi_status
62find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
63{
64	struct acpi_device *acpi_dev = acpi_fetch_acpi_dev(handle);
65	long *cap = context;
66	struct pci_dev *dev;
67
68	static const struct acpi_device_id video_ids[] = {
69		{ACPI_VIDEO_HID, 0},
70		{"", 0},
71	};
72
73	if (acpi_dev && !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/* This depends on ACPI_WMI which is X86 only */
84#ifdef CONFIG_X86
85static bool nvidia_wmi_ec_supported(void)
86{
87	struct wmi_brightness_args args = {
88		.mode = WMI_BRIGHTNESS_MODE_GET,
89		.val = 0,
90		.ret = 0,
91	};
92	struct acpi_buffer buf = { (acpi_size)sizeof(args), &args };
93	acpi_status status;
94
95	status = wmi_evaluate_method(WMI_BRIGHTNESS_GUID, 0,
96				     WMI_BRIGHTNESS_METHOD_SOURCE, &buf, &buf);
97	if (ACPI_FAILURE(status))
98		return false;
99
100	/*
101	 * If brightness is handled by the EC then nvidia-wmi-ec-backlight
102	 * should be used, else the GPU driver(s) should be used.
103	 */
104	return args.ret == WMI_BRIGHTNESS_SOURCE_EC;
105}
106#else
107static bool nvidia_wmi_ec_supported(void)
108{
109	return false;
110}
111#endif
112
113/* Force to use vendor driver when the ACPI device is known to be
114 * buggy */
115static int video_detect_force_vendor(const struct dmi_system_id *d)
116{
117	acpi_backlight_dmi = acpi_backlight_vendor;
118	return 0;
119}
120
121static int video_detect_force_video(const struct dmi_system_id *d)
122{
123	acpi_backlight_dmi = acpi_backlight_video;
124	return 0;
125}
126
127static int video_detect_force_native(const struct dmi_system_id *d)
128{
129	acpi_backlight_dmi = acpi_backlight_native;
130	return 0;
131}
132
133static int video_detect_portege_r100(const struct dmi_system_id *d)
134{
135	struct pci_dev *dev;
136	/* Search for Trident CyberBlade XP4m32 to confirm Portégé R100 */
137	dev = pci_get_device(PCI_VENDOR_ID_TRIDENT, 0x2100, NULL);
138	if (dev)
139		acpi_backlight_dmi = acpi_backlight_vendor;
140	return 0;
141}
142
143static const struct dmi_system_id video_detect_dmi_table[] = {
144	/*
145	 * Models which should use the vendor backlight interface,
146	 * because of broken ACPI video backlight control.
147	 */
148	{
149	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1128309 */
150	 .callback = video_detect_force_vendor,
151	 /* Acer KAV80 */
152	 .matches = {
153		DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
154		DMI_MATCH(DMI_PRODUCT_NAME, "KAV80"),
155		},
156	},
157	{
158	 .callback = video_detect_force_vendor,
159	 /* Asus UL30VT */
160	 .matches = {
161		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
162		DMI_MATCH(DMI_PRODUCT_NAME, "UL30VT"),
163		},
164	},
165	{
166	 .callback = video_detect_force_vendor,
167	 /* Asus UL30A */
168	 .matches = {
169		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
170		DMI_MATCH(DMI_PRODUCT_NAME, "UL30A"),
171		},
172	},
173	{
174	 .callback = video_detect_force_vendor,
175	 /* Asus X55U */
176	 .matches = {
177		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
178		DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
179		},
180	},
181	{
182	 /* https://bugs.launchpad.net/bugs/1000146 */
183	 .callback = video_detect_force_vendor,
184	 /* Asus X101CH */
185	 .matches = {
186		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
187		DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"),
188		},
189	},
190	{
191	 .callback = video_detect_force_vendor,
192	 /* Asus X401U */
193	 .matches = {
194		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
195		DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
196		},
197	},
198	{
199	 .callback = video_detect_force_vendor,
200	 /* Asus X501U */
201	 .matches = {
202		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
203		DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
204		},
205	},
206	{
207	 /* https://bugs.launchpad.net/bugs/1000146 */
208	 .callback = video_detect_force_vendor,
209	 /* Asus 1015CX */
210	 .matches = {
211		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
212		DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"),
213		},
214	},
215	{
216	 .callback = video_detect_force_vendor,
217	 /* Samsung N150/N210/N220 */
218	 .matches = {
219		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
220		DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
221		DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
222		},
223	},
224	{
225	 .callback = video_detect_force_vendor,
226	 /* Samsung NF110/NF210/NF310 */
227	 .matches = {
228		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
229		DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
230		DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
231		},
232	},
233	{
234	 .callback = video_detect_force_vendor,
235	 /* Samsung NC210 */
236	 .matches = {
237		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
238		DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
239		DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
240		},
241	},
242	{
243	 .callback = video_detect_force_vendor,
244	 /* Xiaomi Mi Pad 2 */
245	 .matches = {
246			DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
247			DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
248		},
249	},
250
251	/*
252	 * Models which should use the vendor backlight interface,
253	 * because of broken native backlight control.
254	 */
255	{
256	 .callback = video_detect_force_vendor,
257	 /* Sony Vaio PCG-FRV35 */
258	 .matches = {
259		DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
260		DMI_MATCH(DMI_PRODUCT_NAME, "PCG-FRV35"),
261		},
262	},
263
264	/*
265	 * Toshiba models with Transflective display, these need to use
266	 * the toshiba_acpi vendor driver for proper Transflective handling.
267	 */
268	{
269	 .callback = video_detect_force_vendor,
270	 .matches = {
271		DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
272		DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R500"),
273		},
274	},
275	{
276	 .callback = video_detect_force_vendor,
277	 .matches = {
278		DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
279		DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R600"),
280		},
281	},
282
283	/*
284	 * Toshiba Portégé R100 has working both acpi_video and toshiba_acpi
285	 * vendor driver. But none of them gets activated as it has a VGA with
286	 * no kernel driver (Trident CyberBlade XP4m32).
287	 * The DMI strings are generic so check for the VGA chip in callback.
288	 */
289	{
290	 .callback = video_detect_portege_r100,
291	 .matches = {
292		DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
293		DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"),
294		DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"),
295		DMI_MATCH(DMI_BOARD_NAME, "Portable PC")
296		},
297	},
298
299	/*
300	 * Models which need acpi_video backlight control where the GPU drivers
301	 * do not call acpi_video_register_backlight() because no internal panel
302	 * is detected. Typically these are all-in-ones (monitors with builtin
303	 * PC) where the panel connection shows up as regular DP instead of eDP.
304	 */
305	{
306	 .callback = video_detect_force_video,
307	 /* Apple iMac14,1 */
308	 .matches = {
309		DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
310		DMI_MATCH(DMI_PRODUCT_NAME, "iMac14,1"),
311		},
312	},
313	{
314	 .callback = video_detect_force_video,
315	 /* Apple iMac14,2 */
316	 .matches = {
317		DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
318		DMI_MATCH(DMI_PRODUCT_NAME, "iMac14,2"),
319		},
320	},
321
322	/*
323	 * These models have a working acpi_video backlight control, and using
324	 * native backlight causes a regression where backlight does not work
325	 * when userspace is not handling brightness key events. Disable
326	 * native_backlight on these to fix this:
327	 * https://bugzilla.kernel.org/show_bug.cgi?id=81691
328	 */
329	{
330	 .callback = video_detect_force_video,
331	 /* ThinkPad T420 */
332	 .matches = {
333		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
334		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"),
335		},
336	},
337	{
338	 .callback = video_detect_force_video,
339	 /* ThinkPad T520 */
340	 .matches = {
341		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
342		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
343		},
344	},
345	{
346	 .callback = video_detect_force_video,
347	 /* ThinkPad X201s */
348	 .matches = {
349		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
350		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
351		},
352	},
353	{
354	 .callback = video_detect_force_video,
355	 /* ThinkPad X201T */
356	 .matches = {
357		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
358		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201T"),
359		},
360	},
361
362	/* The native backlight controls do not work on some older machines */
363	{
364	 /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */
365	 .callback = video_detect_force_video,
366	 /* HP ENVY 15 Notebook */
367	 .matches = {
368		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
369		DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
370		},
371	},
372	{
373	 .callback = video_detect_force_video,
374	 /* SAMSUNG 870Z5E/880Z5E/680Z5E */
375	 .matches = {
376		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
377		DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
378		},
379	},
380	{
381	 .callback = video_detect_force_video,
382	 /* SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V */
383	 .matches = {
384		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
385		DMI_MATCH(DMI_PRODUCT_NAME,
386			  "370R4E/370R4V/370R5E/3570RE/370R5V"),
387		},
388	},
389	{
390	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */
391	 .callback = video_detect_force_video,
392	 /* SAMSUNG 3570R/370R/470R/450R/510R/4450RV */
393	 .matches = {
394		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
395		DMI_MATCH(DMI_PRODUCT_NAME,
396			  "3570R/370R/470R/450R/510R/4450RV"),
397		},
398	},
399	{
400	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */
401	 .callback = video_detect_force_video,
402	 /* SAMSUNG 670Z5E */
403	 .matches = {
404		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
405		DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"),
406		},
407	},
408	{
409	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
410	 .callback = video_detect_force_video,
411	 /* SAMSUNG 730U3E/740U3E */
412	 .matches = {
413		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
414		DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
415		},
416	},
417	{
418	 /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */
419	 .callback = video_detect_force_video,
420	 /* SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D */
421	 .matches = {
422		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
423		DMI_MATCH(DMI_PRODUCT_NAME,
424			  "900X3C/900X3D/900X3E/900X4C/900X4D"),
425		},
426	},
427	{
428	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1272633 */
429	 .callback = video_detect_force_video,
430	 /* Dell XPS14 L421X */
431	 .matches = {
432		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
433		DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
434		},
435	},
436	{
437	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */
438	 .callback = video_detect_force_video,
439	 /* Dell XPS15 L521X */
440	 .matches = {
441		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
442		DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
443		},
444	},
445	{
446	 /* https://bugzilla.kernel.org/show_bug.cgi?id=108971 */
447	 .callback = video_detect_force_video,
448	 /* SAMSUNG 530U4E/540U4E */
449	 .matches = {
450		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
451		DMI_MATCH(DMI_PRODUCT_NAME, "530U4E/540U4E"),
452		},
453	},
454	{
455	 /* https://bugs.launchpad.net/bugs/1894667 */
456	 .callback = video_detect_force_video,
457	 /* HP 635 Notebook */
458	 .matches = {
459		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
460		DMI_MATCH(DMI_PRODUCT_NAME, "HP 635 Notebook PC"),
461		},
462	},
463
464	/* Non win8 machines which need native backlight nevertheless */
465	{
466	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1201530 */
467	 .callback = video_detect_force_native,
468	 /* Lenovo Ideapad S405 */
469	 .matches = {
470		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
471		DMI_MATCH(DMI_BOARD_NAME, "Lenovo IdeaPad S405"),
472		},
473	},
474	{
475	 /* https://bugzilla.suse.com/show_bug.cgi?id=1208724 */
476	 .callback = video_detect_force_native,
477	 /* Lenovo Ideapad Z470 */
478	 .matches = {
479		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
480		DMI_MATCH(DMI_PRODUCT_VERSION, "IdeaPad Z470"),
481		},
482	},
483	{
484	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
485	 .callback = video_detect_force_native,
486	 /* Lenovo Ideapad Z570 */
487	 .matches = {
488		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
489		DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
490		},
491	},
492	{
493	 .callback = video_detect_force_native,
494	 /* Lenovo E41-25 */
495	 .matches = {
496		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
497		DMI_MATCH(DMI_PRODUCT_NAME, "81FS"),
498		},
499	},
500	{
501	 .callback = video_detect_force_native,
502	 /* Lenovo E41-45 */
503	 .matches = {
504		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
505		DMI_MATCH(DMI_PRODUCT_NAME, "82BK"),
506		},
507	},
508	{
509	 .callback = video_detect_force_native,
510	 /* Lenovo ThinkPad X131e (3371 AMD version) */
511	 .matches = {
512		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
513		DMI_MATCH(DMI_PRODUCT_NAME, "3371"),
514		},
515	},
516	{
517	 .callback = video_detect_force_native,
518	 /* Apple iMac11,3 */
519	 .matches = {
520		DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
521		DMI_MATCH(DMI_PRODUCT_NAME, "iMac11,3"),
522		},
523	},
524	{
525	 /* https://gitlab.freedesktop.org/drm/amd/-/issues/1838 */
526	 .callback = video_detect_force_native,
527	 /* Apple iMac12,1 */
528	 .matches = {
529		DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
530		DMI_MATCH(DMI_PRODUCT_NAME, "iMac12,1"),
531		},
532	},
533	{
534	 /* https://gitlab.freedesktop.org/drm/amd/-/issues/2753 */
535	 .callback = video_detect_force_native,
536	 /* Apple iMac12,2 */
537	 .matches = {
538		DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
539		DMI_MATCH(DMI_PRODUCT_NAME, "iMac12,2"),
540		},
541	},
542	{
543	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
544	 .callback = video_detect_force_native,
545	 /* Apple MacBook Pro 12,1 */
546	 .matches = {
547		DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
548		DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
549		},
550	},
551	{
552	 .callback = video_detect_force_native,
553	 /* Dell Inspiron N4010 */
554	 .matches = {
555		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
556		DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N4010"),
557		},
558	},
559	{
560	 .callback = video_detect_force_native,
561	 /* Dell Vostro V131 */
562	 .matches = {
563		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
564		DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
565		},
566	},
567	{
568	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */
569	 .callback = video_detect_force_native,
570	 /* Dell XPS 17 L702X */
571	 .matches = {
572		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
573		DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"),
574		},
575	},
576	{
577	 .callback = video_detect_force_native,
578	 /* Dell Precision 7510 */
579	 .matches = {
580		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
581		DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7510"),
582		},
583	},
584	{
585	 .callback = video_detect_force_native,
586	 /* Dell Studio 1569 */
587	 .matches = {
588		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
589		DMI_MATCH(DMI_PRODUCT_NAME, "Studio 1569"),
590		},
591	},
592	{
593	 .callback = video_detect_force_native,
594	 /* Acer Aspire 3830TG */
595	 .matches = {
596		DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
597		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3830TG"),
598		},
599	},
600	{
601	 .callback = video_detect_force_native,
602	 /* Acer Aspire 4810T */
603	 .matches = {
604		DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
605		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 4810T"),
606		},
607	},
608	{
609	 .callback = video_detect_force_native,
610	 /* Acer Aspire 5738z */
611	 .matches = {
612		DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
613		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"),
614		DMI_MATCH(DMI_BOARD_NAME, "JV50"),
615		},
616	},
617	{
618	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1012674 */
619	 .callback = video_detect_force_native,
620	 /* Acer Aspire 5741 */
621	 .matches = {
622		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
623		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5741"),
624		},
625	},
626	{
627	 /* https://bugzilla.kernel.org/show_bug.cgi?id=42993 */
628	 .callback = video_detect_force_native,
629	 /* Acer Aspire 5750 */
630	 .matches = {
631		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
632		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"),
633		},
634	},
635	{
636	 /* https://bugzilla.kernel.org/show_bug.cgi?id=42833 */
637	 .callback = video_detect_force_native,
638	 /* Acer Extensa 5235 */
639	 .matches = {
640		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
641		DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5235"),
642		},
643	},
644	{
645	 .callback = video_detect_force_native,
646	 /* Acer TravelMate 4750 */
647	 .matches = {
648		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
649		DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"),
650		},
651	},
652	{
653	 /* https://bugzilla.kernel.org/show_bug.cgi?id=207835 */
654	 .callback = video_detect_force_native,
655	 /* Acer TravelMate 5735Z */
656	 .matches = {
657		DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
658		DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5735Z"),
659		DMI_MATCH(DMI_BOARD_NAME, "BA51_MV"),
660		},
661	},
662	{
663	 /* https://bugzilla.kernel.org/show_bug.cgi?id=36322 */
664	 .callback = video_detect_force_native,
665	 /* Acer TravelMate 5760 */
666	 .matches = {
667		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
668		DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5760"),
669		},
670	},
671	{
672	 .callback = video_detect_force_native,
673	 /* ASUSTeK COMPUTER INC. GA401 */
674	 .matches = {
675		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
676		DMI_MATCH(DMI_PRODUCT_NAME, "GA401"),
677		},
678	},
679	{
680	 .callback = video_detect_force_native,
681	 /* ASUSTeK COMPUTER INC. GA502 */
682	 .matches = {
683		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
684		DMI_MATCH(DMI_PRODUCT_NAME, "GA502"),
685		},
686	},
687	{
688	 .callback = video_detect_force_native,
689	 /* ASUSTeK COMPUTER INC. GA503 */
690	 .matches = {
691		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
692		DMI_MATCH(DMI_PRODUCT_NAME, "GA503"),
693		},
694	},
695	{
696	 .callback = video_detect_force_native,
697	 /* Asus U46E */
698	 .matches = {
699		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
700		DMI_MATCH(DMI_PRODUCT_NAME, "U46E"),
701		},
702	},
703	{
704	 .callback = video_detect_force_native,
705	 /* Asus UX303UB */
706	 .matches = {
707		DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
708		DMI_MATCH(DMI_PRODUCT_NAME, "UX303UB"),
709		},
710	},
711	{
712	 .callback = video_detect_force_native,
713	 /* HP EliteBook 8460p */
714	 .matches = {
715		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
716		DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8460p"),
717		},
718	},
719	{
720	 .callback = video_detect_force_native,
721	 /* HP Pavilion g6-1d80nr / B4U19UA */
722	 .matches = {
723		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
724		DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion g6 Notebook PC"),
725		DMI_MATCH(DMI_PRODUCT_SKU, "B4U19UA"),
726		},
727	},
728	{
729	 .callback = video_detect_force_native,
730	 /* Samsung N150P */
731	 .matches = {
732		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
733		DMI_MATCH(DMI_PRODUCT_NAME, "N150P"),
734		DMI_MATCH(DMI_BOARD_NAME, "N150P"),
735		},
736	},
737	{
738	 .callback = video_detect_force_native,
739	 /* Samsung N145P/N250P/N260P */
740	 .matches = {
741		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
742		DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
743		DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
744		},
745	},
746	{
747	 .callback = video_detect_force_native,
748	 /* Samsung N250P */
749	 .matches = {
750		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
751		DMI_MATCH(DMI_PRODUCT_NAME, "N250P"),
752		DMI_MATCH(DMI_BOARD_NAME, "N250P"),
753		},
754	},
755	{
756	 /* https://bugzilla.kernel.org/show_bug.cgi?id=202401 */
757	 .callback = video_detect_force_native,
758	 /* Sony Vaio VPCEH3U1E */
759	 .matches = {
760		DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
761		DMI_MATCH(DMI_PRODUCT_NAME, "VPCEH3U1E"),
762		},
763	},
764	{
765	 .callback = video_detect_force_native,
766	 /* Sony Vaio VPCY11S1E */
767	 .matches = {
768		DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
769		DMI_MATCH(DMI_PRODUCT_NAME, "VPCY11S1E"),
770		},
771	},
772
773	/*
774	 * These Toshibas have a broken acpi-video interface for brightness
775	 * control. They also have an issue where the panel is off after
776	 * suspend until a special firmware call is made to turn it back
777	 * on. This is handled by the toshiba_acpi kernel module, so that
778	 * module must be enabled for these models to work correctly.
779	 */
780	{
781	 /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */
782	 .callback = video_detect_force_native,
783	 /* Toshiba Portégé R700 */
784	 .matches = {
785		DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
786		DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"),
787		},
788	},
789	{
790	 /* Portégé: https://bugs.freedesktop.org/show_bug.cgi?id=82634 */
791	 /* Satellite: https://bugzilla.kernel.org/show_bug.cgi?id=21012 */
792	 .callback = video_detect_force_native,
793	 /* Toshiba Satellite/Portégé R830 */
794	 .matches = {
795		DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
796		DMI_MATCH(DMI_PRODUCT_NAME, "R830"),
797		},
798	},
799	{
800	 .callback = video_detect_force_native,
801	 /* Toshiba Satellite/Portégé Z830 */
802	 .matches = {
803		DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
804		DMI_MATCH(DMI_PRODUCT_NAME, "Z830"),
805		},
806	},
807
808	/*
809	 * Models which have nvidia-ec-wmi support, but should not use it.
810	 * Note this indicates a likely firmware bug on these models and should
811	 * be revisited if/when Linux gets support for dynamic mux mode.
812	 */
813	{
814	 .callback = video_detect_force_native,
815	 /* Dell G15 5515 */
816	 .matches = {
817		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
818		DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
819		},
820	},
821	{
822	 .callback = video_detect_force_native,
823	 .matches = {
824		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
825		DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 15 3535"),
826		},
827	},
828	{ },
829};
830
831static bool google_cros_ec_present(void)
832{
833	return acpi_dev_found("GOOG0004") || acpi_dev_found("GOOG000C");
834}
835
836/*
837 * Windows 8 and newer no longer use the ACPI video interface, so it often
838 * does not work. So on win8+ systems prefer native brightness control.
839 * Chromebooks should always prefer native backlight control.
840 */
841static bool prefer_native_over_acpi_video(void)
842{
843	return acpi_osi_is_win8() || google_cros_ec_present();
844}
845
846/*
847 * Determine which type of backlight interface to use on this system,
848 * First check cmdline, then dmi quirks, then do autodetect.
849 */
850enum acpi_backlight_type __acpi_video_get_backlight_type(bool native, bool *auto_detect)
851{
852	static DEFINE_MUTEX(init_mutex);
853	static bool nvidia_wmi_ec_present;
854	static bool apple_gmux_present;
855	static bool native_available;
856	static bool init_done;
857	static long video_caps;
858
859	/* Parse cmdline, dmi and acpi only once */
860	mutex_lock(&init_mutex);
861	if (!init_done) {
862		acpi_video_parse_cmdline();
863		dmi_check_system(video_detect_dmi_table);
864		acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
865				    ACPI_UINT32_MAX, find_video, NULL,
866				    &video_caps, NULL);
867		nvidia_wmi_ec_present = nvidia_wmi_ec_supported();
868		apple_gmux_present = apple_gmux_detect(NULL, NULL);
869		init_done = true;
870	}
871	if (native)
872		native_available = true;
873	mutex_unlock(&init_mutex);
874
875	if (auto_detect)
876		*auto_detect = false;
877
878	/*
879	 * The below heuristics / detection steps are in order of descending
880	 * presedence. The commandline takes presedence over anything else.
881	 */
882	if (acpi_backlight_cmdline != acpi_backlight_undef)
883		return acpi_backlight_cmdline;
884
885	/* DMI quirks override any autodetection. */
886	if (acpi_backlight_dmi != acpi_backlight_undef)
887		return acpi_backlight_dmi;
888
889	if (auto_detect)
890		*auto_detect = true;
891
892	/* Special cases such as nvidia_wmi_ec and apple gmux. */
893	if (nvidia_wmi_ec_present)
894		return acpi_backlight_nvidia_wmi_ec;
895
896	if (apple_gmux_present)
897		return acpi_backlight_apple_gmux;
898
899	/* Use ACPI video if available, except when native should be preferred. */
900	if ((video_caps & ACPI_VIDEO_BACKLIGHT) &&
901	     !(native_available && prefer_native_over_acpi_video()))
902		return acpi_backlight_video;
903
904	/* Use native if available */
905	if (native_available)
906		return acpi_backlight_native;
907
908	/*
909	 * The vendor specific BIOS interfaces are only necessary for
910	 * laptops from before ~2008.
911	 *
912	 * For laptops from ~2008 till ~2023 this point is never reached
913	 * because on those (video_caps & ACPI_VIDEO_BACKLIGHT) above is true.
914	 *
915	 * Laptops from after ~2023 no longer support ACPI_VIDEO_BACKLIGHT,
916	 * if this point is reached on those, this likely means that
917	 * the GPU kms driver which sets native_available has not loaded yet.
918	 *
919	 * Returning acpi_backlight_vendor in this case is known to sometimes
920	 * cause a non working vendor specific /sys/class/backlight device to
921	 * get registered.
922	 *
923	 * Return acpi_backlight_none on laptops with ACPI tables written
924	 * for Windows 8 (laptops from after ~2012) to avoid this problem.
925	 */
926	if (acpi_osi_is_win8())
927		return acpi_backlight_none;
928
929	/* No ACPI video/native (old hw), use vendor specific fw methods. */
930	return acpi_backlight_vendor;
931}
932EXPORT_SYMBOL(__acpi_video_get_backlight_type);
933