xref: /onnv-gate/usr/src/uts/i86pc/io/acpi_drv/acpi_video.c (revision 12122:7f066dcce3ad)
18693SKerry.Shu@Sun.COM /*
28693SKerry.Shu@Sun.COM  * CDDL HEADER START
38693SKerry.Shu@Sun.COM  *
48693SKerry.Shu@Sun.COM  * The contents of this file are subject to the terms of the
58693SKerry.Shu@Sun.COM  * Common Development and Distribution License (the "License").
68693SKerry.Shu@Sun.COM  * You may not use this file except in compliance with the License.
78693SKerry.Shu@Sun.COM  *
88693SKerry.Shu@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98693SKerry.Shu@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108693SKerry.Shu@Sun.COM  * See the License for the specific language governing permissions
118693SKerry.Shu@Sun.COM  * and limitations under the License.
128693SKerry.Shu@Sun.COM  *
138693SKerry.Shu@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148693SKerry.Shu@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158693SKerry.Shu@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168693SKerry.Shu@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178693SKerry.Shu@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188693SKerry.Shu@Sun.COM  *
198693SKerry.Shu@Sun.COM  * CDDL HEADER END
208693SKerry.Shu@Sun.COM  */
218693SKerry.Shu@Sun.COM /*
22*12122SKerry.Shu@Sun.COM  *  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
238693SKerry.Shu@Sun.COM  */
248693SKerry.Shu@Sun.COM 
258693SKerry.Shu@Sun.COM /*
268693SKerry.Shu@Sun.COM  * Solaris x86 Generic ACPI Video Extensions Hotkey driver
278693SKerry.Shu@Sun.COM  */
288693SKerry.Shu@Sun.COM #include <sys/hotkey_drv.h>
29*12122SKerry.Shu@Sun.COM #include <sys/smbios.h>
308693SKerry.Shu@Sun.COM 
318693SKerry.Shu@Sun.COM /*
328693SKerry.Shu@Sun.COM  * Vendor specific hotkey support list
338693SKerry.Shu@Sun.COM  * 	1. Toshiba: acpi_toshiba
348693SKerry.Shu@Sun.COM  */
358693SKerry.Shu@Sun.COM struct vendor_hotkey_drv vendor_hotkey_drv_list[] = {
368693SKerry.Shu@Sun.COM /* vendor,	module name,		enable? */
378693SKerry.Shu@Sun.COM {"Toshiba",	"acpi_toshiba",		B_TRUE},
388693SKerry.Shu@Sun.COM /* Terminator */
398693SKerry.Shu@Sun.COM {NULL,		NULL,			B_FALSE}
408693SKerry.Shu@Sun.COM };
418693SKerry.Shu@Sun.COM 
428693SKerry.Shu@Sun.COM enum vga_output_type {
438693SKerry.Shu@Sun.COM 	OUTPUT_OTHER,
448693SKerry.Shu@Sun.COM 	OUTPUT_CRT,
458693SKerry.Shu@Sun.COM 	OUTPUT_TV,
468693SKerry.Shu@Sun.COM 	OUTPUT_DVI,
478693SKerry.Shu@Sun.COM 	OUTPUT_LCD
488693SKerry.Shu@Sun.COM };
498693SKerry.Shu@Sun.COM 
508693SKerry.Shu@Sun.COM struct acpi_video_output {
518693SKerry.Shu@Sun.COM 	struct acpi_drv_dev dev;
528693SKerry.Shu@Sun.COM 	uint32_t			adr;
538693SKerry.Shu@Sun.COM 	enum vga_output_type		type;
548693SKerry.Shu@Sun.COM 	struct acpi_video_output	*next;
558693SKerry.Shu@Sun.COM };
568693SKerry.Shu@Sun.COM 
578693SKerry.Shu@Sun.COM struct acpi_video_brightness {
588693SKerry.Shu@Sun.COM 	struct acpi_drv_dev dev;
598693SKerry.Shu@Sun.COM 	uint32_t			adr;
608693SKerry.Shu@Sun.COM 	uint32_t			nlevel;
618693SKerry.Shu@Sun.COM 	int				*levels;
628693SKerry.Shu@Sun.COM 	int				cur_level;
638693SKerry.Shu@Sun.COM 	uint32_t			cur_level_index;
648693SKerry.Shu@Sun.COM 	uint32_t			output_index;
658693SKerry.Shu@Sun.COM 	struct acpi_video_brightness	*next;
668693SKerry.Shu@Sun.COM };
678693SKerry.Shu@Sun.COM 
688693SKerry.Shu@Sun.COM struct acpi_video_switch {
698693SKerry.Shu@Sun.COM 	struct acpi_drv_dev		dev;
708693SKerry.Shu@Sun.COM 	struct acpi_video_switch	*next;
718693SKerry.Shu@Sun.COM };
728693SKerry.Shu@Sun.COM 
738693SKerry.Shu@Sun.COM /* ACPI video extension hotkey for video switch and brightness control */
748693SKerry.Shu@Sun.COM static struct acpi_video {
758693SKerry.Shu@Sun.COM 	struct acpi_video_output	*vid_outputs;
768693SKerry.Shu@Sun.COM 	uint32_t			total_outputs;
778693SKerry.Shu@Sun.COM 	struct acpi_video_brightness	*vid_brightness;
788693SKerry.Shu@Sun.COM 	uint32_t			total_brightness;
798693SKerry.Shu@Sun.COM 	struct acpi_video_switch	*vid_switch;
808693SKerry.Shu@Sun.COM 	uint32_t			total_switch;
818693SKerry.Shu@Sun.COM } acpi_video_hotkey;
828693SKerry.Shu@Sun.COM 
838693SKerry.Shu@Sun.COM int hotkey_drv_debug = 0;
848693SKerry.Shu@Sun.COM 
85*12122SKerry.Shu@Sun.COM static struct acpi_video_smbios_info {
86*12122SKerry.Shu@Sun.COM 	char *manufacturer;
87*12122SKerry.Shu@Sun.COM 	char *product;
88*12122SKerry.Shu@Sun.COM } acpi_brightness_get_blacklist[] = {
89*12122SKerry.Shu@Sun.COM 	{ /* Dell AdamoXPS laptop */
90*12122SKerry.Shu@Sun.COM 		"Dell Inc.",
91*12122SKerry.Shu@Sun.COM 		"Adamo XPS"
92*12122SKerry.Shu@Sun.COM 	},
93*12122SKerry.Shu@Sun.COM 	{ /* termination entry */
94*12122SKerry.Shu@Sun.COM 		NULL,
95*12122SKerry.Shu@Sun.COM 		NULL
96*12122SKerry.Shu@Sun.COM 	}
97*12122SKerry.Shu@Sun.COM };
98*12122SKerry.Shu@Sun.COM /*
99*12122SKerry.Shu@Sun.COM  * -1 = check acpi_brightness_get_blacklist[].
100*12122SKerry.Shu@Sun.COM  * 0 = enable brightness get.
101*12122SKerry.Shu@Sun.COM  * 1 = disable brightness get.
102*12122SKerry.Shu@Sun.COM  */
103*12122SKerry.Shu@Sun.COM int acpi_brightness_get_disable = -1;
104*12122SKerry.Shu@Sun.COM 
105*12122SKerry.Shu@Sun.COM 
1068693SKerry.Shu@Sun.COM #define	ACPI_METHOD_DOS			"_DOS"
1078693SKerry.Shu@Sun.COM #define	ACPI_METHOD_DOD			"_DOD"
1088693SKerry.Shu@Sun.COM 
1098693SKerry.Shu@Sun.COM #define	ACPI_DEVNAME_CRT		"CRT"
1108693SKerry.Shu@Sun.COM #define	ACPI_DEVNAME_LCD		"LCD"
1118693SKerry.Shu@Sun.COM #define	ACPI_DEVNAME_TV			"TV"
1128693SKerry.Shu@Sun.COM #define	ACPI_METHOD_ADR			"_ADR"
1138693SKerry.Shu@Sun.COM #define	ACPI_METHOD_DDC			"_DDC"
1148693SKerry.Shu@Sun.COM #define	ACPI_METHOD_DCS			"_DCS"
1158693SKerry.Shu@Sun.COM #define	ACPI_METHOD_DGS			"_DGS"
1168693SKerry.Shu@Sun.COM #define	ACPI_METHOD_DSS			"_DSS"
1178693SKerry.Shu@Sun.COM 
1188693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_SWITCH		0x80
1198693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_SWITCH_STATUS	0x81
1208693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_SWITCH_CYCLE	0x82
1218693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_SWITCH_NEXT	0x83
1228693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_SWITCH_PREV	0x84
1238693SKerry.Shu@Sun.COM 
1248693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_BRIGHTNESS_CYCLE	0x85
1258693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_BRIGHTNESS_INC	0x86
1268693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_BRIGHTNESS_DEC	0x87
1278693SKerry.Shu@Sun.COM #define	VIDEO_NOTIFY_BRIGHTNESS_ZERO	0x88
1288693SKerry.Shu@Sun.COM 
1298693SKerry.Shu@Sun.COM /* Output device status */
1308693SKerry.Shu@Sun.COM #define	ACPI_DRV_DCS_CONNECTOR_EXIST	(1 << 0)
1318693SKerry.Shu@Sun.COM #define	ACPI_DRV_DCS_ACTIVE		(1 << 1)
1328693SKerry.Shu@Sun.COM #define	ACPI_DRV_DCS_READY		(1 << 2)
1338693SKerry.Shu@Sun.COM #define	ACPI_DRV_DCS_FUNCTIONAL		(1 << 3)
1348693SKerry.Shu@Sun.COM #define	ACPI_DRV_DCS_ATTACHED		(1 << 4)
1358693SKerry.Shu@Sun.COM 
1368693SKerry.Shu@Sun.COM /* _DOS default value is 1 */
1378693SKerry.Shu@Sun.COM /* _DOS bit 1:0 */
1388693SKerry.Shu@Sun.COM #define	VIDEO_POLICY_SWITCH_OS		0x0
1398693SKerry.Shu@Sun.COM #define	VIDEO_POLICY_SWITCH_BIOS	0x1
1408693SKerry.Shu@Sun.COM #define	VIDEO_POLICY_SWITCH_LOCKED	0x2
1418693SKerry.Shu@Sun.COM #define	VIDEO_POLICY_SWITCH_OS_EVENT	0x3
1428693SKerry.Shu@Sun.COM 
1438693SKerry.Shu@Sun.COM /* _DOS bit 2 */
1448693SKerry.Shu@Sun.COM #define	VIDEO_POLICY_BRIGHTNESS_OS	0x4
1458693SKerry.Shu@Sun.COM #define	VIDEO_POLICY_BRIGHTNESS_BIOS	0x0
1468693SKerry.Shu@Sun.COM 
1478693SKerry.Shu@Sun.COM /* Set _DOS for video control policy */
1488693SKerry.Shu@Sun.COM static void
acpi_video_set_dos(struct acpi_video * vidp,uint32_t policy)1498693SKerry.Shu@Sun.COM acpi_video_set_dos(struct acpi_video *vidp, uint32_t policy)
1508693SKerry.Shu@Sun.COM {
1518693SKerry.Shu@Sun.COM 	struct acpi_video_switch *vidsp;
1528693SKerry.Shu@Sun.COM 	ACPI_STATUS status;
1538693SKerry.Shu@Sun.COM 	ACPI_OBJECT obj;
1548693SKerry.Shu@Sun.COM 	ACPI_OBJECT_LIST objlist;
1558693SKerry.Shu@Sun.COM 
1568693SKerry.Shu@Sun.COM 	obj.Type = ACPI_TYPE_INTEGER;
1578693SKerry.Shu@Sun.COM 	obj.Integer.Value = policy;
1588693SKerry.Shu@Sun.COM 	objlist.Count = 1;
1598693SKerry.Shu@Sun.COM 	objlist.Pointer = &obj;
1608693SKerry.Shu@Sun.COM 
1618693SKerry.Shu@Sun.COM 	vidsp = vidp->vid_switch;
1628693SKerry.Shu@Sun.COM 	while (vidsp != NULL) {
1638693SKerry.Shu@Sun.COM 		status = AcpiEvaluateObject(vidsp->dev.hdl, ACPI_METHOD_DOS,
1648693SKerry.Shu@Sun.COM 		    &objlist, NULL);
1658693SKerry.Shu@Sun.COM 		if (ACPI_FAILURE(status))
1668693SKerry.Shu@Sun.COM 			cmn_err(CE_WARN, "!acpi_video_set_dos failed.");
1678693SKerry.Shu@Sun.COM 		vidsp = vidsp->next;
1688693SKerry.Shu@Sun.COM 	}
1698693SKerry.Shu@Sun.COM }
1708693SKerry.Shu@Sun.COM 
1718693SKerry.Shu@Sun.COM /*
1728693SKerry.Shu@Sun.COM  * Get the current brightness level and index.
1738693SKerry.Shu@Sun.COM  */
1748693SKerry.Shu@Sun.COM static int
acpi_video_brightness_get(struct acpi_video_brightness * vidbp)1758693SKerry.Shu@Sun.COM acpi_video_brightness_get(struct acpi_video_brightness *vidbp)
1768693SKerry.Shu@Sun.COM {
1778693SKerry.Shu@Sun.COM 	int i;
1788693SKerry.Shu@Sun.COM 
179*12122SKerry.Shu@Sun.COM 	if (acpi_brightness_get_disable) {
180*12122SKerry.Shu@Sun.COM 		/* simply initialize current brightness to the highest level */
181*12122SKerry.Shu@Sun.COM 		vidbp->cur_level_index = vidbp->nlevel - 1;
182*12122SKerry.Shu@Sun.COM 		vidbp->cur_level = vidbp->levels[vidbp->cur_level_index];
183*12122SKerry.Shu@Sun.COM 		return (ACPI_DRV_OK);
184*12122SKerry.Shu@Sun.COM 	}
185*12122SKerry.Shu@Sun.COM 
1868693SKerry.Shu@Sun.COM 	if (acpica_eval_int(vidbp->dev.hdl, "_BQC", &vidbp->cur_level)
1878693SKerry.Shu@Sun.COM 	    != AE_OK) {
1888693SKerry.Shu@Sun.COM 		vidbp->cur_level = 0;
1898693SKerry.Shu@Sun.COM 		return (ACPI_DRV_ERR);
1908693SKerry.Shu@Sun.COM 	}
1918693SKerry.Shu@Sun.COM 
1928693SKerry.Shu@Sun.COM 	for (i = 0; i < vidbp->nlevel; i++) {
1938693SKerry.Shu@Sun.COM 		if (vidbp->levels[i] == vidbp->cur_level) {
1948693SKerry.Shu@Sun.COM 			vidbp->cur_level_index = i;
1958693SKerry.Shu@Sun.COM 			if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
1968693SKerry.Shu@Sun.COM 				cmn_err(CE_NOTE, "!acpi_video_brightness_get():"
1978693SKerry.Shu@Sun.COM 				    " cur_level = %d, cur_level_index = %d\n",
1988693SKerry.Shu@Sun.COM 				    vidbp->cur_level, i);
1998693SKerry.Shu@Sun.COM 			}
2008693SKerry.Shu@Sun.COM 			break;
2018693SKerry.Shu@Sun.COM 		}
2028693SKerry.Shu@Sun.COM 	}
2038693SKerry.Shu@Sun.COM 
2048693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
2058693SKerry.Shu@Sun.COM }
2068693SKerry.Shu@Sun.COM 
2078693SKerry.Shu@Sun.COM static int
acpi_video_brightness_set(struct acpi_video_brightness * vidbp,uint32_t level)2088693SKerry.Shu@Sun.COM acpi_video_brightness_set(struct acpi_video_brightness *vidbp, uint32_t level)
2098693SKerry.Shu@Sun.COM {
2108693SKerry.Shu@Sun.COM 	if (acpi_drv_set_int(vidbp->dev.hdl, "_BCM", vidbp->levels[level])
2118693SKerry.Shu@Sun.COM 	    != AE_OK) {
2128693SKerry.Shu@Sun.COM 		return (ACPI_DRV_ERR);
2138693SKerry.Shu@Sun.COM 	}
2148693SKerry.Shu@Sun.COM 
2158693SKerry.Shu@Sun.COM 	vidbp->cur_level = vidbp->levels[level];
2168693SKerry.Shu@Sun.COM 	vidbp->cur_level_index = level;
2178693SKerry.Shu@Sun.COM 
2188693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
2198693SKerry.Shu@Sun.COM }
2208693SKerry.Shu@Sun.COM 
2218693SKerry.Shu@Sun.COM void
hotkey_drv_gen_sysevent(dev_info_t * dip,char * event)2228693SKerry.Shu@Sun.COM hotkey_drv_gen_sysevent(dev_info_t *dip, char *event)
2238693SKerry.Shu@Sun.COM {
2248693SKerry.Shu@Sun.COM 	int err;
2258693SKerry.Shu@Sun.COM 
2268693SKerry.Shu@Sun.COM 	/* Generate/log EC_ACPIEV sysevent */
2278693SKerry.Shu@Sun.COM 	err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_ACPIEV,
2288693SKerry.Shu@Sun.COM 	    event, NULL, NULL, DDI_NOSLEEP);
2298693SKerry.Shu@Sun.COM 
2308693SKerry.Shu@Sun.COM 	if (err != DDI_SUCCESS) {
2318693SKerry.Shu@Sun.COM 		cmn_err(CE_WARN,
2328693SKerry.Shu@Sun.COM 		    "!failed to log hotkey sysevent, err code %x\n", err);
2338693SKerry.Shu@Sun.COM 	}
2348693SKerry.Shu@Sun.COM }
2358693SKerry.Shu@Sun.COM 
2368693SKerry.Shu@Sun.COM /*ARGSUSED*/
2378693SKerry.Shu@Sun.COM static void
acpi_video_switch_notify(ACPI_HANDLE hdl,uint32_t notify,void * ctx)2388693SKerry.Shu@Sun.COM acpi_video_switch_notify(ACPI_HANDLE hdl, uint32_t notify, void *ctx)
2398693SKerry.Shu@Sun.COM {
2408693SKerry.Shu@Sun.COM 	if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
2418693SKerry.Shu@Sun.COM 		cmn_err(CE_NOTE, "!acpi_video_switch_notify: got event 0x%x.\n",
2428693SKerry.Shu@Sun.COM 		    notify);
2438693SKerry.Shu@Sun.COM 	}
2448693SKerry.Shu@Sun.COM 
2458693SKerry.Shu@Sun.COM 	mutex_enter(acpi_hotkey.hotkey_lock);
2468693SKerry.Shu@Sun.COM 	switch (notify) {
2478693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_SWITCH:
2488693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_SWITCH_CYCLE:
2498693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_SWITCH_NEXT:
2508693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_SWITCH_PREV:
2518693SKerry.Shu@Sun.COM 		hotkey_drv_gen_sysevent(acpi_hotkey.dip,
2528693SKerry.Shu@Sun.COM 		    ESC_ACPIEV_DISPLAY_SWITCH);
2538693SKerry.Shu@Sun.COM 		break;
2548693SKerry.Shu@Sun.COM 
2558693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_SWITCH_STATUS:
2568693SKerry.Shu@Sun.COM 		break;
2578693SKerry.Shu@Sun.COM 
2588693SKerry.Shu@Sun.COM 	default:
2598693SKerry.Shu@Sun.COM 		if (hotkey_drv_debug) {
2608693SKerry.Shu@Sun.COM 			cmn_err(CE_NOTE,
2618693SKerry.Shu@Sun.COM 			    "!acpi_video_switch_notify: unknown event 0x%x.\n",
2628693SKerry.Shu@Sun.COM 			    notify);
2638693SKerry.Shu@Sun.COM 		}
2648693SKerry.Shu@Sun.COM 	}
2658693SKerry.Shu@Sun.COM 	mutex_exit(acpi_hotkey.hotkey_lock);
2668693SKerry.Shu@Sun.COM }
2678693SKerry.Shu@Sun.COM 
2688693SKerry.Shu@Sun.COM /*ARGSUSED*/
2698693SKerry.Shu@Sun.COM static void
acpi_video_brightness_notify(ACPI_HANDLE hdl,uint32_t notify,void * ctx)2708693SKerry.Shu@Sun.COM acpi_video_brightness_notify(ACPI_HANDLE hdl, uint32_t notify, void *ctx)
2718693SKerry.Shu@Sun.COM {
2728693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp = ctx;
2738693SKerry.Shu@Sun.COM 
2748693SKerry.Shu@Sun.COM 	if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
2758693SKerry.Shu@Sun.COM 		cmn_err(CE_NOTE,
2768693SKerry.Shu@Sun.COM 		    "!acpi_video_brightness_notify: got event 0x%x.\n",
2778693SKerry.Shu@Sun.COM 		    notify);
2788693SKerry.Shu@Sun.COM 	}
2798693SKerry.Shu@Sun.COM 
2808693SKerry.Shu@Sun.COM 	mutex_enter(acpi_hotkey.hotkey_lock);
2818693SKerry.Shu@Sun.COM 	switch (notify) {
2828693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_BRIGHTNESS_CYCLE:
2838693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_BRIGHTNESS_INC:
2848693SKerry.Shu@Sun.COM 		if (vidbp->cur_level_index < vidbp->nlevel - 1) {
2858693SKerry.Shu@Sun.COM 			if (acpi_video_brightness_set(vidbp,
2868693SKerry.Shu@Sun.COM 			    vidbp->cur_level_index + 1) != ACPI_DRV_OK) {
2878693SKerry.Shu@Sun.COM 				break;
2888693SKerry.Shu@Sun.COM 			}
2898693SKerry.Shu@Sun.COM 		}
2908693SKerry.Shu@Sun.COM 		acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_UP, 0);
2918693SKerry.Shu@Sun.COM 		break;
2928693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_BRIGHTNESS_DEC:
2938693SKerry.Shu@Sun.COM 		if (vidbp->cur_level_index > 0) {
2948693SKerry.Shu@Sun.COM 			if (acpi_video_brightness_set(vidbp,
2958693SKerry.Shu@Sun.COM 			    vidbp->cur_level_index - 1) != ACPI_DRV_OK) {
2968693SKerry.Shu@Sun.COM 				break;
2978693SKerry.Shu@Sun.COM 			}
2988693SKerry.Shu@Sun.COM 		}
2998693SKerry.Shu@Sun.COM 		acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_DOWN,
3008693SKerry.Shu@Sun.COM 		    0);
3018693SKerry.Shu@Sun.COM 		break;
3028693SKerry.Shu@Sun.COM 	case VIDEO_NOTIFY_BRIGHTNESS_ZERO:
3038693SKerry.Shu@Sun.COM 		if (acpi_video_brightness_set(vidbp, 0) != ACPI_DRV_OK) {
3048693SKerry.Shu@Sun.COM 			break;
3058693SKerry.Shu@Sun.COM 		}
3068693SKerry.Shu@Sun.COM 		acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_DOWN,
3078693SKerry.Shu@Sun.COM 		    0);
3088693SKerry.Shu@Sun.COM 		break;
3098693SKerry.Shu@Sun.COM 
3108693SKerry.Shu@Sun.COM 	default:
3118693SKerry.Shu@Sun.COM 		if (hotkey_drv_debug) {
3128693SKerry.Shu@Sun.COM 			cmn_err(CE_NOTE, "!acpi_video_brightness_notify: "
3138693SKerry.Shu@Sun.COM 			    "unknown event 0x%x.\n", notify);
3148693SKerry.Shu@Sun.COM 		}
3158693SKerry.Shu@Sun.COM 	}
3168693SKerry.Shu@Sun.COM 	mutex_exit(acpi_hotkey.hotkey_lock);
3178693SKerry.Shu@Sun.COM }
3188693SKerry.Shu@Sun.COM 
3198693SKerry.Shu@Sun.COM static int
acpi_video_notify_intall(struct acpi_video * vidp)3208693SKerry.Shu@Sun.COM acpi_video_notify_intall(struct acpi_video *vidp)
3218693SKerry.Shu@Sun.COM {
3228693SKerry.Shu@Sun.COM 	ACPI_STATUS status;
3238693SKerry.Shu@Sun.COM 	struct acpi_video_switch *vidsp;
3248693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp;
3258693SKerry.Shu@Sun.COM 	int i;
3268693SKerry.Shu@Sun.COM 
3278693SKerry.Shu@Sun.COM 	/* bind video switch notify */
3288693SKerry.Shu@Sun.COM 	vidsp = vidp->vid_switch;
3298693SKerry.Shu@Sun.COM 	for (i = 0; i < vidp->total_switch && vidsp != NULL; i++) {
3308693SKerry.Shu@Sun.COM 		status = AcpiInstallNotifyHandler(vidsp->dev.hdl,
3318693SKerry.Shu@Sun.COM 		    ACPI_DEVICE_NOTIFY, acpi_video_switch_notify, vidsp);
3328693SKerry.Shu@Sun.COM 		if (ACPI_FAILURE(status)) {
3338693SKerry.Shu@Sun.COM 			cmn_err(CE_WARN,
3348693SKerry.Shu@Sun.COM 			    "!vids handler install failed = %d, vids = %p.",
3358693SKerry.Shu@Sun.COM 			    status, (void *) vidsp);
3368693SKerry.Shu@Sun.COM 		}
3378693SKerry.Shu@Sun.COM 		vidsp = vidsp->next;
3388693SKerry.Shu@Sun.COM 	}
3398693SKerry.Shu@Sun.COM 
3408693SKerry.Shu@Sun.COM 	/* bind brightness control notify */
3418693SKerry.Shu@Sun.COM 	vidbp = vidp->vid_brightness;
3428693SKerry.Shu@Sun.COM 	for (i = 0; i < vidp->total_brightness && vidbp != NULL; i++) {
3438693SKerry.Shu@Sun.COM 		status = AcpiInstallNotifyHandler(vidbp->dev.hdl,
3448693SKerry.Shu@Sun.COM 		    ACPI_DEVICE_NOTIFY, acpi_video_brightness_notify, vidbp);
3458693SKerry.Shu@Sun.COM 		if (ACPI_FAILURE(status)) {
3468693SKerry.Shu@Sun.COM 			cmn_err(CE_WARN,
3478693SKerry.Shu@Sun.COM 			    "!brightness handler install failed = %x, "
3488693SKerry.Shu@Sun.COM 			    "brightness = %p.", status, (void *) vidbp);
3498693SKerry.Shu@Sun.COM 		}
3508693SKerry.Shu@Sun.COM 		vidbp = vidbp->next;
3518693SKerry.Shu@Sun.COM 	}
3528693SKerry.Shu@Sun.COM 
3538693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
3548693SKerry.Shu@Sun.COM }
3558693SKerry.Shu@Sun.COM 
3568693SKerry.Shu@Sun.COM static int
acpi_video_notify_unintall(struct acpi_video * vidp)3578693SKerry.Shu@Sun.COM acpi_video_notify_unintall(struct acpi_video *vidp)
3588693SKerry.Shu@Sun.COM {
3598693SKerry.Shu@Sun.COM 	struct acpi_video_switch *vidsp;
3608693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp;
3618693SKerry.Shu@Sun.COM 	int i;
3628693SKerry.Shu@Sun.COM 
3638693SKerry.Shu@Sun.COM 	/* unbind video switch notify */
3648693SKerry.Shu@Sun.COM 	vidsp = vidp->vid_switch;
3658693SKerry.Shu@Sun.COM 	for (i = 0; i < vidp->total_switch && vidsp != NULL; i++) {
3668693SKerry.Shu@Sun.COM 		(void) AcpiRemoveNotifyHandler(vidsp->dev.hdl,
3678693SKerry.Shu@Sun.COM 		    ACPI_DEVICE_NOTIFY, acpi_video_switch_notify);
3688693SKerry.Shu@Sun.COM 		vidsp = vidsp->next;
3698693SKerry.Shu@Sun.COM 	}
3708693SKerry.Shu@Sun.COM 
3718693SKerry.Shu@Sun.COM 	/* unbind brightness control notify */
3728693SKerry.Shu@Sun.COM 	vidbp = vidp->vid_brightness;
3738693SKerry.Shu@Sun.COM 	for (i = 0; i < vidp->total_brightness && vidbp != NULL; i++) {
3748693SKerry.Shu@Sun.COM 		(void) AcpiRemoveNotifyHandler(vidbp->dev.hdl,
3758693SKerry.Shu@Sun.COM 		    ACPI_DEVICE_NOTIFY, acpi_video_brightness_notify);
3768693SKerry.Shu@Sun.COM 		vidbp = vidbp->next;
3778693SKerry.Shu@Sun.COM 	}
3788693SKerry.Shu@Sun.COM 
3798693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
3808693SKerry.Shu@Sun.COM }
3818693SKerry.Shu@Sun.COM 
3828693SKerry.Shu@Sun.COM static int
acpi_video_free(struct acpi_video * vidp)3838693SKerry.Shu@Sun.COM acpi_video_free(struct acpi_video *vidp)
3848693SKerry.Shu@Sun.COM {
3858693SKerry.Shu@Sun.COM 	struct acpi_video_switch *vidsp;
3868693SKerry.Shu@Sun.COM 	struct acpi_video_switch *vidsp_next;
3878693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp;
3888693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp_next;
3898693SKerry.Shu@Sun.COM 	struct acpi_video_output *vidop;
3908693SKerry.Shu@Sun.COM 	struct acpi_video_output *vidop_next;
3918693SKerry.Shu@Sun.COM 
3928693SKerry.Shu@Sun.COM 	/* free video switch objects */
3938693SKerry.Shu@Sun.COM 	vidsp = vidp->vid_switch;
3948693SKerry.Shu@Sun.COM 	while (vidsp != NULL) {
3958693SKerry.Shu@Sun.COM 		vidsp_next = vidsp->next;
3968693SKerry.Shu@Sun.COM 		kmem_free(vidsp, sizeof (struct acpi_video_switch));
3978693SKerry.Shu@Sun.COM 		vidsp = vidsp_next;
3988693SKerry.Shu@Sun.COM 	}
3998693SKerry.Shu@Sun.COM 
4008693SKerry.Shu@Sun.COM 	/* free video brightness control objects */
4018693SKerry.Shu@Sun.COM 	vidbp = vidp->vid_brightness;
4028693SKerry.Shu@Sun.COM 	while (vidbp != NULL) {
4038693SKerry.Shu@Sun.COM 		vidbp_next = vidbp->next;
4048693SKerry.Shu@Sun.COM 		kmem_free(vidbp, sizeof (struct acpi_video_brightness));
4058693SKerry.Shu@Sun.COM 		vidbp = vidbp_next;
4068693SKerry.Shu@Sun.COM 	}
4078693SKerry.Shu@Sun.COM 
4088693SKerry.Shu@Sun.COM 	/* free video output objects */
4098693SKerry.Shu@Sun.COM 	vidop = vidp->vid_outputs;
4108693SKerry.Shu@Sun.COM 	while (vidop != NULL) {
4118693SKerry.Shu@Sun.COM 		vidop_next = vidop->next;
4128693SKerry.Shu@Sun.COM 		kmem_free(vidop, sizeof (struct acpi_video_output));
4138693SKerry.Shu@Sun.COM 		vidop = vidop_next;
4148693SKerry.Shu@Sun.COM 	}
4158693SKerry.Shu@Sun.COM 
4168693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
4178693SKerry.Shu@Sun.COM }
4188693SKerry.Shu@Sun.COM 
4198693SKerry.Shu@Sun.COM static int
acpi_video_fini(struct acpi_video * vidp)4208693SKerry.Shu@Sun.COM acpi_video_fini(struct acpi_video *vidp)
4218693SKerry.Shu@Sun.COM {
4228693SKerry.Shu@Sun.COM 	(void) acpi_video_notify_unintall(vidp);
4238693SKerry.Shu@Sun.COM 
4248693SKerry.Shu@Sun.COM 	return (acpi_video_free(vidp));
4258693SKerry.Shu@Sun.COM }
4268693SKerry.Shu@Sun.COM 
4278693SKerry.Shu@Sun.COM static int
acpi_video_enum_output(ACPI_HANDLE hdl,struct acpi_video * vidp)4288693SKerry.Shu@Sun.COM acpi_video_enum_output(ACPI_HANDLE hdl, struct acpi_video *vidp)
4298693SKerry.Shu@Sun.COM {
4308693SKerry.Shu@Sun.COM 	int adr;
4318693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp;
4328693SKerry.Shu@Sun.COM 	struct acpi_video_output *vidop;
4338693SKerry.Shu@Sun.COM 	ACPI_BUFFER buf = {ACPI_ALLOCATE_BUFFER, NULL};
4348693SKerry.Shu@Sun.COM 	ACPI_OBJECT *objp;
4358693SKerry.Shu@Sun.COM 
4368693SKerry.Shu@Sun.COM 
4378693SKerry.Shu@Sun.COM 	if (acpica_eval_int(hdl, "_ADR", &adr) != AE_OK)
4388693SKerry.Shu@Sun.COM 		return (ACPI_DRV_ERR);
4398693SKerry.Shu@Sun.COM 
4408693SKerry.Shu@Sun.COM 	/* Allocate object */
4418693SKerry.Shu@Sun.COM 	vidop = kmem_zalloc(sizeof (struct acpi_video_output), KM_SLEEP);
4428693SKerry.Shu@Sun.COM 	vidop->dev.hdl = hdl;
4438693SKerry.Shu@Sun.COM 	(void) acpi_drv_dev_init(&vidop->dev);
4448693SKerry.Shu@Sun.COM 	vidop->adr = adr;
4458693SKerry.Shu@Sun.COM 	vidop->type = adr;
4468693SKerry.Shu@Sun.COM 	vidop->next = vidp->vid_outputs;
4478693SKerry.Shu@Sun.COM 	vidp->vid_outputs = vidop;
4488693SKerry.Shu@Sun.COM 
4498693SKerry.Shu@Sun.COM 	if (ACPI_SUCCESS(AcpiEvaluateObjectTyped(hdl, "_BCL",
4508693SKerry.Shu@Sun.COM 	    NULL, &buf, ACPI_TYPE_PACKAGE))) {
4518693SKerry.Shu@Sun.COM 		int i, j, k, l, m, nlev, tmp;
4528693SKerry.Shu@Sun.COM 
4538693SKerry.Shu@Sun.COM 		vidbp = kmem_zalloc(sizeof (struct acpi_video_brightness),
4548693SKerry.Shu@Sun.COM 		    KM_SLEEP);
4558693SKerry.Shu@Sun.COM 		vidbp->dev = vidop->dev;
4568693SKerry.Shu@Sun.COM 		vidop->adr = adr;
4578693SKerry.Shu@Sun.COM 		vidbp->output_index = vidp->total_outputs;
4588693SKerry.Shu@Sun.COM 		objp = buf.Pointer;
4598693SKerry.Shu@Sun.COM 
4608693SKerry.Shu@Sun.COM 		/*
4618693SKerry.Shu@Sun.COM 		 * op->nlev will be needed to free op->levels.
4628693SKerry.Shu@Sun.COM 		 */
4638693SKerry.Shu@Sun.COM 		vidbp->nlevel = nlev = objp->Package.Count;
4648693SKerry.Shu@Sun.COM 		vidbp->levels = kmem_zalloc(nlev * sizeof (uint32_t), KM_SLEEP);
4658693SKerry.Shu@Sun.COM 
4668693SKerry.Shu@Sun.COM 		/*
4678693SKerry.Shu@Sun.COM 		 * Get all the supported brightness levels.
4688693SKerry.Shu@Sun.COM 		 */
4698693SKerry.Shu@Sun.COM 		for (i = 0; i < nlev; i++) {
4708693SKerry.Shu@Sun.COM 			ACPI_OBJECT *o = &objp->Package.Elements[i];
4718693SKerry.Shu@Sun.COM 			int lev = o->Integer.Value;
4728693SKerry.Shu@Sun.COM 
4738693SKerry.Shu@Sun.COM 			if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
4748693SKerry.Shu@Sun.COM 				cmn_err(CE_NOTE, "!acpi_video_enum_output() "
4758693SKerry.Shu@Sun.COM 				    "brlev=%d i=%d nlev=%d\n", lev, i, nlev);
4768693SKerry.Shu@Sun.COM 			}
4778693SKerry.Shu@Sun.COM 			if (o->Type != ACPI_TYPE_INTEGER) {
4788693SKerry.Shu@Sun.COM 				continue;
4798693SKerry.Shu@Sun.COM 			}
4808693SKerry.Shu@Sun.COM 			vidbp->levels[i] = lev;
4818693SKerry.Shu@Sun.COM 		}
4828693SKerry.Shu@Sun.COM 
4838693SKerry.Shu@Sun.COM 		/*
4848693SKerry.Shu@Sun.COM 		 * Sort the brightness levels.
4858693SKerry.Shu@Sun.COM 		 */
4868693SKerry.Shu@Sun.COM 		for (j = 0; j < nlev; j++) {
4878693SKerry.Shu@Sun.COM 			for (k = 0; k < nlev - 1; k++) {
4888693SKerry.Shu@Sun.COM 				if (vidbp->levels[k] > vidbp->levels[k+1]) {
4898693SKerry.Shu@Sun.COM 					tmp = vidbp->levels[k+1];
4908693SKerry.Shu@Sun.COM 					vidbp->levels[k+1] = vidbp->levels[k];
4918693SKerry.Shu@Sun.COM 					vidbp->levels[k] = tmp;
4928693SKerry.Shu@Sun.COM 				}
4938693SKerry.Shu@Sun.COM 			}
4948693SKerry.Shu@Sun.COM 		}
4958693SKerry.Shu@Sun.COM 
4968693SKerry.Shu@Sun.COM 		/*
4978693SKerry.Shu@Sun.COM 		 * The first two levels could be duplicated, so remove
4988693SKerry.Shu@Sun.COM 		 * any duplicates.
4998693SKerry.Shu@Sun.COM 		 */
5008693SKerry.Shu@Sun.COM 		for (l = 0; l < nlev - 1; l++) {
5018693SKerry.Shu@Sun.COM 			if (vidbp->levels[l] == vidbp->levels[l+1]) {
5028693SKerry.Shu@Sun.COM 				for (m = l + 1; m < nlev - 1; m++) {
5038693SKerry.Shu@Sun.COM 					vidbp->levels[m] = vidbp->levels[m+1];
5048693SKerry.Shu@Sun.COM 				}
5058693SKerry.Shu@Sun.COM 				nlev--;
5068693SKerry.Shu@Sun.COM 			}
5078693SKerry.Shu@Sun.COM 		}
5088693SKerry.Shu@Sun.COM 
5098693SKerry.Shu@Sun.COM 		vidbp->nlevel = nlev;
5108693SKerry.Shu@Sun.COM 		(void) acpi_video_brightness_get(vidbp);
5118693SKerry.Shu@Sun.COM 		vidbp->next = vidp->vid_brightness;
5128693SKerry.Shu@Sun.COM 		vidp->vid_brightness = vidbp;
5138693SKerry.Shu@Sun.COM 		vidp->total_brightness++;
5148693SKerry.Shu@Sun.COM 
5158693SKerry.Shu@Sun.COM 		AcpiOsFree(objp);
5168693SKerry.Shu@Sun.COM 	}
5178693SKerry.Shu@Sun.COM 
5188693SKerry.Shu@Sun.COM 	vidp->total_outputs++;
5198693SKerry.Shu@Sun.COM 
5208693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
5218693SKerry.Shu@Sun.COM }
5228693SKerry.Shu@Sun.COM 
5238693SKerry.Shu@Sun.COM /*ARGSUSED*/
5248693SKerry.Shu@Sun.COM static ACPI_STATUS
acpi_video_find_and_alloc(ACPI_HANDLE hdl,UINT32 nest,void * ctx,void ** rv)5258693SKerry.Shu@Sun.COM acpi_video_find_and_alloc(ACPI_HANDLE hdl, UINT32 nest, void *ctx,
5268693SKerry.Shu@Sun.COM     void **rv)
5278693SKerry.Shu@Sun.COM {
5288693SKerry.Shu@Sun.COM 	ACPI_HANDLE tmphdl;
5298693SKerry.Shu@Sun.COM 	ACPI_STATUS err;
5308693SKerry.Shu@Sun.COM 	ACPI_BUFFER buf = {ACPI_ALLOCATE_BUFFER, NULL};
5318693SKerry.Shu@Sun.COM 	struct acpi_video *vidp;
5328693SKerry.Shu@Sun.COM 	struct acpi_video_switch *vidsp;
5338693SKerry.Shu@Sun.COM 
5348693SKerry.Shu@Sun.COM 	err = AcpiGetHandle(hdl, ACPI_METHOD_DOS, &tmphdl);
5358693SKerry.Shu@Sun.COM 	if (err != AE_OK)
5368693SKerry.Shu@Sun.COM 		return (AE_OK);
5378693SKerry.Shu@Sun.COM 
5388693SKerry.Shu@Sun.COM 	err = AcpiGetHandle(hdl, ACPI_METHOD_DOD, &tmphdl);
5398693SKerry.Shu@Sun.COM 	if (err != AE_OK)
5408693SKerry.Shu@Sun.COM 		return (AE_OK);
5418693SKerry.Shu@Sun.COM 
5428693SKerry.Shu@Sun.COM 	vidp = (struct acpi_video *)ctx;
5438693SKerry.Shu@Sun.COM 	vidsp = kmem_zalloc(sizeof (struct acpi_video_switch), KM_SLEEP);
5448693SKerry.Shu@Sun.COM 	vidsp->dev.hdl = hdl;
5458693SKerry.Shu@Sun.COM 	(void) acpi_drv_dev_init(&vidsp->dev);
5468693SKerry.Shu@Sun.COM 	vidsp->next = vidp->vid_switch;
5478693SKerry.Shu@Sun.COM 	vidp->vid_switch = vidsp;
5488693SKerry.Shu@Sun.COM 	vidp->total_switch++;
5498693SKerry.Shu@Sun.COM 
5508693SKerry.Shu@Sun.COM 	/*
5518693SKerry.Shu@Sun.COM 	 * Enumerate the output devices.
5528693SKerry.Shu@Sun.COM 	 */
5538693SKerry.Shu@Sun.COM 	while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_DEVICE,
5548693SKerry.Shu@Sun.COM 	    hdl, tmphdl, &tmphdl))) {
5558693SKerry.Shu@Sun.COM 		(void) acpi_video_enum_output(tmphdl, vidp);
5568693SKerry.Shu@Sun.COM 	}
5578693SKerry.Shu@Sun.COM 
5588693SKerry.Shu@Sun.COM 	if (!ACPI_FAILURE(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) {
5598693SKerry.Shu@Sun.COM 		if (buf.Pointer) {
5608693SKerry.Shu@Sun.COM 			if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
5618693SKerry.Shu@Sun.COM 				cmn_err(CE_NOTE,
5628693SKerry.Shu@Sun.COM 				    "!acpi video switch hdl = 0x%p, path = %s.",
5638693SKerry.Shu@Sun.COM 				    hdl, (char *)buf.Pointer);
5648693SKerry.Shu@Sun.COM 			}
5658693SKerry.Shu@Sun.COM 			AcpiOsFree(buf.Pointer);
5668693SKerry.Shu@Sun.COM 		}
5678693SKerry.Shu@Sun.COM 	}
5688693SKerry.Shu@Sun.COM 
5698693SKerry.Shu@Sun.COM 	return (AE_OK);
5708693SKerry.Shu@Sun.COM }
5718693SKerry.Shu@Sun.COM 
5728693SKerry.Shu@Sun.COM int
hotkey_brightness_inc(hotkey_drv_t * htkp)5738693SKerry.Shu@Sun.COM hotkey_brightness_inc(hotkey_drv_t *htkp)
5748693SKerry.Shu@Sun.COM {
5758693SKerry.Shu@Sun.COM 	struct acpi_video *vidp;
5768693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp;
5778693SKerry.Shu@Sun.COM 
5788693SKerry.Shu@Sun.COM 	vidp = (struct acpi_video *)htkp->acpi_video;
5798693SKerry.Shu@Sun.COM 
5808693SKerry.Shu@Sun.COM 	for (vidbp = vidp->vid_brightness; vidbp != NULL; vidbp = vidbp->next) {
5818693SKerry.Shu@Sun.COM 		if (vidbp->cur_level_index < vidbp->nlevel - 1) {
5828693SKerry.Shu@Sun.COM 			if (acpi_video_brightness_set(vidbp,
5838693SKerry.Shu@Sun.COM 			    vidbp->cur_level_index + 1) != ACPI_DRV_OK) {
5848693SKerry.Shu@Sun.COM 				return (ACPI_DRV_ERR);
5858693SKerry.Shu@Sun.COM 			}
5868693SKerry.Shu@Sun.COM 		}
5878693SKerry.Shu@Sun.COM 	}
5888693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
5898693SKerry.Shu@Sun.COM }
5908693SKerry.Shu@Sun.COM 
5918693SKerry.Shu@Sun.COM int
hotkey_brightness_dec(hotkey_drv_t * htkp)5928693SKerry.Shu@Sun.COM hotkey_brightness_dec(hotkey_drv_t *htkp)
5938693SKerry.Shu@Sun.COM {
5948693SKerry.Shu@Sun.COM 	struct acpi_video *vidp;
5958693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp;
5968693SKerry.Shu@Sun.COM 
5978693SKerry.Shu@Sun.COM 	vidp = (struct acpi_video *)htkp->acpi_video;
5988693SKerry.Shu@Sun.COM 
5998693SKerry.Shu@Sun.COM 	for (vidbp = vidp->vid_brightness; vidbp != NULL; vidbp = vidbp->next) {
6008693SKerry.Shu@Sun.COM 		if (vidbp->cur_level_index > 0) {
6018693SKerry.Shu@Sun.COM 			if (acpi_video_brightness_set(vidbp,
6028693SKerry.Shu@Sun.COM 			    vidbp->cur_level_index - 1) != ACPI_DRV_OK) {
6038693SKerry.Shu@Sun.COM 				return (ACPI_DRV_ERR);
6048693SKerry.Shu@Sun.COM 			}
6058693SKerry.Shu@Sun.COM 		}
6068693SKerry.Shu@Sun.COM 	}
6078693SKerry.Shu@Sun.COM 
6088693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
6098693SKerry.Shu@Sun.COM }
6108693SKerry.Shu@Sun.COM 
6118693SKerry.Shu@Sun.COM /*ARGSUSED*/
6128693SKerry.Shu@Sun.COM int
acpi_video_ioctl(void * p,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)6138693SKerry.Shu@Sun.COM acpi_video_ioctl(void *p, int cmd, intptr_t arg, int mode, cred_t *cr,
6148693SKerry.Shu@Sun.COM     int *rval)
6158693SKerry.Shu@Sun.COM {
6168693SKerry.Shu@Sun.COM 	struct acpi_video *vidp = p;
6178693SKerry.Shu@Sun.COM 	struct acpi_video_brightness *vidbp;
6188693SKerry.Shu@Sun.COM 	int res = 0;
6198693SKerry.Shu@Sun.COM 
6208693SKerry.Shu@Sun.COM 	if (vidp == NULL)
6218693SKerry.Shu@Sun.COM 		return (ENXIO);
6228693SKerry.Shu@Sun.COM 
6238693SKerry.Shu@Sun.COM 	vidbp = vidp->vid_brightness;
6248693SKerry.Shu@Sun.COM 	if (vidbp == NULL)
6258693SKerry.Shu@Sun.COM 		return (ENXIO);
6268693SKerry.Shu@Sun.COM 
6278693SKerry.Shu@Sun.COM 	if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
6288693SKerry.Shu@Sun.COM 		cmn_err(CE_NOTE, "!acpi_video_ioctl cmd %d\n", cmd);
6298693SKerry.Shu@Sun.COM 	}
6308693SKerry.Shu@Sun.COM 
6318693SKerry.Shu@Sun.COM 	switch (cmd) {
6328693SKerry.Shu@Sun.COM 	case ACPI_DRV_IOC_INFO:
6338693SKerry.Shu@Sun.COM 	{
6348693SKerry.Shu@Sun.COM 		struct acpi_drv_output_info inf;
6358693SKerry.Shu@Sun.COM 
6368693SKerry.Shu@Sun.COM 		inf.adr = vidbp->adr;
6378693SKerry.Shu@Sun.COM 		inf.nlev = vidbp->nlevel;
6388693SKerry.Shu@Sun.COM 		if (copyout(&inf, (void *)arg, sizeof (inf))) {
6398693SKerry.Shu@Sun.COM 			res = EFAULT;
6408693SKerry.Shu@Sun.COM 		}
6418693SKerry.Shu@Sun.COM 		break;
6428693SKerry.Shu@Sun.COM 	}
6438693SKerry.Shu@Sun.COM 
6448693SKerry.Shu@Sun.COM 	case ACPI_DRV_IOC_LEVELS:
6458693SKerry.Shu@Sun.COM 		if (copyout(vidbp->levels, (void *)arg,
6468693SKerry.Shu@Sun.COM 		    sizeof (*vidbp->levels) * vidbp->nlevel)) {
6478693SKerry.Shu@Sun.COM 			res = EFAULT;
6488693SKerry.Shu@Sun.COM 		}
6498693SKerry.Shu@Sun.COM 		break;
6508693SKerry.Shu@Sun.COM 
6518693SKerry.Shu@Sun.COM 	case ACPI_DRV_IOC_STATUS:
6528693SKerry.Shu@Sun.COM 	{
6538693SKerry.Shu@Sun.COM 		/*
6548693SKerry.Shu@Sun.COM 		 * Need to get the current levels through ACPI first
6558693SKerry.Shu@Sun.COM 		 * then go through array of levels to find index.
6568693SKerry.Shu@Sun.COM 		 */
6578693SKerry.Shu@Sun.COM 		struct acpi_drv_output_status status;
6588693SKerry.Shu@Sun.COM 		int i;
6598693SKerry.Shu@Sun.COM 
6608693SKerry.Shu@Sun.COM 		status.state = 0;
6618693SKerry.Shu@Sun.COM 		status.num_levels = vidbp->nlevel;
6628693SKerry.Shu@Sun.COM 		status.cur_level = vidbp->cur_level;
6638693SKerry.Shu@Sun.COM 		for (i = 0; i < vidbp->nlevel; i++) {
6648693SKerry.Shu@Sun.COM 			if (vidbp->levels[i] == vidbp->cur_level) {
6658693SKerry.Shu@Sun.COM 				status.cur_level_index = i;
6668693SKerry.Shu@Sun.COM 				if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
6678693SKerry.Shu@Sun.COM 					cmn_err(CE_NOTE, "!ACPI_DRV_IOC_STATUS "
6688693SKerry.Shu@Sun.COM 					    "cur_level_index %d\n", i);
6698693SKerry.Shu@Sun.COM 				}
6708693SKerry.Shu@Sun.COM 				break;
6718693SKerry.Shu@Sun.COM 			}
6728693SKerry.Shu@Sun.COM 		}
6738693SKerry.Shu@Sun.COM 		if (copyout(&status, (void *)arg, sizeof (status))) {
6748693SKerry.Shu@Sun.COM 			res = EFAULT;
6758693SKerry.Shu@Sun.COM 		}
6768693SKerry.Shu@Sun.COM 		break;
6778693SKerry.Shu@Sun.COM 	}
6788693SKerry.Shu@Sun.COM 
6798693SKerry.Shu@Sun.COM 	case ACPI_DRV_IOC_SET_BRIGHTNESS: {
6808693SKerry.Shu@Sun.COM 		int level;
6818693SKerry.Shu@Sun.COM 
6828693SKerry.Shu@Sun.COM 		if (drv_priv(cr)) {
6838693SKerry.Shu@Sun.COM 			res = EPERM;
6848693SKerry.Shu@Sun.COM 			break;
6858693SKerry.Shu@Sun.COM 		}
6868693SKerry.Shu@Sun.COM 		if (copyin((void *)arg, &level, sizeof (level))) {
6878693SKerry.Shu@Sun.COM 			res = EFAULT;
6888693SKerry.Shu@Sun.COM 			break;
6898693SKerry.Shu@Sun.COM 		}
6908693SKerry.Shu@Sun.COM 		if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
6918693SKerry.Shu@Sun.COM 			cmn_err(CE_NOTE,
6928693SKerry.Shu@Sun.COM 			    "!acpi_video_ioctl: set BRIGHTNESS level=%d\n",
6938693SKerry.Shu@Sun.COM 			    level);
6948693SKerry.Shu@Sun.COM 		}
6958693SKerry.Shu@Sun.COM 		if (acpi_video_brightness_set(vidbp, level) != ACPI_DRV_OK) {
6968693SKerry.Shu@Sun.COM 			res = EFAULT;
6978693SKerry.Shu@Sun.COM 		}
6988693SKerry.Shu@Sun.COM 		break;
6998693SKerry.Shu@Sun.COM 	}
7008693SKerry.Shu@Sun.COM 
7018693SKerry.Shu@Sun.COM 	default:
7028693SKerry.Shu@Sun.COM 		res = EINVAL;
7038693SKerry.Shu@Sun.COM 		break;
7048693SKerry.Shu@Sun.COM 	}
7058693SKerry.Shu@Sun.COM 
7068693SKerry.Shu@Sun.COM 	return (res);
7078693SKerry.Shu@Sun.COM }
7088693SKerry.Shu@Sun.COM 
7098693SKerry.Shu@Sun.COM /*ARGSUSED*/
7108693SKerry.Shu@Sun.COM int
acpi_drv_hotkey_ioctl(int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)7118693SKerry.Shu@Sun.COM acpi_drv_hotkey_ioctl(int cmd, intptr_t arg, int mode, cred_t *cr,
7128693SKerry.Shu@Sun.COM     int *rval)
7138693SKerry.Shu@Sun.COM {
7148693SKerry.Shu@Sun.COM 	hotkey_drv_t *htkp = &acpi_hotkey;
7158693SKerry.Shu@Sun.COM 
7168693SKerry.Shu@Sun.COM 	switch (htkp->hotkey_method) {
7178693SKerry.Shu@Sun.COM 	case HOTKEY_METHOD_ACPI_VIDEO:
7188693SKerry.Shu@Sun.COM 		return (acpi_video_ioctl(htkp->acpi_video, cmd, arg, mode,
7198693SKerry.Shu@Sun.COM 		    cr, rval));
7208693SKerry.Shu@Sun.COM 	case HOTKEY_METHOD_MISC:
7218693SKerry.Shu@Sun.COM 	case HOTKEY_METHOD_VENDOR:
7228693SKerry.Shu@Sun.COM 		return (htkp->vendor_ioctl(htkp, cmd, arg, mode, cr, rval));
7238693SKerry.Shu@Sun.COM 	case HOTKEY_METHOD_NONE:
7248693SKerry.Shu@Sun.COM 	default:
7258693SKerry.Shu@Sun.COM 		return (ENXIO);
7268693SKerry.Shu@Sun.COM 	}
7278693SKerry.Shu@Sun.COM }
7288693SKerry.Shu@Sun.COM 
729*12122SKerry.Shu@Sun.COM static void
acpi_video_check_blacklist(void)730*12122SKerry.Shu@Sun.COM acpi_video_check_blacklist(void)
731*12122SKerry.Shu@Sun.COM {
732*12122SKerry.Shu@Sun.COM 	smbios_hdl_t *smhdl = NULL;
733*12122SKerry.Shu@Sun.COM 	id_t smid;
734*12122SKerry.Shu@Sun.COM 	smbios_system_t smsys;
735*12122SKerry.Shu@Sun.COM 	smbios_info_t sminfo;
736*12122SKerry.Shu@Sun.COM 	char *mfg, *product;
737*12122SKerry.Shu@Sun.COM 	struct acpi_video_smbios_info *pblacklist;
738*12122SKerry.Shu@Sun.COM 
739*12122SKerry.Shu@Sun.COM 	acpi_brightness_get_disable = 0;
740*12122SKerry.Shu@Sun.COM 	smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL);
741*12122SKerry.Shu@Sun.COM 	if (smhdl == NULL ||
742*12122SKerry.Shu@Sun.COM 	    ((smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR) ||
743*12122SKerry.Shu@Sun.COM 	    (smbios_info_common(smhdl, smid, &sminfo) == SMB_ERR)) {
744*12122SKerry.Shu@Sun.COM 		goto done;
745*12122SKerry.Shu@Sun.COM 	}
746*12122SKerry.Shu@Sun.COM 
747*12122SKerry.Shu@Sun.COM 	mfg = (char *)sminfo.smbi_manufacturer;
748*12122SKerry.Shu@Sun.COM 	product = (char *)sminfo.smbi_product;
749*12122SKerry.Shu@Sun.COM 	for (pblacklist = acpi_brightness_get_blacklist;
750*12122SKerry.Shu@Sun.COM 	    pblacklist->manufacturer != NULL; pblacklist++) {
751*12122SKerry.Shu@Sun.COM 		if ((strcmp(mfg, pblacklist->manufacturer) == 0) &&
752*12122SKerry.Shu@Sun.COM 		    (strcmp(product, pblacklist->product) == 0)) {
753*12122SKerry.Shu@Sun.COM 			acpi_brightness_get_disable = 1;
754*12122SKerry.Shu@Sun.COM 		}
755*12122SKerry.Shu@Sun.COM 	}
756*12122SKerry.Shu@Sun.COM done:
757*12122SKerry.Shu@Sun.COM 	if (smhdl != NULL)
758*12122SKerry.Shu@Sun.COM 		smbios_close(smhdl);
759*12122SKerry.Shu@Sun.COM }
760*12122SKerry.Shu@Sun.COM 
7618693SKerry.Shu@Sun.COM static int
hotkey_acpi_video_check(hotkey_drv_t * htkp)7628693SKerry.Shu@Sun.COM hotkey_acpi_video_check(hotkey_drv_t *htkp)
7638693SKerry.Shu@Sun.COM {
7648693SKerry.Shu@Sun.COM 	struct acpi_video *vidp;
7658693SKerry.Shu@Sun.COM 
7668693SKerry.Shu@Sun.COM 	vidp = &acpi_video_hotkey;
7678693SKerry.Shu@Sun.COM 	bzero(vidp, sizeof (struct acpi_video));
768*12122SKerry.Shu@Sun.COM 	if (acpi_brightness_get_disable == -1)
769*12122SKerry.Shu@Sun.COM 		acpi_video_check_blacklist();
7708693SKerry.Shu@Sun.COM 	/* Find ACPI Video device handle */
7718693SKerry.Shu@Sun.COM 	if (ACPI_FAILURE(AcpiGetDevices(NULL, acpi_video_find_and_alloc,
7728693SKerry.Shu@Sun.COM 	    vidp, NULL))) {
7738693SKerry.Shu@Sun.COM 		return (ACPI_DRV_ERR);
7748693SKerry.Shu@Sun.COM 	}
7758693SKerry.Shu@Sun.COM 
7768693SKerry.Shu@Sun.COM 	htkp->acpi_video = vidp;
7778693SKerry.Shu@Sun.COM 	if (htkp->hotkey_method == HOTKEY_METHOD_NONE) {
7788693SKerry.Shu@Sun.COM 		if (acpi_video_notify_intall(vidp) != ACPI_DRV_OK) {
7798693SKerry.Shu@Sun.COM 			(void) acpi_video_fini(vidp);
7808693SKerry.Shu@Sun.COM 			htkp->acpi_video = NULL;
7818693SKerry.Shu@Sun.COM 			return (ACPI_DRV_ERR);
7828693SKerry.Shu@Sun.COM 		}
7838693SKerry.Shu@Sun.COM 	}
7848693SKerry.Shu@Sun.COM 	htkp->hotkey_method |= HOTKEY_METHOD_ACPI_VIDEO;
7858693SKerry.Shu@Sun.COM 
7868693SKerry.Shu@Sun.COM 	acpi_video_set_dos(vidp, VIDEO_POLICY_BRIGHTNESS_OS |
7878693SKerry.Shu@Sun.COM 	    VIDEO_POLICY_SWITCH_OS);
7888693SKerry.Shu@Sun.COM 
7898693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
7908693SKerry.Shu@Sun.COM }
7918693SKerry.Shu@Sun.COM 
7928693SKerry.Shu@Sun.COM int
hotkey_init(hotkey_drv_t * htkp)7938693SKerry.Shu@Sun.COM hotkey_init(hotkey_drv_t *htkp)
7948693SKerry.Shu@Sun.COM {
7958693SKerry.Shu@Sun.COM 	int i;
7968693SKerry.Shu@Sun.COM 	int modid;
7978693SKerry.Shu@Sun.COM 	modctl_t *modp;
7988693SKerry.Shu@Sun.COM 
7998693SKerry.Shu@Sun.COM 	htkp->modid = -1;
8008693SKerry.Shu@Sun.COM 	/* Try to find vendor specific method */
8018693SKerry.Shu@Sun.COM 	for (i = 0; vendor_hotkey_drv_list[i].module != NULL; i++) {
8028693SKerry.Shu@Sun.COM 		if (!vendor_hotkey_drv_list[i].enable)
8038693SKerry.Shu@Sun.COM 			continue;
8048693SKerry.Shu@Sun.COM 
8058693SKerry.Shu@Sun.COM 		if ((modid = modload("drv", vendor_hotkey_drv_list[i].module))
8068693SKerry.Shu@Sun.COM 		    == -1) {
8078693SKerry.Shu@Sun.COM 			continue;
8088693SKerry.Shu@Sun.COM 		}
8098693SKerry.Shu@Sun.COM 
8108693SKerry.Shu@Sun.COM 		htkp->modid = modid;
8118693SKerry.Shu@Sun.COM 		if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
8128693SKerry.Shu@Sun.COM 			cmn_err(CE_NOTE, "!loaded %s specific method.\n",
8138693SKerry.Shu@Sun.COM 			    vendor_hotkey_drv_list[i].vid);
8148693SKerry.Shu@Sun.COM 		}
8158693SKerry.Shu@Sun.COM 	}
8168693SKerry.Shu@Sun.COM 
8178693SKerry.Shu@Sun.COM 	/* Check availability of ACPI Video Extension method */
8188693SKerry.Shu@Sun.COM 	if (htkp->hotkey_method == HOTKEY_METHOD_NONE ||
8198693SKerry.Shu@Sun.COM 	    htkp->check_acpi_video) {
8208693SKerry.Shu@Sun.COM 		if (hotkey_acpi_video_check(htkp) == ACPI_DRV_OK) {
8218693SKerry.Shu@Sun.COM 			if (hotkey_drv_debug & HOTKEY_DBG_NOTICE)
8228693SKerry.Shu@Sun.COM 				cmn_err(CE_NOTE, "!find ACPI video method.\n");
8238693SKerry.Shu@Sun.COM 		} else
8248693SKerry.Shu@Sun.COM 			goto fail;
8258693SKerry.Shu@Sun.COM 	}
8268693SKerry.Shu@Sun.COM 
8278693SKerry.Shu@Sun.COM 	if (htkp->modid != -1) {
8288693SKerry.Shu@Sun.COM 		modp = mod_hold_by_id(htkp->modid);
8298693SKerry.Shu@Sun.COM 		mutex_enter(&mod_lock);
8308693SKerry.Shu@Sun.COM 		modp->mod_ref = 1;
8318693SKerry.Shu@Sun.COM 		modp->mod_loadflags |= MOD_NOAUTOUNLOAD;
8328693SKerry.Shu@Sun.COM 		mutex_exit(&mod_lock);
8338693SKerry.Shu@Sun.COM 		mod_release_mod(modp);
8348693SKerry.Shu@Sun.COM 	}
8358693SKerry.Shu@Sun.COM 
8368693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
8378693SKerry.Shu@Sun.COM 
8388693SKerry.Shu@Sun.COM fail:
8398693SKerry.Shu@Sun.COM 	if (htkp->vendor_fini != NULL)
8408693SKerry.Shu@Sun.COM 		htkp->vendor_fini(htkp);
8418693SKerry.Shu@Sun.COM 	if (htkp->modid != -1)
8428693SKerry.Shu@Sun.COM 		(void) modunload(htkp->modid);
8438693SKerry.Shu@Sun.COM 
8448693SKerry.Shu@Sun.COM 	return (ACPI_DRV_ERR);
8458693SKerry.Shu@Sun.COM }
8468693SKerry.Shu@Sun.COM 
8478693SKerry.Shu@Sun.COM 
8488693SKerry.Shu@Sun.COM int
hotkey_fini(hotkey_drv_t * htkp)8498693SKerry.Shu@Sun.COM hotkey_fini(hotkey_drv_t *htkp)
8508693SKerry.Shu@Sun.COM {
8518693SKerry.Shu@Sun.COM 	modctl_t *modp;
8528693SKerry.Shu@Sun.COM 
8538693SKerry.Shu@Sun.COM 	if (htkp->vendor_fini != NULL)
8548693SKerry.Shu@Sun.COM 		htkp->vendor_fini(htkp);
8558693SKerry.Shu@Sun.COM 	if (htkp->acpi_video != NULL)
8568693SKerry.Shu@Sun.COM 		(void) acpi_video_fini(htkp->acpi_video);
8578693SKerry.Shu@Sun.COM 	if (htkp->modid != -1) {
8588693SKerry.Shu@Sun.COM 		modp = mod_hold_by_id(htkp->modid);
8598693SKerry.Shu@Sun.COM 		mutex_enter(&mod_lock);
8608693SKerry.Shu@Sun.COM 		modp->mod_ref = 0;
8618693SKerry.Shu@Sun.COM 		modp->mod_loadflags &= ~MOD_NOAUTOUNLOAD;
8628693SKerry.Shu@Sun.COM 		mutex_exit(&mod_lock);
8638693SKerry.Shu@Sun.COM 		mod_release_mod(modp);
8648693SKerry.Shu@Sun.COM 		(void) modunload(htkp->modid);
8658693SKerry.Shu@Sun.COM 	}
8668693SKerry.Shu@Sun.COM 
8678693SKerry.Shu@Sun.COM 	return (ACPI_DRV_OK);
8688693SKerry.Shu@Sun.COM }
869