1 #include <linux/pci.h> 2 #include <linux/acpi.h> 3 #include <linux/slab.h> 4 #include <acpi/acpi_drivers.h> 5 #include <acpi/acpi_bus.h> 6 #include <acpi/video.h> 7 8 #include "drmP.h" 9 #include "drm.h" 10 #include "drm_sarea.h" 11 #include "drm_crtc_helper.h" 12 #include "nouveau_drv.h" 13 #include "nouveau_drm.h" 14 #include "nv50_display.h" 15 #include "nouveau_connector.h" 16 17 #include <linux/vga_switcheroo.h> 18 19 #define NOUVEAU_DSM_SUPPORTED 0x00 20 #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 21 22 #define NOUVEAU_DSM_ACTIVE 0x01 23 #define NOUVEAU_DSM_ACTIVE_QUERY 0x00 24 25 #define NOUVEAU_DSM_LED 0x02 26 #define NOUVEAU_DSM_LED_STATE 0x00 27 #define NOUVEAU_DSM_LED_OFF 0x10 28 #define NOUVEAU_DSM_LED_STAMINA 0x11 29 #define NOUVEAU_DSM_LED_SPEED 0x12 30 31 #define NOUVEAU_DSM_POWER 0x03 32 #define NOUVEAU_DSM_POWER_STATE 0x00 33 #define NOUVEAU_DSM_POWER_SPEED 0x01 34 #define NOUVEAU_DSM_POWER_STAMINA 0x02 35 36 static struct nouveau_dsm_priv { 37 bool dsm_detected; 38 acpi_handle dhandle; 39 acpi_handle rom_handle; 40 } nouveau_dsm_priv; 41 42 static const char nouveau_dsm_muid[] = { 43 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 44 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 45 }; 46 47 static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) 48 { 49 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 50 struct acpi_object_list input; 51 union acpi_object params[4]; 52 union acpi_object *obj; 53 int err; 54 55 input.count = 4; 56 input.pointer = params; 57 params[0].type = ACPI_TYPE_BUFFER; 58 params[0].buffer.length = sizeof(nouveau_dsm_muid); 59 params[0].buffer.pointer = (char *)nouveau_dsm_muid; 60 params[1].type = ACPI_TYPE_INTEGER; 61 params[1].integer.value = 0x00000102; 62 params[2].type = ACPI_TYPE_INTEGER; 63 params[2].integer.value = func; 64 params[3].type = ACPI_TYPE_INTEGER; 65 params[3].integer.value = arg; 66 67 err = acpi_evaluate_object(handle, "_DSM", &input, &output); 68 if (err) { 69 printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); 70 return err; 71 } 72 73 obj = (union acpi_object *)output.pointer; 74 75 if (obj->type == ACPI_TYPE_INTEGER) 76 if (obj->integer.value == 0x80000002) 77 return -ENODEV; 78 79 if (obj->type == ACPI_TYPE_BUFFER) { 80 if (obj->buffer.length == 4 && result) { 81 *result = 0; 82 *result |= obj->buffer.pointer[0]; 83 *result |= (obj->buffer.pointer[1] << 8); 84 *result |= (obj->buffer.pointer[2] << 16); 85 *result |= (obj->buffer.pointer[3] << 24); 86 } 87 } 88 89 kfree(output.pointer); 90 return 0; 91 } 92 93 static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 94 { 95 return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); 96 } 97 98 static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 99 { 100 int arg; 101 if (state == VGA_SWITCHEROO_ON) 102 arg = NOUVEAU_DSM_POWER_SPEED; 103 else 104 arg = NOUVEAU_DSM_POWER_STAMINA; 105 nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); 106 return 0; 107 } 108 109 static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 110 { 111 if (id == VGA_SWITCHEROO_IGD) 112 return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 113 else 114 return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); 115 } 116 117 static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 118 enum vga_switcheroo_state state) 119 { 120 if (id == VGA_SWITCHEROO_IGD) 121 return 0; 122 123 return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); 124 } 125 126 static int nouveau_dsm_init(void) 127 { 128 return 0; 129 } 130 131 static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 132 { 133 /* easy option one - intel vendor ID means Integrated */ 134 if (pdev->vendor == PCI_VENDOR_ID_INTEL) 135 return VGA_SWITCHEROO_IGD; 136 137 /* is this device on Bus 0? - this may need improving */ 138 if (pdev->bus->number == 0) 139 return VGA_SWITCHEROO_IGD; 140 141 return VGA_SWITCHEROO_DIS; 142 } 143 144 static struct vga_switcheroo_handler nouveau_dsm_handler = { 145 .switchto = nouveau_dsm_switchto, 146 .power_state = nouveau_dsm_power_state, 147 .init = nouveau_dsm_init, 148 .get_client_id = nouveau_dsm_get_client_id, 149 }; 150 151 static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) 152 { 153 acpi_handle dhandle, nvidia_handle; 154 acpi_status status; 155 int ret; 156 uint32_t result; 157 158 dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 159 if (!dhandle) 160 return false; 161 162 status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); 163 if (ACPI_FAILURE(status)) { 164 return false; 165 } 166 167 ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, 168 NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); 169 if (ret < 0) 170 return false; 171 172 nouveau_dsm_priv.dhandle = dhandle; 173 return true; 174 } 175 176 static bool nouveau_dsm_detect(void) 177 { 178 char acpi_method_name[255] = { 0 }; 179 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 180 struct pci_dev *pdev = NULL; 181 int has_dsm = 0; 182 int vga_count = 0; 183 184 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 185 vga_count++; 186 187 has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); 188 } 189 190 if (vga_count == 2 && has_dsm) { 191 acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); 192 printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 193 acpi_method_name); 194 nouveau_dsm_priv.dsm_detected = true; 195 return true; 196 } 197 return false; 198 } 199 200 void nouveau_register_dsm_handler(void) 201 { 202 bool r; 203 204 r = nouveau_dsm_detect(); 205 if (!r) 206 return; 207 208 vga_switcheroo_register_handler(&nouveau_dsm_handler); 209 } 210 211 void nouveau_unregister_dsm_handler(void) 212 { 213 vga_switcheroo_unregister_handler(); 214 } 215 216 /* retrieve the ROM in 4k blocks */ 217 static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, 218 int offset, int len) 219 { 220 acpi_status status; 221 union acpi_object rom_arg_elements[2], *obj; 222 struct acpi_object_list rom_arg; 223 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 224 225 rom_arg.count = 2; 226 rom_arg.pointer = &rom_arg_elements[0]; 227 228 rom_arg_elements[0].type = ACPI_TYPE_INTEGER; 229 rom_arg_elements[0].integer.value = offset; 230 231 rom_arg_elements[1].type = ACPI_TYPE_INTEGER; 232 rom_arg_elements[1].integer.value = len; 233 234 status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); 235 if (ACPI_FAILURE(status)) { 236 printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status)); 237 return -ENODEV; 238 } 239 obj = (union acpi_object *)buffer.pointer; 240 memcpy(bios+offset, obj->buffer.pointer, len); 241 kfree(buffer.pointer); 242 return len; 243 } 244 245 bool nouveau_acpi_rom_supported(struct pci_dev *pdev) 246 { 247 acpi_status status; 248 acpi_handle dhandle, rom_handle; 249 250 if (!nouveau_dsm_priv.dsm_detected) 251 return false; 252 253 dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 254 if (!dhandle) 255 return false; 256 257 status = acpi_get_handle(dhandle, "_ROM", &rom_handle); 258 if (ACPI_FAILURE(status)) 259 return false; 260 261 nouveau_dsm_priv.rom_handle = rom_handle; 262 return true; 263 } 264 265 int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) 266 { 267 return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); 268 } 269 270 int 271 nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) 272 { 273 struct nouveau_connector *nv_connector = nouveau_connector(connector); 274 struct acpi_device *acpidev; 275 acpi_handle handle; 276 int type, ret; 277 void *edid; 278 279 switch (connector->connector_type) { 280 case DRM_MODE_CONNECTOR_LVDS: 281 case DRM_MODE_CONNECTOR_eDP: 282 type = ACPI_VIDEO_DISPLAY_LCD; 283 break; 284 default: 285 return -EINVAL; 286 } 287 288 handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); 289 if (!handle) 290 return -ENODEV; 291 292 ret = acpi_bus_get_device(handle, &acpidev); 293 if (ret) 294 return -ENODEV; 295 296 ret = acpi_video_get_edid(acpidev, type, -1, &edid); 297 if (ret < 0) 298 return ret; 299 300 nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL); 301 return 0; 302 } 303