tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: ACPI Issues on Lifebook P7120 (with fixes)
Hi,
I attach the new version of the files. The main changes are:
(1) integration of Jared's display switching code.
(2) the acpidisplay device now attaches as child of vga.
(3) use of PRIx8/16/32/64 conversion macros.
For (1) I took the liberty to adapt the code, but of course I am open
for discussion. :-) In particular I left out the resume function.
For (2) I had to introduce an interface attribute (called pcidisplay) to
attach as a child of vga. I would be happy to learn that there is
another way. A config_found_ia() call is added to vga_pci.c. Maybe
this call should also be added to framebuffer devices (e.g. radeonfb) ?
To find the ACPI device, I first walk the namespace to find the PCI bus
and then walk the sub-tree to find the device with the right _ADR. The
implementation tries to be safe: if we find several candidates then the
driver does not attach.
It is still possible to attach as a child of acpi (there are two match
and attach functions that share a common sub-function where the real
work is done).
Grégoire
Copy acpi_display.c, fujbl_acpi.c and fujhk_acpi.c into /usr/src/sys/dev/acpi/
Cd into /usr/src/sys and apply the provided patch autoconf.patch
Add the following lines in the kernel configuration file:
acpidisp* at pcidisplaybus?
#acpidisp* at acpi?
#options ACPI_DISP_DEBUG
#options ACPI_DISP_SWITCH_OVERRIDE
fujbl* at acpi?
fujhk* at acpi?
Build kernel as usual, e.g. with build.sh.
With the above configuration, acpidisp is attached as a child of vga(4).
To have it attached as a child of acpi instead:
1. comment the first line and uncomment the second one.
2. cd into /usr/src/sys/dev/acpi and apply the provided patch acpi.c.patch.
The hotkey buttons are configured in the driver fujhk as PSWITCH_TYPE_HOTKEY
buttons. They are processed by the /etc/powerd/scripts/hotkey_button powerd
script.
/* $Id: acpi_display.c 1023 2008-12-11 16:42:07Z sutre $ */
/*
* ACPI Display Adapter Driver.
*
* Appendix B of the ACPI specification (revision 3) specifies ACPI extensions
* for display adapters. Systems containing a built-in display adapter are
* required to implement these extensions (well, it's what the spec says).
*
* This implementation (1) relays brightness notifications to the PMF framework
* and (2) performs display switching when a display switch notification (0x80)
* is received. Kernel configuration option ACPI_DISP_SWITCH_OVERRIDE controls
* the display switching method:
* - without this option, the BIOS shall perform display switching, and no
* display switch notification shall be received (hence (2) is disabled).
* - with this option, the BIOS shall not perform display switching, and instead
* display switch notifications shall be received (hence (2) is enabled).
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$Id: acpi_display.c 1023 2008-12-11 16:42:07Z sutre $");
#include <sys/inttypes.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kmem.h>
#include <dev/acpi/acpivar.h>
#include <dev/pci/pcivar.h>
#include "acpi.h"
/* Notifications specific to display adapter devices (appendix B.5) */
#define ACPI_NOTIFY_CycleOutputDevice 0x80
#define ACPI_NOTIFY_OutputDeviceStatusChange 0x81
#define ACPI_NOTIFY_CycleDisplayOutputHotkeyPressed 0x82
#define ACPI_NOTIFY_NextDisplayOutputHotkeyPressed 0x83
#define ACPI_NOTIFY_PreviousDisplayOutputHotkeyPressed 0x84
/* Notifications specific to display output devices (appendix B.7) */
#define ACPI_NOTIFY_CycleBrightness 0x85
#define ACPI_NOTIFY_IncreaseBrightness 0x86
#define ACPI_NOTIFY_DecreaseBrightness 0x87
#define ACPI_NOTIFY_ZeroBrightness 0x88
#define ACPI_NOTIFY_DisplayDeviceOff 0x89
/* Output device display types (appendix B.4.2, table B-2) */
#define ACPI_DISP_OUTDEV_ATTR_TYPE_OTHER 0
#define ACPI_DISP_OUTDEV_ATTR_TYPE_VGA 1
#define ACPI_DISP_OUTDEV_ATTR_TYPE_TV 2
#define ACPI_DISP_OUTDEV_ATTR_TYPE_EXTDIG 3
#define ACPI_DISP_OUTDEV_ATTR_TYPE_INTDFP 4
/* Format of output device attributes (appendix B.4.2, table B-2) */
union acpidisp_od_attrs {
UINT32 raw;
struct {
uint8_t index:4;
uint8_t port:4;
uint8_t type:4;
uint8_t vendor_specific:4;
uint8_t bios_detect:1;
uint8_t non_vga:1;
uint8_t pipe_id:3;
uint16_t reserved:10;
uint8_t device_id_scheme:1;
} __packed fmt;
};
/* Format of output device status (appendix B.6.6, table B-4) */
union acpidisp_od_status {
UINT32 raw;
struct {
uint8_t exists:1;
uint8_t activated:1;
uint8_t ready:1;
uint8_t not_defective:1;
uint8_t attached:1;
uint32_t reserved:27;
} __packed fmt;
};
/* Format of output device state (appendix B.6.8, table B-6) */
union acpidisp_od_state {
UINT32 raw;
struct {
uint8_t active:1;
uint32_t reserved:29;
uint8_t b30:1;
uint8_t b31:1;
} __packed fmt;
};
/* Output device id of a value of type union acpidisp_od_attrs */
#define ACPI_DISP_OUTDEV_ID(oda) ((oda).raw & 0xffff)
/*
* acpidisp_outdev:
*
* Description of an ACPI video output device.
*/
struct acpidisp_outdev {
ACPI_HANDLE od_handle;
/* our ACPI handle */
union acpidisp_od_attrs od_attrs;
/* attributes (appendix B.4.2) */
struct acpidisp_softc *od_softc;
/* backpointer to display softc */
char od_name[5];
/* Human-readable device name */
};
/*
* acpidisp_softc:
*
* Software state of the ACPI display adapter driver.
*/
struct acpidisp_softc {
device_t sc_dev;
/* base device info */
ACPI_HANDLE sc_handle;
/* our ACPI handle */
struct acpidisp_outdev *sc_outdev;
/* array of output devices */
uint8_t sc_noutdev;
/* number of output devices */
};
static int acpidisp_match_common(ACPI_HANDLE);
static int acpidisp_match_acpi(device_t, struct cfdata *, void *);
static int acpidisp_match_pci(device_t, struct cfdata *, void *);
static void acpidisp_attach_common(device_t, device_t, ACPI_HANDLE);
static void acpidisp_attach_acpi(device_t, device_t, void *);
static void acpidisp_attach_pci(device_t, device_t, void *);
CFATTACH_DECL_NEW(acpidisp_acpi, sizeof(struct acpidisp_softc),
acpidisp_match_acpi, acpidisp_attach_acpi, NULL, NULL);
CFATTACH_DECL_NEW(acpidisp_pci, sizeof(struct acpidisp_softc),
acpidisp_match_pci, acpidisp_attach_pci, NULL, NULL);
static void acpidisp_notify_handler(ACPI_HANDLE, UINT32, void *);
static void acpidisp_outdev_notify_handler(ACPI_HANDLE, UINT32, void *);
static void acpidisp_cycle_output_device(void *);
static void acpidisp_outdev_increase_brightness(void *);
static void acpidisp_outdev_decrease_brightness(void *);
static void acpidisp_od_attrs_to_string(union acpidisp_od_attrs, char *,
size_t);
static ACPI_STATUS acpidisp_enumerate_output_devices(struct
acpidisp_softc *);
static ACPI_STATUS acpidisp_register_output_device(struct
acpidisp_softc *, uint8_t, ACPI_OBJECT *);
static ACPI_STATUS acpidisp_acpi_eval_set_integer(ACPI_HANDLE,
const char *, ACPI_INTEGER);
static ACPI_STATUS acpidisp_match_node_with_address(ACPI_HANDLE, UINT32,
void *, void **);
static ACPI_STATUS acpidisp_find_pci_device(u_int, u_int, u_int,
ACPI_HANDLE *);
static ACPI_STATUS acpidisp_match_pci_bus(ACPI_HANDLE, UINT32, void *,
void **);
static const char * const acpidisp_pci_bus_ids[] = {
"PNP0A03",
"PNP0A08",
NULL
};
/*
* acpidisp_match_common:
*
* Autoconfiguration `match' common subroutine.
*/
static int
acpidisp_match_common(ACPI_HANDLE handle)
{
ACPI_HANDLE hdl;
ACPI_STATUS rv;
ACPI_BUFFER buf;
ACPI_DEVICE_INFO *info;
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
rv = AcpiGetObjectInfo(handle, &buf);
if (ACPI_FAILURE(rv)) {
aprint_error("acpidisp_match_common: "
"failed to get ACPI object info: %s\n",
AcpiFormatException(rv));
return 0;
}
info = buf.Pointer;
/*
* There is no straightforward way to match display adapter devices.
* We assume here that an ACPI device node is a display adapter device
* if (1) it has a valid address and (2) it has methods _DOS and _DOD.
*/
if ((info->Type == ACPI_TYPE_DEVICE) &&
(info->Valid & ACPI_VALID_ADR) &&
ACPI_SUCCESS(AcpiGetHandle(handle, "_DOS", &hdl)) &&
ACPI_SUCCESS(AcpiGetHandle(handle, "_DOD", &hdl))) {
AcpiOsFree(buf.Pointer);
return 1;
}
else {
AcpiOsFree(buf.Pointer);
return 0;
}
}
/*
* acpidisp_match_acpi:
*
* Autoconfiguration `match' routine for the acpinodebus interface.
*/
static int
acpidisp_match_acpi(device_t parent, struct cfdata *match, void *aux)
{
struct acpi_attach_args *aa = aux;
return acpidisp_match_common(aa->aa_node->ad_handle);
}
/*
* acpidisp_match_pci:
*
* Autoconfiguration `match' routine for the pcidisplaybus interface.
*/
static int
acpidisp_match_pci(device_t parent, struct cfdata *match, void *aux)
{
#if NACPI > 0
struct pci_attach_args *pa = aux;
ACPI_HANDLE handle;
ACPI_STATUS rv;
if (acpi_active == 0)
return 0;
rv = acpidisp_find_pci_device(pa->pa_bus, pa->pa_device,
pa->pa_function, &handle);
if (ACPI_FAILURE(rv))
return 0;
KASSERT(handle != NULL);
return acpidisp_match_common(handle);
#else
return 0;
#endif
}
/*
* acpidisp_attach_common:
*
* Autoconfiguration `attach' common subroutine.
*/
static void
acpidisp_attach_common(device_t parent, device_t self, ACPI_HANDLE handle)
{
struct acpidisp_softc *sc = device_private(self);
ACPI_STATUS rv;
char outdevinfo[256];
uint8_t i;
aprint_naive(": ACPI Display Adapter\n");
aprint_normal(": ACPI Display Adapter\n");
sc->sc_dev = self;
sc->sc_handle = handle;
sc->sc_noutdev = 0;
#ifdef ACPI_DISP_SWITCH_OVERRIDE
/*
* The following _DOS call disables output switching by the BIOS and
* the BIOS shall instead generate display switch events. Brightness
* control of the BIOS remains unchanged. See appendix B.4.1.
*/
rv = acpidisp_acpi_eval_set_integer(handle, "_DOS", 0x0);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(self, "failed to evaluate _DOS: %s\n",
AcpiFormatException(rv));
}
#endif
/* Enumerate and register output devices */
rv = acpidisp_enumerate_output_devices(sc);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(self,
"unable to enumerate (all) output devices: %s\n",
AcpiFormatException(rv));
}
/* Print information about output devices */
aprint_verbose_dev(self, "%" PRIu8 " video output devices\n",
sc->sc_noutdev);
for (i = 0 ; i < sc->sc_noutdev ; i++) {
acpidisp_od_attrs_to_string(sc->sc_outdev[i].od_attrs,
outdevinfo, sizeof(outdevinfo));
aprint_verbose_dev(self,
"(%s, 0x%04" PRIx16 "): %s",
sc->sc_outdev[i].od_name,
(uint16_t) ACPI_DISP_OUTDEV_ID(sc->sc_outdev[i].od_attrs),
outdevinfo);
aprint_debug(" (raw attributes: 0x%08" PRIx32 ")",
sc->sc_outdev[i].od_attrs.raw);
aprint_verbose("\n");
}
/* Install notify handler for this display adapter device */
rv = AcpiInstallNotifyHandler(handle,
ACPI_DEVICE_NOTIFY, acpidisp_notify_handler, sc);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(self,
"unable to register DEVICE NOTIFY handler: %s\n",
AcpiFormatException(rv));
}
/* Install notify handler for the child output devices */
for (i = 0 ; i < sc->sc_noutdev ; i++) {
rv = AcpiInstallNotifyHandler(sc->sc_outdev[i].od_handle,
ACPI_DEVICE_NOTIFY, acpidisp_outdev_notify_handler,
sc->sc_outdev + i);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(self,
"unable to register DEVICE NOTIFY handler: %s\n",
AcpiFormatException(rv));
}
}
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
}
/*
* acpidisp_attach_acpi:
*
* Autoconfiguration `attach' routine for the acpinodebus interface.
*/
static void
acpidisp_attach_acpi(device_t parent, device_t self, void *aux)
{
struct acpi_attach_args *aa = aux;
acpidisp_attach_common(parent, self, aa->aa_node->ad_handle);
}
/*
* acpidisp_attach_pci:
*
* Autoconfiguration `attach' routine for the pcidisplaybus interface.
*/
static void
acpidisp_attach_pci(device_t parent, device_t self, void *aux)
{
#if NACPI > 0
struct pci_attach_args *pa = aux;
ACPI_HANDLE handle;
ACPI_STATUS rv;
KASSERT(acpi_active != 0);
/* XXX Already done in match: try to cache the result somewhere */
rv = acpidisp_find_pci_device(pa->pa_bus, pa->pa_device,
pa->pa_function, &handle);
KASSERT(ACPI_SUCCESS(rv));
KASSERT(handle != NULL);
acpidisp_attach_common(parent, self, handle);
#else
return 0;
#endif
}
static void
acpidisp_od_attrs_to_string(union acpidisp_od_attrs oda, char *cp, size_t l)
{
const char *type;
char *ep;
ep = cp + l;
if (oda.fmt.device_id_scheme == 1) {
switch (oda.fmt.type) {
case ACPI_DISP_OUTDEV_ATTR_TYPE_OTHER:
type = "Other"; break;
case ACPI_DISP_OUTDEV_ATTR_TYPE_VGA:
type = "VGA/CRT/VESA Monitor"; break;
case ACPI_DISP_OUTDEV_ATTR_TYPE_TV:
type = "TV/HDTV Monitor"; break;
case ACPI_DISP_OUTDEV_ATTR_TYPE_EXTDIG:
type = "Ext. Digital Monitor"; break;
case ACPI_DISP_OUTDEV_ATTR_TYPE_INTDFP:
type = "Int. Digital Flat Panel"; break;
default:
type = "Unknown"; break;
}
cp += snprintf(cp, ep - cp, "%s index %d port %d",
type,
oda.fmt.index,
oda.fmt.port);
}
else
cp += snprintf(cp, ep - cp, "custom device id");
if (oda.fmt.bios_detect)
cp += snprintf(cp, ep - cp, ", bios detect");
if (oda.fmt.non_vga)
cp += snprintf(cp, ep - cp, ", non vga");
cp += snprintf(cp, ep - cp, ", pipe %d", oda.fmt.pipe_id);
}
static ACPI_STATUS
acpidisp_enumerate_output_devices(struct acpidisp_softc *sc)
{
ACPI_BUFFER buf;
ACPI_STATUS rv;
ACPI_OBJECT *pkg;
uint8_t i, k;
rv = acpi_eval_struct(sc->sc_handle, "_DOD", &buf);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(sc->sc_dev, "failed to evaluate _DOD: %s\n",
AcpiFormatException(rv));
return rv;
}
pkg = buf.Pointer;
if (pkg == NULL || pkg->Type != ACPI_TYPE_PACKAGE) {
aprint_error_dev(sc->sc_dev, "_DOD didn't return a package\n");
return AE_BAD_DATA;
}
sc->sc_outdev = kmem_alloc(pkg->Package.Count *
sizeof(struct acpidisp_outdev), KM_SLEEP);
for (i = 0, k = 0 ; i < pkg->Package.Count ; i++) {
rv = acpidisp_register_output_device(sc, k,
&pkg->Package.Elements[i]);
if (ACPI_SUCCESS(rv))
k++;
}
sc->sc_noutdev = k;
AcpiOsFree(buf.Pointer);
return rv;
}
static ACPI_STATUS
acpidisp_register_output_device(struct acpidisp_softc *sc, uint8_t k,
ACPI_OBJECT *obj)
{
ACPI_HANDLE hdl;
ACPI_STATUS rv;
ACPI_INTEGER attr, addr;
ACPI_BUFFER buf;
if (obj == NULL || obj->Type != ACPI_TYPE_INTEGER) {
aprint_error_dev(sc->sc_dev,
"unexpected non-integer ACPI object\n");
return AE_BAD_PARAMETER;
}
attr = obj->Integer.Value;
addr = attr & 0xffff;
hdl = NULL;
rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, sc->sc_handle, 100,
acpidisp_match_node_with_address, &addr, &hdl);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(sc->sc_dev,
"failure in ACPI namespace walk: %s\n",
AcpiFormatException(rv));
return rv;
}
if (hdl == NULL) {
aprint_error_dev(sc->sc_dev,
"failed to find output device : %" PRIx64 "\n", attr);
return AE_NOT_FOUND;
}
sc->sc_outdev[k].od_handle = hdl;
sc->sc_outdev[k].od_attrs.raw = attr;
sc->sc_outdev[k].od_softc = sc;
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
rv = AcpiGetName(hdl, ACPI_SINGLE_NAME, &buf);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(sc->sc_dev, "failed to get ACPI name: %s\n",
AcpiFormatException(rv));
strlcpy(sc->sc_outdev[k].od_name, "????", 5);
}
else {
strlcpy(sc->sc_outdev[k].od_name, buf.Pointer, 5);
AcpiOsFree(buf.Pointer);
}
return AE_OK;
}
/*
* acpidisp_notify_handler:
*
* Callback from ACPI interrupt handler to notify us of an event on the
* display adapter device.
*/
static void
acpidisp_notify_handler(ACPI_HANDLE handle, UINT32 notify,
void *context)
{
struct acpidisp_softc *sc = context;
ACPI_STATUS rv;
#ifdef ACPI_DISP_DEBUG
aprint_debug_dev(sc->sc_dev,
"received notify message: 0x%" PRIx32
" (on display adapter device)\n", notify);
#endif
switch (notify) {
case ACPI_NOTIFY_CycleOutputDevice:
rv = AcpiOsExecute(OSL_NOTIFY_HANDLER,
acpidisp_cycle_output_device, sc);
if (ACPI_FAILURE(rv))
aprint_error_dev(sc->sc_dev,
"unable to queue cycle output device callback: "
"%s\n", AcpiFormatException(rv));
return ;
case ACPI_NOTIFY_OutputDeviceStatusChange:
case ACPI_NOTIFY_CycleDisplayOutputHotkeyPressed:
case ACPI_NOTIFY_NextDisplayOutputHotkeyPressed:
case ACPI_NOTIFY_PreviousDisplayOutputHotkeyPressed:
aprint_debug_dev(sc->sc_dev,
"received unhandled notify message: 0x%" PRIx32 "\n",
notify);
return ;
default:
aprint_error_dev(sc->sc_dev,
"received unknown notify message: 0x%" PRIx32 "\n",
notify);
return ;
}
}
/*
* acpidisp_outdev_notify_handler:
*
* Callback from ACPI interrupt handler to notify us of an event on a
* display output device.
*/
static void
acpidisp_outdev_notify_handler(ACPI_HANDLE handle, UINT32 notify,
void *context)
{
struct acpidisp_outdev *od = context;
ACPI_STATUS rv;
ACPI_OSD_EXEC_CALLBACK callback = NULL;
const char *callback_name = NULL;
#ifdef ACPI_DISP_DEBUG
aprint_debug_dev(od->od_softc->sc_dev,
"received notify message: 0x%" PRIx32 " on output device: %s\n",
notify, od->od_name);
#endif
switch (notify) {
case ACPI_NOTIFY_IncreaseBrightness:
callback = acpidisp_outdev_increase_brightness;
callback_name = "increase brightness";
break;
case ACPI_NOTIFY_DecreaseBrightness:
callback = acpidisp_outdev_decrease_brightness;
callback_name = "decrease brightness";
break;
case ACPI_NOTIFY_CycleBrightness:
case ACPI_NOTIFY_ZeroBrightness:
case ACPI_NOTIFY_DisplayDeviceOff:
aprint_debug_dev(od->od_softc->sc_dev,
"received unhandled notify message: 0x%" PRIx32 "\n",
notify);
return ;
default:
aprint_error_dev(od->od_softc->sc_dev,
"received unknown notify message: 0x%" PRIx32 "\n",
notify);
return ;
}
KASSERT((callback != NULL) && (callback_name != NULL));
rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, callback, od);
if (ACPI_FAILURE(rv))
aprint_error_dev(od->od_softc->sc_dev,
"unable to queue %s callback: %s\n", callback_name,
AcpiFormatException(rv));
}
static void
acpidisp_cycle_output_device(void *arg)
{
struct acpidisp_softc *sc = arg;
ACPI_HANDLE handle;
ACPI_INTEGER val;
union acpidisp_od_status dcs;
union acpidisp_od_state dgs;
ACPI_STATUS rv;
uint8_t i;
for (i = 0 ; i < sc->sc_noutdev ; i++) {
#ifdef ACPI_DISP_DEBUG
aprint_debug_dev(sc->sc_dev, "%s: ", sc->sc_outdev[i].od_name);
#endif
handle = sc->sc_outdev[i].od_handle;
rv = acpi_eval_integer(handle, "_DGS", &val);
if (ACPI_FAILURE(rv))
continue;
#ifdef ACPI_DISP_DEBUG
aprint_debug("_DGS -> 0x%" PRIx64, val);
#endif
dgs.raw = val & 0x1;
rv = acpi_eval_integer(handle, "_DCS", &val);
if (ACPI_FAILURE(rv))
continue;
#ifdef ACPI_DISP_DEBUG
aprint_debug(", _DCS -> 0x%" PRIx64, val);
#endif
dcs.raw = val & 0xffffffff;
if (dcs.fmt.ready == 0)
dgs.raw = 0;
else
dgs.fmt.b30 = 1;
if (i == sc->sc_noutdev - 1)
dgs.fmt.b31 = 1; /* commit */
val = dgs.raw;
rv = acpidisp_acpi_eval_set_integer(handle, "_DSS", val);
if (ACPI_FAILURE(rv))
aprint_error_dev(sc->sc_dev, "failed to evaluate _DSS: "
"%s\n", AcpiFormatException(rv));
#ifdef ACPI_DISP_DEBUG
aprint_debug(", _DSS(0x%" PRIx64 ")", val);
aprint_debug(", %s\n", dgs.fmt.active ? "enabled" : "disabled");
#endif
}
}
static void
acpidisp_outdev_increase_brightness(void *arg)
{
/*
* Here we should first try _BCM when it is available, and only resort
* to pmf_event_inject otherwise. Let's keep it simple for now.
*/
pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
}
static void
acpidisp_outdev_decrease_brightness(void *arg)
{
/*
* Here we should first try _BCM when it is available, and only resort
* to pmf_event_inject otherwise. Let's keep it simple for now.
*/
pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
}
/******************************************************************************
* ACPI Utility Routines.
******************************************************************************/
static ACPI_STATUS
acpidisp_acpi_eval_set_integer(ACPI_HANDLE handle, const char *path,
ACPI_INTEGER arg)
{
ACPI_STATUS rv;
ACPI_OBJECT param_arg;
ACPI_OBJECT_LIST param_args;
if (handle == NULL)
handle = ACPI_ROOT_OBJECT;
param_arg.Type = ACPI_TYPE_INTEGER;
param_arg.Integer.Value = arg;
param_args.Count = 1;
param_args.Pointer = ¶m_arg;
rv = AcpiEvaluateObject(handle, path, ¶m_args, NULL);
return rv;
}
/*
* acpidisp_match_node_with_address:
*
* Walker to find the ACPI node having a given address (_ADR).
*/
static ACPI_STATUS
acpidisp_match_node_with_address(ACPI_HANDLE handle, UINT32 level,
void *context, void **retval)
{
ACPI_INTEGER *addrp = context;
ACPI_HANDLE *hdlp = retval;
ACPI_STATUS rv;
ACPI_INTEGER val;
rv = acpi_eval_integer(handle, "_ADR", &val);
if (ACPI_FAILURE(rv))
return AE_OK; /* Continue walk */
if (val != *addrp)
return AE_OK; /* Continue walk */
/* Match found */
if (*hdlp != NULL)
/* Already found a match */
return AE_ALREADY_EXISTS; /* Stop walk with error */
*hdlp = handle;
return AE_OK; /* Continue walk to check unicity */
}
/*
* acpidisp_find_pci_device:
*
* Finds a PCI device in the ACPI name space. The PCI device to look for
* is given as a triple (bus, device, function). The argument handlep
* points to a location where the ACPI handle of the device will be written
* if the device is (uniquely) found.
*
* There is no straightforward way to match PCI devices. This function
* looks for an ACPI device that (1) is a descendant of the ACPI device
* that corresponds to the given PCI bus, and (2) has an ACPI address
* (_ADR) that is equal to: (device << 16) | function. The return status
* is either:
* - AE_NOT_FOUND if no such device was found.
* - AE_OK if one and only one such device was found.
* - AE_ALREADY_EXISTS if multiple such devices were found.
*/
static ACPI_STATUS
acpidisp_find_pci_device(u_int bus, u_int device, u_int function,
ACPI_HANDLE *handlep)
{
ACPI_STATUS rv;
ACPI_INTEGER addr;
ACPI_HANDLE hdl_bus, hdl;
hdl_bus = NULL;
rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 100,
acpidisp_match_pci_bus, &bus, &hdl_bus);
if (ACPI_FAILURE(rv)) {
aprint_error("acpidisp_find_pci_device: "
"failure in ACPI namespace walk: %s\n",
AcpiFormatException(rv));
return rv;
}
if (hdl_bus == NULL) {
aprint_debug("acpidisp_find_pci_device: "
"couldn't find ACPI device for PCI bus: %u\n",
bus);
return AE_NOT_FOUND;
}
addr = (device << 16) | function;
/*
* We walk the ACPI sub-tree of hdl_bus to find the device with address
* equal to addr. Since AcpiWalkNamespace does not walk the given ACPI
* handle (only its children and their descendents), we first apply the
* walker function to hdl_bus.
*/
hdl = NULL;
acpidisp_match_node_with_address(hdl_bus, 0, &addr, &hdl);
rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl_bus, 100,
acpidisp_match_node_with_address, &addr, &hdl);
if (ACPI_FAILURE(rv)) {
aprint_error("acpidisp_find_pci_device: "
"failure in ACPI namespace walk: %s\n",
AcpiFormatException(rv));
return rv;
}
if (hdl == NULL) {
aprint_debug("acpidisp_find_pci_device: "
"couldn't find ACPI device for PCI: bus %u dev %u fun %u\n",
bus, device, function);
return AE_NOT_FOUND;
}
*handlep = hdl;
return AE_OK;
}
/*
* acpidisp_match_pci_bus:
*
* Walker to find the ACPI node corresponding to a given PCI bus number.
*/
static ACPI_STATUS
acpidisp_match_pci_bus(ACPI_HANDLE handle, UINT32 level,
void *context, void **retval)
{
u_int *busp = context;
ACPI_HANDLE *hdlp = retval;
ACPI_STATUS rv;
ACPI_INTEGER bbn;
ACPI_HANDLE hdl;
ACPI_BUFFER buf;
ACPI_DEVICE_INFO *info;
bool id_match;
/*
* Check whether this is a PCI bus from the device's HID and CIDs.
*/
id_match = false;
/* Get device info */
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
rv = AcpiGetObjectInfo(handle, &buf);
if (ACPI_SUCCESS(rv)) {
info = buf.Pointer;
id_match = acpi_match_hid(info, acpidisp_pci_bus_ids);
AcpiOsFree(buf.Pointer);
}
if (! id_match)
return AE_OK; /* Continue walk */
/*
* We found a PCI bus, let us check whether it is the right number
*/
bbn = 0; /* bus number is 0 by default */
if (ACPI_SUCCESS(AcpiGetHandle(handle, "_BBN", &hdl))) {
if (ACPI_FAILURE(acpi_eval_integer(handle, "_BBN", &bbn)))
return AE_OK; /* Continue walk */
}
if (bbn != *busp)
return AE_OK; /* Continue walk */
/* Match found */
if (*hdlp != NULL)
/* Already found a match */
return AE_ALREADY_EXISTS; /* Stop walk with error */
*hdlp = handle;
return AE_OK; /* Continue walk to check unicity */
}
Index: conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.924
diff -u -r1.924 files
--- conf/files 15 Oct 2008 06:51:20 -0000 1.924
+++ conf/files 9 Dec 2008 18:10:38 -0000
@@ -1018,6 +1018,9 @@
define drm {}
include dev/drm/files.drm
+# Attribute used by ACPI Display to attach to pci display devices (vga)
+define pcidisplaybus { }
+
# Definitions for wscons
# device attributes: display, display with emulator, keyboard, and mouse
#
@@ -1045,7 +1048,7 @@
defflag opt_vga.h VGA_CONSOLE_ATI_BROKEN_FONTSEL
defflag opt_vga.h VGA_RASTERCONSOLE
defflag opt_vga.h VGA_RESET
-device vga: displaydev, wsemuldisplaydev, pcdisplayops, drm
+device vga: displaydev, wsemuldisplaydev, pcdisplayops, drm, pcidisplaybus
file dev/ic/vga.c vga & !vga_rasterconsole needs-flag
file dev/ic/vga_raster.c vga_rasterconsole needs-flag
file dev/ic/vga_subr.c vga | vga_rasterconsole
Index: dev/acpi/files.acpi
===================================================================
RCS file: /cvsroot/src/sys/dev/acpi/files.acpi,v
retrieving revision 1.52
diff -u -r1.52 files.acpi
--- dev/acpi/files.acpi 18 May 2008 22:05:59 -0000 1.52
+++ dev/acpi/files.acpi 9 Dec 2008 18:10:38 -0000
@@ -52,9 +52,12 @@
file dev/acpi/acpi_bat.c acpibat
# ACPI Display Device
-device acpidisplay
-attach acpidisplay at acpinodebus
-file dev/acpi/acpi_display.c acpidisplay
+device acpidisp
+attach acpidisp at acpinodebus with acpidisp_acpi
+# TODO: tell autoconf(9) that the following device depends on acpi.
+# This device will not match/attach if acpi is not built (NACPI = 0).
+attach acpidisp at pcidisplaybus with acpidisp_pci
+file dev/acpi/acpi_display.c acpidisp
# ACPI Thermal Zone
device acpitz: sysmon_envsys
@@ -141,3 +144,13 @@
device acpidalb
attach acpidalb at acpinodebus
file dev/acpi/dalb_acpi.c acpidalb
+
+# Internal LCD Backlight in Fujitsu Lifebooks
+device fujbl
+attach fujbl at acpinodebus
+file dev/acpi/fujbl_acpi.c fujbl
+
+# Hotkey Button in Fujitsu Lifebooks
+device fujhk
+attach fujhk at acpinodebus
+file dev/acpi/fujhk_acpi.c fujhk
Index: dev/pci/vga_pci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/vga_pci.c,v
retrieving revision 1.44
diff -u -r1.44 vga_pci.c
--- dev/pci/vga_pci.c 3 Aug 2008 02:12:22 -0000 1.44
+++ dev/pci/vga_pci.c 9 Dec 2008 18:10:38 -0000
@@ -251,6 +251,9 @@
if (!pmf_device_register(self, NULL, vga_pci_resume))
aprint_error_dev(self, "couldn't establish power handler\n");
config_found_ia(self, "drm", aux, vga_drm_print);
+
+ /* Configure PCI display subdevices (e.g. ACPI Display) */
+ config_found_ia(self, "pcidisplaybus", aux, NULL);
}
static int
/* $Id: fujbl_acpi.c 1020 2008-12-11 15:29:13Z sutre $ */
/*
* Internal LCD Backlight Driver for Fujitsu Lifebooks.
*
* The Fujitsu FUJ02B1 device provides methods to control the backlight level:
* - GBLL and/or GBLS to get the value of the current backlight level.
* - SBLL and/or SBL2 to set the backlight level to some given value.
* - RBLL to get the number of backlight levels.
*
* We assume that valid backlight levels range from 0 to n-1 where n is the
* number of backlight levels returned by the method RBLL.
*
* The driver does not listen to notification messages sent by the FUJ02B1
* device, but it listens to PMF brightness events and changes the backlight
* level accordingly. Such PMF brightness events would typically be sent by the
* acpidisplay driver in response to brightness notifications on ACPI display
* output devices.
*
* Other methods are provided by the FUJ02B1 device (to control the internal
* mouse and/or audio volume), but this driver does not use them.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$Id: fujbl_acpi.c 1020 2008-12-11 15:29:13Z sutre $");
#include <sys/inttypes.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <dev/acpi/acpivar.h>
#include <dev/sysmon/sysmonvar.h>
struct fujbl_softc {
struct acpi_devnode *sc_node; /* our ACPI devnode */
uint32_t sc_nlevels; /* number of backlight levels */
uint32_t sc_level; /* current backlight level */
};
/*
* We use sc_nlevels to check for validity of a desired backlight level before
* calling SBLL/SBL2. This check might be unnecessary (it is indeed unnecessary
* on the Lifebook P7120), but we keep it for safety.
*
* We store the current backlight level into sc_level at suspend and restore the
* backlight level at resume. Without these suspend/resume hooks, the internal
* LCD screen remains black (no backlight) after resume on the Lifebook P7120.
*/
static const char * const fujbl_ids[] = {
"FUJ02B1",
NULL
};
static int fujbl_match(device_t, struct cfdata *, void *);
static void fujbl_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(fujbl, sizeof(struct fujbl_softc),
fujbl_match, fujbl_attach, NULL, NULL);
static bool fujbl_suspend(device_t PMF_FN_PROTO);
static bool fujbl_resume(device_t PMF_FN_PROTO);
static void fujbl_init(device_t);
static void fujbl_brightness_up(device_t);
static void fujbl_brightness_down(device_t);
static ACPI_STATUS fujbl_get_level(device_t, ACPI_INTEGER *);
static ACPI_STATUS fujbl_set_level(device_t, ACPI_INTEGER);
static ACPI_STATUS fujbl_acpi_eval_set_integer(ACPI_HANDLE, const char *,
ACPI_INTEGER);
static int
fujbl_match(device_t parent, struct cfdata *match, void *aux)
{
struct acpi_attach_args *aa = aux;
if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
return 0;
return acpi_match_hid(aa->aa_node->ad_devinfo, fujbl_ids);
}
static void
fujbl_attach(device_t parent, device_t self, void *aux)
{
struct fujbl_softc *sc = device_private(self);
struct acpi_attach_args *aa = aux;
aprint_naive("\n");
aprint_normal(": Fujitsu Backlight\n");
sc->sc_node = aa->aa_node;
sc->sc_nlevels = 0xffffffff;
fujbl_init(self);
if (!pmf_device_register(self, fujbl_suspend, fujbl_resume))
aprint_error_dev(self, "couldn't establish power handler\n");
if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
fujbl_brightness_up, true))
aprint_error_dev(self, "couldn't establish power handler\n");
if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
fujbl_brightness_down, true))
aprint_error_dev(self, "couldn't establish power handler\n");
}
static void
fujbl_init(device_t dv)
{
struct fujbl_softc *sc = device_private(dv);
ACPI_STATUS rv;
ACPI_INTEGER val;
rv = acpi_eval_integer(sc->sc_node->ad_handle, "RBLL", &val);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(dv, "failed to evaluate RBLL: %s\n",
AcpiFormatException(rv));
return ;
}
sc->sc_nlevels = val;
}
static ACPI_STATUS
fujbl_get_level(device_t dv, ACPI_INTEGER *valp)
{
struct fujbl_softc *sc = device_private(dv);
ACPI_STATUS rv;
rv = acpi_eval_integer(sc->sc_node->ad_handle, "GBLL", valp);
if (ACPI_FAILURE(rv))
rv = acpi_eval_integer(sc->sc_node->ad_handle, "GBLS", valp);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(dv,
"failed to evaluate GBLL or GBLS: %s\n",
AcpiFormatException(rv));
}
/* Get rid of the notification bit */
*valp = *valp & 0x7fffffff;
return rv;
}
static ACPI_STATUS
fujbl_set_level(device_t dv, ACPI_INTEGER val)
{
struct fujbl_softc *sc = device_private(dv);
ACPI_STATUS rv;
rv = fujbl_acpi_eval_set_integer(sc->sc_node->ad_handle, "SBLL", val);
if (ACPI_FAILURE(rv))
rv = fujbl_acpi_eval_set_integer(sc->sc_node->ad_handle, "SBL2",
val);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(dv,
"failed to evaluate SBLL or SBL2: %s\n",
AcpiFormatException(rv));
}
return rv;
}
static void
fujbl_brightness_up(device_t dv)
{
struct fujbl_softc *sc = device_private(dv);
ACPI_INTEGER val;
if (ACPI_FAILURE(fujbl_get_level(dv, &val)))
return ;
if (val >= sc->sc_nlevels - 1)
return ; /* Already at max backlight level */
if (ACPI_FAILURE(fujbl_set_level(dv, val+1)))
return ;
}
static void
fujbl_brightness_down(device_t dv)
{
ACPI_INTEGER val;
if (ACPI_FAILURE(fujbl_get_level(dv, &val)))
return ;
if (val <= 0)
return ; /* Already at min backlight level */
if (ACPI_FAILURE(fujbl_set_level(dv, val-1)))
return ;
}
static bool
fujbl_suspend(device_t dv PMF_FN_ARGS)
{
struct fujbl_softc *sc = device_private(dv);
ACPI_INTEGER val;
if (ACPI_SUCCESS(fujbl_get_level(dv, &val)))
sc->sc_level = val;
else
sc->sc_level = 0xffffffff;
return true;
}
static bool
fujbl_resume(device_t dv PMF_FN_ARGS)
{
struct fujbl_softc *sc = device_private(dv);
if (sc->sc_level != 0xffffffff)
fujbl_set_level(dv, sc->sc_level);
return true;
}
/******************************************************************************
* ACPI Utility Routines.
******************************************************************************/
static ACPI_STATUS
fujbl_acpi_eval_set_integer(ACPI_HANDLE handle, const char *path,
ACPI_INTEGER arg)
{
ACPI_STATUS rv;
ACPI_OBJECT param_arg;
ACPI_OBJECT_LIST param_args;
if (handle == NULL)
handle = ACPI_ROOT_OBJECT;
param_arg.Type = ACPI_TYPE_INTEGER;
param_arg.Integer.Value = arg;
param_args.Count = 1;
param_args.Pointer = ¶m_arg;
rv = AcpiEvaluateObject(handle, path, ¶m_args, NULL);
return rv;
}
/* $Id: fujhk_acpi.c 1013 2008-12-06 18:07:34Z sutre $ */
/*
* Hotkey Button Driver for Fujitsu Lifebooks.
*
* This driver currently only supports 4 hotkey buttons.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$Id: fujhk_acpi.c 1013 2008-12-06 18:07:34Z sutre $");
#include <sys/inttypes.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <dev/acpi/acpivar.h>
#include <dev/sysmon/sysmonvar.h>
/* Notification code (only one type of notifications) */
#define FUJHK_ACPI_NOTIFY 0x80
/* Number and names of hotkey buttons */
#define FUJHK_NHOTKEYS 4
/* Names of hotkey buttons */
static const char * const fujhk_hotkey_names[] = {
"fujitsu-hotkey-0",
"fujitsu-hotkey-1",
"fujitsu-hotkey-2",
"fujitsu-hotkey-3"
};
struct fujhk_softc {
struct acpi_devnode *sc_node;
/* our ACPI devnode */
struct sysmon_pswitch sc_smpsw[FUJHK_NHOTKEYS];
/* our sysmon glue */
};
static const char * const fujhk_ids[] = {
"FUJ02E3",
NULL
};
static int fujhk_match(device_t, struct cfdata *, void *);
static void fujhk_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(fujhk, sizeof(struct fujhk_softc),
fujhk_match, fujhk_attach, NULL, NULL);
static void fujhk_button_event(void *);
static void fujhk_notify_handler(ACPI_HANDLE, UINT32, void *);
static int
fujhk_match(device_t parent, struct cfdata *match, void *aux)
{
struct acpi_attach_args *aa = aux;
if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
return 0;
return acpi_match_hid(aa->aa_node->ad_devinfo, fujhk_ids);
}
static void
fujhk_attach(device_t parent, device_t self, void *aux)
{
struct fujhk_softc *sc = device_private(self);
struct acpi_attach_args *aa = aux;
ACPI_STATUS rv;
int i;
aprint_naive("\n");
aprint_normal(": Fujitsu Hotkeys\n");
sc->sc_node = aa->aa_node;
/* Register power switches with sysmon */
for (i = 0 ; i < FUJHK_NHOTKEYS ; i++) {
sc->sc_smpsw[i].smpsw_name = fujhk_hotkey_names[i];
sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY;
if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) {
aprint_error_dev(self,
"unable to register with sysmon\n");
return ;
}
}
/* Install notify handler for events */
rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
ACPI_DEVICE_NOTIFY, fujhk_notify_handler, self);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(self,
"unable to register DEVICE NOTIFY handler: %s\n",
AcpiFormatException(rv));
}
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
}
static void
fujhk_button_event(void *arg)
{
device_t dv = arg;
struct fujhk_softc *sc = device_private(dv);
ACPI_STATUS rv;
ACPI_INTEGER girb;
int i;
rv = acpi_eval_integer(sc->sc_node->ad_handle, "GIRB", &girb);
if (ACPI_FAILURE(rv)) {
aprint_error_dev(dv, "failed to evaluate GIRB: %s\n",
AcpiFormatException(rv));
return ;
}
if (girb == 0x40000000)
/* Hotkey button release event, nothing to do */
return ;
if ((girb & 0xfffffffc) != 0x40000410) {
/* This should not happen */
aprint_error_dev(dv,
"unexpected GIRB value: 0x%" PRIx64 "\n", girb);
return ;
}
/* We're good: GIRB of the form 0x4000041N with N in [0, 3] */
i = girb & 0x3;
sysmon_pswitch_event(&sc->sc_smpsw[i], PSWITCH_EVENT_PRESSED);
}
static void
fujhk_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
{
device_t dv = context;
ACPI_STATUS rv;
if (notify != FUJHK_ACPI_NOTIFY) {
aprint_error_dev(dv,
"received unknown notify message: 0x%" PRIx32 "\n", notify);
return ;
}
rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, fujhk_button_event, dv);
if (ACPI_FAILURE(rv))
aprint_error_dev(dv,
"unable to queue button event callback: %s\n",
AcpiFormatException(rv));
}
Home |
Main Index |
Thread Index |
Old Index