@@ -1457,7 +1457,8 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
acpi_video_device_enumerate(video);
acpi_video_device_rebind(video);
acpi_bus_generate_proc_event(device, event, 0);
- keycode = KEY_SWITCHVIDEOMODE;
+ if (!acpi_notifier_call_chain(device, event, 0))
+ keycode = KEY_SWITCHVIDEOMODE;
break;
case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
@@ -1479,7 +1480,8 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
break;
}
- if (event != ACPI_VIDEO_NOTIFY_SWITCH)
+ if (event != ACPI_VIDEO_NOTIFY_SWITCH ||
+ event != ACPI_VIDEO_NOTIFY_PROBE)
acpi_notifier_call_chain(device, event, 0);
if (keycode) {
@@ -3,6 +3,7 @@
#include <linux/slab.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
+#include <acpi/video.h>
#include "drmP.h"
#include "drm.h"
@@ -13,36 +14,150 @@
#include <linux/vga_switcheroo.h>
+struct atif_verify_output {
+ u16 size; /* structure size in bytes (includes size field) */
+ u16 version; /* version */
+ u32 notif_mask; /* supported notifications mask */
+ u32 func_bits; /* supported functions bit vector */
+} __attribute__((packed));
+
+static struct atif_verify_output *verify_output;
+
+struct atif_get_sysparams_output {
+ u16 size; /* structure size in bytes (includes size field) */
+ u32 valid_flags_mask; /* valid flags mask */
+ u32 flags; /* flags */
+ u8 notify_code; /* notify command code */
+} __attribute__((packed));
+
+static u8 notify_code;
+
/* Call the ATIF method
*
* Note: currently we discard the output
*/
-static int radeon_atif_call(acpi_handle handle)
+static int radeon_atif_call(acpi_handle handle, int function, struct acpi_buffer *params, struct acpi_buffer *output)
{
acpi_status status;
union acpi_object atif_arg_elements[2];
struct acpi_object_list atif_arg;
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *obj;
- atif_arg.count = 2;
+ atif_arg.count = 1;
atif_arg.pointer = &atif_arg_elements[0];
atif_arg_elements[0].type = ACPI_TYPE_INTEGER;
- atif_arg_elements[0].integer.value = ATIF_FUNCTION_VERIFY_INTERFACE;
- atif_arg_elements[1].type = ACPI_TYPE_INTEGER;
- atif_arg_elements[1].integer.value = 0;
+ atif_arg_elements[0].integer.value = function;
- status = acpi_evaluate_object(handle, "ATIF", &atif_arg, &buffer);
+ if (params) {
+ atif_arg.count = 2;
+ atif_arg_elements[1].type = ACPI_TYPE_BUFFER;
+ atif_arg_elements[1].buffer.length = params->length;
+ atif_arg_elements[1].buffer.pointer = params->pointer;
+ }
- /* Fail only if calling the method fails and ATIF is supported */
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- DRM_DEBUG_DRIVER("failed to evaluate ATIF got %s\n",
- acpi_format_exception(status));
- kfree(buffer.pointer);
+ status = acpi_evaluate_object(handle, "ATIF", &atif_arg, output);
+ if (ACPI_FAILURE(status))
return 1;
+
+ obj = (union acpi_object *) output->pointer;
+ if (!obj)
+ return 1;
+ else if (obj->type != ACPI_TYPE_BUFFER) {
+ kfree(obj);
+ return 1;
+ } else if (obj->buffer.length != 256) {
+ DRM_DEBUG_DRIVER("Unknown ATIF output buffer length %d\n", obj->buffer.length);
+ kfree(obj);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int radeon_atif_verify(acpi_handle handle)
+{
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct atif_verify_output *voutput;
+ union acpi_object *obj;
+ int ret;
+
+ /* evaluate the ATIF verify function */
+ ret = radeon_atif_call(handle, ATIF_FUNCTION_VERIFY_INTERFACE, NULL, &output);
+ if (ret) {
+ kfree(output.pointer);
+ return ret;
+ }
+
+ obj = (union acpi_object *) output.pointer;
+
+ voutput = (struct atif_verify_output *) obj->buffer.pointer;
+ verify_output = kzalloc(sizeof(struct atif_verify_output), GFP_KERNEL); /* TODO: kfree */
+ verify_output->size = voutput->size;
+ verify_output->version = voutput->version;
+ verify_output->notif_mask = voutput->notif_mask;
+ verify_output->func_bits = voutput->func_bits;
+
+ kfree(output.pointer);
+ return 0;
+}
+
+static int radeon_acpi_video_event(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ /* The only video events relevant to opregion are 0x81 or n. These indicate
+ either a docking event, lid switch or display switch request. In
+ Linux, these are handled by the dock, button and video drivers.
+ */
+
+ struct acpi_bus_event *event = data;
+ int ret = NOTIFY_BAD; /* Question: for fill acpi's blocking notifier chains */
+
+ if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0)
+ return NOTIFY_DONE;
+
+ if (event->type != notify_code)
+ return NOTIFY_DONE;
+
+ /* TODO: run anything should take care by radeon */
+
+ return ret;
+}
+
+static struct notifier_block radeon_acpi_notifier = {
+ .notifier_call = radeon_acpi_video_event,
+};
+
+static int radeon_atif_get_system_param(acpi_handle handle)
+{
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct atif_get_sysparams_output *params_output;
+ union acpi_object *obj;
+ u32 flags;
+ int ret;
+
+ /* evaluate the ATIF get system parameters function */
+ ret = radeon_atif_call(handle, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS, NULL, &output);
+ if (ret) {
+ kfree(output.pointer);
+ return ret;
}
- kfree(buffer.pointer);
+ obj = (union acpi_object *) output.pointer;
+
+ params_output = (struct atif_get_sysparams_output *) obj->buffer.pointer;
+ flags = params_output->flags;
+
+ if (flags) {
+ if (flags == 1)
+ notify_code = 0x81;
+ else if (flags == 2)
+ notify_code = params_output->notify_code;
+
+ register_acpi_notifier(&radeon_acpi_notifier);
+ }
+
+ kfree(output.pointer);
return 0;
}
@@ -59,11 +174,16 @@ int radeon_acpi_init(struct radeon_device *rdev)
if (!ASIC_IS_AVIVO(rdev) || !rdev->bios || !handle)
return 0;
- /* Call the ATIF method */
- ret = radeon_atif_call(handle);
+ /* Call the ATIF verify function */
+ ret = radeon_atif_verify(handle);
if (ret)
return ret;
+ if (verify_output->func_bits & ATIF_GET_SYSTEM_PARAMETERS_SUPPORTED)
+ ret = radeon_atif_get_system_param(handle);
+ if (ret)
+ return ret;
+
return 0;
}