Path: blob/master/drivers/gpu/drm/nouveau/nouveau_acpi.c
15112 views
#include <linux/pci.h>1#include <linux/acpi.h>2#include <linux/slab.h>3#include <acpi/acpi_drivers.h>4#include <acpi/acpi_bus.h>5#include <acpi/video.h>6#include <acpi/acpi.h>7#include <linux/mxm-wmi.h>89#include "drmP.h"10#include "drm.h"11#include "drm_sarea.h"12#include "drm_crtc_helper.h"13#include "nouveau_drv.h"14#include "nouveau_drm.h"15#include "nv50_display.h"16#include "nouveau_connector.h"1718#include <linux/vga_switcheroo.h>1920#define NOUVEAU_DSM_SUPPORTED 0x0021#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x002223#define NOUVEAU_DSM_ACTIVE 0x0124#define NOUVEAU_DSM_ACTIVE_QUERY 0x002526#define NOUVEAU_DSM_LED 0x0227#define NOUVEAU_DSM_LED_STATE 0x0028#define NOUVEAU_DSM_LED_OFF 0x1029#define NOUVEAU_DSM_LED_STAMINA 0x1130#define NOUVEAU_DSM_LED_SPEED 0x123132#define NOUVEAU_DSM_POWER 0x0333#define NOUVEAU_DSM_POWER_STATE 0x0034#define NOUVEAU_DSM_POWER_SPEED 0x0135#define NOUVEAU_DSM_POWER_STAMINA 0x023637static struct nouveau_dsm_priv {38bool dsm_detected;39bool optimus_detected;40acpi_handle dhandle;41acpi_handle rom_handle;42} nouveau_dsm_priv;4344#define NOUVEAU_DSM_HAS_MUX 0x145#define NOUVEAU_DSM_HAS_OPT 0x24647static const char nouveau_dsm_muid[] = {480xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,490xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,50};5152static const char nouveau_op_dsm_muid[] = {530xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47,540xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,55};5657static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)58{59struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };60struct acpi_object_list input;61union acpi_object params[4];62union acpi_object *obj;63int err;6465input.count = 4;66input.pointer = params;67params[0].type = ACPI_TYPE_BUFFER;68params[0].buffer.length = sizeof(nouveau_op_dsm_muid);69params[0].buffer.pointer = (char *)nouveau_op_dsm_muid;70params[1].type = ACPI_TYPE_INTEGER;71params[1].integer.value = 0x00000100;72params[2].type = ACPI_TYPE_INTEGER;73params[2].integer.value = func;74params[3].type = ACPI_TYPE_BUFFER;75params[3].buffer.length = 0;7677err = acpi_evaluate_object(handle, "_DSM", &input, &output);78if (err) {79printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);80return err;81}8283obj = (union acpi_object *)output.pointer;8485if (obj->type == ACPI_TYPE_INTEGER)86if (obj->integer.value == 0x80000002) {87return -ENODEV;88}8990if (obj->type == ACPI_TYPE_BUFFER) {91if (obj->buffer.length == 4 && result) {92*result = 0;93*result |= obj->buffer.pointer[0];94*result |= (obj->buffer.pointer[1] << 8);95*result |= (obj->buffer.pointer[2] << 16);96*result |= (obj->buffer.pointer[3] << 24);97}98}99100kfree(output.pointer);101return 0;102}103104static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)105{106struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };107struct acpi_object_list input;108union acpi_object params[4];109union acpi_object *obj;110int err;111112input.count = 4;113input.pointer = params;114params[0].type = ACPI_TYPE_BUFFER;115params[0].buffer.length = sizeof(nouveau_dsm_muid);116params[0].buffer.pointer = (char *)nouveau_dsm_muid;117params[1].type = ACPI_TYPE_INTEGER;118params[1].integer.value = 0x00000102;119params[2].type = ACPI_TYPE_INTEGER;120params[2].integer.value = func;121params[3].type = ACPI_TYPE_INTEGER;122params[3].integer.value = arg;123124err = acpi_evaluate_object(handle, "_DSM", &input, &output);125if (err) {126printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);127return err;128}129130obj = (union acpi_object *)output.pointer;131132if (obj->type == ACPI_TYPE_INTEGER)133if (obj->integer.value == 0x80000002)134return -ENODEV;135136if (obj->type == ACPI_TYPE_BUFFER) {137if (obj->buffer.length == 4 && result) {138*result = 0;139*result |= obj->buffer.pointer[0];140*result |= (obj->buffer.pointer[1] << 8);141*result |= (obj->buffer.pointer[2] << 16);142*result |= (obj->buffer.pointer[3] << 24);143}144}145146kfree(output.pointer);147return 0;148}149150static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)151{152mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);153mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);154return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);155}156157static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)158{159int arg;160if (state == VGA_SWITCHEROO_ON)161arg = NOUVEAU_DSM_POWER_SPEED;162else163arg = NOUVEAU_DSM_POWER_STAMINA;164nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL);165return 0;166}167168static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)169{170if (id == VGA_SWITCHEROO_IGD)171return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);172else173return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);174}175176static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,177enum vga_switcheroo_state state)178{179if (id == VGA_SWITCHEROO_IGD)180return 0;181182return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);183}184185static int nouveau_dsm_init(void)186{187return 0;188}189190static int nouveau_dsm_get_client_id(struct pci_dev *pdev)191{192/* easy option one - intel vendor ID means Integrated */193if (pdev->vendor == PCI_VENDOR_ID_INTEL)194return VGA_SWITCHEROO_IGD;195196/* is this device on Bus 0? - this may need improving */197if (pdev->bus->number == 0)198return VGA_SWITCHEROO_IGD;199200return VGA_SWITCHEROO_DIS;201}202203static struct vga_switcheroo_handler nouveau_dsm_handler = {204.switchto = nouveau_dsm_switchto,205.power_state = nouveau_dsm_power_state,206.init = nouveau_dsm_init,207.get_client_id = nouveau_dsm_get_client_id,208};209210static int nouveau_dsm_pci_probe(struct pci_dev *pdev)211{212acpi_handle dhandle, nvidia_handle;213acpi_status status;214int ret, retval = 0;215uint32_t result;216217dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);218if (!dhandle)219return false;220221status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);222if (ACPI_FAILURE(status)) {223return false;224}225226ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED,227NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);228if (ret == 0)229retval |= NOUVEAU_DSM_HAS_MUX;230231ret = nouveau_optimus_dsm(dhandle, 0, 0, &result);232if (ret == 0)233retval |= NOUVEAU_DSM_HAS_OPT;234235if (retval)236nouveau_dsm_priv.dhandle = dhandle;237238return retval;239}240241static bool nouveau_dsm_detect(void)242{243char acpi_method_name[255] = { 0 };244struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};245struct pci_dev *pdev = NULL;246int has_dsm = 0;247int has_optimus;248int vga_count = 0;249bool guid_valid;250int retval;251bool ret = false;252253/* lookup the MXM GUID */254guid_valid = mxm_wmi_supported();255256if (guid_valid)257printk("MXM: GUID detected in BIOS\n");258259/* now do DSM detection */260while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {261vga_count++;262263retval = nouveau_dsm_pci_probe(pdev);264if (retval & NOUVEAU_DSM_HAS_MUX)265has_dsm |= 1;266if (retval & NOUVEAU_DSM_HAS_OPT)267has_optimus = 1;268}269270if (vga_count == 2 && has_dsm && guid_valid) {271acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);272printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",273acpi_method_name);274nouveau_dsm_priv.dsm_detected = true;275ret = true;276}277278if (has_optimus == 1)279nouveau_dsm_priv.optimus_detected = true;280281return ret;282}283284void nouveau_register_dsm_handler(void)285{286bool r;287288r = nouveau_dsm_detect();289if (!r)290return;291292vga_switcheroo_register_handler(&nouveau_dsm_handler);293}294295void nouveau_unregister_dsm_handler(void)296{297vga_switcheroo_unregister_handler();298}299300/* retrieve the ROM in 4k blocks */301static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,302int offset, int len)303{304acpi_status status;305union acpi_object rom_arg_elements[2], *obj;306struct acpi_object_list rom_arg;307struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};308309rom_arg.count = 2;310rom_arg.pointer = &rom_arg_elements[0];311312rom_arg_elements[0].type = ACPI_TYPE_INTEGER;313rom_arg_elements[0].integer.value = offset;314315rom_arg_elements[1].type = ACPI_TYPE_INTEGER;316rom_arg_elements[1].integer.value = len;317318status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);319if (ACPI_FAILURE(status)) {320printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));321return -ENODEV;322}323obj = (union acpi_object *)buffer.pointer;324memcpy(bios+offset, obj->buffer.pointer, len);325kfree(buffer.pointer);326return len;327}328329bool nouveau_acpi_rom_supported(struct pci_dev *pdev)330{331acpi_status status;332acpi_handle dhandle, rom_handle;333334if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected)335return false;336337dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);338if (!dhandle)339return false;340341status = acpi_get_handle(dhandle, "_ROM", &rom_handle);342if (ACPI_FAILURE(status))343return false;344345nouveau_dsm_priv.rom_handle = rom_handle;346return true;347}348349int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)350{351return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);352}353354int355nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)356{357struct nouveau_connector *nv_connector = nouveau_connector(connector);358struct acpi_device *acpidev;359acpi_handle handle;360int type, ret;361void *edid;362363switch (connector->connector_type) {364case DRM_MODE_CONNECTOR_LVDS:365case DRM_MODE_CONNECTOR_eDP:366type = ACPI_VIDEO_DISPLAY_LCD;367break;368default:369return -EINVAL;370}371372handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);373if (!handle)374return -ENODEV;375376ret = acpi_bus_get_device(handle, &acpidev);377if (ret)378return -ENODEV;379380ret = acpi_video_get_edid(acpidev, type, -1, &edid);381if (ret < 0)382return ret;383384nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);385return 0;386}387388389