diff options
author | Dave Airlie <airlied@redhat.com> | 2010-03-01 16:22:38 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-03-01 16:22:38 +1000 |
commit | 1c62233508ef7104f8a78e571fdf5c72d0dc0200 (patch) | |
tree | 31e19cbff5c1080d3015d20b24dd43ee95f4ed8c /drivers/gpu/drm/radeon | |
parent | 6d9c13513661c1991bf5f4e6e1363c721292d819 (diff) | |
parent | 6a9ee8af344e3bd7dbd61e67037096cdf7f83289 (diff) |
Merge branch 'gpu-switcher' of /ssd/git//linux-2.6 into drm-next-stage
* 'gpu-switcher' of /ssd/git//linux-2.6:
vga_switcheroo: initial implementation (v15)
fb: for framebuffer handover don't exit the loop early.
Conflicts:
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/radeon.h
Diffstat (limited to 'drivers/gpu/drm/radeon')
-rw-r--r-- | drivers/gpu/drm/radeon/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_atpx_handler.c | 258 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_bios.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_device.c | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_fb.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_kms.c | 3 |
9 files changed, 357 insertions, 6 deletions
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index b46f115d1c25..0a4d526e4f44 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -60,7 +60,7 @@ radeon-y += radeon_device.o radeon_kms.o \ rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \ - evergreen.o + evergreen.o radeon_atpx_handler.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 2434d553bbbc..ad9d55f94398 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -119,6 +119,10 @@ struct radeon_device; /* * BIOS. */ +#define ATRM_BIOS_PAGE 4096 + +bool radeon_atrm_supported(struct pci_dev *pdev); +int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len); bool radeon_get_bios(struct radeon_device *rdev); @@ -957,6 +961,8 @@ struct radeon_device { int audio_bits_per_sample; uint8_t audio_status_bits; uint8_t audio_category_code; + + bool powered_down; }; int radeon_device_init(struct radeon_device *rdev, @@ -1167,6 +1173,8 @@ extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base); extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); +extern int radeon_resume_kms(struct drm_device *dev); +extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state); /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ struct r100_mc_save { diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c new file mode 100644 index 000000000000..0ae52f19071d --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2010 Red Hat Inc. + * Author : Dave Airlie <airlied@redhat.com> + * + * Licensed under GPLv2 + * + * ATPX support for both Intel/ATI + */ + +#include <linux/vga_switcheroo.h> +#include <acpi/acpi.h> +#include <acpi/acpi_bus.h> +#include <linux/pci.h> + +#define ATPX_VERSION 0 +#define ATPX_GPU_PWR 2 +#define ATPX_MUX_SELECT 3 + +#define ATPX_INTEGRATED 0 +#define ATPX_DISCRETE 1 + +#define ATPX_MUX_IGD 0 +#define ATPX_MUX_DISCRETE 1 + +static struct radeon_atpx_priv { + bool atpx_detected; + /* handle for device - and atpx */ + acpi_handle dhandle; + acpi_handle atpx_handle; + acpi_handle atrm_handle; +} radeon_atpx_priv; + +/* retrieve the ROM in 4k blocks */ +static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, + int offset, int len) +{ + acpi_status status; + union acpi_object atrm_arg_elements[2], *obj; + struct acpi_object_list atrm_arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; + + atrm_arg.count = 2; + atrm_arg.pointer = &atrm_arg_elements[0]; + + atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; + atrm_arg_elements[0].integer.value = offset; + + atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; + atrm_arg_elements[1].integer.value = len; + + status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); + if (ACPI_FAILURE(status)) { + printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); + return -ENODEV; + } + + obj = (union acpi_object *)buffer.pointer; + memcpy(bios+offset, obj->buffer.pointer, len); + kfree(buffer.pointer); + return len; +} + +bool radeon_atrm_supported(struct pci_dev *pdev) +{ + /* get the discrete ROM only via ATRM */ + if (!radeon_atpx_priv.atpx_detected) + return false; + + if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) + return false; + return true; +} + + +int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len) +{ + return radeon_atrm_call(radeon_atpx_priv.atrm_handle, bios, offset, len); +} + +static int radeon_atpx_get_version(acpi_handle handle) +{ + acpi_status status; + union acpi_object atpx_arg_elements[2], *obj; + struct acpi_object_list atpx_arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + atpx_arg.count = 2; + atpx_arg.pointer = &atpx_arg_elements[0]; + + atpx_arg_elements[0].type = ACPI_TYPE_INTEGER; + atpx_arg_elements[0].integer.value = ATPX_VERSION; + + atpx_arg_elements[1].type = ACPI_TYPE_INTEGER; + atpx_arg_elements[1].integer.value = ATPX_VERSION; + + status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer); + if (ACPI_FAILURE(status)) { + printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status)); + return -ENOSYS; + } + obj = (union acpi_object *)buffer.pointer; + if (obj && (obj->type == ACPI_TYPE_BUFFER)) + printk(KERN_INFO "radeon atpx: version is %d\n", *((u8 *)(obj->buffer.pointer) + 2)); + kfree(buffer.pointer); + return 0; +} + +static int radeon_atpx_execute(acpi_handle handle, int cmd_id, u16 value) +{ + acpi_status status; + union acpi_object atpx_arg_elements[2]; + struct acpi_object_list atpx_arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + uint8_t buf[4] = {0}; + + if (!handle) + return -EINVAL; + + atpx_arg.count = 2; + atpx_arg.pointer = &atpx_arg_elements[0]; + + atpx_arg_elements[0].type = ACPI_TYPE_INTEGER; + atpx_arg_elements[0].integer.value = cmd_id; + + buf[2] = value & 0xff; + buf[3] = (value >> 8) & 0xff; + + atpx_arg_elements[1].type = ACPI_TYPE_BUFFER; + atpx_arg_elements[1].buffer.length = 4; + atpx_arg_elements[1].buffer.pointer = buf; + + status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer); + if (ACPI_FAILURE(status)) { + printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status)); + return -ENOSYS; + } + kfree(buffer.pointer); + + return 0; +} + +static int radeon_atpx_set_discrete_state(acpi_handle handle, int state) +{ + return radeon_atpx_execute(handle, ATPX_GPU_PWR, state); +} + +static int radeon_atpx_switch_mux(acpi_handle handle, int mux_id) +{ + return radeon_atpx_execute(handle, ATPX_MUX_SELECT, mux_id); +} + + +static int radeon_atpx_switchto(enum vga_switcheroo_client_id id) +{ + if (id == VGA_SWITCHEROO_IGD) + radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 0); + else + radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 1); + return 0; +} + +static int radeon_atpx_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + /* on w500 ACPI can't change intel gpu state */ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + radeon_atpx_set_discrete_state(radeon_atpx_priv.atpx_handle, state); + return 0; +} + +static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev) +{ + acpi_handle dhandle, atpx_handle, atrm_handle; + acpi_status status; + + dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); + if (!dhandle) + return false; + + status = acpi_get_handle(dhandle, "ATPX", &atpx_handle); + if (ACPI_FAILURE(status)) + return false; + + status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); + if (ACPI_FAILURE(status)) + return false; + + radeon_atpx_priv.dhandle = dhandle; + radeon_atpx_priv.atpx_handle = atpx_handle; + radeon_atpx_priv.atrm_handle = atrm_handle; + return true; +} + +static int radeon_atpx_init(void) +{ + /* set up the ATPX handle */ + + radeon_atpx_get_version(radeon_atpx_priv.atpx_handle); + return 0; +} + +static int radeon_atpx_get_client_id(struct pci_dev *pdev) +{ + if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) + return VGA_SWITCHEROO_IGD; + else + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler radeon_atpx_handler = { + .switchto = radeon_atpx_switchto, + .power_state = radeon_atpx_power_state, + .init = radeon_atpx_init, + .get_client_id = radeon_atpx_get_client_id, +}; + +static bool radeon_atpx_detect(void) +{ + char acpi_method_name[255] = { 0 }; + struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; + struct pci_dev *pdev = NULL; + bool has_atpx = false; + int vga_count = 0; + + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { + vga_count++; + + has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true); + } + + if (has_atpx && vga_count == 2) { + acpi_get_name(radeon_atpx_priv.atpx_handle, ACPI_FULL_PATHNAME, &buffer); + printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n", + acpi_method_name); + radeon_atpx_priv.atpx_detected = true; + return true; + } + return false; +} + +void radeon_register_atpx_handler(void) +{ + bool r; + + /* detect if we have any ATPX + 2 VGA in the system */ + r = radeon_atpx_detect(); + if (!r) + return; + + vga_switcheroo_register_handler(&radeon_atpx_handler); +} + +void radeon_unregister_atpx_handler(void) +{ + vga_switcheroo_unregister_handler(); +} diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 26856ed8d972..557240460526 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -30,6 +30,7 @@ #include "radeon.h" #include "atom.h" +#include <linux/vga_switcheroo.h> /* * BIOS. */ @@ -62,7 +63,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev) iounmap(bios); return false; } - memcpy(rdev->bios, bios, size); + memcpy_fromio(rdev->bios, bios, size); iounmap(bios); return true; } @@ -93,6 +94,38 @@ static bool radeon_read_bios(struct radeon_device *rdev) return true; } +/* ATRM is used to get the BIOS on the discrete cards in + * dual-gpu systems. + */ +static bool radeon_atrm_get_bios(struct radeon_device *rdev) +{ + int ret; + int size = 64 * 1024; + int i; + + if (!radeon_atrm_supported(rdev->pdev)) + return false; + + rdev->bios = kmalloc(size, GFP_KERNEL); + if (!rdev->bios) { + DRM_ERROR("Unable to allocate bios\n"); + return false; + } + + for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { + ret = radeon_atrm_get_bios_chunk(rdev->bios, + (i * ATRM_BIOS_PAGE), + ATRM_BIOS_PAGE); + if (ret <= 0) + break; + } + + if (i == 0 || rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { + kfree(rdev->bios); + return false; + } + return true; +} static bool r700_read_disabled_bios(struct radeon_device *rdev) { uint32_t viph_control; @@ -388,16 +421,16 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev) return legacy_read_disabled_bios(rdev); } + bool radeon_get_bios(struct radeon_device *rdev) { bool r; uint16_t tmp; - if (rdev->flags & RADEON_IS_IGP) { + r = radeon_atrm_get_bios(rdev); + if (r == false) r = igp_read_bios_from_vram(rdev); - if (r == false) - r = radeon_read_bios(rdev); - } else + if (r == false) r = radeon_read_bios(rdev); if (r == false) { r = radeon_read_disabled_bios(rdev); @@ -408,6 +441,7 @@ bool radeon_get_bios(struct radeon_device *rdev) return false; } if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { + printk("BIOS signature incorrect %x %x\n", rdev->bios[0], rdev->bios[1]); goto free_bios; } diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 91a9b966238e..e28e4ed5f720 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -30,6 +30,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/radeon_drm.h> #include <linux/vgaarb.h> +#include <linux/vga_switcheroo.h> #include "radeon_reg.h" #include "radeon.h" #include "radeon_asic.h" @@ -655,6 +656,36 @@ void radeon_check_arguments(struct radeon_device *rdev) } } +static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct radeon_device *rdev = dev->dev_private; + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; + if (state == VGA_SWITCHEROO_ON) { + printk(KERN_INFO "radeon: switched on\n"); + /* don't suspend or resume card normally */ + rdev->powered_down = false; + radeon_resume_kms(dev); + } else { + printk(KERN_INFO "radeon: switched off\n"); + radeon_suspend_kms(dev, pmm); + /* don't suspend or resume card normally */ + rdev->powered_down = true; + } +} + +static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + bool can_switch; + + spin_lock(&dev->count_lock); + can_switch = (dev->open_count == 0); + spin_unlock(&dev->count_lock); + return can_switch; +} + + int radeon_device_init(struct radeon_device *rdev, struct drm_device *ddev, struct pci_dev *pdev, @@ -737,6 +768,9 @@ int radeon_device_init(struct radeon_device *rdev, /* this will fail for cards that aren't VGA class devices, just * ignore it */ vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); + vga_switcheroo_register_client(rdev->pdev, + radeon_switcheroo_set_state, + radeon_switcheroo_can_switch); r = radeon_init(rdev); if (r) @@ -768,6 +802,7 @@ void radeon_device_fini(struct radeon_device *rdev) rdev->shutdown = true; radeon_fini(rdev); destroy_workqueue(rdev->wq); + vga_switcheroo_unregister_client(rdev->pdev); vga_client_register(rdev->pdev, NULL, NULL, NULL); iounmap(rdev->rmmio); rdev->rmmio = NULL; @@ -791,6 +826,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) } rdev = dev->dev_private; + if (rdev->powered_down) + return 0; /* unpin the front buffers */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); @@ -836,6 +873,9 @@ int radeon_resume_kms(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; + if (rdev->powered_down) + return 0; + acquire_console_sem(); pci_set_power_state(dev->pdev, PCI_D0); pci_restore_state(dev->pdev); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index be99d4e55a34..f6456e02d1fe 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -343,6 +343,7 @@ static int __init radeon_init(void) driver = &kms_driver; driver->driver_features |= DRIVER_MODESET; driver->num_ioctls = radeon_max_kms_ioctl; + radeon_register_atpx_handler(); } /* if the vga console setting is enabled still * let modprobe override it */ @@ -352,6 +353,7 @@ static int __init radeon_init(void) static void __exit radeon_exit(void) { drm_exit(driver); + radeon_unregister_atpx_handler(); } module_init(radeon_init); diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index cf911859eac2..4fe16461bb1b 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h @@ -462,6 +462,9 @@ extern void r600_blit_swap(struct drm_device *dev, int sx, int sy, int dx, int dy, int w, int h, int src_pitch, int dst_pitch, int cpp); +/* atpx handler */ +void radeon_register_atpx_handler(void); +void radeon_unregister_atpx_handler(void); /* Flags for stats.boxes */ #define RADEON_BOX_DMA_IDLE 0x1 diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index cda112cc7a6c..8fccbf29235e 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -39,6 +39,8 @@ #include "drm_fb_helper.h" +#include <linux/vga_switcheroo.h> + struct radeon_fb_device { struct drm_fb_helper helper; struct radeon_framebuffer *rfb; @@ -286,6 +288,7 @@ int radeonfb_create(struct drm_device *dev, rfbdev->rdev = rdev; mutex_unlock(&rdev->ddev->struct_mutex); + vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); return 0; out_unref: diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 3c5002ea3f8f..20ec276e7596 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -30,6 +30,8 @@ #include "radeon.h" #include "radeon_drm.h" +#include <linux/vga_switcheroo.h> + int radeon_driver_unload_kms(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; @@ -136,6 +138,7 @@ int radeon_driver_firstopen_kms(struct drm_device *dev) void radeon_driver_lastclose_kms(struct drm_device *dev) { + vga_switcheroo_process_delayed_switch(); } int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) |