diff options
author | Mattia Dongili <malattia@linux.it> | 2012-12-20 17:21:10 -0500 |
---|---|---|
committer | Matthew Garrett <matthew.garrett@nebula.com> | 2013-02-27 08:30:36 -0500 |
commit | 3a7abcd809e75b3588dd4a6529238e2a9b009c9a (patch) | |
tree | 44b9cdafdb810ea5dfad3b458d99c44a0f856633 /drivers/platform | |
parent | 3ec1c3983d73b1e3d4cfd72afab94c34eceafe8a (diff) |
sony-laptop: allow reading the status of gfx switch
Signed-off-by: Mattia Dongili <malattia@linux.it>
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 121 |
1 files changed, 112 insertions, 9 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 0bee6c2f8864..46c71f49a6b4 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -158,6 +158,11 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd); | |||
158 | static int sony_nc_lid_resume_setup(struct platform_device *pd); | 158 | static int sony_nc_lid_resume_setup(struct platform_device *pd); |
159 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); | 159 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); |
160 | 160 | ||
161 | static int sony_nc_gfx_switch_setup(struct platform_device *pd, | ||
162 | unsigned int handle); | ||
163 | static void sony_nc_gfx_switch_cleanup(struct platform_device *pd); | ||
164 | static int __sony_nc_gfx_switch_status_get(void); | ||
165 | |||
161 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd); | 166 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd); |
162 | static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); | 167 | static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); |
163 | 168 | ||
@@ -1241,17 +1246,13 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) | |||
1241 | /* Hybrid GFX switching */ | 1246 | /* Hybrid GFX switching */ |
1242 | sony_call_snc_handle(handle, 0x0000, &result); | 1247 | sony_call_snc_handle(handle, 0x0000, &result); |
1243 | dprintk("GFX switch event received (reason: %s)\n", | 1248 | dprintk("GFX switch event received (reason: %s)\n", |
1244 | (result & 0x01) ? | 1249 | (result == 0x1) ? "switch change" : |
1245 | "switch change" : "unknown"); | 1250 | (result == 0x2) ? "output switch" : |
1246 | 1251 | (result == 0x3) ? "output switch" : | |
1247 | /* verify the switch state | 1252 | ""); |
1248 | * 1: discrete GFX | ||
1249 | * 0: integrated GFX | ||
1250 | */ | ||
1251 | sony_call_snc_handle(handle, 0x0100, &result); | ||
1252 | 1253 | ||
1253 | ev_type = GFX_SWITCH; | 1254 | ev_type = GFX_SWITCH; |
1254 | real_ev = result & 0xff; | 1255 | real_ev = __sony_nc_gfx_switch_status_get(); |
1255 | break; | 1256 | break; |
1256 | 1257 | ||
1257 | default: | 1258 | default: |
@@ -1350,6 +1351,13 @@ static void sony_nc_function_setup(struct acpi_device *device, | |||
1350 | pr_err("couldn't set up thermal profile function (%d)\n", | 1351 | pr_err("couldn't set up thermal profile function (%d)\n", |
1351 | result); | 1352 | result); |
1352 | break; | 1353 | break; |
1354 | case 0x0128: | ||
1355 | case 0x0146: | ||
1356 | result = sony_nc_gfx_switch_setup(pf_device, handle); | ||
1357 | if (result) | ||
1358 | pr_err("couldn't set up GFX Switch status (%d)\n", | ||
1359 | result); | ||
1360 | break; | ||
1353 | case 0x0131: | 1361 | case 0x0131: |
1354 | result = sony_nc_highspeed_charging_setup(pf_device); | 1362 | result = sony_nc_highspeed_charging_setup(pf_device); |
1355 | if (result) | 1363 | if (result) |
@@ -1414,6 +1422,10 @@ static void sony_nc_function_cleanup(struct platform_device *pd) | |||
1414 | case 0x0122: | 1422 | case 0x0122: |
1415 | sony_nc_thermal_cleanup(pd); | 1423 | sony_nc_thermal_cleanup(pd); |
1416 | break; | 1424 | break; |
1425 | case 0x0128: | ||
1426 | case 0x0146: | ||
1427 | sony_nc_gfx_switch_cleanup(pd); | ||
1428 | break; | ||
1417 | case 0x0131: | 1429 | case 0x0131: |
1418 | sony_nc_highspeed_charging_cleanup(pd); | 1430 | sony_nc_highspeed_charging_cleanup(pd); |
1419 | break; | 1431 | break; |
@@ -2355,6 +2367,97 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd) | |||
2355 | } | 2367 | } |
2356 | } | 2368 | } |
2357 | 2369 | ||
2370 | /* GFX Switch position */ | ||
2371 | enum gfx_switch { | ||
2372 | SPEED, | ||
2373 | STAMINA, | ||
2374 | AUTO | ||
2375 | }; | ||
2376 | struct snc_gfx_switch_control { | ||
2377 | struct device_attribute attr; | ||
2378 | unsigned int handle; | ||
2379 | }; | ||
2380 | static struct snc_gfx_switch_control *gfxs_ctl; | ||
2381 | |||
2382 | /* returns 0 for speed, 1 for stamina */ | ||
2383 | static int __sony_nc_gfx_switch_status_get(void) | ||
2384 | { | ||
2385 | unsigned int result; | ||
2386 | |||
2387 | if (sony_call_snc_handle(gfxs_ctl->handle, 0x0100, &result)) | ||
2388 | return -EIO; | ||
2389 | |||
2390 | switch (gfxs_ctl->handle) { | ||
2391 | case 0x0146: | ||
2392 | /* 1: discrete GFX (speed) | ||
2393 | * 0: integrated GFX (stamina) | ||
2394 | */ | ||
2395 | return result & 0x1 ? SPEED : STAMINA; | ||
2396 | break; | ||
2397 | case 0x0128: | ||
2398 | /* it's a more elaborated bitmask, for now: | ||
2399 | * 2: integrated GFX (stamina) | ||
2400 | * 0: discrete GFX (speed) | ||
2401 | */ | ||
2402 | dprintk("GFX Status: 0x%x\n", result); | ||
2403 | return result & 0x80 ? AUTO : | ||
2404 | result & 0x02 ? STAMINA : SPEED; | ||
2405 | break; | ||
2406 | } | ||
2407 | return -EINVAL; | ||
2408 | } | ||
2409 | |||
2410 | static ssize_t sony_nc_gfx_switch_status_show(struct device *dev, | ||
2411 | struct device_attribute *attr, | ||
2412 | char *buffer) | ||
2413 | { | ||
2414 | int pos = __sony_nc_gfx_switch_status_get(); | ||
2415 | |||
2416 | if (pos < 0) | ||
2417 | return pos; | ||
2418 | |||
2419 | return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina"); | ||
2420 | } | ||
2421 | |||
2422 | static int sony_nc_gfx_switch_setup(struct platform_device *pd, | ||
2423 | unsigned int handle) | ||
2424 | { | ||
2425 | unsigned int result; | ||
2426 | |||
2427 | gfxs_ctl = kzalloc(sizeof(struct snc_gfx_switch_control), GFP_KERNEL); | ||
2428 | if (!gfxs_ctl) | ||
2429 | return -ENOMEM; | ||
2430 | |||
2431 | gfxs_ctl->handle = handle; | ||
2432 | |||
2433 | sysfs_attr_init(&gfxs_ctl->attr.attr); | ||
2434 | gfxs_ctl->attr.attr.name = "gfx_switch_status"; | ||
2435 | gfxs_ctl->attr.attr.mode = S_IRUGO; | ||
2436 | gfxs_ctl->attr.show = sony_nc_gfx_switch_status_show; | ||
2437 | |||
2438 | result = device_create_file(&pd->dev, &gfxs_ctl->attr); | ||
2439 | if (result) | ||
2440 | goto gfxerror; | ||
2441 | |||
2442 | return 0; | ||
2443 | |||
2444 | gfxerror: | ||
2445 | kfree(gfxs_ctl); | ||
2446 | gfxs_ctl = NULL; | ||
2447 | |||
2448 | return result; | ||
2449 | } | ||
2450 | |||
2451 | static void sony_nc_gfx_switch_cleanup(struct platform_device *pd) | ||
2452 | { | ||
2453 | if (gfxs_ctl) { | ||
2454 | device_remove_file(&pd->dev, &gfxs_ctl->attr); | ||
2455 | |||
2456 | kfree(gfxs_ctl); | ||
2457 | gfxs_ctl = NULL; | ||
2458 | } | ||
2459 | } | ||
2460 | |||
2358 | /* High speed charging function */ | 2461 | /* High speed charging function */ |
2359 | static struct device_attribute *hsc_handle; | 2462 | static struct device_attribute *hsc_handle; |
2360 | 2463 | ||