├── nvidia-control ├── Makefile └── nvidia-control.c ├── TODO ├── dkms.conf ├── sys-files-documentation.txt ├── Makefile ├── README ├── README.np ├── test.c ├── nvidia_bl.c └── sony-laptop.c /nvidia-control/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += nvidia-control.o 2 | 3 | all: 4 | make -C /lib/modules/`uname -r`/build M=`pwd` 5 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Include the code from nvidia_bl into the sony_laptop module and 2 | register/unregister on demand the backlight devices (sony platform for 3 | intel, nvidia_bl for nvidia). 4 | 5 | Make the alt_rfkill behaviour auto-detectable depending on the present 6 | devices 7 | -------------------------------------------------------------------------------- /dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="sony-laptop-zseries" 2 | PACKAGE_VERSION="0.9np8pre1" 3 | CLEAN="make clean" 4 | MAKE[0]="make SYSSRC=$kernel_source_dir" 5 | BUILT_MODULE_NAME[0]="sony-laptop" 6 | DEST_MODULE_LOCATION[0]="/kernel/drivers/platform/x86/" 7 | # nvidia_bl support currently disabled 8 | #BUILT_MODULE_NAME[1]="nvidia_bl" 9 | #DEST_MODULE_LOCATION[1]="/kernel/../extra" 10 | AUTOINSTALL="yes" 11 | 12 | -------------------------------------------------------------------------------- /sys-files-documentation.txt: -------------------------------------------------------------------------------- 1 | The following files in /sys/devices/platform/sony-laptop 2 | allow controlling various aspects. 3 | 4 | The documentation here was created by Norbert Preining by reading the 5 | source, so it might be wrong and burn your computer ;-) 6 | 7 | lid_resume_control 8 | controls resume when lid is opened 9 | 0 do nothing 10 | 1 resume from S4 (disk) 11 | 2 resume from S3 (ram) 12 | 3 resume from S4 and S3 13 | 14 | odd_power 15 | controls the cdrom drive connection 16 | 0 disconnected (off) 17 | 1 connected (on) 18 | 19 | battery_care_limiter 20 | controls up to which level a battery is charged 21 | 0 no limit 22 | 1 80% 23 | 2 50% 24 | 25 | thermal_control 26 | controls the temperature/fan activity (AFAIU) 27 | 0 balanced 28 | 1 performance 29 | 2 silent 30 | 31 | thermal_profiles 32 | number of different profiles??? 33 | 34 | touchpad 35 | turns touchpad on or off 36 | 0 off 37 | 1 on 38 | 39 | wwanpower 40 | ??? 41 | 42 | bluetoothpower 43 | ??? 44 | 45 | power/* 46 | ??? 47 | 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += sony-laptop.o 2 | #obj-m += nvidia_bl.o 3 | obj-m += test.o 4 | 5 | KDIR := /lib/modules/$(shell uname -r) 6 | ifdef SYSSRC 7 | KERNEL_SOURCES := $(SYSSRC) 8 | else 9 | ifdef SYSINCLUDE 10 | KERNEL_SOURCES := $(SYSINCLUDE)/.. 11 | else 12 | KERNEL_SOURCES := $(shell test -d $(KDIR)/source && echo $(KDIR)/source || echo $(KDIR)/build) 13 | endif 14 | endif 15 | PWD := $(shell pwd) 16 | 17 | all: default 18 | 19 | default: 20 | $(MAKE) -C $(KERNEL_SOURCES) SUBDIRS=$(PWD) modules 21 | 22 | clean: 23 | $(MAKE) -C $(KERNEL_SOURCES) SUBDIRS=$(PWD) clean 24 | @rm -f modules.order 25 | @rm -f Module.markers 26 | @rm -f *~ 27 | 28 | install: 29 | mkdir -p $(KDIR)/updates/ 30 | cp sony-laptop.ko $(KDIR)/updates/ 31 | #cp nvidia_bl.ko $(KDIR)/updates/ 32 | depmod -a 33 | 34 | uninstall: 35 | rm $(KDIR)/updates/sony-laptop.ko 36 | #rm $(KDIR)/updates/nvidia_bl.ko 37 | rmmod sony-laptop 38 | #rmmod nvidia_bl 39 | depmod -a 40 | modprobe sony-laptop 41 | 42 | test: 43 | rmmod sony-laptop 44 | insmod sony-laptop.ko debug=1 45 | 46 | dumpparams: 47 | rmmod test 48 | insmod test.ko 49 | dmesg > dump_dmesg.log 50 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is an update to the sony-laptop kernel module for Z series laptop. This driver has been tested on a VGN-Z21VN/X. It can break other Vaio laptops. 2 | 3 | Disclaimer: This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 4 | 5 | = Howto build = 6 | 7 | make compile 8 | make test load the compiled module, check dmesg output 9 | make install installs to the system as update module 10 | make uninstall remove from the system, fallback to standard module 11 | make clean 12 | 13 | 14 | = Change Log = 15 | 0.1 16 | 2009/02/07 17 | Add support for switching on/off 3G module 18 | Initialize ECON variable (DSDT behaves different if !=0), maybe not necessary 19 | 20 | 0.2 21 | 2009/02/08 22 | Merged version from http://hanzubon.jp/tmp/VGN-Z90S/sony-laptop.c 23 | 24 | 0.3 25 | 2009/02/13 26 | Operating Linux in Stamina Mode ONLY 27 | WARNING! Switching to Speed Mode by booting into XP is NOT possible after having booted with this module version. 28 | Also loading this driver at runtime will break a running X server. 29 | Packaged as directory with Makefile 30 | 31 | For more information see 32 | http://www.basyskom.org/~eva/log_installation_vaio_z21vnx.html 33 | http://thinkfat.blogspot.com/ 34 | -------------------------------------------------------------------------------- /README.np: -------------------------------------------------------------------------------- 1 | 2 | This version of the sony-laptop module is the one distributed by 3 | Eva (see original README) plus the following changes: 4 | 5 | 0.9np8 6 | - no changes to the code since pre3 7 | 8 | 0.9np8pre3 9 | - merge patches from Marco Chiappero for largely improved support of Sony 10 | laptops http://www.absence.it/vaio-acpi/source/patches/ 11 | (cd power, kbd backlight, gforce,...) 12 | 13 | 0.9np8pre2 14 | - more 2.6.39-rc1 merge 15 | 16 | 0.9np8pre1 17 | - adapt to current 2.6.39rc round 18 | - include patches by Adam Hill for newer laptops 19 | 20 | 0.9np7: 21 | - incorporate changes from upstream module for backlight 22 | - incorporate use_kevent patch from Dmitry Torokhov 23 | 24 | 0.9np6: 25 | - release working only with 2.6.33 and above, backward compatibility 26 | with older kernels dropped. Please use 0.9np5 27 | - bring in line with kernel development, make all changes to kernel 28 | code governed by #ifdev SONY_ZSERIES to see easier where are 29 | changes. 30 | 31 | 0.9np5: 32 | - make alternative rfkill setup routine the default, as it works 33 | the same way on old BIOS. If there are problems using the 34 | modules parameter old_rfkill=1 reverts to old setup method. 35 | I will try to forward that to upstream, too. 36 | 37 | 0.9np4: 38 | - implement alternative rfkill setup routine to fix the rfkills on 39 | the zseries with Qualcomm WWAN (patch by vick ) 40 | 41 | 0.9np3: 42 | - add some Makefile code suggested by Raphael Gradenwitz 43 | - add a dkms.conf file 44 | - add nvidia_bl.c module source code from Mario Schwalbe 45 | it crashed my kernel (black screen, nothing working but Sysrq), but 46 | it might be worth investigating its features. 47 | - bring sony-laptop.c in line with upstream's code from the kernel, 48 | and guard all changes with #ifdef SONY_LAPTOP 49 | - change key handling to match what there is in the kernel, that is 50 | commit [master 85b4f88], and might break some functionality. 51 | 52 | 0.9np2: 53 | - include fix for initial state of rfkills is wrong, see wireless-dev list 54 | (from Alan Jenkins) 55 | - adapt to 2.6.32-rc, but still let it work on 2.6.31 56 | 57 | 0.9np1: 58 | - works with 2.6.31 (and probably not below) 59 | - include patches from Mattia sent to acpi list for 2.6.32 60 | 61 | 62 | INSTALLATION: 63 | ------------- 64 | If you have dkms installed you can do as root 65 | - unpack the module to /usr/src 66 | - call as root 67 | dkms add -m sony-laptop-zseries -v VERSION 68 | where VERSION is the current version you have unpacked. 69 | After that new modules should be build automatically with kernel upgrades. 70 | 71 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test.c 3 | * 4 | * Created on: Feb 22, 2009 5 | * Author: matze 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define SONY_VAIO 1 15 | 16 | #ifdef SONY_VAIO 17 | static const char *vars[] = { 18 | "\\SP2O", "\\SP1O", "\\IO1B", "\\IO1L", "\\IO2B", 19 | "\\IO2L", "\\IO3B", "\\IO3L", "\\MCHB", "\\MCHL", "\\EGPB", 20 | "\\EGPL", "\\DMIB", "\\DMIL", "\\IFPB", "\\IFPL", "\\PEBS", 21 | "\\PELN", "\\TTTB", "\\TTTL", "\\SMBS", "\\PBLK", "\\PMBS", 22 | "\\PMLN", "\\LVL2", "\\LVL3", "\\LVL4", "\\SMIP", "\\GPBS", 23 | "\\GPLN", "\\APCB", "\\APCL", "\\PM30", "\\SRCB", "\\SRCL", 24 | "\\SUSW", "\\ACPH", "\\ASSB", "\\AOTB", "\\AAXB", "\\PEHP", 25 | "\\SHPC", "\\PEPM", "\\PEER", "\\PECS", "\\ITKE", "\\TRTP", 26 | "\\TRTD", "\\TRTI", "\\TRTA", "\\GCDD", "\\DSTA", "\\DSLO", 27 | "\\DSLC", "\\PITS", "\\SBCS", "\\SALS", "\\LSSS", "\\PSSS", 28 | "\\SOOT", "\\ESCS", "\\PDBR", "\\SMBL", "\\STRP", "\\ECOK", 29 | "\\SSPS", 30 | "\\HPLG", "\\HPEJ", "\\HPLE", "\\HGAP", "\\HNCD", "\\HNCA", 31 | "\\HPND", "\\POVR", "\\HDAE", "\\HDHE", "\\ADAD", "\\SNGT", 32 | "\\HGDD", "\\GPIO.GP27", "\\GPIO.GP28", 33 | "\\_SB.PCI0.LPC.H8EC.HPWR", 34 | "\\_SB.PCI0.LPC.H8EC.HLMX", 35 | "\\_SB.PCI0.LPC.H8EC.HCMM", 36 | "\\_SB.PCI0.LPC.H8EC.DLED", 37 | "\\_SB.PCI0.LPC.H8EC.ILED", 38 | "\\_SB.PCI0.LPC.H8EC.SWPS", 39 | "\\_SB.PCI0.LPC.H8EC.HPOK", 40 | "\\_SB.PCI0.LPC.H8EC.HHPD", 41 | "\\_SB.PCI0.LPC.H8EC.DHPD", 42 | NULL, 43 | }; 44 | #else 45 | static const char *vars[] = { 46 | "\\SP1O", "\\IOCE", "\\IOCL", "\\IO1B", "\\IO1L", 47 | "\\IOEC", "\\IO4L", "\\IO5L", "\\IO2B", "\\IO2L", "\\IOPM", 48 | "\\IO3B", "\\IO3L", "\\SI1P", "\\MCHB", "\\MCHL", "\\EGPB", 49 | "\\EGPL", "\\DMIB", "\\DMIL", "\\PEBS", "\\PELN", "\\LAPB", 50 | "\\SMBS", "\\SMBL", 51 | NULL, 52 | }; 53 | #endif 54 | 55 | static int dump_dsdt_variable(const char *path) 56 | { 57 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 58 | union acpi_object *obj; 59 | int result; 60 | 61 | result = acpi_evaluate_object(NULL, (char*)path, NULL, &output); 62 | if (result) { 63 | printk("%s failed: %d\n", path, result); 64 | return -1; 65 | } 66 | 67 | obj = (union acpi_object*)output.pointer; 68 | printk("%s type %d ", path, obj->type); 69 | if (obj->type == ACPI_TYPE_PACKAGE) { 70 | int i; 71 | printk("returned package sized %d\n", obj->package.count); 72 | for (i = 0; i < obj->package.count; i++) 73 | printk("%d %08x\n", i, (int)obj->package.elements[i].integer.value); 74 | } else 75 | if (obj->type == ACPI_TYPE_INTEGER) { 76 | printk("int %08X\n", (int)obj->integer.value); 77 | } else 78 | if (obj->type == ACPI_TYPE_BUFFER) { 79 | int i; 80 | printk("returned buffer sized %d\n", obj->buffer.length); 81 | for (i = 0; i < obj->buffer.length; i++) 82 | printk("%02x", obj->buffer.pointer[i]); 83 | printk("\n"); 84 | } 85 | 86 | kfree(output.pointer); 87 | return 0; 88 | }; 89 | 90 | static void dump_acpi_vars(void) 91 | { 92 | int i = 0; 93 | 94 | while (vars[i]) { 95 | dump_dsdt_variable(vars[i]); 96 | i++; 97 | } 98 | } 99 | 100 | static int __init test_init(void) 101 | { 102 | dump_acpi_vars(); 103 | return 0; 104 | } 105 | 106 | static void __exit test_exit(void) 107 | { 108 | } 109 | 110 | module_init(test_init); 111 | module_exit(test_exit); 112 | -------------------------------------------------------------------------------- /nvidia-control/nvidia-control.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static int nvidia_dsm(struct pci_dev *dev, int func, int arg, int *result) 7 | { 8 | static char muid[] = { 9 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 10 | 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 11 | }; 12 | 13 | struct acpi_handle *handle; 14 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 15 | struct acpi_object_list input; 16 | union acpi_object params[4]; 17 | union acpi_object *obj; 18 | int err; 19 | 20 | handle = DEVICE_ACPI_HANDLE(&dev->dev); 21 | 22 | if (!handle) 23 | return -ENODEV; 24 | 25 | input.count = 4; 26 | input.pointer = params; 27 | params[0].type = ACPI_TYPE_BUFFER; 28 | params[0].buffer.length = sizeof(muid); 29 | params[0].buffer.pointer = (char *)muid; 30 | params[1].type = ACPI_TYPE_INTEGER; 31 | params[1].integer.value = 0x00000102; 32 | params[2].type = ACPI_TYPE_INTEGER; 33 | params[2].integer.value = func; 34 | params[3].type = ACPI_TYPE_INTEGER; 35 | params[3].integer.value = arg; 36 | 37 | err = acpi_evaluate_object(handle, "_DSM", &input, &output); 38 | if (err) { 39 | printk(KERN_ERR "nvidia-control: failed to evaluate _DSM: %d\n", 40 | err); 41 | return err; 42 | } 43 | 44 | obj = (union acpi_object *)output.pointer; 45 | 46 | if (obj->type == ACPI_TYPE_INTEGER) 47 | if (obj->integer.value == 0x80000002) 48 | return -ENODEV; 49 | 50 | if (obj->type == ACPI_TYPE_BUFFER) { 51 | if (obj->buffer.length == 4 && result) { 52 | *result = 0; 53 | *result |= obj->buffer.pointer[0]; 54 | *result |= (obj->buffer.pointer[1] << 8); 55 | *result |= (obj->buffer.pointer[2] << 16); 56 | *result |= (obj->buffer.pointer[3] << 24); 57 | } 58 | } 59 | 60 | kfree(output.pointer); 61 | return 0; 62 | } 63 | 64 | int nvidia_control_setup(struct pci_dev *dev) 65 | { 66 | int result; 67 | 68 | if (nvidia_dsm(dev, 1, 0, &result)) 69 | return -ENODEV; 70 | 71 | printk(KERN_INFO "nvidia-control: hardware status gave 0x%x\n", result); 72 | 73 | if (result &= 0x1) { /* Stamina mode - disable the external GPU */ 74 | nvidia_dsm(dev, 0x2, 0x11, NULL); 75 | nvidia_dsm(dev, 0x3, 0x02, NULL); 76 | } else { /* Ensure that the external GPU is enabled */ 77 | nvidia_dsm(dev, 0x2, 0x12, NULL); 78 | nvidia_dsm(dev, 0x3, 0x01, NULL); 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | int nvidia_control_probe(struct pci_dev *dev) 85 | { 86 | int support = 0; 87 | 88 | if (nvidia_dsm(dev, 0, 0, &support)) 89 | return -ENODEV; 90 | 91 | if (!support) 92 | return -ENODEV; 93 | 94 | return 0; 95 | } 96 | 97 | static int __init nvidia_control_init(void) 98 | { 99 | struct pci_dev *dev = NULL; 100 | while ((dev = pci_get_device(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, dev))) { 101 | if (dev->class == 0x30000) 102 | if (!nvidia_control_probe(dev)) 103 | nvidia_control_setup(dev); 104 | } 105 | return 0; 106 | } 107 | 108 | static void __exit nvidia_control_exit(void) 109 | { 110 | return; 111 | } 112 | 113 | static const struct pci_device_id pci_ids [] = { 114 | { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 115 | (PCI_CLASS_DISPLAY_VGA << 8), 0xffff00}, 116 | { }, 117 | }; 118 | 119 | MODULE_DEVICE_TABLE(pci, pci_ids); 120 | 121 | module_init(nvidia_control_init); 122 | module_exit(nvidia_control_exit); 123 | 124 | MODULE_LICENSE("GPL"); 125 | -------------------------------------------------------------------------------- /nvidia_bl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Backlight driver for Nvidia graphics adapters. 3 | * 4 | * Copyright (c) 2008-2009 Mario Schwalbe 5 | * Based on the mechanism dicovered by the author of NvClock: 6 | * Copyright (c) 2001-2009 Roderick Colenbrander 7 | * Site: http://nvclock.sourceforge.net 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | */ 14 | 15 | /* 16 | * Maybe the check against the subsystem vendor should be removed, 17 | * but there's no guarantee that the chip's smartdimmer signals 18 | * are actually connected to the display logic. Right now, these 19 | * are the supported (read connected) vendors according to NvClock. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /* Check the subsystem vendor ID to ignore unsupported devices */ 32 | #define CONFIG_NVIDIA_BL_CHECK_SUBSYSTEM_VENDOR 33 | 34 | /* Check DMI system information to ignore wrong devices */ 35 | //#define CONFIG_NVIDIA_BL_CHECK_DMI 36 | 37 | /* Check for the new backlight suspend/resume feature */ 38 | #if defined(BL_CORE_SUSPENDRESUME) 39 | #define USE_BACKLIGHT_SUSPEND 40 | /* Otherwise use a platform driver if PM is enabled */ 41 | #elif defined(CONFIG_PM) 42 | #define USE_PLATFORM_DRIVER 43 | #endif 44 | 45 | /* Register constants */ 46 | #define NV5X_PDISPLAY_OFFSET 0x00610000 47 | #define NV5X_PDISPLAY_SOR0_BRIGHTNESS 0x0000c084 48 | #define NV5X_PDIPSLAY_SOR0_BRIGHTNESS_CONTROL_ENABLED 0x80000000 49 | 50 | /* Driver private data structure */ 51 | struct driver_data { 52 | /* PCI region (BAR) the smartdimmer register is in */ 53 | unsigned bar; 54 | /* Register offset into this region */ 55 | unsigned long reg_offset; 56 | /* Register size in byte */ 57 | unsigned reg_size; 58 | 59 | /* Number of brightness levels supported */ 60 | unsigned levels; 61 | /* Backlight operations structure */ 62 | struct backlight_ops backlight_ops; 63 | 64 | /* The device we drive */ 65 | struct pci_dev *dev; 66 | /* Pointer to the mapped smartdimmer register */ 67 | /* volatile */ void __iomem *smartdimmer; 68 | }; 69 | 70 | /* Module parameters */ 71 | static int debug; 72 | module_param_named(debug, debug, int, 0644); 73 | MODULE_PARM_DESC(debug, "Set to one to enable debugging messages."); 74 | 75 | /* To be used with buggy tools that do not support more than 256 levels */ 76 | static int shift; 77 | module_param_named(shift, shift, int, 0644); 78 | MODULE_PARM_DESC(shift, "Shift the value by n bits to reduce the range."); 79 | 80 | /* 81 | * Implementation for NV4X chips 82 | * (NV40, NV41, NV43, NV44, NV46, NV47, NV49, NV4B, C51) 83 | */ 84 | static int nv4x_get_intensity(struct backlight_device *bd) 85 | { 86 | const struct driver_data *dd = bl_get_data(bd); 87 | unsigned short intensity = ioread16(dd->smartdimmer) & 0x1f; 88 | 89 | intensity >>= shift; 90 | 91 | if (debug) 92 | printk(KERN_DEBUG "nvidia_bl: read brightness of %d\n", 93 | intensity); 94 | 95 | return intensity; 96 | } 97 | 98 | static int nv4x_set_intensity(struct backlight_device *bd) 99 | { 100 | const struct driver_data *dd = bl_get_data(bd); 101 | unsigned intensity = bd->props.brightness; 102 | 103 | if (debug) 104 | printk(KERN_DEBUG "nvidia_bl: setting brightness to %d\n", 105 | intensity); 106 | 107 | intensity <<= shift; 108 | 109 | iowrite16((ioread16(dd->smartdimmer) & ~0x1f) | intensity, 110 | dd->smartdimmer); 111 | return 0; 112 | } 113 | 114 | static const struct driver_data nv4x_driver_data = { 115 | .bar = 0, 116 | .reg_offset = 0x15f2, 117 | .reg_size = 2, 118 | .levels = 32, 119 | .backlight_ops = { 120 | #ifdef USE_BACKLIGHT_SUSPEND 121 | .options = BL_CORE_SUSPENDRESUME, 122 | #endif 123 | .get_brightness = nv4x_get_intensity, 124 | .update_status = nv4x_set_intensity, 125 | } 126 | }; 127 | 128 | /* 129 | * Implementation for NV5X chips 130 | * (NV50, G84, G86, G92, G94, G96, GT200) 131 | */ 132 | static int nv5x_get_intensity(struct backlight_device *bd) 133 | { 134 | const struct driver_data *dd = bl_get_data(bd); 135 | unsigned intensity = ioread32(dd->smartdimmer) & 136 | ~NV5X_PDIPSLAY_SOR0_BRIGHTNESS_CONTROL_ENABLED; 137 | 138 | intensity >>= shift; 139 | 140 | if (debug) 141 | printk(KERN_DEBUG "nvidia_bl: read brightness of %d\n", 142 | intensity); 143 | 144 | return intensity; 145 | } 146 | 147 | static int nv5x_set_intensity(struct backlight_device *bd) 148 | { 149 | const struct driver_data *dd = bl_get_data(bd); 150 | unsigned intensity = bd->props.brightness; 151 | 152 | if (debug) 153 | printk(KERN_DEBUG "nvidia_bl: setting brightness to %d\n", 154 | intensity); 155 | 156 | intensity <<= shift; 157 | 158 | iowrite32(intensity | NV5X_PDIPSLAY_SOR0_BRIGHTNESS_CONTROL_ENABLED, 159 | dd->smartdimmer); 160 | return 0; 161 | } 162 | 163 | static const struct driver_data nv5x_driver_data = { 164 | .bar = 0, 165 | .reg_offset = NV5X_PDISPLAY_OFFSET + NV5X_PDISPLAY_SOR0_BRIGHTNESS, 166 | .reg_size = 4, 167 | .levels = 1024, 168 | .backlight_ops = { 169 | #ifdef USE_BACKLIGHT_SUSPEND 170 | .options = BL_CORE_SUSPENDRESUME, 171 | #endif 172 | .get_brightness = nv5x_get_intensity, 173 | .update_status = nv5x_set_intensity, 174 | } 175 | }; 176 | 177 | /* 178 | * Device matching. 179 | * The list of supported devices was primarily taken from NvClock, 180 | * but only contains the mobile chips. 181 | */ 182 | static DEFINE_PCI_DEVICE_TABLE(nvidia_bl_device_table) = { 183 | /* nVidia Geforce Go 7800GTX */ 184 | { PCI_VDEVICE(NVIDIA, 0x0099), (kernel_ulong_t)&nv4x_driver_data }, 185 | /* nVidia Geforce Go 6800 */ 186 | { PCI_VDEVICE(NVIDIA, 0x00c8), (kernel_ulong_t)&nv4x_driver_data }, 187 | /* nVidia Geforce Go 6800Ultra */ 188 | { PCI_VDEVICE(NVIDIA, 0x00c9), (kernel_ulong_t)&nv4x_driver_data }, 189 | /* nVidia QuadroFX Go 1400 */ 190 | { PCI_VDEVICE(NVIDIA, 0x00cc), (kernel_ulong_t)&nv4x_driver_data }, 191 | /* nVidia Geforce Go 6600 */ 192 | { PCI_VDEVICE(NVIDIA, 0x0144), (kernel_ulong_t)&nv4x_driver_data }, 193 | /* nVidia GeForce Go 6600TE/6200TE */ 194 | { PCI_VDEVICE(NVIDIA, 0x0146), (kernel_ulong_t)&nv4x_driver_data }, 195 | /* nVidia Geforce Go 6600 */ 196 | { PCI_VDEVICE(NVIDIA, 0x0148), (kernel_ulong_t)&nv4x_driver_data }, 197 | /* nVidia Geforce Go 6600GT */ 198 | { PCI_VDEVICE(NVIDIA, 0x0149), (kernel_ulong_t)&nv4x_driver_data }, 199 | /* nVidia Geforce Go 6200 */ 200 | { PCI_VDEVICE(NVIDIA, 0x0164), (kernel_ulong_t)&nv4x_driver_data }, 201 | /* nVidia Geforce Go 6400 */ 202 | { PCI_VDEVICE(NVIDIA, 0x0166), (kernel_ulong_t)&nv4x_driver_data }, 203 | /* nVidia Geforce Go 6200 */ 204 | { PCI_VDEVICE(NVIDIA, 0x0167), (kernel_ulong_t)&nv4x_driver_data }, 205 | /* nVidia Geforce Go 6400 */ 206 | { PCI_VDEVICE(NVIDIA, 0x0168), (kernel_ulong_t)&nv4x_driver_data }, 207 | /* nVidia Geforce Go 7300 */ 208 | { PCI_VDEVICE(NVIDIA, 0x01d7), (kernel_ulong_t)&nv4x_driver_data }, 209 | /* nVidia Geforce Go 7400 */ 210 | { PCI_VDEVICE(NVIDIA, 0x01d8), (kernel_ulong_t)&nv4x_driver_data }, 211 | /* nVidia Geforce Go 7400GS */ 212 | { PCI_VDEVICE(NVIDIA, 0x01d9), (kernel_ulong_t)&nv4x_driver_data }, 213 | /* nVidia Quadro NVS 110M */ 214 | { PCI_VDEVICE(NVIDIA, 0x01da), (kernel_ulong_t)&nv4x_driver_data }, 215 | /* nVidia Quadro NVS 120M */ 216 | { PCI_VDEVICE(NVIDIA, 0x01db), (kernel_ulong_t)&nv4x_driver_data }, 217 | /* nVidia QuadroFX 350M */ 218 | { PCI_VDEVICE(NVIDIA, 0x01dc), (kernel_ulong_t)&nv4x_driver_data }, 219 | /* nVidia Geforce 7500LE */ 220 | { PCI_VDEVICE(NVIDIA, 0x01dd), (kernel_ulong_t)&nv4x_driver_data }, 221 | /* NV44M */ 222 | { PCI_VDEVICE(NVIDIA, 0x0228), (kernel_ulong_t)&nv4x_driver_data }, 223 | /* nVidia Geforce Go 7950GTX */ 224 | { PCI_VDEVICE(NVIDIA, 0x0297), (kernel_ulong_t)&nv4x_driver_data }, 225 | /* nVidia Geforce Go 7900GS */ 226 | { PCI_VDEVICE(NVIDIA, 0x0298), (kernel_ulong_t)&nv4x_driver_data }, 227 | /* nVidia Geforce Go 7900GTX */ 228 | { PCI_VDEVICE(NVIDIA, 0x0299), (kernel_ulong_t)&nv4x_driver_data }, 229 | /* nVidia QuadroFX 2500M */ 230 | { PCI_VDEVICE(NVIDIA, 0x029a), (kernel_ulong_t)&nv4x_driver_data }, 231 | /* nVidia QuadroFX 1500M */ 232 | { PCI_VDEVICE(NVIDIA, 0x029b), (kernel_ulong_t)&nv4x_driver_data }, 233 | /* nVidia Geforce Go 7700 */ 234 | { PCI_VDEVICE(NVIDIA, 0x0397), (kernel_ulong_t)&nv4x_driver_data }, 235 | /* nVidia Geforce Go 7600 */ 236 | { PCI_VDEVICE(NVIDIA, 0x0398), (kernel_ulong_t)&nv4x_driver_data }, 237 | /* nVidia Geforce Go 7600GT */ 238 | { PCI_VDEVICE(NVIDIA, 0x0399), (kernel_ulong_t)&nv4x_driver_data }, 239 | /* nVidia Quadro NVS 300M */ 240 | { PCI_VDEVICE(NVIDIA, 0x039a), (kernel_ulong_t)&nv4x_driver_data }, 241 | /* nVidia Geforce Go 7900SE */ 242 | { PCI_VDEVICE(NVIDIA, 0x039b), (kernel_ulong_t)&nv4x_driver_data }, 243 | /* nVidia QuadroFX 550M */ 244 | { PCI_VDEVICE(NVIDIA, 0x039c), (kernel_ulong_t)&nv4x_driver_data }, 245 | /* nVidia Geforce 9500M GS */ 246 | { PCI_VDEVICE(NVIDIA, 0x0405), (kernel_ulong_t)&nv5x_driver_data }, 247 | /* nVidia Geforce NB9P-GE */ 248 | { PCI_VDEVICE(NVIDIA, 0x0406), (kernel_ulong_t)&nv5x_driver_data }, 249 | /* nVidia Geforce 8600M GT */ 250 | { PCI_VDEVICE(NVIDIA, 0x0407), (kernel_ulong_t)&nv5x_driver_data }, 251 | /* nVidia Geforce 8600M GTS */ 252 | { PCI_VDEVICE(NVIDIA, 0x0408), (kernel_ulong_t)&nv5x_driver_data }, 253 | /* nVidia Geforce 8700M GT */ 254 | { PCI_VDEVICE(NVIDIA, 0x0409), (kernel_ulong_t)&nv5x_driver_data }, 255 | /* nVidia Quadro NVS 370M */ 256 | { PCI_VDEVICE(NVIDIA, 0x040a), (kernel_ulong_t)&nv5x_driver_data }, 257 | /* nVidia Quadro NVS 320M */ 258 | { PCI_VDEVICE(NVIDIA, 0x040b), (kernel_ulong_t)&nv5x_driver_data }, 259 | /* nVidia QuadroFX 570M */ 260 | { PCI_VDEVICE(NVIDIA, 0x040c), (kernel_ulong_t)&nv5x_driver_data }, 261 | /* nVidia QuadroFX 1600M */ 262 | { PCI_VDEVICE(NVIDIA, 0x040d), (kernel_ulong_t)&nv5x_driver_data }, 263 | /* nVidia Geforce 8600M GS */ 264 | { PCI_VDEVICE(NVIDIA, 0x0425), (kernel_ulong_t)&nv5x_driver_data }, 265 | /* nVidia Geforce 8400M GT */ 266 | { PCI_VDEVICE(NVIDIA, 0x0426), (kernel_ulong_t)&nv5x_driver_data }, 267 | /* nVidia Geforce 8400M GS */ 268 | { PCI_VDEVICE(NVIDIA, 0x0427), (kernel_ulong_t)&nv5x_driver_data }, 269 | /* nVidia Geforce 8400M G */ 270 | { PCI_VDEVICE(NVIDIA, 0x0428), (kernel_ulong_t)&nv5x_driver_data }, 271 | /* nVidia Quadro NVS 140M */ 272 | { PCI_VDEVICE(NVIDIA, 0x0429), (kernel_ulong_t)&nv5x_driver_data }, 273 | /* nVidia Quadro NVS 130M */ 274 | { PCI_VDEVICE(NVIDIA, 0x042a), (kernel_ulong_t)&nv5x_driver_data }, 275 | /* nVidia Quadro NVS 135M */ 276 | { PCI_VDEVICE(NVIDIA, 0x042b), (kernel_ulong_t)&nv5x_driver_data }, 277 | /* nVidia Quadro FX 360M */ 278 | { PCI_VDEVICE(NVIDIA, 0x042d), (kernel_ulong_t)&nv5x_driver_data }, 279 | /* nVidia Geforce 9300M G */ 280 | { PCI_VDEVICE(NVIDIA, 0x042e), (kernel_ulong_t)&nv5x_driver_data }, 281 | /* nVidia Geforce 8800M GTS */ 282 | { PCI_VDEVICE(NVIDIA, 0x0609), (kernel_ulong_t)&nv5x_driver_data }, 283 | /* nVidia Geforce 8800M GTX */ 284 | { PCI_VDEVICE(NVIDIA, 0x060c), (kernel_ulong_t)&nv5x_driver_data }, 285 | /* nVidia QuadroFX 3600M */ 286 | { PCI_VDEVICE(NVIDIA, 0x061c), (kernel_ulong_t)&nv5x_driver_data }, 287 | /* nVidia Geforce 9600M GT */ 288 | { PCI_VDEVICE(NVIDIA, 0x0647), (kernel_ulong_t)&nv5x_driver_data }, 289 | /* nVidia Geforce 9600M GS */ 290 | { PCI_VDEVICE(NVIDIA, 0x0648), (kernel_ulong_t)&nv5x_driver_data }, 291 | /* nVidia Geforce 9600M GT */ 292 | { PCI_VDEVICE(NVIDIA, 0x0649), (kernel_ulong_t)&nv5x_driver_data }, 293 | /* nVidia Geforce 9500M G */ 294 | { PCI_VDEVICE(NVIDIA, 0x064b), (kernel_ulong_t)&nv5x_driver_data }, 295 | /* nVidia Geforce 9300M GS */ 296 | { PCI_VDEVICE(NVIDIA, 0x06e5), (kernel_ulong_t)&nv5x_driver_data }, 297 | /* nVidia Geforce 9200M GS */ 298 | { PCI_VDEVICE(NVIDIA, 0x06e8), (kernel_ulong_t)&nv5x_driver_data }, 299 | /* nVidia Geforce 9300M GS */ 300 | { PCI_VDEVICE(NVIDIA, 0x06e9), (kernel_ulong_t)&nv5x_driver_data }, 301 | /* nVidia Quadro NVS 150M */ 302 | { PCI_VDEVICE(NVIDIA, 0x06ea), (kernel_ulong_t)&nv5x_driver_data }, 303 | /* nVidia Quadro NVS 160M */ 304 | { PCI_VDEVICE(NVIDIA, 0x06eb), (kernel_ulong_t)&nv5x_driver_data }, 305 | /* nVidia Geforce G105M */ 306 | { PCI_VDEVICE(NVIDIA, 0x06ec), (kernel_ulong_t)&nv5x_driver_data }, 307 | /* nVidia QuadroFX 370M */ 308 | { PCI_VDEVICE(NVIDIA, 0x06fb), (kernel_ulong_t)&nv5x_driver_data }, 309 | /* nVidia Geforce 9400M */ 310 | { PCI_VDEVICE(NVIDIA, 0x0863), (kernel_ulong_t)&nv5x_driver_data }, 311 | /* NVIDIA GeForce 9400M */ 312 | { PCI_VDEVICE(NVIDIA, 0x0870), (kernel_ulong_t)&nv5x_driver_data }, 313 | /* NVIDIA GeForce 9200 */ 314 | { PCI_VDEVICE(NVIDIA, 0x0871), (kernel_ulong_t)&nv5x_driver_data }, 315 | /* NVIDIA GeForce G102M */ 316 | { PCI_VDEVICE(NVIDIA, 0x0872), (kernel_ulong_t)&nv5x_driver_data }, 317 | /* NVIDIA GeForce G102M */ 318 | { PCI_VDEVICE(NVIDIA, 0x0873), (kernel_ulong_t)&nv5x_driver_data }, 319 | /* NVIDIA Quadro FX 470 */ 320 | { PCI_VDEVICE(NVIDIA, 0x087a), (kernel_ulong_t)&nv5x_driver_data }, 321 | /* end of list */ 322 | { } 323 | }; 324 | 325 | #ifdef CONFIG_NVIDIA_BL_CHECK_SUBSYSTEM_VENDOR 326 | 327 | /* According to NvClock, supported subsystem vendors. 328 | * Defined separately to not unnecessarily enlarge the previous array. */ 329 | static const unsigned nvidia_bl_subvendors[] __devinitconst = { 330 | PCI_VENDOR_ID_APPLE, 331 | PCI_VENDOR_ID_HP, 332 | PCI_VENDOR_ID_SAMSUNG, 333 | PCI_VENDOR_ID_SONY, 334 | 0x1a46, /* PCI_VENDOR_ID_ZEPTO not defined */ 335 | }; 336 | 337 | #endif 338 | 339 | #ifdef CONFIG_NVIDIA_BL_CHECK_DMI 340 | 341 | /* 342 | * DMI matching. 343 | * Used to ignore the wrong device on machines incorporating 2 344 | * graphics adapters, such as the Apple MacBook Pro 5. 345 | */ 346 | static const struct dmi_system_id *nvidia_bl_dmi_system_id; 347 | 348 | static int nvidia_bl_dmi_match(const struct dmi_system_id *id) 349 | { 350 | printk(KERN_INFO "nvidia_bl: %s detected\n", id->ident); 351 | nvidia_bl_dmi_system_id = id; 352 | return 1; 353 | } 354 | 355 | static const struct dmi_system_id /* __initdata */ nvidia_bl_ignore_table[] = { 356 | { 357 | .callback = &nvidia_bl_dmi_match, 358 | .ident = "MacBookPro 5,1", 359 | .matches = { 360 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), 361 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,1"), 362 | }, 363 | .driver_data = (void *)0x0863, /* nVidia Geforce 9400M */ 364 | }, 365 | { 366 | .callback = &nvidia_bl_dmi_match, 367 | .ident = "MacBookPro 5,2", 368 | .matches = { 369 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), 370 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,2"), 371 | }, 372 | .driver_data = (void *)0x0863, /* nVidia Geforce 9400M */ 373 | }, 374 | { } 375 | }; 376 | 377 | #endif 378 | 379 | /* 380 | * Driver data implementation 381 | */ 382 | static const struct pci_device_id *nvidia_bl_match_id(struct pci_dev *dev) 383 | { 384 | /* Search id in table */ 385 | const struct pci_device_id *id = pci_match_id(nvidia_bl_device_table, dev); 386 | 387 | #ifdef CONFIG_NVIDIA_BL_CHECK_SUBSYSTEM_VENDOR 388 | int i; 389 | if (id) 390 | /* ... and check subsystem vendor */ 391 | for (i = 0; i < ARRAY_SIZE(nvidia_bl_subvendors); i++) 392 | if (dev->subsystem_vendor == nvidia_bl_subvendors[i]) 393 | return id; 394 | return NULL; 395 | #else 396 | return id; 397 | #endif 398 | } 399 | 400 | static int nvidia_bl_find_device(struct driver_data *dd, unsigned ignore_device) 401 | { 402 | struct pci_dev *dev = NULL; 403 | const struct pci_device_id *id; 404 | 405 | /* For each PCI device */ 406 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev))) { 407 | /* ... lookup id struct */ 408 | id = nvidia_bl_match_id(dev); 409 | if (id && (!ignore_device || (dev->device != ignore_device))) { 410 | printk(KERN_INFO "nvidia_bl: Supported Nvidia graphics" 411 | " adapter %04x:%04x:%04x:%04x detected\n", 412 | id->vendor, id->device, 413 | dev->subsystem_vendor, dev->subsystem_device); 414 | 415 | /* Setup driver data */ 416 | *dd = *((struct driver_data *)id->driver_data); 417 | dd->dev = dev; 418 | return 0; 419 | } 420 | } 421 | 422 | printk(KERN_INFO "nvidia_bl: No supported Nvidia graphics adapter" 423 | " found\n"); 424 | return -ENODEV; 425 | } 426 | 427 | static int nvidia_bl_map_smartdimmer(struct driver_data *dd) 428 | { 429 | /* Get resource properties */ 430 | const unsigned long bar_start = pci_resource_start(dd->dev, dd->bar), 431 | bar_end = pci_resource_end(dd->dev, dd->bar), 432 | bar_flags = pci_resource_flags(dd->dev, dd->bar); 433 | /* Calculate register address */ 434 | const unsigned long reg_addr = bar_start + dd->reg_offset; 435 | 436 | /* Sanity check 1: Should be a memory region containing registers */ 437 | if (!(bar_flags & IORESOURCE_MEM)) 438 | return -ENODEV; 439 | if (bar_flags & IORESOURCE_PREFETCH) 440 | return -ENODEV; 441 | 442 | /* Sanity check 2: Address should not exceed the PCI BAR */ 443 | if (reg_addr + dd->reg_size - 1 > bar_end) 444 | return -ENODEV; 445 | 446 | if (debug) 447 | printk(KERN_DEBUG "nvidia_bl: using BAR #%d at 0x%lx, " 448 | "smartdimmer at 0x%lx\n", dd->bar, bar_start, reg_addr); 449 | 450 | /* Now really map (The address need not be page-aligned.) */ 451 | dd->smartdimmer = ioremap_nocache(reg_addr, dd->reg_size); 452 | if (!dd->smartdimmer) 453 | return -ENXIO; 454 | 455 | return 0; 456 | } 457 | 458 | static void nvidia_bl_unmap_smartdimmer(struct driver_data *dd) 459 | { 460 | iounmap(dd->smartdimmer); 461 | dd->smartdimmer = NULL; 462 | } 463 | 464 | /* 465 | * Driver implementation 466 | */ 467 | static struct driver_data driver_data; 468 | static struct backlight_device *nvidia_bl_device; 469 | 470 | #ifdef USE_PLATFORM_DRIVER 471 | static int nvidia_bl_probe(struct platform_device *pdev) 472 | #else 473 | static int __init nvidia_bl_init(void) 474 | #endif 475 | { 476 | unsigned ignore_device = 0; 477 | int err; 478 | 479 | /* Bail-out if PCI subsystem is not initialized */ 480 | if (no_pci_devices()) 481 | return -ENODEV; 482 | 483 | #ifdef CONFIG_NVIDIA_BL_CHECK_DMI 484 | /* Check DMI whether we need to ignore some device */ 485 | dmi_check_system(nvidia_bl_ignore_table); 486 | if (nvidia_bl_dmi_system_id) 487 | ignore_device = 488 | (unsigned long)nvidia_bl_dmi_system_id->driver_data; 489 | #endif 490 | 491 | /* Look for a supported PCI device */ 492 | err = nvidia_bl_find_device(&driver_data, ignore_device); 493 | if (err) 494 | return err; 495 | 496 | /* Map smartdimmer */ 497 | err = nvidia_bl_map_smartdimmer(&driver_data); 498 | if (err) 499 | return err; 500 | 501 | /* Register at backlight framework */ 502 | nvidia_bl_device = backlight_device_register("nvidia_backlight", NULL, 503 | &driver_data, 504 | &driver_data.backlight_ops); 505 | if (IS_ERR(nvidia_bl_device)) { 506 | nvidia_bl_unmap_smartdimmer(&driver_data); 507 | return PTR_ERR(nvidia_bl_device); 508 | } 509 | 510 | /* Set up backlight device */ 511 | nvidia_bl_device->props.max_brightness = 512 | (driver_data.levels >> shift) - 1; 513 | nvidia_bl_device->props.brightness = 514 | nvidia_bl_device->ops->get_brightness(nvidia_bl_device); 515 | backlight_update_status(nvidia_bl_device); 516 | return 0; 517 | } 518 | 519 | #ifdef USE_PLATFORM_DRIVER 520 | static int nvidia_bl_remove(struct platform_device *pdev) 521 | #else 522 | static void __exit nvidia_bl_exit(void) 523 | #endif 524 | { 525 | /* Unregister at backlight framework */ 526 | if (nvidia_bl_device) 527 | backlight_device_unregister(nvidia_bl_device); 528 | 529 | /* Unmap smartdimmer */ 530 | if (driver_data.smartdimmer) 531 | nvidia_bl_unmap_smartdimmer(&driver_data); 532 | 533 | /* Release PCI device */ 534 | if (driver_data.dev) 535 | pci_dev_put(driver_data.dev); 536 | #ifdef USE_PLATFORM_DRIVER 537 | return 0; 538 | #endif 539 | } 540 | 541 | /* 542 | * Platform driver implementation 543 | */ 544 | #ifdef USE_PLATFORM_DRIVER 545 | 546 | static int nvidia_bl_resume(struct platform_device *pdev) 547 | { 548 | if (debug) 549 | printk(KERN_DEBUG "nvidia_bl: resuming with" 550 | " brightness %d\n", nvidia_bl_device->props.brightness); 551 | 552 | backlight_update_status(nvidia_bl_device); 553 | return 0; 554 | } 555 | 556 | static struct platform_driver nvidia_bl_driver = { 557 | .probe = nvidia_bl_probe, 558 | .remove = nvidia_bl_remove, 559 | .resume = nvidia_bl_resume, 560 | .driver = { 561 | .owner = THIS_MODULE, 562 | .name = "nvidia_bl" 563 | }, 564 | }; 565 | 566 | static struct platform_device *nvidia_bl_platform_device; 567 | 568 | static int __init nvidia_bl_init(void) 569 | { 570 | int err; 571 | 572 | err = platform_driver_register(&nvidia_bl_driver); 573 | if (err) 574 | return err; 575 | 576 | nvidia_bl_platform_device = 577 | platform_device_register_simple("nvidia_bl", -1, NULL, 0); 578 | if (!nvidia_bl_platform_device) { 579 | platform_driver_unregister(&nvidia_bl_driver); 580 | return -ENOMEM; 581 | } 582 | 583 | return 0; 584 | } 585 | 586 | static void __exit nvidia_bl_exit(void) 587 | { 588 | platform_device_unregister(nvidia_bl_platform_device); 589 | platform_driver_unregister(&nvidia_bl_driver); 590 | } 591 | 592 | #endif /* USE_PLATFORM_DRIVER */ 593 | 594 | module_init(nvidia_bl_init); 595 | module_exit(nvidia_bl_exit); 596 | 597 | MODULE_AUTHOR("Mario Schwalbe "); 598 | MODULE_DESCRIPTION("Nvidia-based graphics adapter backlight driver"); 599 | MODULE_LICENSE("GPL"); 600 | 601 | -------------------------------------------------------------------------------- /sony-laptop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ACPI Sony Notebook Control Driver (SNC and SPIC) 3 | * 4 | * Copyright (C) 2004-2005 Stelian Pop 5 | * Copyright (C) 2007-2009 Mattia Dongili 6 | * Copyright (C) 2011 Marco Chiappero 7 | * Copyright (C) 2011 Javier Achirica 8 | * 9 | * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c 10 | * which are copyrighted by their respective authors. 11 | * 12 | * The SNY6001 driver part is based on the sonypi driver which includes 13 | * material from: 14 | * 15 | * Copyright (C) 2001-2005 Stelian Pop 16 | * 17 | * Copyright (C) 2005 Narayanan R S 18 | * 19 | * Copyright (C) 2001-2002 Alcôve 20 | * 21 | * Copyright (C) 2001 Michael Ashley 22 | * 23 | * Copyright (C) 2001 Junichi Morita 24 | * 25 | * Copyright (C) 2000 Takaya Kinjo 26 | * 27 | * Copyright (C) 2000 Andrew Tridgell 28 | * 29 | * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. 30 | * 31 | * This program is free software; you can redistribute it and/or modify 32 | * it under the terms of the GNU General Public License as published by 33 | * the Free Software Foundation; either version 2 of the License, or 34 | * (at your option) any later version. 35 | * 36 | * This program is distributed in the hope that it will be useful, 37 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 38 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 39 | * GNU General Public License for more details. 40 | * 41 | * You should have received a copy of the GNU General Public License 42 | * along with this program; if not, write to the Free Software 43 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 44 | * 45 | */ 46 | 47 | /* 48 | * defining SONY_ZSERIES includes code for graphics card switching 49 | */ 50 | #define SONY_ZSERIES 51 | 52 | 53 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 54 | 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #ifdef CONFIG_SONYPI_COMPAT 79 | #include 80 | #include 81 | #endif 82 | #ifdef SONY_ZSERIES 83 | #include 84 | #endif 85 | 86 | 87 | #define dprintk(fmt, ...) \ 88 | do { \ 89 | if (debug) \ 90 | pr_warn(fmt, ##__VA_ARGS__); \ 91 | } while (0) 92 | 93 | #ifdef SONY_ZSERIES 94 | #define SONY_LAPTOP_DRIVER_VERSION "0.9np8" 95 | #else 96 | #define SONY_LAPTOP_DRIVER_VERSION "0.6" 97 | #endif 98 | 99 | #define SONY_NC_CLASS "sony-nc" 100 | #define SONY_NC_HID "SNY5001" 101 | #define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver" 102 | 103 | #define SONY_PIC_CLASS "sony-pic" 104 | #define SONY_PIC_HID "SNY6001" 105 | #define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control Driver" 106 | 107 | MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); 108 | MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)"); 109 | MODULE_LICENSE("GPL"); 110 | MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION); 111 | 112 | static int debug; 113 | module_param(debug, int, 0); 114 | MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help " 115 | "the development of this driver"); 116 | 117 | static int no_spic; /* = 0 */ 118 | module_param(no_spic, int, 0444); 119 | MODULE_PARM_DESC(no_spic, 120 | "set this if you don't want to enable the SPIC device"); 121 | 122 | static int compat; /* = 0 */ 123 | module_param(compat, int, 0444); 124 | MODULE_PARM_DESC(compat, 125 | "set this if you want to enable backward compatibility mode"); 126 | 127 | static unsigned long mask = 0xffffffff; 128 | module_param(mask, ulong, 0644); 129 | MODULE_PARM_DESC(mask, 130 | "set this to the mask of event you want to enable (see doc)"); 131 | 132 | static int camera; /* = 0 */ 133 | module_param(camera, int, 0444); 134 | MODULE_PARM_DESC(camera, 135 | "set this to 1 to enable Motion Eye camera controls " 136 | "(only use it if you have a C1VE or C1VN model)"); 137 | 138 | #ifdef CONFIG_SONYPI_COMPAT 139 | static int minor = -1; 140 | module_param(minor, int, 0); 141 | MODULE_PARM_DESC(minor, 142 | "minor number of the misc device for the SPIC compatibility code, " 143 | "default is -1 (automatic)"); 144 | #endif 145 | 146 | static int kbd_backlight; /* = 0 */ 147 | module_param(kbd_backlight, int, 0444); 148 | MODULE_PARM_DESC(kbd_backlight, 149 | "set this to 0 to disable keyboard backlight, " 150 | "1 to enable it (default: 0)"); 151 | 152 | static int kbd_backlight_timeout; /* = 0 */ 153 | module_param(kbd_backlight_timeout, int, 0444); 154 | MODULE_PARM_DESC(kbd_backlight_timeout, 155 | "set this to 0 to set the default 10 seconds timeout, " 156 | "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout " 157 | "(default: 0)"); 158 | 159 | static int force_shock_notifications; /* = 0 */ 160 | module_param(force_shock_notifications, int, 0); 161 | MODULE_PARM_DESC(force_shock_notifications, 162 | "set this to 1 to force the generation of shock protection " 163 | "events, even though the notebook do not support head " 164 | "unloading for the installed drive drive"); 165 | 166 | #ifdef SONY_ZSERIES 167 | static int speed_stamina; 168 | module_param(speed_stamina, int, 0444); 169 | MODULE_PARM_DESC(speed_stamina, 170 | "Set this to 1 to enable SPEED mode on module load (EXPERIMENTAL)"); 171 | static int sony_dsm_type = 0; 172 | static char *sony_acpi_path_dsm[] = 173 | { 174 | "\\_SB.PCI0.OVGA._DSM", 175 | "\\_SB.PCI0.P0P2.DGPU._DSM" 176 | }; 177 | static char *sony_acpi_path_hsc1[] = 178 | { 179 | "\\_SB.PCI0.LPC.SNC.HSC1", 180 | "\\_SB.PCI0.LPCB.SNC.HSC1" 181 | }; 182 | /* static acpi_handle sony_nc_acpi_handle; */ 183 | static int acpi_callgetfunc(acpi_handle, char*, unsigned int*); 184 | #endif 185 | /*********** Input Devices ***********/ 186 | 187 | #define SONY_LAPTOP_BUF_SIZE 128 188 | struct sony_laptop_input_s { 189 | atomic_t users; 190 | struct input_dev *jog_dev; 191 | struct input_dev *key_dev; 192 | struct kfifo fifo; 193 | spinlock_t fifo_lock; 194 | struct timer_list release_key_timer; 195 | }; 196 | 197 | static struct sony_laptop_input_s sony_laptop_input = { 198 | .users = ATOMIC_INIT(0), 199 | }; 200 | 201 | struct sony_laptop_keypress { 202 | struct input_dev *dev; 203 | int key; 204 | }; 205 | 206 | /* Correspondance table between sonypi events 207 | * and input layer indexes in the keymap 208 | */ 209 | static int sony_laptop_input_index[] = { 210 | -1, /* 0 no event */ 211 | -1, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */ 212 | -1, /* 2 SONYPI_EVENT_JOGDIAL_UP */ 213 | -1, /* 3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */ 214 | -1, /* 4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */ 215 | -1, /* 5 SONYPI_EVENT_JOGDIAL_PRESSED */ 216 | -1, /* 6 SONYPI_EVENT_JOGDIAL_RELEASED */ 217 | 0, /* 7 SONYPI_EVENT_CAPTURE_PRESSED */ 218 | 1, /* 8 SONYPI_EVENT_CAPTURE_RELEASED */ 219 | 2, /* 9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */ 220 | 3, /* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */ 221 | 4, /* 11 SONYPI_EVENT_FNKEY_ESC */ 222 | 5, /* 12 SONYPI_EVENT_FNKEY_F1 */ 223 | 6, /* 13 SONYPI_EVENT_FNKEY_F2 */ 224 | 7, /* 14 SONYPI_EVENT_FNKEY_F3 */ 225 | 8, /* 15 SONYPI_EVENT_FNKEY_F4 */ 226 | 9, /* 16 SONYPI_EVENT_FNKEY_F5 */ 227 | 10, /* 17 SONYPI_EVENT_FNKEY_F6 */ 228 | 11, /* 18 SONYPI_EVENT_FNKEY_F7 */ 229 | 12, /* 19 SONYPI_EVENT_FNKEY_F8 */ 230 | 13, /* 20 SONYPI_EVENT_FNKEY_F9 */ 231 | 14, /* 21 SONYPI_EVENT_FNKEY_F10 */ 232 | 15, /* 22 SONYPI_EVENT_FNKEY_F11 */ 233 | 16, /* 23 SONYPI_EVENT_FNKEY_F12 */ 234 | 17, /* 24 SONYPI_EVENT_FNKEY_1 */ 235 | 18, /* 25 SONYPI_EVENT_FNKEY_2 */ 236 | 19, /* 26 SONYPI_EVENT_FNKEY_D */ 237 | 20, /* 27 SONYPI_EVENT_FNKEY_E */ 238 | 21, /* 28 SONYPI_EVENT_FNKEY_F */ 239 | 22, /* 29 SONYPI_EVENT_FNKEY_S */ 240 | 23, /* 30 SONYPI_EVENT_FNKEY_B */ 241 | 24, /* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */ 242 | 25, /* 32 SONYPI_EVENT_PKEY_P1 */ 243 | 26, /* 33 SONYPI_EVENT_PKEY_P2 */ 244 | 27, /* 34 SONYPI_EVENT_PKEY_P3 */ 245 | 28, /* 35 SONYPI_EVENT_BACK_PRESSED */ 246 | -1, /* 36 SONYPI_EVENT_LID_CLOSED */ 247 | -1, /* 37 SONYPI_EVENT_LID_OPENED */ 248 | 29, /* 38 SONYPI_EVENT_BLUETOOTH_ON */ 249 | 30, /* 39 SONYPI_EVENT_BLUETOOTH_OFF */ 250 | 31, /* 40 SONYPI_EVENT_HELP_PRESSED */ 251 | 32, /* 41 SONYPI_EVENT_FNKEY_ONLY */ 252 | 33, /* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */ 253 | 34, /* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */ 254 | 35, /* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */ 255 | 36, /* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */ 256 | 37, /* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */ 257 | 38, /* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */ 258 | 39, /* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */ 259 | 40, /* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */ 260 | 41, /* 50 SONYPI_EVENT_ZOOM_PRESSED */ 261 | 42, /* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */ 262 | 43, /* 52 SONYPI_EVENT_MEYE_FACE */ 263 | 44, /* 53 SONYPI_EVENT_MEYE_OPPOSITE */ 264 | 45, /* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */ 265 | 46, /* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */ 266 | -1, /* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */ 267 | -1, /* 57 SONYPI_EVENT_BATTERY_INSERT */ 268 | -1, /* 58 SONYPI_EVENT_BATTERY_REMOVE */ 269 | -1, /* 59 SONYPI_EVENT_FNKEY_RELEASED */ 270 | 47, /* 60 SONYPI_EVENT_WIRELESS_ON */ 271 | 48, /* 61 SONYPI_EVENT_WIRELESS_OFF */ 272 | 49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */ 273 | 50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */ 274 | 51, /* 64 SONYPI_EVENT_CD_EJECT_PRESSED */ 275 | 52, /* 65 SONYPI_EVENT_MODEKEY_PRESSED */ 276 | 53, /* 66 SONYPI_EVENT_PKEY_P4 */ 277 | 54, /* 67 SONYPI_EVENT_PKEY_P5 */ 278 | 55, /* 68 SONYPI_EVENT_SETTINGKEY_PRESSED */ 279 | 56, /* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */ 280 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ 281 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ 282 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ 283 | 59, /* 73 SONYPI_EVENT_VENDOR_PRESSED */ 284 | }; 285 | 286 | static int sony_laptop_input_keycode_map[] = { 287 | KEY_CAMERA, /* 0 SONYPI_EVENT_CAPTURE_PRESSED */ 288 | KEY_RESERVED, /* 1 SONYPI_EVENT_CAPTURE_RELEASED */ 289 | KEY_RESERVED, /* 2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */ 290 | KEY_RESERVED, /* 3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */ 291 | KEY_FN_ESC, /* 4 SONYPI_EVENT_FNKEY_ESC */ 292 | KEY_FN_F1, /* 5 SONYPI_EVENT_FNKEY_F1 */ 293 | KEY_FN_F2, /* 6 SONYPI_EVENT_FNKEY_F2 */ 294 | KEY_FN_F3, /* 7 SONYPI_EVENT_FNKEY_F3 */ 295 | KEY_FN_F4, /* 8 SONYPI_EVENT_FNKEY_F4 */ 296 | KEY_FN_F5, /* 9 SONYPI_EVENT_FNKEY_F5 */ 297 | KEY_FN_F6, /* 10 SONYPI_EVENT_FNKEY_F6 */ 298 | KEY_FN_F7, /* 11 SONYPI_EVENT_FNKEY_F7 */ 299 | KEY_FN_F8, /* 12 SONYPI_EVENT_FNKEY_F8 */ 300 | KEY_FN_F9, /* 13 SONYPI_EVENT_FNKEY_F9 */ 301 | KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */ 302 | KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */ 303 | KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */ 304 | KEY_FN_F1, /* 17 SONYPI_EVENT_FNKEY_1 */ 305 | KEY_FN_F2, /* 18 SONYPI_EVENT_FNKEY_2 */ 306 | KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */ 307 | KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */ 308 | KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */ 309 | KEY_FN_S, /* 22 SONYPI_EVENT_FNKEY_S */ 310 | KEY_FN_B, /* 23 SONYPI_EVENT_FNKEY_B */ 311 | KEY_BLUETOOTH, /* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */ 312 | KEY_PROG1, /* 25 SONYPI_EVENT_PKEY_P1 */ 313 | KEY_PROG2, /* 26 SONYPI_EVENT_PKEY_P2 */ 314 | KEY_PROG3, /* 27 SONYPI_EVENT_PKEY_P3 */ 315 | KEY_BACK, /* 28 SONYPI_EVENT_BACK_PRESSED */ 316 | KEY_BLUETOOTH, /* 29 SONYPI_EVENT_BLUETOOTH_ON */ 317 | KEY_BLUETOOTH, /* 30 SONYPI_EVENT_BLUETOOTH_OFF */ 318 | KEY_HELP, /* 31 SONYPI_EVENT_HELP_PRESSED */ 319 | KEY_FN, /* 32 SONYPI_EVENT_FNKEY_ONLY */ 320 | KEY_RESERVED, /* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */ 321 | KEY_RESERVED, /* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */ 322 | KEY_RESERVED, /* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */ 323 | KEY_RESERVED, /* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */ 324 | KEY_RESERVED, /* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */ 325 | KEY_RESERVED, /* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */ 326 | KEY_RESERVED, /* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */ 327 | KEY_RESERVED, /* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */ 328 | KEY_ZOOM, /* 41 SONYPI_EVENT_ZOOM_PRESSED */ 329 | BTN_THUMB, /* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */ 330 | KEY_RESERVED, /* 43 SONYPI_EVENT_MEYE_FACE */ 331 | KEY_RESERVED, /* 44 SONYPI_EVENT_MEYE_OPPOSITE */ 332 | KEY_RESERVED, /* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */ 333 | KEY_RESERVED, /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */ 334 | KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */ 335 | KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */ 336 | KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */ 337 | KEY_ZOOMOUT, /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */ 338 | KEY_EJECTCD, /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */ 339 | KEY_F13, /* 52 SONYPI_EVENT_MODEKEY_PRESSED */ 340 | KEY_PROG4, /* 53 SONYPI_EVENT_PKEY_P4 */ 341 | KEY_F14, /* 54 SONYPI_EVENT_PKEY_P5 */ 342 | KEY_F15, /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */ 343 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ 344 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ 345 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ 346 | KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */ 347 | }; 348 | 349 | /* release buttons after a short delay if pressed */ 350 | static void do_sony_laptop_release_key(unsigned long unused) 351 | { 352 | struct sony_laptop_keypress kp; 353 | unsigned long flags; 354 | 355 | spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags); 356 | 357 | if (kfifo_out(&sony_laptop_input.fifo, 358 | (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { 359 | input_report_key(kp.dev, kp.key, 0); 360 | input_sync(kp.dev); 361 | } 362 | 363 | /* If there is something in the fifo schedule next release. */ 364 | if (kfifo_len(&sony_laptop_input.fifo) != 0) 365 | mod_timer(&sony_laptop_input.release_key_timer, 366 | jiffies + msecs_to_jiffies(10)); 367 | 368 | spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags); 369 | } 370 | 371 | /* forward event to the input subsystem */ 372 | static void sony_laptop_report_input_event(u8 event) 373 | { 374 | struct input_dev *jog_dev = sony_laptop_input.jog_dev; 375 | struct input_dev *key_dev = sony_laptop_input.key_dev; 376 | struct sony_laptop_keypress kp = { NULL }; 377 | 378 | if (event == SONYPI_EVENT_FNKEY_RELEASED || 379 | event == SONYPI_EVENT_ANYBUTTON_RELEASED) { 380 | /* Nothing, not all VAIOs generate this event */ 381 | return; 382 | } 383 | 384 | /* report events */ 385 | switch (event) { 386 | /* jog_dev events */ 387 | case SONYPI_EVENT_JOGDIAL_UP: 388 | case SONYPI_EVENT_JOGDIAL_UP_PRESSED: 389 | input_report_rel(jog_dev, REL_WHEEL, 1); 390 | input_sync(jog_dev); 391 | return; 392 | 393 | case SONYPI_EVENT_JOGDIAL_DOWN: 394 | case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED: 395 | input_report_rel(jog_dev, REL_WHEEL, -1); 396 | input_sync(jog_dev); 397 | return; 398 | 399 | /* key_dev events */ 400 | case SONYPI_EVENT_JOGDIAL_PRESSED: 401 | kp.key = BTN_MIDDLE; 402 | kp.dev = jog_dev; 403 | break; 404 | 405 | default: 406 | if (event >= ARRAY_SIZE(sony_laptop_input_index)) { 407 | dprintk("sony_laptop_report_input_event, " 408 | "event not known: %d\n", event); 409 | break; 410 | } 411 | if (sony_laptop_input_index[event] != -1) { 412 | kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]]; 413 | if (kp.key != KEY_UNKNOWN) 414 | kp.dev = key_dev; 415 | } 416 | break; 417 | } 418 | 419 | if (kp.dev) { 420 | input_report_key(kp.dev, kp.key, 1); 421 | /* we emit the scancode so we can always remap the key */ 422 | input_event(kp.dev, EV_MSC, MSC_SCAN, event); 423 | input_sync(kp.dev); 424 | 425 | /* schedule key release */ 426 | kfifo_in_locked(&sony_laptop_input.fifo, 427 | (unsigned char *)&kp, sizeof(kp), 428 | &sony_laptop_input.fifo_lock); 429 | mod_timer(&sony_laptop_input.release_key_timer, 430 | jiffies + msecs_to_jiffies(10)); 431 | } else 432 | dprintk("unknown input event %.2x\n", event); 433 | } 434 | 435 | static int sony_laptop_setup_input(struct acpi_device *acpi_device) 436 | { 437 | struct input_dev *jog_dev; 438 | struct input_dev *key_dev; 439 | int i; 440 | int error; 441 | 442 | /* don't run again if already initialized */ 443 | if (atomic_add_return(1, &sony_laptop_input.users) > 1) 444 | return 0; 445 | 446 | /* kfifo */ 447 | spin_lock_init(&sony_laptop_input.fifo_lock); 448 | error = kfifo_alloc(&sony_laptop_input.fifo, 449 | SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); 450 | if (error) { 451 | pr_err("kfifo_alloc failed\n"); 452 | goto err_dec_users; 453 | } 454 | 455 | setup_timer(&sony_laptop_input.release_key_timer, 456 | do_sony_laptop_release_key, 0); 457 | 458 | /* input keys */ 459 | key_dev = input_allocate_device(); 460 | if (!key_dev) { 461 | error = -ENOMEM; 462 | goto err_free_kfifo; 463 | } 464 | 465 | key_dev->name = "Sony Vaio Keys"; 466 | key_dev->id.bustype = BUS_ISA; 467 | key_dev->id.vendor = PCI_VENDOR_ID_SONY; 468 | key_dev->dev.parent = &acpi_device->dev; 469 | 470 | /* Initialize the Input Drivers: special keys */ 471 | input_set_capability(key_dev, EV_MSC, MSC_SCAN); 472 | 473 | __set_bit(EV_KEY, key_dev->evbit); 474 | key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); 475 | key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); 476 | key_dev->keycode = &sony_laptop_input_keycode_map; 477 | for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) 478 | __set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit); 479 | __clear_bit(KEY_RESERVED, key_dev->keybit); 480 | 481 | error = input_register_device(key_dev); 482 | if (error) 483 | goto err_free_keydev; 484 | 485 | sony_laptop_input.key_dev = key_dev; 486 | 487 | /* jogdial */ 488 | jog_dev = input_allocate_device(); 489 | if (!jog_dev) { 490 | error = -ENOMEM; 491 | goto err_unregister_keydev; 492 | } 493 | 494 | jog_dev->name = "Sony Vaio Jogdial"; 495 | jog_dev->id.bustype = BUS_ISA; 496 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; 497 | key_dev->dev.parent = &acpi_device->dev; 498 | 499 | input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); 500 | input_set_capability(jog_dev, EV_REL, REL_WHEEL); 501 | 502 | error = input_register_device(jog_dev); 503 | if (error) 504 | goto err_free_jogdev; 505 | 506 | sony_laptop_input.jog_dev = jog_dev; 507 | 508 | return 0; 509 | 510 | err_free_jogdev: 511 | input_free_device(jog_dev); 512 | 513 | err_unregister_keydev: 514 | input_unregister_device(key_dev); 515 | /* to avoid kref underflow below at input_free_device */ 516 | key_dev = NULL; 517 | 518 | err_free_keydev: 519 | input_free_device(key_dev); 520 | 521 | err_free_kfifo: 522 | kfifo_free(&sony_laptop_input.fifo); 523 | 524 | err_dec_users: 525 | atomic_dec(&sony_laptop_input.users); 526 | return error; 527 | } 528 | 529 | static void sony_laptop_remove_input(void) 530 | { 531 | struct sony_laptop_keypress kp = { NULL }; 532 | 533 | /* Cleanup only after the last user has gone */ 534 | if (!atomic_dec_and_test(&sony_laptop_input.users)) 535 | return; 536 | 537 | del_timer_sync(&sony_laptop_input.release_key_timer); 538 | 539 | /* 540 | * Generate key-up events for remaining keys. Note that we don't 541 | * need locking since nobody is adding new events to the kfifo. 542 | */ 543 | while (kfifo_out(&sony_laptop_input.fifo, 544 | (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { 545 | input_report_key(kp.dev, kp.key, 0); 546 | input_sync(kp.dev); 547 | } 548 | 549 | /* destroy input devs */ 550 | input_unregister_device(sony_laptop_input.key_dev); 551 | sony_laptop_input.key_dev = NULL; 552 | 553 | if (sony_laptop_input.jog_dev) { 554 | input_unregister_device(sony_laptop_input.jog_dev); 555 | sony_laptop_input.jog_dev = NULL; 556 | } 557 | 558 | kfifo_free(&sony_laptop_input.fifo); 559 | } 560 | 561 | /*********** Platform Device ***********/ 562 | #ifdef SONY_ZSERIES 563 | static int sony_ovga_dsm(int func, int arg) 564 | { 565 | static char muid[] = { 566 | /*00*/ 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, /* MUID */ 567 | /*08*/ 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 568 | }; 569 | 570 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 571 | struct acpi_object_list input; 572 | union acpi_object params[4]; 573 | int result; 574 | 575 | input.count = 4; 576 | input.pointer = params; 577 | params[0].type = ACPI_TYPE_BUFFER; 578 | params[0].buffer.length = sizeof(muid); 579 | params[0].buffer.pointer = (char*)muid; 580 | params[1].type = ACPI_TYPE_INTEGER; 581 | params[1].integer.value = 0x00000102; 582 | params[2].type = ACPI_TYPE_INTEGER; 583 | params[2].integer.value = func; 584 | params[3].type = ACPI_TYPE_INTEGER; 585 | params[3].integer.value = arg; 586 | 587 | result = acpi_evaluate_object(NULL, (char*)sony_acpi_path_dsm[sony_dsm_type], &input, &output); 588 | if (result) { 589 | printk("%s failed: %d, func %d, para, %d.\n", sony_acpi_path_dsm[sony_dsm_type], result, func, arg); 590 | return -1; 591 | } 592 | kfree(output.pointer); 593 | return 0; 594 | } 595 | 596 | static int sony_led_stamina(void) 597 | { 598 | return sony_ovga_dsm(2, 0x11); 599 | } 600 | static int sony_led_speed(void) 601 | { 602 | return sony_ovga_dsm(2, 0x12); 603 | } 604 | 605 | #ifdef DEBUG 606 | static int sony_led_off(void) 607 | { 608 | return sony_ovga_dsm(2, 0x13); 609 | } 610 | 611 | static int sony_dgpu_sta(void) 612 | { 613 | return sony_ovga_dsm(3, 0x00); 614 | } 615 | #endif 616 | 617 | static int sony_dgpu_off(void) 618 | { 619 | return sony_ovga_dsm(3, 0x02); 620 | } 621 | 622 | static int sony_dgpu_on(void) 623 | { 624 | return sony_ovga_dsm(3, 0x01); 625 | } 626 | 627 | static ssize_t sony_pf_store_speed_stamina(struct device *dev, 628 | struct device_attribute *attr, 629 | const char *buffer, size_t count) 630 | { 631 | if (!strncmp(buffer, "speed", strlen("speed"))) { 632 | sony_dgpu_on(); 633 | sony_led_speed(); 634 | speed_stamina = 1; 635 | } else 636 | if (!strncmp(buffer, "stamina", strlen("stamina"))) { 637 | sony_dgpu_off(); 638 | sony_led_stamina(); 639 | speed_stamina = 0; 640 | } else 641 | return -EINVAL; 642 | 643 | return count; 644 | } 645 | 646 | static ssize_t sony_pf_show_speed_stamina(struct device *dev, 647 | struct device_attribute *attr, char *buffer) 648 | { 649 | return snprintf(buffer, PAGE_SIZE, "%s\n", speed_stamina ? "speed":"stamina"); 650 | } 651 | 652 | static struct device_attribute sony_pf_speed_stamina_attr = 653 | __ATTR(speed_stamina, S_IWUSR|S_IRUGO, 654 | sony_pf_show_speed_stamina, sony_pf_store_speed_stamina); 655 | 656 | static int sony_pf_probe(struct platform_device *pdev) 657 | { 658 | int result; 659 | 660 | /* Determine which variant, VGN or VPC */ 661 | if(ACPI_SUCCESS(acpi_callgetfunc(NULL, "\\_SB.PCI0.P0P2.DGPU._STA", &result))) 662 | sony_dsm_type = 1; 663 | 664 | pr_info("Determined GFX switch ACPI path as %s.\n", sony_acpi_path_dsm[sony_dsm_type]); 665 | result = device_create_file(&pdev->dev, &sony_pf_speed_stamina_attr); 666 | if (result) 667 | printk(KERN_DEBUG "sony_pf_probe: failed to add speed/stamina switch\n"); 668 | 669 | /* initialize default, look at module param speed_stamina or switch */ 670 | if (!ACPI_SUCCESS(acpi_callgetfunc(NULL, sony_acpi_path_hsc1[sony_dsm_type], &result))) { 671 | result = -1; 672 | dprintk("sony_nc_notify: " 673 | "cannot query speed/stamina switch\n"); 674 | } 675 | else 676 | { 677 | pr_info("Speed/stamina switch: %s.\n", (result & 0x80)?"auto":(result & 2)?"stamina":"speed"); 678 | if(!(result & 2)) 679 | speed_stamina = 1; 680 | else if((result & 0x80) && sony_dsm_type == 1) 681 | { 682 | if((ACPI_SUCCESS(acpi_callgetfunc(NULL, "\\_SB.ADP1._PSR", &result))) && (result == 1)) 683 | { 684 | pr_info("PSU connected - Selecting speed mode.\n"); 685 | speed_stamina = 1; 686 | } 687 | } 688 | } 689 | 690 | if (speed_stamina == 1) { 691 | sony_dgpu_on(); 692 | sony_led_speed(); 693 | } else { 694 | sony_dgpu_off(); 695 | sony_led_stamina(); 696 | } 697 | return 0; 698 | } 699 | static int sony_resume_noirq(struct device *pdev) 700 | { 701 | /* on resume, restore previous state */ 702 | if (speed_stamina == 1) { 703 | sony_dgpu_on(); 704 | sony_led_speed(); 705 | } else if (speed_stamina == 0){ 706 | sony_dgpu_off(); 707 | sony_led_stamina(); 708 | } 709 | return 0; 710 | } 711 | 712 | static struct dev_pm_ops sony_dev_pm_ops = { 713 | .resume_noirq = sony_resume_noirq, 714 | }; 715 | #endif /* SONY_ZSERIES */ 716 | 717 | 718 | static atomic_t sony_pf_users = ATOMIC_INIT(0); 719 | static struct platform_driver sony_pf_driver = { 720 | .driver = { 721 | .name = "sony-laptop", 722 | .owner = THIS_MODULE, 723 | #ifdef SONY_ZSERIES 724 | #ifdef CONFIG_PM 725 | .pm = &sony_dev_pm_ops, 726 | #endif 727 | #endif 728 | } 729 | #ifdef SONY_ZSERIES 730 | , 731 | .probe = sony_pf_probe, 732 | #endif 733 | }; 734 | static struct platform_device *sony_pf_device; 735 | 736 | static int sony_pf_add(void) 737 | { 738 | int ret = 0; 739 | 740 | /* don't run again if already initialized */ 741 | if (atomic_add_return(1, &sony_pf_users) > 1) 742 | return 0; 743 | 744 | ret = platform_driver_register(&sony_pf_driver); 745 | if (ret) 746 | goto out; 747 | 748 | sony_pf_device = platform_device_alloc("sony-laptop", -1); 749 | if (!sony_pf_device) { 750 | ret = -ENOMEM; 751 | goto out_platform_registered; 752 | } 753 | 754 | ret = platform_device_add(sony_pf_device); 755 | if (ret) 756 | goto out_platform_alloced; 757 | 758 | return 0; 759 | 760 | out_platform_alloced: 761 | platform_device_put(sony_pf_device); 762 | sony_pf_device = NULL; 763 | out_platform_registered: 764 | platform_driver_unregister(&sony_pf_driver); 765 | out: 766 | atomic_dec(&sony_pf_users); 767 | return ret; 768 | } 769 | 770 | static void sony_pf_remove(void) 771 | { 772 | /* deregister only after the last user has gone */ 773 | if (!atomic_dec_and_test(&sony_pf_users)) 774 | return; 775 | 776 | platform_device_unregister(sony_pf_device); 777 | platform_driver_unregister(&sony_pf_driver); 778 | } 779 | 780 | /*********** SNC (SNY5001) Device ***********/ 781 | 782 | /* the device uses 1-based values, while the backlight subsystem uses 783 | 0-based values */ 784 | #define SONY_MAX_BRIGHTNESS 8 785 | 786 | #define SNC_VALIDATE_IN 0 787 | #define SNC_VALIDATE_OUT 1 788 | 789 | static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *, 790 | char *); 791 | static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *, 792 | const char *, size_t); 793 | static int boolean_validate(const int, const int); 794 | static int brightness_default_validate(const int, const int); 795 | 796 | struct sony_nc_value { 797 | char *name; /* name of the entry */ 798 | char **acpiget; /* names of the ACPI get function */ 799 | char **acpiset; /* names of the ACPI set function */ 800 | int (*validate)(const int, const int); /* input/output validation */ 801 | int value; /* current setting */ 802 | int valid; /* Has ever been set */ 803 | int debug; /* active only in debug mode ? */ 804 | struct device_attribute devattr; /* sysfs attribute */ 805 | }; 806 | 807 | #define SNC_HANDLE_NAMES(_name, _values...) \ 808 | static char *snc_##_name[] = { _values, NULL } 809 | 810 | #define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \ 811 | { \ 812 | .name = __stringify(_name), \ 813 | .acpiget = _getters, \ 814 | .acpiset = _setters, \ 815 | .validate = _validate, \ 816 | .debug = _debug, \ 817 | .devattr = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \ 818 | } 819 | 820 | #define SNC_HANDLE_NULL { .name = NULL } 821 | 822 | SNC_HANDLE_NAMES(fnkey_get, "GHKE"); 823 | 824 | SNC_HANDLE_NAMES(brightness_def_get, "GPBR"); 825 | SNC_HANDLE_NAMES(brightness_def_set, "SPBR"); 826 | 827 | SNC_HANDLE_NAMES(cdpower_get, "GCDP"); 828 | SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW"); 829 | 830 | SNC_HANDLE_NAMES(audiopower_get, "GAZP"); 831 | SNC_HANDLE_NAMES(audiopower_set, "AZPW"); 832 | 833 | SNC_HANDLE_NAMES(lanpower_get, "GLNP"); 834 | SNC_HANDLE_NAMES(lanpower_set, "LNPW"); 835 | 836 | SNC_HANDLE_NAMES(lidstate_get, "GLID"); 837 | 838 | SNC_HANDLE_NAMES(indicatorlamp_get, "GILS"); 839 | SNC_HANDLE_NAMES(indicatorlamp_set, "SILS"); 840 | 841 | SNC_HANDLE_NAMES(gainbass_get, "GMGB"); 842 | SNC_HANDLE_NAMES(gainbass_set, "CMGB"); 843 | 844 | SNC_HANDLE_NAMES(PID_get, "GPID"); 845 | 846 | SNC_HANDLE_NAMES(CTR_get, "GCTR"); 847 | SNC_HANDLE_NAMES(CTR_set, "SCTR"); 848 | 849 | SNC_HANDLE_NAMES(PCR_get, "GPCR"); 850 | SNC_HANDLE_NAMES(PCR_set, "SPCR"); 851 | 852 | SNC_HANDLE_NAMES(CMI_get, "GCMI"); 853 | SNC_HANDLE_NAMES(CMI_set, "SCMI"); 854 | 855 | static struct sony_nc_value sony_nc_values[] = { 856 | SNC_HANDLE(brightness_default, snc_brightness_def_get, 857 | snc_brightness_def_set, brightness_default_validate, 0), 858 | SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0), 859 | SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, 860 | boolean_validate, 0), 861 | SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set, 862 | boolean_validate, 0), 863 | SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set, 864 | boolean_validate, 1), 865 | SNC_HANDLE(lidstate, snc_lidstate_get, NULL, 866 | boolean_validate, 0), 867 | SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set, 868 | boolean_validate, 0), 869 | SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set, 870 | boolean_validate, 0), 871 | /* unknown methods */ 872 | SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1), 873 | SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1), 874 | SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1), 875 | SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1), 876 | SNC_HANDLE_NULL 877 | }; 878 | 879 | static acpi_handle sony_nc_acpi_handle; 880 | static struct acpi_device *sony_nc_acpi_device; 881 | 882 | /* 883 | * acpi_evaluate_object wrappers 884 | */ 885 | static int acpi_callgetfunc(acpi_handle handle, char *name, 886 | unsigned int *result) 887 | { 888 | struct acpi_buffer output; 889 | union acpi_object out_obj; 890 | acpi_status status; 891 | 892 | output.length = sizeof(out_obj); 893 | output.pointer = &out_obj; 894 | 895 | status = acpi_evaluate_object(handle, name, NULL, &output); 896 | if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) { 897 | *result = out_obj.integer.value; 898 | return 0; 899 | } 900 | 901 | pr_warn("acpi_callreadfunc failed\n"); 902 | 903 | return -1; 904 | } 905 | 906 | static int acpi_callsetfunc(acpi_handle handle, char *name, u32 value, 907 | unsigned int *result) 908 | { 909 | struct acpi_object_list params; 910 | union acpi_object in_obj; 911 | struct acpi_buffer output; 912 | union acpi_object out_obj; 913 | acpi_status status; 914 | 915 | params.count = 1; 916 | params.pointer = &in_obj; 917 | in_obj.type = ACPI_TYPE_INTEGER; 918 | in_obj.integer.value = value; 919 | 920 | output.length = sizeof(out_obj); 921 | output.pointer = &out_obj; 922 | 923 | status = acpi_evaluate_object(handle, name, ¶ms, &output); 924 | if (status == AE_OK) { 925 | if (result != NULL) { 926 | if (out_obj.type != ACPI_TYPE_INTEGER) { 927 | pr_warn("acpi_evaluate_object bad " 928 | "return type\n"); 929 | return -1; 930 | } 931 | *result = out_obj.integer.value; 932 | } 933 | return 0; 934 | } 935 | 936 | pr_warn("acpi_evaluate_object failed\n"); 937 | 938 | return -1; 939 | } 940 | 941 | static int acpi_callsetfunc_buffer(acpi_handle handle, u64 value, 942 | u8 array[], unsigned int size) 943 | { 944 | u8 buffer[sizeof(value)]; 945 | int length = -1; 946 | struct acpi_object_list params; 947 | union acpi_object in_obj; 948 | union acpi_object *values; 949 | struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 950 | acpi_status status; 951 | 952 | if (!array || !size) 953 | return length; 954 | 955 | /* use a buffer type as parameter to overcome any 32 bits ACPI limit */ 956 | memcpy(buffer, &value, sizeof(buffer)); 957 | 958 | params.count = 1; 959 | params.pointer = &in_obj; 960 | in_obj.type = ACPI_TYPE_BUFFER; 961 | in_obj.buffer.length = sizeof(buffer); 962 | in_obj.buffer.pointer = buffer; 963 | 964 | /* since SN06 is the only known method returning a buffer we 965 | * can hard code it, it is not necessary to have a parameter 966 | */ 967 | status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, 968 | &output); 969 | values = (union acpi_object *) output.pointer; 970 | if (ACPI_FAILURE(status) || !values) { 971 | dprintk("acpi_evaluate_object failed\n"); 972 | goto error; 973 | } 974 | 975 | /* some buggy DSDTs return integer when the output does 976 | not execede the 4 bytes size 977 | */ 978 | if (values->type == ACPI_TYPE_BUFFER) { 979 | if (values->buffer.length <= 0) 980 | goto error; 981 | 982 | length = size > values->buffer.length ? 983 | values->buffer.length : size; 984 | 985 | memcpy(array, values->buffer.pointer, length); 986 | } else if (values->type == ACPI_TYPE_INTEGER) { 987 | u32 result = values->integer.value; 988 | if (size < 4) 989 | goto error; 990 | 991 | length = 0; 992 | while (length != 4) { 993 | array[length] = result & 0xff; 994 | result >>= 8; 995 | length++; 996 | } 997 | } else { 998 | pr_err("Invalid return object 0x%.2x\n", values->type); 999 | goto error; 1000 | } 1001 | 1002 | error: 1003 | kfree(output.pointer); 1004 | return length; 1005 | } 1006 | 1007 | struct sony_nc_handles { 1008 | u16 cap[0x10]; 1009 | struct device_attribute devattr; 1010 | }; 1011 | 1012 | static struct sony_nc_handles *handles; 1013 | 1014 | static ssize_t sony_nc_handles_show(struct device *dev, 1015 | struct device_attribute *attr, char *buffer) 1016 | { 1017 | ssize_t len = 0; 1018 | int i; 1019 | 1020 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { 1021 | len += snprintf(buffer + len, PAGE_SIZE - len, "0x%.4x ", 1022 | handles->cap[i]); 1023 | } 1024 | len += snprintf(buffer + len, PAGE_SIZE - len, "\n"); 1025 | 1026 | return len; 1027 | } 1028 | 1029 | static int sony_nc_handles_setup(struct platform_device *pd) 1030 | { 1031 | unsigned int i, result; 1032 | 1033 | handles = kzalloc(sizeof(*handles), GFP_KERNEL); 1034 | if (!handles) 1035 | return -ENOMEM; 1036 | 1037 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { 1038 | if (!acpi_callsetfunc(sony_nc_acpi_handle, 1039 | "SN00", i + 0x20, &result)) { 1040 | dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", 1041 | result, i); 1042 | handles->cap[i] = result; 1043 | } 1044 | } 1045 | 1046 | if (debug) { 1047 | sysfs_attr_init(&handles->devattr.attr); 1048 | handles->devattr.attr.name = "handles"; 1049 | handles->devattr.attr.mode = S_IRUGO; 1050 | handles->devattr.show = sony_nc_handles_show; 1051 | 1052 | /* allow reading capabilities via sysfs */ 1053 | if (device_create_file(&pd->dev, &handles->devattr)) { 1054 | kfree(handles); 1055 | handles = NULL; 1056 | return -1; 1057 | } 1058 | } 1059 | 1060 | return 0; 1061 | } 1062 | 1063 | static int sony_nc_handles_cleanup(struct platform_device *pd) 1064 | { 1065 | if (handles) { 1066 | if (debug) 1067 | device_remove_file(&pd->dev, &handles->devattr); 1068 | kfree(handles); 1069 | handles = NULL; 1070 | } 1071 | return 0; 1072 | } 1073 | 1074 | static int sony_find_snc_handle(unsigned int handle) 1075 | { 1076 | int i; 1077 | 1078 | /* not initialized yet or invalid handle, return early */ 1079 | if (!handles || !handle) 1080 | return -1; 1081 | 1082 | for (i = 0; i < 0x10; i++) { 1083 | if (handles->cap[i] == handle) { 1084 | dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", 1085 | handle, i); 1086 | return i; 1087 | } 1088 | } 1089 | dprintk("handle 0x%.4x not found\n", handle); 1090 | return -1; 1091 | } 1092 | 1093 | /* call command method SN07, accepts a 32 bit integer, returns a integer */ 1094 | static int sony_call_snc_handle(unsigned int handle, unsigned int argument, 1095 | unsigned int *result) 1096 | { 1097 | int ret = 0; 1098 | int offset = sony_find_snc_handle(handle); 1099 | 1100 | if (offset < 0) 1101 | return -1; 1102 | 1103 | /* max 32 bit wide argument, for wider input use SN06 */ 1104 | ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, 1105 | result); 1106 | dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, 1107 | *result); 1108 | return ret; 1109 | } 1110 | 1111 | /* call command method SN06, accepts a wide input buffer, returns a buffer */ 1112 | static int sony_call_snc_handle_buffer(unsigned int handle, u64 argument, 1113 | u8 result[], unsigned int size) 1114 | { 1115 | int ret = 0; 1116 | int offset = sony_find_snc_handle(handle); 1117 | 1118 | if (offset < 0) 1119 | return -1; 1120 | 1121 | ret = acpi_callsetfunc_buffer(sony_nc_acpi_handle, 1122 | offset | argument, result, size); 1123 | dprintk("called SN06 with 0x%.4llx (%u bytes read)\n", 1124 | offset | argument, ret); 1125 | 1126 | return ret; 1127 | } 1128 | 1129 | /* 1130 | * sony_nc_values input/output validate functions 1131 | */ 1132 | 1133 | /* brightness_default_validate: 1134 | * 1135 | * manipulate input output values to keep consistency with the 1136 | * backlight framework for which brightness values are 0-based. 1137 | */ 1138 | static int brightness_default_validate(const int direction, const int value) 1139 | { 1140 | switch (direction) { 1141 | case SNC_VALIDATE_OUT: 1142 | return value - 1; 1143 | case SNC_VALIDATE_IN: 1144 | if (value >= 0 && value < SONY_MAX_BRIGHTNESS) 1145 | return value + 1; 1146 | } 1147 | return -EINVAL; 1148 | } 1149 | 1150 | /* boolean_validate: 1151 | * 1152 | * on input validate boolean values 0/1, on output just pass the 1153 | * received value. 1154 | */ 1155 | static int boolean_validate(const int direction, const int value) 1156 | { 1157 | if (direction == SNC_VALIDATE_IN) { 1158 | if (value != 0 && value != 1) 1159 | return -EINVAL; 1160 | } 1161 | return value; 1162 | } 1163 | 1164 | /* 1165 | * Sysfs show/store common to all sony_nc_values 1166 | */ 1167 | static ssize_t sony_nc_sysfs_show(struct device *dev, 1168 | struct device_attribute *attr, 1169 | char *buffer) 1170 | { 1171 | unsigned int value; 1172 | struct sony_nc_value *item = 1173 | container_of(attr, struct sony_nc_value, devattr); 1174 | 1175 | if (!*item->acpiget) 1176 | return -EIO; 1177 | 1178 | if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0) 1179 | return -EIO; 1180 | 1181 | if (item->validate) 1182 | value = item->validate(SNC_VALIDATE_OUT, value); 1183 | 1184 | return snprintf(buffer, PAGE_SIZE, "%d\n", value); 1185 | } 1186 | 1187 | static ssize_t sony_nc_sysfs_store(struct device *dev, 1188 | struct device_attribute *attr, 1189 | const char *buffer, size_t count) 1190 | { 1191 | unsigned long value; 1192 | struct sony_nc_value *item = 1193 | container_of(attr, struct sony_nc_value, devattr); 1194 | 1195 | if (!item->acpiset) 1196 | return -EIO; 1197 | 1198 | if (count > 31) 1199 | return -EINVAL; 1200 | 1201 | if (strict_strtoul(buffer, 10, &value)) 1202 | return -EINVAL; 1203 | 1204 | if (item->validate) 1205 | value = item->validate(SNC_VALIDATE_IN, value); 1206 | 1207 | if (value < 0) 1208 | return value; 1209 | 1210 | if (acpi_callsetfunc(sony_nc_acpi_handle, 1211 | *item->acpiset, value, NULL) < 0) 1212 | return -EIO; 1213 | item->value = value; 1214 | item->valid = 1; 1215 | return count; 1216 | } 1217 | 1218 | /* 1219 | * New SNC-only Vaios event mapping to driver known keys 1220 | */ 1221 | struct sony_nc_event { 1222 | u8 data; 1223 | u8 event; 1224 | }; 1225 | 1226 | static struct sony_nc_event sony_100_events[] = { 1227 | { 0x81, SONYPI_EVENT_FNKEY_F1 }, 1228 | { 0x01, SONYPI_EVENT_FNKEY_RELEASED }, 1229 | { 0x82, SONYPI_EVENT_FNKEY_F2 }, 1230 | { 0x02, SONYPI_EVENT_FNKEY_RELEASED }, 1231 | { 0x83, SONYPI_EVENT_FNKEY_F3 }, 1232 | { 0x03, SONYPI_EVENT_FNKEY_RELEASED }, 1233 | { 0x84, SONYPI_EVENT_FNKEY_F4 }, 1234 | { 0x04, SONYPI_EVENT_FNKEY_RELEASED }, 1235 | { 0x85, SONYPI_EVENT_FNKEY_F5 }, 1236 | { 0x05, SONYPI_EVENT_FNKEY_RELEASED }, 1237 | { 0x86, SONYPI_EVENT_FNKEY_F6 }, 1238 | { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, 1239 | { 0x87, SONYPI_EVENT_FNKEY_F7 }, 1240 | { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, 1241 | { 0x88, SONYPI_EVENT_FNKEY_F8 }, 1242 | { 0x08, SONYPI_EVENT_FNKEY_RELEASED }, 1243 | { 0x89, SONYPI_EVENT_FNKEY_F9 }, 1244 | { 0x09, SONYPI_EVENT_FNKEY_RELEASED }, 1245 | { 0x8A, SONYPI_EVENT_FNKEY_F10 }, 1246 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, 1247 | { 0x8B, SONYPI_EVENT_FNKEY_F11 }, 1248 | { 0x0B, SONYPI_EVENT_FNKEY_RELEASED }, 1249 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, 1250 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, 1251 | { 0x90, SONYPI_EVENT_PKEY_P1 }, 1252 | { 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1253 | { 0x91, SONYPI_EVENT_PKEY_P2 }, 1254 | { 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1255 | { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, 1256 | { 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1257 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, 1258 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1259 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, 1260 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1261 | { 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED }, 1262 | { 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1263 | { 0xa5, SONYPI_EVENT_VENDOR_PRESSED }, 1264 | { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1265 | { 0xa6, SONYPI_EVENT_HELP_PRESSED }, 1266 | { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1267 | { 0, 0 }, 1268 | }; 1269 | 1270 | static struct sony_nc_event sony_127_events[] = { 1271 | { 0x81, SONYPI_EVENT_MODEKEY_PRESSED }, 1272 | { 0x01, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1273 | { 0x82, SONYPI_EVENT_PKEY_P1 }, 1274 | { 0x02, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1275 | { 0x83, SONYPI_EVENT_PKEY_P2 }, 1276 | { 0x03, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1277 | { 0x84, SONYPI_EVENT_PKEY_P3 }, 1278 | { 0x04, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1279 | { 0x85, SONYPI_EVENT_PKEY_P4 }, 1280 | { 0x05, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1281 | { 0x86, SONYPI_EVENT_PKEY_P5 }, 1282 | { 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1283 | { 0x87, SONYPI_EVENT_SETTINGKEY_PRESSED }, 1284 | { 0x07, SONYPI_EVENT_ANYBUTTON_RELEASED }, 1285 | { 0, 0 }, 1286 | }; 1287 | 1288 | /* 1289 | * ACPI device 1290 | */ 1291 | static int sony_nc_function_setup(unsigned int handle) 1292 | { 1293 | unsigned int result; 1294 | 1295 | if (handle == 0x0102) 1296 | sony_call_snc_handle(0x0102, 0x100, &result); 1297 | else 1298 | sony_call_snc_handle(handle, 0, &result); 1299 | 1300 | return 0; 1301 | } 1302 | 1303 | static int sony_nc_hotkeys_decode(unsigned int handle) 1304 | { 1305 | int ret = -EINVAL; 1306 | unsigned int result = 0; 1307 | struct sony_nc_event *key_event; 1308 | 1309 | if (sony_call_snc_handle(handle, 0x200, &result)) { 1310 | dprintk("sony_nc_hotkeys_decode," 1311 | " unable to retrieve the hotkey\n"); 1312 | } else { 1313 | result &= 0xff; 1314 | 1315 | if (handle == 0x100) 1316 | key_event = sony_100_events; 1317 | else 1318 | key_event = sony_127_events; 1319 | 1320 | for (; key_event->data; key_event++) { 1321 | if (key_event->data == result) { 1322 | ret = key_event->event; 1323 | break; 1324 | } 1325 | } 1326 | 1327 | if (!key_event->data) 1328 | pr_info("Unknown hotkey 0x%.2x (handle 0x%.2x)\n", 1329 | result, handle); 1330 | else 1331 | dprintk("sony_nc_hotkeys_decode, hotkey 0x%.2x decoded " 1332 | "to event 0x%.2x\n", result, ret); 1333 | } 1334 | 1335 | return ret; 1336 | } 1337 | 1338 | enum sony_nc_rfkill { 1339 | SONY_WIFI, 1340 | SONY_BLUETOOTH, 1341 | SONY_WWAN, 1342 | SONY_WIMAX, 1343 | N_SONY_RFKILL, 1344 | }; 1345 | struct sony_rfkill_data { 1346 | struct rfkill *devices[N_SONY_RFKILL]; 1347 | const unsigned int address[N_SONY_RFKILL]; 1348 | unsigned int handle; 1349 | }; 1350 | static struct sony_rfkill_data sony_rfkill = { 1351 | {NULL}, {0x300, 0x500, 0x700, 0x900}, 0}; 1352 | 1353 | static int sony_nc_rfkill_update_wwan(void) 1354 | { 1355 | unsigned int result, cmd; 1356 | bool battery; 1357 | bool swblock; 1358 | 1359 | if (sony_call_snc_handle(sony_rfkill.handle, 0x0200, &result)) 1360 | return -EIO; 1361 | battery = !!(result & 0x2); 1362 | 1363 | /* retrieve the device block state */ 1364 | if (sony_call_snc_handle(sony_rfkill.handle, 1365 | sony_rfkill.address[SONY_WWAN], &result)) 1366 | return -EIO; 1367 | swblock = !(result & 0x02); 1368 | 1369 | if (battery && !swblock) { 1370 | /* set the power state according with swblock */ 1371 | cmd = 0xff0000; 1372 | } else if (!battery && !swblock) { 1373 | swblock = true; 1374 | cmd = 0x20000; 1375 | } else { 1376 | return 0; 1377 | } 1378 | 1379 | cmd |= sony_rfkill.address[SONY_WWAN] + 0x100; 1380 | 1381 | /* set the power state */ 1382 | sony_call_snc_handle(sony_rfkill.handle, cmd, &result); 1383 | 1384 | /* update the rfkill sw state */ 1385 | rfkill_set_sw_state(sony_rfkill.devices[SONY_WWAN], swblock); 1386 | 1387 | return 0; 1388 | } 1389 | 1390 | static int sony_nc_get_rfkill_hwblock(void) 1391 | { 1392 | unsigned int result; 1393 | 1394 | if (sony_call_snc_handle(sony_rfkill.handle, 0x200, &result)) 1395 | return -1; 1396 | 1397 | return result & 0x1; 1398 | } 1399 | 1400 | static void sony_nc_rfkill_cleanup(void) 1401 | { 1402 | int i; 1403 | 1404 | for (i = 0; i < N_SONY_RFKILL; i++) { 1405 | if (sony_rfkill.devices[i]) { 1406 | rfkill_unregister(sony_rfkill.devices[i]); 1407 | rfkill_destroy(sony_rfkill.devices[i]); 1408 | } 1409 | } 1410 | } 1411 | 1412 | static int sony_nc_rfkill_set(void *data, bool blocked) 1413 | { 1414 | unsigned int result, argument = sony_rfkill.address[(long) data]; 1415 | 1416 | /* wwan state change not allowed when the battery is not present */ 1417 | sony_call_snc_handle(sony_rfkill.handle, 0x0200, &result); 1418 | if (((long) data == SONY_WWAN) && !(result & 0x2)) { 1419 | if (!blocked) { 1420 | /* notify user space: the battery must be present */ 1421 | acpi_bus_generate_proc_event(sony_nc_acpi_device, 1422 | 2, 2); 1423 | acpi_bus_generate_netlink_event( 1424 | sony_nc_acpi_device->pnp.device_class, 1425 | dev_name(&sony_nc_acpi_device->dev), 1426 | 2, 2); 1427 | } 1428 | 1429 | return -1; 1430 | } 1431 | 1432 | /* do not force an already set state */ 1433 | sony_call_snc_handle(sony_rfkill.handle, argument, &result); 1434 | if ((result & 0x1) == !blocked) 1435 | return 0; 1436 | 1437 | argument += 0x100; 1438 | if (!blocked) 1439 | argument |= 0xff0000; 1440 | 1441 | return sony_call_snc_handle(sony_rfkill.handle, argument, &result); 1442 | } 1443 | 1444 | static const struct rfkill_ops sony_rfkill_ops = { 1445 | .set_block = sony_nc_rfkill_set, 1446 | }; 1447 | 1448 | static int sony_nc_setup_rfkill(struct acpi_device *device, 1449 | enum sony_nc_rfkill nc_type) 1450 | { 1451 | int err = 0; 1452 | struct rfkill *rfk; 1453 | enum rfkill_type type; 1454 | const char *name; 1455 | unsigned int result; 1456 | bool hwblock, swblock, wwblock; 1457 | 1458 | switch (nc_type) { 1459 | case SONY_WIFI: 1460 | type = RFKILL_TYPE_WLAN; 1461 | name = "sony-wifi"; 1462 | break; 1463 | case SONY_BLUETOOTH: 1464 | type = RFKILL_TYPE_BLUETOOTH; 1465 | name = "sony-bluetooth"; 1466 | break; 1467 | case SONY_WWAN: 1468 | type = RFKILL_TYPE_WWAN; 1469 | name = "sony-wwan"; 1470 | break; 1471 | case SONY_WIMAX: 1472 | type = RFKILL_TYPE_WIMAX; 1473 | name = "sony-wimax"; 1474 | break; 1475 | default: 1476 | return -EINVAL; 1477 | } 1478 | 1479 | rfk = rfkill_alloc(name, &device->dev, type, 1480 | &sony_rfkill_ops, (void *)nc_type); 1481 | if (!rfk) 1482 | return -ENOMEM; 1483 | 1484 | sony_call_snc_handle(sony_rfkill.handle, 0x200, &result); 1485 | hwblock = !(result & 0x1); 1486 | wwblock = !(result & 0x2); 1487 | 1488 | result = 0; 1489 | sony_call_snc_handle(sony_rfkill.handle, sony_rfkill.address[nc_type], 1490 | &result); 1491 | swblock = !(result & 0x2); 1492 | 1493 | /* hard block the WWAN module if no battery is present */ 1494 | if ((nc_type == SONY_WWAN) && wwblock) 1495 | swblock = true; 1496 | 1497 | rfkill_init_sw_state(rfk, swblock); 1498 | rfkill_set_hw_state(rfk, hwblock); 1499 | 1500 | err = rfkill_register(rfk); 1501 | if (err) { 1502 | rfkill_destroy(rfk); 1503 | return err; 1504 | } 1505 | sony_rfkill.devices[nc_type] = rfk; 1506 | return err; 1507 | } 1508 | 1509 | static void sony_nc_rfkill_update(void) 1510 | { 1511 | enum sony_nc_rfkill i; 1512 | unsigned int result; 1513 | bool hwblock, swblock, wwblock; 1514 | 1515 | sony_call_snc_handle(sony_rfkill.handle, 0x200, &result); 1516 | hwblock = !(result & 0x1); 1517 | wwblock = !(result & 0x2); 1518 | 1519 | for (i = 0; i < N_SONY_RFKILL; i++) { 1520 | unsigned int argument = sony_rfkill.address[i]; 1521 | 1522 | if (!sony_rfkill.devices[i]) 1523 | continue; 1524 | 1525 | sony_call_snc_handle(sony_rfkill.handle, argument, &result); 1526 | /* block wwan when no battery is present */ 1527 | if ((i == SONY_WWAN) && wwblock) 1528 | swblock = true; 1529 | else 1530 | swblock = !(result & 0x2); 1531 | 1532 | rfkill_set_states(sony_rfkill.devices[i], 1533 | swblock, hwblock); 1534 | } 1535 | } 1536 | 1537 | static int sony_nc_rfkill_setup(struct acpi_device *device, unsigned int handle) 1538 | { 1539 | #define RFKILL_BUFF_SIZE 8 1540 | u8 dev_code, i, buff[RFKILL_BUFF_SIZE] = { 0 }; 1541 | 1542 | sony_rfkill.handle = handle; 1543 | 1544 | /* need to read the whole buffer returned by the acpi call to SN06 1545 | * here otherwise we may miss some features 1546 | */ 1547 | if (sony_call_snc_handle_buffer(sony_rfkill.handle, 0x000, 1548 | buff, RFKILL_BUFF_SIZE) < 0) 1549 | return -EIO; 1550 | 1551 | /* the buffer is filled with magic numbers describing the devices 1552 | * available, 0xff terminates the enumeration 1553 | */ 1554 | for (i = 0; i < RFKILL_BUFF_SIZE; i++) { 1555 | 1556 | dev_code = buff[i]; 1557 | if (dev_code == 0xff) 1558 | break; 1559 | 1560 | /* 1561 | known codes: 1562 | 1563 | 0x00 WLAN 1564 | 0x10 BLUETOOTH 1565 | 0x20 WWAN GPRS-EDGE 1566 | 0x21 WWAN HSDPA 1567 | 0x22 WWAN EV-DO 1568 | 0x23 WWAN GPS 1569 | 0x25 Gobi WWAN no GPS 1570 | 0x26 Gobi WWAN + GPS 1571 | 0x28 Gobi WWAN no GPS 1572 | 0x29 Gobi WWAN + GPS 1573 | 0x50 Gobi WWAN no GPS 1574 | 0x51 Gobi WWAN + GPS 1575 | 0x30 WIMAX 1576 | 0x70 no SIM card slot 1577 | 0x71 SIM card slot 1578 | */ 1579 | dprintk("Radio devices, looking at 0x%.2x\n", dev_code); 1580 | 1581 | if (dev_code == 0 && !sony_rfkill.devices[SONY_WIFI]) 1582 | sony_nc_setup_rfkill(device, SONY_WIFI); 1583 | 1584 | if (dev_code == 0x10 && !sony_rfkill.devices[SONY_BLUETOOTH]) 1585 | sony_nc_setup_rfkill(device, SONY_BLUETOOTH); 1586 | 1587 | if (((0xf0 & dev_code) == 0x20 || (0xf0 & dev_code) == 0x50) && 1588 | !sony_rfkill.devices[SONY_WWAN]) 1589 | sony_nc_setup_rfkill(device, SONY_WWAN); 1590 | 1591 | if (dev_code == 0x30 && !sony_rfkill.devices[SONY_WIMAX]) 1592 | sony_nc_setup_rfkill(device, SONY_WIMAX); 1593 | } 1594 | 1595 | return 0; 1596 | } 1597 | 1598 | /* ALS controlled backlight feature */ 1599 | /* generic ALS data and interface */ 1600 | #define ALS_TABLE_SIZE 25 1601 | 1602 | struct als_device_ops { 1603 | int (*init)(const u8 defaults[]); 1604 | int (*exit)(void); 1605 | int (*event_handler)(void); 1606 | int (*set_power)(unsigned int); 1607 | int (*get_power)(unsigned int *); 1608 | int (*get_lux)(unsigned int *, unsigned int *); 1609 | int (*get_kelvin)(unsigned int *); 1610 | }; 1611 | 1612 | static struct sony_als_device { 1613 | unsigned int handle; 1614 | 1615 | unsigned int power; 1616 | unsigned int managed; 1617 | 1618 | unsigned int levels_num; 1619 | u8 *levels; 1620 | unsigned int defaults_num; 1621 | u8 *defaults; 1622 | u8 parameters[ALS_TABLE_SIZE]; 1623 | 1624 | /* common device operations */ 1625 | const struct als_device_ops *ops; 1626 | 1627 | /* basic ALS sys interface */ 1628 | unsigned int attrs_num; 1629 | struct device_attribute attrs[7]; 1630 | } *sony_als; 1631 | 1632 | /* 1633 | model specific ALS data and controls 1634 | TAOS TSL256x device data 1635 | */ 1636 | #define LUX_SHIFT_BITS 16 /* for non-floating point math */ 1637 | /* scale 100000 multiplied fractional coefficients rounding the values */ 1638 | #define SCALE(u) ((((((u64) u) << LUX_SHIFT_BITS) / 10000) + 5) / 10) 1639 | 1640 | #define TSL256X_REG_CTRL 0x00 1641 | #define TSL256X_REG_TIMING 0x01 1642 | #define TSL256X_REG_TLOW 0x02 1643 | #define TSL256X_REG_THIGH 0x04 1644 | #define TSL256X_REG_INT 0x06 1645 | #define TSL256X_REG_ID 0x0a 1646 | #define TSL256X_REG_DATA0 0x0c 1647 | #define TSL256X_REG_DATA1 0x0e 1648 | 1649 | #define TSL256X_POWER_ON 0x03 1650 | #define TSL256X_POWER_OFF 0x00 1651 | 1652 | #define TSL256X_POWER_MASK 0x03 1653 | #define TSL256X_INT_MASK 0x10 1654 | 1655 | struct tsl256x_coeff { 1656 | u32 ratio; 1657 | u32 ch0; 1658 | u32 ch1; 1659 | u32 ka; 1660 | s32 kb; 1661 | }; 1662 | 1663 | struct tsl256x_data { 1664 | unsigned int gaintime; 1665 | unsigned int periods; 1666 | u8 *defaults; 1667 | struct tsl256x_coeff const *coeff_table; 1668 | }; 1669 | static struct tsl256x_data *tsl256x_handle; 1670 | 1671 | static const struct tsl256x_coeff tsl256x_coeff_fn[] = { 1672 | { 1673 | .ratio = SCALE(12500), /* 0.125 * 2^LUX_SHIFT_BITS */ 1674 | .ch0 = SCALE(3040), /* 0.0304 * 2^LUX_SHIFT_BITS */ 1675 | .ch1 = SCALE(2720), /* 0.0272 * 2^LUX_SHIFT_BITS */ 1676 | .ka = SCALE(313550000), 1677 | .kb = -10651, 1678 | }, { 1679 | .ratio = SCALE(25000), /* 0.250 * 2^LUX_SHIFT_BITS */ 1680 | .ch0 = SCALE(3250), /* 0.0325 * 2^LUX_SHIFT_BITS */ 1681 | .ch1 = SCALE(4400), /* 0.0440 * 2^LUX_SHIFT_BITS */ 1682 | .ka = SCALE(203390000), 1683 | .kb = -2341, 1684 | }, { 1685 | .ratio = SCALE(37500), /* 0.375 * 2^LUX_SHIFT_BITS */ 1686 | .ch0 = SCALE(3510), /* 0.0351 * 2^LUX_SHIFT_BITS */ 1687 | .ch1 = SCALE(5440), /* 0.0544 * 2^LUX_SHIFT_BITS */ 1688 | .ka = SCALE(152180000), 1689 | .kb = 157, 1690 | }, { 1691 | .ratio = SCALE(50000), /* 0.50 * 2^LUX_SHIFT_BITS */ 1692 | .ch0 = SCALE(3810), /* 0.0381 * 2^LUX_SHIFT_BITS */ 1693 | .ch1 = SCALE(6240), /* 0.0624 * 2^LUX_SHIFT_BITS */ 1694 | .ka = SCALE(163580000), 1695 | .kb = -145, 1696 | }, { 1697 | .ratio = SCALE(61000), /* 0.61 * 2^LUX_SHIFT_BITS */ 1698 | .ch0 = SCALE(2240), /* 0.0224 * 2^LUX_SHIFT_BITS */ 1699 | .ch1 = SCALE(3100), /* 0.0310 * 2^LUX_SHIFT_BITS */ 1700 | .ka = SCALE(180800000), 1701 | .kb = -495, 1702 | }, { 1703 | .ratio = SCALE(80000), /* 0.80 * 2^LUX_SHIFT_BITS */ 1704 | .ch0 = SCALE(1280), /* 0.0128 * 2^LUX_SHIFT_BITS */ 1705 | .ch1 = SCALE(1530), /* 0.0153 * 2^LUX_SHIFT_BITS */ 1706 | .ka = SCALE(197340000), 1707 | .kb = -765 1708 | }, { 1709 | .ratio = SCALE(130000),/* 1.3 * 2^LUX_SHIFT_BITS */ 1710 | .ch0 = SCALE(146), /* 0.00146 * 2^LUX_SHIFT_BITS */ 1711 | .ch1 = SCALE(112), /* 0.00112 * 2^LUX_SHIFT_BITS */ 1712 | .ka = SCALE(182900000), 1713 | .kb = -608, 1714 | }, { 1715 | .ratio = UINT_MAX, /* for higher ratios */ 1716 | .ch0 = 0, 1717 | .ch1 = 0, 1718 | .ka = 0, 1719 | .kb = 830, 1720 | } 1721 | }; 1722 | 1723 | static const struct tsl256x_coeff tsl256x_coeff_cs[] = { 1724 | { 1725 | .ratio = SCALE(13000), /* 0.130 * 2^LUX_SHIFT_BITS */ 1726 | .ch0 = SCALE(3150), /* 0.0315 * 2^LUX_SHIFT_BITS */ 1727 | .ch1 = SCALE(2620), /* 0.0262 * 2^LUX_SHIFT_BITS */ 1728 | .ka = SCALE(300370000), 1729 | .kb = -9587, 1730 | }, { 1731 | .ratio = SCALE(26000), /* 0.260 * 2^LUX_SHIFT_BITS */ 1732 | .ch0 = SCALE(3370), /* 0.0337 * 2^LUX_SHIFT_BITS */ 1733 | .ch1 = SCALE(4300), /* 0.0430 * 2^LUX_SHIFT_BITS */ 1734 | .ka = SCALE(194270000), 1735 | .kb = -1824, 1736 | }, { 1737 | .ratio = SCALE(39000), /* 0.390 * 2^LUX_SHIFT_BITS */ 1738 | .ch0 = SCALE(3630), /* 0.0363 * 2^LUX_SHIFT_BITS */ 1739 | .ch1 = SCALE(5290), /* 0.0529 * 2^LUX_SHIFT_BITS */ 1740 | .ka = SCALE(152520000), 1741 | .kb = 145, 1742 | }, { 1743 | .ratio = SCALE(52000), /* 0.520 * 2^LUX_SHIFT_BITS */ 1744 | .ch0 = SCALE(3920), /* 0.0392 * 2^LUX_SHIFT_BITS */ 1745 | .ch1 = SCALE(6050), /* 0.0605 * 2^LUX_SHIFT_BITS */ 1746 | .ka = SCALE(165960000), 1747 | .kb = -200, 1748 | }, { 1749 | .ratio = SCALE(65000), /* 0.650 * 2^LUX_SHIFT_BITS */ 1750 | .ch0 = SCALE(2290), /* 0.0229 * 2^LUX_SHIFT_BITS */ 1751 | .ch1 = SCALE(2910), /* 0.0291 * 2^LUX_SHIFT_BITS */ 1752 | .ka = SCALE(184800000), 1753 | .kb = -566, 1754 | }, { 1755 | .ratio = SCALE(80000), /* 0.800 * 2^LUX_SHIFT_BITS */ 1756 | .ch0 = SCALE(1570), /* 0.0157 * 2^LUX_SHIFT_BITS */ 1757 | .ch1 = SCALE(1800), /* 0.0180 * 2^LUX_SHIFT_BITS */ 1758 | .ka = SCALE(199220000), 1759 | .kb = -791, 1760 | }, { 1761 | .ratio = SCALE(130000),/* 0.130 * 2^LUX_SHIFT_BITS */ 1762 | .ch0 = SCALE(338), /* 0.00338 * 2^LUX_SHIFT_BITS */ 1763 | .ch1 = SCALE(260), /* 0.00260 * 2^LUX_SHIFT_BITS */ 1764 | .ka = SCALE(182900000), 1765 | .kb = -608, 1766 | }, { 1767 | .ratio = UINT_MAX, /* for higher ratios */ 1768 | .ch0 = 0, 1769 | .ch1 = 0, 1770 | .ka = 0, 1771 | .kb = 830, 1772 | } 1773 | }; 1774 | 1775 | /* TAOS helper & control functions */ 1776 | static inline int tsl256x_exec_writebyte(unsigned int reg, 1777 | unsigned int const *value) 1778 | { 1779 | unsigned int result; 1780 | 1781 | return (sony_call_snc_handle(sony_als->handle, (*value << 0x18) | 1782 | (reg << 0x10) | 0x800500, &result) || !(result & 0x01)) 1783 | ? -EIO : 0; 1784 | } 1785 | 1786 | static inline int tsl256x_exec_writeword(unsigned int reg, 1787 | unsigned int const *value) 1788 | { 1789 | u8 result[1]; 1790 | u64 arg = *value; 1791 | 1792 | /* using sony_call_snc_handle_buffer due to possible input overflows */ 1793 | return ((sony_call_snc_handle_buffer(sony_als->handle, (arg << 0x18) | 1794 | (reg << 0x10) | 0xA00700, result, 1) < 0) || 1795 | !(result[0] & 0x01)) ? -EIO : 0; 1796 | } 1797 | 1798 | static inline int tsl256x_exec_readbyte(unsigned int reg, unsigned int *result) 1799 | { 1800 | if (sony_call_snc_handle(sony_als->handle, (reg << 0x10) 1801 | | 0x800400, result) || !(*result & 0x01)) 1802 | return -EIO; 1803 | *result = (*result >> 0x08) & 0xFF; 1804 | 1805 | return 0; 1806 | } 1807 | 1808 | static inline int tsl256x_exec_readword(unsigned int reg, unsigned int *result) 1809 | { 1810 | if (sony_call_snc_handle(sony_als->handle, (reg << 0x10) 1811 | | 0xA00600, result) || !(*result & 0x01)) 1812 | return -EIO; 1813 | *result = (*result >> 0x08) & 0xFFFF; 1814 | 1815 | return 0; 1816 | } 1817 | 1818 | static int tsl256x_interrupt_ctrls(unsigned int *interrupt, 1819 | unsigned int *periods) 1820 | { 1821 | unsigned int value, result; 1822 | 1823 | /* if no interrupt parameter, retrieve interrupt status */ 1824 | if (!interrupt) { 1825 | if (tsl256x_exec_readbyte(TSL256X_REG_INT, &result)) 1826 | return -EIO; 1827 | 1828 | value = (result & TSL256X_INT_MASK); 1829 | } else { 1830 | value = *interrupt << 0x04; 1831 | } 1832 | 1833 | /* if no periods provided use the last one set */ 1834 | value |= (periods ? *periods : tsl256x_handle->periods); 1835 | 1836 | if (tsl256x_exec_writebyte(TSL256X_REG_INT, &value)) 1837 | return -EIO; 1838 | 1839 | if (periods) 1840 | tsl256x_handle->periods = *periods; 1841 | 1842 | return 0; 1843 | } 1844 | 1845 | static int tsl256x_setup(void) 1846 | { 1847 | unsigned int interr = 1, zero = 0; 1848 | 1849 | /* 1850 | * reset the threshold settings to trigger an event as soon 1851 | * as the event goes on, forcing a backlight adaptation to 1852 | * the current lighting conditions 1853 | */ 1854 | tsl256x_exec_writeword(TSL256X_REG_TLOW, &zero); 1855 | tsl256x_exec_writeword(TSL256X_REG_THIGH, &zero); 1856 | 1857 | /* set gain and time */ 1858 | if (tsl256x_exec_writebyte(TSL256X_REG_TIMING, 1859 | &tsl256x_handle->gaintime)) 1860 | return -EIO; 1861 | 1862 | /* restore persistence value and enable the interrupt generation */ 1863 | if (tsl256x_interrupt_ctrls(&interr, &tsl256x_handle->periods)) 1864 | return -EIO; 1865 | 1866 | return 0; 1867 | } 1868 | 1869 | static int tsl256x_set_power(unsigned int status) 1870 | { 1871 | int ret; 1872 | 1873 | if (status) { 1874 | ret = tsl256x_setup(); 1875 | if (ret) 1876 | return ret; 1877 | } 1878 | 1879 | status = status ? TSL256X_POWER_ON : TSL256X_POWER_OFF; 1880 | ret = tsl256x_exec_writebyte(TSL256X_REG_CTRL, &status); 1881 | 1882 | return ret; 1883 | } 1884 | 1885 | static int tsl256x_get_power(unsigned int *status) 1886 | { 1887 | if (tsl256x_exec_readbyte(TSL256X_REG_CTRL, status)) 1888 | return -EIO; 1889 | 1890 | *status = ((*status & TSL256X_POWER_MASK) == TSL256X_POWER_ON) ? 1 : 0; 1891 | 1892 | return 0; 1893 | } 1894 | 1895 | static int tsl256x_get_raw_data(unsigned int *ch0, unsigned int *ch1) 1896 | { 1897 | if (!ch0) 1898 | return -1; 1899 | 1900 | if (tsl256x_exec_readword(TSL256X_REG_DATA0, ch0)) 1901 | return -EIO; 1902 | 1903 | if (ch1) { 1904 | if (tsl256x_exec_readword(TSL256X_REG_DATA1, ch1)) 1905 | return -EIO; 1906 | } 1907 | 1908 | return 0; 1909 | } 1910 | 1911 | static int tsl256x_set_thresholds(const unsigned int *ch0) 1912 | { 1913 | unsigned int tlow, thigh; 1914 | 1915 | tlow = (*ch0 * tsl256x_handle->defaults[0]) / 100; 1916 | thigh = ((*ch0 * tsl256x_handle->defaults[1]) / 100) + 1; 1917 | 1918 | if (thigh > 0xffff) 1919 | thigh = 0xffff; 1920 | 1921 | if (tsl256x_exec_writeword(TSL256X_REG_TLOW, &tlow) || 1922 | tsl256x_exec_writeword(TSL256X_REG_THIGH, &thigh)) 1923 | return -EIO; 1924 | 1925 | return 0; 1926 | } 1927 | 1928 | #define MAX_LUX 1500 1929 | static void tsl256x_calculate_lux(const u32 ch0, const u32 ch1, 1930 | unsigned int *integ, unsigned int *fract) 1931 | { 1932 | /* the raw output from the sensor is just a "count" value, as 1933 | it is the result of the integration of the analog sensor 1934 | signal, the counts-to-lux curve (and its approximation can 1935 | be found on the datasheet. 1936 | */ 1937 | const struct tsl256x_coeff *coeff = tsl256x_handle->coeff_table; 1938 | u32 ratio, temp, integer, fractional; 1939 | 1940 | if (ch0 >= 65535 || ch1 >= 65535) 1941 | goto saturation; 1942 | 1943 | /* STEP 1: ratio calculation, for ch0 & ch1 coeff selection */ 1944 | 1945 | /* protect against division by 0 */ 1946 | ratio = ch0 ? ((ch1 << (LUX_SHIFT_BITS + 1)) / ch0) : UINT_MAX; 1947 | /* round the ratio value */ 1948 | ratio = ratio == UINT_MAX ? ratio : (ratio + 1) >> 1; 1949 | 1950 | /* coeff selection rule */ 1951 | while (coeff->ratio < ratio) 1952 | coeff++; 1953 | 1954 | /* STEP 2: lux calculation formula using the right coeffcients */ 1955 | temp = (ch0 * coeff->ch0) - (ch1 * coeff->ch1); 1956 | /* the sensor is placed under a plastic or glass cover which filters 1957 | a certain ammount of light (depending on that particular material). 1958 | To have an accurate reading, we need to compensate for this loss, 1959 | multiplying for compensation parameter, taken from the DSDT. 1960 | */ 1961 | temp *= tsl256x_handle->defaults[3] / 10; 1962 | 1963 | /* STEP 3: separate integer and fractional part */ 1964 | /* remove the integer part and multiply for the 10^N, N decimals */ 1965 | fractional = (temp % (1 << LUX_SHIFT_BITS)) * 100; /* two decimals */ 1966 | /* scale down the value */ 1967 | fractional >>= LUX_SHIFT_BITS; 1968 | 1969 | /* strip off fractional portion to obtain the integer part */ 1970 | integer = temp >> LUX_SHIFT_BITS; 1971 | 1972 | if (integer > MAX_LUX) 1973 | goto saturation; 1974 | 1975 | *integ = integer; 1976 | *fract = fractional; 1977 | 1978 | return; 1979 | 1980 | saturation: 1981 | *integ = MAX_LUX; 1982 | *fract = 0; 1983 | } 1984 | 1985 | static void tsl256x_calculate_kelvin(const u32 *ch0, const u32 *ch1, 1986 | unsigned int *temperature) 1987 | { 1988 | const struct tsl256x_coeff *coeff = tsl256x_handle->coeff_table; 1989 | u32 ratio; 1990 | 1991 | /* protect against division by 0 */ 1992 | ratio = *ch0 ? ((*ch1 << (LUX_SHIFT_BITS + 1)) / *ch0) : UINT_MAX; 1993 | /* round the ratio value */ 1994 | ratio = (ratio + 1) >> 1; 1995 | 1996 | /* coeff selection rule */ 1997 | while (coeff->ratio < ratio) 1998 | coeff++; 1999 | 2000 | *temperature = ratio ? coeff->ka / ratio + coeff->kb : 0; 2001 | } 2002 | 2003 | static int tsl256x_get_lux(unsigned int *integ, unsigned int *fract) 2004 | { 2005 | int ret = 0; 2006 | unsigned int ch0, ch1; 2007 | 2008 | if (!integ || !fract) 2009 | return -1; 2010 | 2011 | ret = tsl256x_get_raw_data(&ch0, &ch1); 2012 | if (!ret) 2013 | tsl256x_calculate_lux(ch0, ch1, integ, fract); 2014 | 2015 | return ret; 2016 | } 2017 | 2018 | static int tsl256x_get_kelvin(unsigned int *temperature) 2019 | { 2020 | int ret = -1; 2021 | unsigned int ch0, ch1; 2022 | 2023 | if (!temperature) 2024 | return ret; 2025 | 2026 | ret = tsl256x_get_raw_data(&ch0, &ch1); 2027 | if (!ret) 2028 | tsl256x_calculate_kelvin(&ch0, &ch1, temperature); 2029 | 2030 | return ret; 2031 | } 2032 | 2033 | static int tsl256x_get_id(char *model, unsigned int *id, bool *cs) 2034 | { 2035 | int ret; 2036 | unsigned int result; 2037 | char *name = NULL; 2038 | bool unknown = false; 2039 | bool type_cs = false; 2040 | 2041 | ret = tsl256x_exec_readbyte(TSL256X_REG_ID, &result); 2042 | if (ret) 2043 | return ret; 2044 | 2045 | switch ((result >> 0x04) & 0x0F) { 2046 | case 5: 2047 | name = "TAOS TSL2561"; 2048 | break; 2049 | case 4: 2050 | name = "TAOS TSL2560"; 2051 | break; 2052 | case 3: 2053 | name = "TAOS TSL2563"; 2054 | break; 2055 | case 2: 2056 | name = "TAOS TSL2562"; 2057 | break; 2058 | case 1: 2059 | type_cs = true; 2060 | name = "TAOS TSL2561CS"; 2061 | break; 2062 | case 0: 2063 | type_cs = true; 2064 | name = "TAOS TSL2560CS"; 2065 | break; 2066 | default: 2067 | unknown = true; 2068 | break; 2069 | } 2070 | 2071 | if (id) 2072 | *id = result; 2073 | if (cs) 2074 | *cs = type_cs; 2075 | if (model && name) 2076 | strcpy(model, name); 2077 | 2078 | return unknown; 2079 | } 2080 | 2081 | static int tsl256x_event_handler(void) 2082 | { 2083 | unsigned int ch0, interr = 1; 2084 | 2085 | /* wait for the EC to clear the interrupt */ 2086 | /* schedule_timeout_interruptible(msecs_to_jiffies(100)); */ 2087 | /* ...or force the interrupt clear immediately */ 2088 | sony_call_snc_handle(sony_als->handle, 0x04C60500, &interr); 2089 | 2090 | /* read the raw data */ 2091 | tsl256x_get_raw_data(&ch0, NULL); 2092 | 2093 | /* set the thresholds */ 2094 | tsl256x_set_thresholds(&ch0); 2095 | 2096 | /* enable interrupt */ 2097 | tsl256x_interrupt_ctrls(&interr, NULL); 2098 | 2099 | return 0; 2100 | } 2101 | 2102 | static int tsl256x_init(const u8 defaults[]) 2103 | { 2104 | unsigned int id; 2105 | int ret = 0; 2106 | bool cs; /* if CS package choose CS coefficients */ 2107 | char model[64]; 2108 | 2109 | /* detect the device */ 2110 | ret = tsl256x_get_id(model, &id, &cs); 2111 | if (ret < 0) 2112 | return ret; 2113 | if (ret) { 2114 | dprintk("unsupported ALS found (unknown model " 2115 | "number %u rev. %u\n", id >> 4, id & 0x0F); 2116 | return ret; 2117 | } else { 2118 | dprintk("found ALS model number %u rev. %u (%s)\n", 2119 | id >> 4, id & 0x0F, model); 2120 | } 2121 | 2122 | tsl256x_handle = kzalloc(sizeof(struct tsl256x_data), GFP_KERNEL); 2123 | if (!tsl256x_handle) 2124 | return -ENOMEM; 2125 | 2126 | tsl256x_handle->defaults = kzalloc(sizeof(u8) * 4, GFP_KERNEL); 2127 | if (!tsl256x_handle->defaults) { 2128 | kfree(tsl256x_handle); 2129 | return -ENOMEM; 2130 | } 2131 | 2132 | /* populate the device data */ 2133 | tsl256x_handle->defaults[0] = defaults[3]; /* low threshold % */ 2134 | tsl256x_handle->defaults[1] = defaults[4]; /* high threshold % */ 2135 | tsl256x_handle->defaults[2] = defaults[9]; /* sensor interrupt rate */ 2136 | tsl256x_handle->defaults[3] = defaults[10]; /* light compensat. rate */ 2137 | tsl256x_handle->gaintime = 0x12; 2138 | tsl256x_handle->periods = defaults[9]; 2139 | tsl256x_handle->coeff_table = cs ? tsl256x_coeff_cs : tsl256x_coeff_fn; 2140 | 2141 | ret = tsl256x_setup(); 2142 | 2143 | return ret; 2144 | } 2145 | 2146 | static int tsl256x_exit(void) 2147 | { 2148 | unsigned int interr = 0, periods = tsl256x_handle->defaults[2]; 2149 | 2150 | /* disable the interrupt generation, restore defaults */ 2151 | tsl256x_interrupt_ctrls(&interr, &periods); 2152 | 2153 | tsl256x_handle->coeff_table = NULL; 2154 | kfree(tsl256x_handle->defaults); 2155 | tsl256x_handle->defaults = NULL; 2156 | kfree(tsl256x_handle); 2157 | 2158 | return 0; 2159 | } 2160 | 2161 | /* TAOS TSL256x specific ops */ 2162 | static const struct als_device_ops tsl256x_ops = { 2163 | .init = tsl256x_init, 2164 | .exit = tsl256x_exit, 2165 | .event_handler = tsl256x_event_handler, 2166 | .set_power = tsl256x_set_power, 2167 | .get_power = tsl256x_get_power, 2168 | .get_lux = tsl256x_get_lux, 2169 | .get_kelvin = tsl256x_get_kelvin, 2170 | }; 2171 | 2172 | /* unknown ALS sensors controlled by the EC present on newer Vaios */ 2173 | static inline int ngals_get_raw_data(unsigned int *data) 2174 | { 2175 | if (sony_call_snc_handle(sony_als->handle, 0x1000, data)) 2176 | return -EIO; 2177 | 2178 | return 0; 2179 | } 2180 | 2181 | static int ngals_get_lux(unsigned int *integ, unsigned int *fract) 2182 | { 2183 | unsigned int data; 2184 | 2185 | if (sony_call_snc_handle(sony_als->handle, 0x1000, &data)) 2186 | return -EIO; 2187 | 2188 | /* if we have a valid lux data */ 2189 | if (!!(data & 0xff0000) == 0x01) { 2190 | *integ = 0xffff & data; 2191 | *fract = 0; 2192 | } else { 2193 | return -1; 2194 | } 2195 | 2196 | return 0; 2197 | } 2198 | 2199 | static const struct als_device_ops ngals_ops = { 2200 | .init = NULL, 2201 | .exit = NULL, 2202 | .event_handler = NULL, 2203 | .set_power = NULL, 2204 | .get_power = NULL, 2205 | .get_lux = ngals_get_lux, 2206 | .get_kelvin = NULL, 2207 | }; 2208 | 2209 | /* ALS common data and functions */ 2210 | static int sony_nc_als_event_handler(void) 2211 | { 2212 | /* call the device handler */ 2213 | if (sony_als->ops->event_handler) 2214 | sony_als->ops->event_handler(); 2215 | 2216 | return 0; 2217 | } 2218 | 2219 | static int sony_nc_als_power_set(unsigned int status) 2220 | { 2221 | if (!sony_als->ops->set_power) 2222 | return -EPERM; 2223 | 2224 | if (sony_als->ops->set_power(status)) 2225 | return -EIO; 2226 | 2227 | sony_als->power = status; 2228 | 2229 | return 0; 2230 | } 2231 | 2232 | static int sony_nc_als_managed_set(unsigned int status) 2233 | { 2234 | int ret = 0; 2235 | unsigned int result, cmd; 2236 | static bool was_on; 2237 | 2238 | /* turn on/off the event notification 2239 | * (and enable als_backlight writes) 2240 | */ 2241 | cmd = sony_als->handle == 0x0143 ? 0x2200 : 0x0900; 2242 | if (sony_call_snc_handle(sony_als->handle, 2243 | (status << 0x10) | cmd, &result)) 2244 | return -EIO; 2245 | 2246 | sony_als->managed = status; 2247 | 2248 | /* turn on the ALS; this will also enable the interrupt generation */ 2249 | if (status) /* store the power state else check the previous state */ 2250 | was_on = sony_als->power; 2251 | else if (was_on) 2252 | return 0; 2253 | 2254 | ret = sony_nc_als_power_set(status); 2255 | if (ret == -EPERM) /* new models do not allow power control */ 2256 | ret = 0; 2257 | 2258 | return ret; 2259 | } 2260 | 2261 | static unsigned int level; 2262 | static int sony_nc_als_get_brightness(struct backlight_device *bd) 2263 | { 2264 | if (bd->props.brightness != level) 2265 | dprintk("bd->props.brightness != level\n"); 2266 | 2267 | return level; 2268 | } 2269 | 2270 | static int sony_nc_als_update_status(struct backlight_device *bd) 2271 | { 2272 | unsigned int value, result; 2273 | 2274 | if (sony_als->managed) { 2275 | if (bd->props.brightness != level) { 2276 | char *env[2] = { "ALS=2", NULL}; 2277 | kobject_uevent_env(&sony_nc_acpi_device->dev.kobj, 2278 | KOBJ_CHANGE, env); 2279 | 2280 | dprintk("generating ALS event 3 (reason: 2)\n"); 2281 | acpi_bus_generate_proc_event(sony_nc_acpi_device, 2282 | 3, 2); 2283 | acpi_bus_generate_netlink_event( 2284 | sony_nc_acpi_device->pnp.device_class, 2285 | dev_name(&sony_nc_acpi_device->dev), 2286 | 3, 2); 2287 | } 2288 | } else { 2289 | unsigned int cmd; 2290 | 2291 | value = sony_als->levels[bd->props.brightness]; 2292 | cmd = sony_als->handle == 0x0143 ? 0x3000 : 0x0100; 2293 | if (sony_call_snc_handle(sony_als->handle, 2294 | (value << 0x10) | cmd, &result)) 2295 | return -EIO; 2296 | } 2297 | 2298 | level = bd->props.brightness; 2299 | 2300 | return level; 2301 | } 2302 | 2303 | /* ALS sys interface */ 2304 | static ssize_t sony_nc_als_power_show(struct device *dev, 2305 | struct device_attribute *attr, char *buffer) 2306 | { 2307 | ssize_t count = 0; 2308 | int status; 2309 | 2310 | if (!sony_als->ops->get_power) 2311 | return -EPERM; 2312 | 2313 | if (sony_als->ops->get_power(&status)) 2314 | return -EIO; 2315 | 2316 | count = snprintf(buffer, PAGE_SIZE, "%d\n", status); 2317 | 2318 | return count; 2319 | } 2320 | 2321 | static ssize_t sony_nc_als_power_store(struct device *dev, 2322 | struct device_attribute *attr, 2323 | const char *buffer, size_t count) 2324 | { 2325 | int ret; 2326 | unsigned long value; 2327 | 2328 | if (count > 31) 2329 | return -EINVAL; 2330 | 2331 | if (strict_strtoul(buffer, 10, &value) || value > 1) 2332 | return -EINVAL; 2333 | 2334 | /* no action if already set */ 2335 | if (value == sony_als->power) 2336 | return count; 2337 | 2338 | ret = sony_nc_als_power_set(value); 2339 | if (ret) 2340 | return ret; 2341 | 2342 | return count; 2343 | } 2344 | 2345 | static ssize_t sony_nc_als_managed_show(struct device *dev, 2346 | struct device_attribute *attr, char *buffer) 2347 | { 2348 | ssize_t count = 0; 2349 | unsigned int status, cmd; 2350 | 2351 | cmd = sony_als->handle == 0x0143 ? 0x2100 : 0x0A00; 2352 | if (sony_call_snc_handle(sony_als->handle, cmd, &status)) 2353 | return -EIO; 2354 | 2355 | count = snprintf(buffer, PAGE_SIZE, "%d\n", status & 0x01); 2356 | 2357 | return count; 2358 | } 2359 | 2360 | static ssize_t sony_nc_als_managed_store(struct device *dev, 2361 | struct device_attribute *attr, 2362 | const char *buffer, size_t count) 2363 | { 2364 | unsigned long value; 2365 | 2366 | if (count > 31) 2367 | return -EINVAL; 2368 | 2369 | if (strict_strtoul(buffer, 10, &value) || value > 1) 2370 | return -EINVAL; 2371 | 2372 | if (sony_als->managed != value) { 2373 | int ret = sony_nc_als_managed_set(value); 2374 | if (ret) 2375 | return ret; 2376 | } 2377 | 2378 | return count; 2379 | } 2380 | 2381 | static ssize_t sony_nc_als_lux_show(struct device *dev, 2382 | struct device_attribute *attr, char *buffer) 2383 | { 2384 | ssize_t count = 0; 2385 | unsigned int integ = 0, fract = 0; 2386 | 2387 | if (sony_als->power) 2388 | /* sony_als->ops->get_lux is mandatory, no check */ 2389 | sony_als->ops->get_lux(&integ, &fract); 2390 | 2391 | count = snprintf(buffer, PAGE_SIZE, "%u.%.2u\n", integ, fract); 2392 | 2393 | return count; 2394 | } 2395 | 2396 | static ssize_t sony_nc_als_parameters_show(struct device *dev, 2397 | struct device_attribute *attr, char *buffer) 2398 | { 2399 | ssize_t count = 0; 2400 | unsigned int i, num; 2401 | u8 *list; 2402 | 2403 | if (!strcmp(attr->attr.name, "als_defaults")) { 2404 | list = sony_als->defaults; 2405 | num = sony_als->defaults_num; 2406 | } else { /* als_backlight_levels */ 2407 | list = sony_als->levels; 2408 | num = sony_als->levels_num; 2409 | } 2410 | 2411 | for (i = 0; i < num; i++) 2412 | count += snprintf(buffer + count, PAGE_SIZE - count, 2413 | "0x%.2x ", list[i]); 2414 | 2415 | count += snprintf(buffer + count, PAGE_SIZE - count, "\n"); 2416 | 2417 | return count; 2418 | } 2419 | 2420 | static ssize_t sony_nc_als_backlight_show(struct device *dev, 2421 | struct device_attribute *attr, char *buffer) 2422 | { 2423 | ssize_t count = 0; 2424 | unsigned int result, cmd; 2425 | 2426 | cmd = sony_als->handle == 0x0143 ? 0x3100 : 0x0200; 2427 | if (sony_call_snc_handle(sony_als->handle, cmd, &result)) 2428 | return -EIO; 2429 | 2430 | count = snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff); 2431 | 2432 | return count; 2433 | } 2434 | 2435 | static ssize_t sony_nc_als_backlight_store(struct device *dev, 2436 | struct device_attribute *attr, 2437 | const char *buffer, size_t count) 2438 | { 2439 | unsigned long value; 2440 | unsigned int result, cmd, max = sony_als->levels_num - 1; 2441 | 2442 | if (count > 31) 2443 | return -EINVAL; 2444 | 2445 | if (strict_strtoul(buffer, 10, &value)) 2446 | return -EINVAL; 2447 | 2448 | if (!sony_als->managed) 2449 | return -EPERM; 2450 | 2451 | /* verify that the provided value falls inside the model 2452 | specific backlight range */ 2453 | if ((value < sony_als->levels[0]) 2454 | || (value > sony_als->levels[max])) 2455 | return -EINVAL; 2456 | 2457 | cmd = sony_als->handle == 0x0143 ? 0x3000 : 0x0100; 2458 | if (sony_call_snc_handle(sony_als->handle, (value << 0x10) | cmd, 2459 | &result)) 2460 | return -EIO; 2461 | 2462 | return count; 2463 | } 2464 | 2465 | static ssize_t sony_nc_als_kelvin_show(struct device *dev, 2466 | struct device_attribute *attr, char *buffer) 2467 | { 2468 | ssize_t count = 0; 2469 | unsigned int kelvin = 0; 2470 | 2471 | if (sony_als->ops->get_kelvin && sony_als->power) 2472 | sony_als->ops->get_kelvin(&kelvin); 2473 | 2474 | count = snprintf(buffer, PAGE_SIZE, "%d\n", kelvin); 2475 | 2476 | return count; 2477 | } 2478 | 2479 | /* ALS attach/detach functions */ 2480 | static int sony_nc_als_setup(struct platform_device *pd, unsigned int handle) 2481 | { 2482 | int i = 0; 2483 | 2484 | /* check the device presence */ 2485 | if (handle == 0x0137) { 2486 | unsigned int result; 2487 | 2488 | if (sony_call_snc_handle(handle, 0xB00, &result)) 2489 | return -EIO; 2490 | 2491 | if (!(result & 0x01)) { 2492 | pr_info("no ALS present\n"); 2493 | return 0; 2494 | } 2495 | } 2496 | 2497 | sony_als = kzalloc(sizeof(struct sony_als_device), GFP_KERNEL); 2498 | if (!sony_als) 2499 | return -ENOMEM; 2500 | 2501 | /* set model specific data */ 2502 | /* if handle 0x012f or 0x0137 use tsl256x_ops, else new als controls */ 2503 | if (handle == 0x0143) { 2504 | sony_als->ops = &ngals_ops; 2505 | sony_als->levels_num = 16; 2506 | sony_als->defaults_num = 9; 2507 | } else { 2508 | sony_als->ops = &tsl256x_ops; 2509 | sony_als->levels_num = 9; 2510 | sony_als->defaults_num = 13; 2511 | } 2512 | /* backlight levels are the first levels_num values, the remaining 2513 | defaults_num values are default settings for als regulation 2514 | */ 2515 | sony_als->levels = sony_als->parameters; 2516 | sony_als->defaults = sony_als->parameters + sony_als->levels_num; 2517 | 2518 | sony_als->handle = handle; 2519 | 2520 | /* get power state */ 2521 | if (sony_als->ops->get_power) { 2522 | if (sony_als->ops->get_power(&sony_als->power)) 2523 | pr_warn("unable to retrieve the power status\n"); 2524 | } 2525 | 2526 | /* set managed to 0, userspace daemon should enable it */ 2527 | sony_nc_als_managed_set(0); 2528 | 2529 | /* get ALS parameters */ 2530 | if (sony_call_snc_handle_buffer(sony_als->handle, 0x0000, 2531 | sony_als->parameters, ALS_TABLE_SIZE) < 0) 2532 | goto nosensor; 2533 | 2534 | /* initial device configuration */ 2535 | if (sony_als->ops->init) 2536 | if (sony_als->ops->init(sony_als->defaults)) { 2537 | pr_warn("ALS setup failed\n"); 2538 | goto nosensor; 2539 | } 2540 | 2541 | /* set up the sys interface */ 2542 | 2543 | /* notifications and backlight enable control file */ 2544 | sysfs_attr_init(&sony_als->attrs[0].attr); 2545 | sony_als->attrs[0].attr.name = "als_managed"; 2546 | sony_als->attrs[0].attr.mode = S_IRUGO | S_IWUSR; 2547 | sony_als->attrs[0].show = sony_nc_als_managed_show; 2548 | sony_als->attrs[0].store = sony_nc_als_managed_store; 2549 | /* lux equivalent value */ 2550 | sysfs_attr_init(&sony_als->attrs[1].attr); 2551 | sony_als->attrs[1].attr.name = "als_lux"; 2552 | sony_als->attrs[1].attr.mode = S_IRUGO; 2553 | sony_als->attrs[1].show = sony_nc_als_lux_show; 2554 | /* ALS default parameters */ 2555 | sysfs_attr_init(&sony_als->attrs[2].attr); 2556 | sony_als->attrs[2].attr.name = "als_defaults"; 2557 | sony_als->attrs[2].attr.mode = S_IRUGO; 2558 | sony_als->attrs[2].show = sony_nc_als_parameters_show; 2559 | /* ALS default backlight levels */ 2560 | sysfs_attr_init(&sony_als->attrs[3].attr); 2561 | sony_als->attrs[3].attr.name = "als_backlight_levels"; 2562 | sony_als->attrs[3].attr.mode = S_IRUGO; 2563 | sony_als->attrs[3].show = sony_nc_als_parameters_show; 2564 | /* als backlight control */ 2565 | sysfs_attr_init(&sony_als->attrs[4].attr); 2566 | sony_als->attrs[4].attr.name = "als_backlight"; 2567 | sony_als->attrs[4].attr.mode = S_IRUGO | S_IWUSR; 2568 | sony_als->attrs[4].show = sony_nc_als_backlight_show; 2569 | sony_als->attrs[4].store = sony_nc_als_backlight_store; 2570 | 2571 | sony_als->attrs_num = 5; 2572 | /* end mandatory sys interface */ 2573 | 2574 | if (sony_als->ops->get_power || sony_als->ops->set_power) { 2575 | int i = sony_als->attrs_num++; 2576 | 2577 | /* als power control */ 2578 | sysfs_attr_init(&sony_als->attrs[i].attr); 2579 | sony_als->attrs[i].attr.name = "als_power"; 2580 | sony_als->attrs[i].attr.mode = S_IRUGO | S_IWUSR; 2581 | sony_als->attrs[i].show = sony_nc_als_power_show; 2582 | sony_als->attrs[i].store = sony_nc_als_power_store; 2583 | } 2584 | 2585 | if (sony_als->ops->get_kelvin) { 2586 | int i = sony_als->attrs_num++; 2587 | 2588 | /* light temperature */ 2589 | sysfs_attr_init(&sony_als->attrs[i].attr); 2590 | sony_als->attrs[i].attr.name = "als_kelvin"; 2591 | sony_als->attrs[i].attr.mode = S_IRUGO; 2592 | sony_als->attrs[i].show = sony_nc_als_kelvin_show; 2593 | } 2594 | 2595 | /* everything or nothing, otherwise unable to control the ALS */ 2596 | for (; i < sony_als->attrs_num; i++) { 2597 | if (device_create_file(&pd->dev, &sony_als->attrs[i])) 2598 | goto attrserror; 2599 | } 2600 | 2601 | return 0; 2602 | 2603 | attrserror: 2604 | for (; i > 0; i--) 2605 | device_remove_file(&pd->dev, &sony_als->attrs[i]); 2606 | nosensor: 2607 | kfree(sony_als); 2608 | sony_als = NULL; 2609 | 2610 | return -1; 2611 | } 2612 | 2613 | static void sony_nc_als_resume(void) 2614 | { 2615 | if (sony_als->managed) /* it restores the power state too */ 2616 | sony_nc_als_managed_set(1); 2617 | else if (sony_als->power) 2618 | sony_nc_als_power_set(1); 2619 | } 2620 | 2621 | static int sony_nc_als_cleanup(struct platform_device *pd) 2622 | { 2623 | if (sony_als) { 2624 | int i; 2625 | 2626 | for (i = 0; i < sony_als->attrs_num; i++) 2627 | device_remove_file(&pd->dev, &sony_als->attrs[i]); 2628 | 2629 | /* disable the events notification */ 2630 | if (sony_als->managed) 2631 | if (sony_nc_als_managed_set(0)) 2632 | pr_info("ALS notifications disable failed\n"); 2633 | 2634 | if (sony_als->power) 2635 | if (sony_nc_als_power_set(0)) 2636 | pr_info("ALS power off failed\n"); 2637 | 2638 | if (sony_als->ops->exit) 2639 | if (sony_als->ops->exit()) 2640 | pr_info("ALS device cleaning failed\n"); 2641 | 2642 | kfree(sony_als); 2643 | sony_als = NULL; 2644 | } 2645 | 2646 | return 0; 2647 | } 2648 | /* end ALS code */ 2649 | 2650 | /* Keyboard backlight feature */ 2651 | static struct sony_kbdbl_data { 2652 | unsigned int handle; 2653 | unsigned int base; 2654 | unsigned int mode; 2655 | unsigned int timeout; 2656 | struct device_attribute mode_attr; 2657 | struct device_attribute timeout_attr; 2658 | } *sony_kbdbl; 2659 | 2660 | static int __sony_nc_kbd_backlight_mode_set(u8 value) 2661 | { 2662 | unsigned int result; 2663 | 2664 | if (value > 1) 2665 | return -EINVAL; 2666 | 2667 | if (sony_call_snc_handle(sony_kbdbl->handle, (value << 0x10) | 2668 | (sony_kbdbl->base), &result)) 2669 | return -EIO; 2670 | 2671 | sony_kbdbl->mode = value; 2672 | 2673 | /* Try to turn the light on/off immediately */ 2674 | sony_call_snc_handle(sony_kbdbl->handle, (value << 0x10) | 2675 | (sony_kbdbl->base + 0x100), &result); 2676 | 2677 | return 0; 2678 | } 2679 | 2680 | static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, 2681 | struct device_attribute *attr, 2682 | const char *buffer, size_t count) 2683 | { 2684 | int ret = 0; 2685 | unsigned long value; 2686 | 2687 | if (count > 31) 2688 | return -EINVAL; 2689 | 2690 | if (strict_strtoul(buffer, 10, &value)) 2691 | return -EINVAL; 2692 | 2693 | ret = __sony_nc_kbd_backlight_mode_set(value); 2694 | if (ret < 0) 2695 | return ret; 2696 | 2697 | return count; 2698 | } 2699 | 2700 | static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, 2701 | struct device_attribute *attr, char *buffer) 2702 | { 2703 | ssize_t count = 0; 2704 | 2705 | count = snprintf(buffer, PAGE_SIZE, "%d\n", sony_kbdbl->mode); 2706 | 2707 | return count; 2708 | } 2709 | 2710 | static int __sony_nc_kbd_backlight_timeout_set(u8 value) 2711 | { 2712 | unsigned int result; 2713 | 2714 | if (value > 3) 2715 | return -EINVAL; 2716 | 2717 | if (sony_call_snc_handle(sony_kbdbl->handle, (value << 0x10) | 2718 | (sony_kbdbl->base + 0x200), &result)) 2719 | return -EIO; 2720 | 2721 | sony_kbdbl->timeout = value; 2722 | 2723 | return 0; 2724 | } 2725 | 2726 | static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, 2727 | struct device_attribute *attr, 2728 | const char *buffer, size_t count) 2729 | { 2730 | int ret = 0; 2731 | unsigned long value; 2732 | 2733 | if (count > 31) 2734 | return -EINVAL; 2735 | 2736 | if (strict_strtoul(buffer, 10, &value)) 2737 | return -EINVAL; 2738 | 2739 | ret = __sony_nc_kbd_backlight_timeout_set(value); 2740 | if (ret < 0) 2741 | return ret; 2742 | 2743 | return count; 2744 | } 2745 | 2746 | static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, 2747 | struct device_attribute *attr, char *buffer) 2748 | { 2749 | ssize_t count = 0; 2750 | 2751 | count = snprintf(buffer, PAGE_SIZE, "%d\n", sony_kbdbl->timeout); 2752 | 2753 | return count; 2754 | } 2755 | 2756 | static int sony_nc_kbd_backlight_setup(struct platform_device *pd, 2757 | unsigned int handle) 2758 | { 2759 | unsigned int result, base_cmd; 2760 | bool found = false; 2761 | 2762 | /* verify the kbd backlight presence, some models do not have it */ 2763 | if (handle == 0x0137) { 2764 | if (sony_call_snc_handle(handle, 0x0B00, &result)) 2765 | return -EIO; 2766 | 2767 | found = !!(result & 0x02); 2768 | base_cmd = 0x0C00; 2769 | } else { 2770 | if (sony_call_snc_handle(handle, 0x0100, &result)) 2771 | return -EIO; 2772 | 2773 | found = result & 0x01; 2774 | base_cmd = 0x4000; 2775 | } 2776 | 2777 | if (!found) { 2778 | dprintk("no backlight keyboard found\n"); 2779 | return 0; 2780 | } 2781 | 2782 | sony_kbdbl = kzalloc(sizeof(*sony_kbdbl), GFP_KERNEL); 2783 | if (!sony_kbdbl) 2784 | return -ENOMEM; 2785 | 2786 | sysfs_attr_init(&sony_kbdbl->mode_attr.attr); 2787 | sony_kbdbl->mode_attr.attr.name = "kbd_backlight"; 2788 | sony_kbdbl->mode_attr.attr.mode = S_IRUGO | S_IWUSR; 2789 | sony_kbdbl->mode_attr.show = sony_nc_kbd_backlight_mode_show; 2790 | sony_kbdbl->mode_attr.store = sony_nc_kbd_backlight_mode_store; 2791 | 2792 | sysfs_attr_init(&sony_kbdbl->timeout_attr.attr); 2793 | sony_kbdbl->timeout_attr.attr.name = "kbd_backlight_timeout"; 2794 | sony_kbdbl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; 2795 | sony_kbdbl->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; 2796 | sony_kbdbl->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; 2797 | 2798 | if (device_create_file(&pd->dev, &sony_kbdbl->mode_attr)) 2799 | goto outkzalloc; 2800 | 2801 | if (device_create_file(&pd->dev, &sony_kbdbl->timeout_attr)) 2802 | goto outmode; 2803 | 2804 | sony_kbdbl->handle = handle; 2805 | sony_kbdbl->base = base_cmd; 2806 | 2807 | __sony_nc_kbd_backlight_mode_set(kbd_backlight); 2808 | __sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout); 2809 | 2810 | return 0; 2811 | 2812 | outmode: 2813 | device_remove_file(&pd->dev, &sony_kbdbl->mode_attr); 2814 | outkzalloc: 2815 | kfree(sony_kbdbl); 2816 | sony_kbdbl = NULL; 2817 | return -1; 2818 | } 2819 | 2820 | static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) 2821 | { 2822 | if (sony_kbdbl) { 2823 | unsigned int result; 2824 | 2825 | device_remove_file(&pd->dev, &sony_kbdbl->mode_attr); 2826 | device_remove_file(&pd->dev, &sony_kbdbl->timeout_attr); 2827 | 2828 | /* restore the default hw behaviour */ 2829 | sony_call_snc_handle(sony_kbdbl->handle, 2830 | sony_kbdbl->base | 0x10000, &result); 2831 | sony_call_snc_handle(sony_kbdbl->handle, 2832 | sony_kbdbl->base + 0x200, &result); 2833 | 2834 | kfree(sony_kbdbl); 2835 | sony_kbdbl = NULL; 2836 | } 2837 | return 0; 2838 | } 2839 | 2840 | static void sony_nc_kbd_backlight_resume(void) 2841 | { 2842 | unsigned int result; 2843 | 2844 | if (!sony_kbdbl) 2845 | return; 2846 | 2847 | if (sony_kbdbl->mode == 0) 2848 | sony_call_snc_handle(sony_kbdbl->handle, 2849 | sony_kbdbl->base, &result); 2850 | 2851 | if (sony_kbdbl->timeout != 0) 2852 | sony_call_snc_handle(sony_kbdbl->handle, 2853 | (sony_kbdbl->base + 0x200) | 2854 | (sony_kbdbl->timeout << 0x10), &result); 2855 | } 2856 | 2857 | /* GSensor, HDD Shock Protection */ 2858 | enum axis { 2859 | X_AXIS = 4, /* frontal */ 2860 | Y_AXIS, /* lateral */ 2861 | Z_AXIS /* vertical */ 2862 | }; 2863 | 2864 | static struct sony_gsensor_device { 2865 | unsigned int handle; 2866 | unsigned int attrs_num; 2867 | struct device_attribute *attrs; 2868 | } *sony_gsensor; 2869 | 2870 | /* the EC uses pin #11 of the SATA power connector to command the 2871 | immediate idle feature; however some drives do not implement it 2872 | and pin #11 is NC. Let's verify, otherwise no automatic 2873 | protection is possible by the hardware 2874 | */ 2875 | static int sony_nc_gsensor_support_get(unsigned int *support) 2876 | { 2877 | unsigned int result; 2878 | 2879 | if (sony_call_snc_handle(sony_gsensor->handle, 0x0200, &result)) 2880 | return -EIO; 2881 | 2882 | *support = sony_gsensor->handle == 0x0134 2883 | ? !!(result & 0x20) 2884 | : !!(result & 0x01); 2885 | 2886 | return 0; 2887 | } 2888 | 2889 | static int sony_nc_gsensor_status_set(int value) 2890 | { 2891 | unsigned int result, capable, reg, arg; 2892 | bool update = false; 2893 | 2894 | if (sony_nc_gsensor_support_get(&capable)) 2895 | return -EIO; 2896 | 2897 | if (!capable) 2898 | pr_warn("hardware protection not available, the HDD" 2899 | " do not support this feature\n"); 2900 | 2901 | /* do not return immediately even though there is no HW 2902 | * capability, userspace can thus receive the shock 2903 | * notifications and call the ATA7 immediate idle command to 2904 | * unload the heads. Just return after enabling notifications 2905 | */ 2906 | reg = sony_gsensor->handle == 0x0134 ? 2907 | (!value << 0x08) : (value << 0x10); 2908 | 2909 | if (sony_call_snc_handle(sony_gsensor->handle, reg, &result)) 2910 | return -EIO; 2911 | 2912 | if (!capable) 2913 | return 0; 2914 | 2915 | /* if the requested protection setting is different 2916 | from the current one 2917 | */ 2918 | reg = sony_gsensor->handle == 0x0134 ? 0x0200 : 0x0400; 2919 | if (sony_call_snc_handle(sony_gsensor->handle, reg, &result)) 2920 | return -EIO; 2921 | 2922 | if (sony_gsensor->handle == 0x0134) { 2923 | if (!!(result & 0x04) != value) { 2924 | arg = (result & 0x1B) | (value << 0x02); 2925 | update = true; 2926 | } 2927 | } else { 2928 | if ((result & 0x01) != value) { 2929 | arg = value; 2930 | update = true; 2931 | } 2932 | } 2933 | 2934 | if (update && sony_call_snc_handle(sony_gsensor->handle, 2935 | (arg << 0x10) | 0x0300, &result)) 2936 | return -EIO; 2937 | 2938 | return 0; 2939 | } 2940 | 2941 | static int sony_nc_gsensor_axis_get(enum axis name) 2942 | { 2943 | unsigned int result; 2944 | 2945 | if (sony_call_snc_handle(sony_gsensor->handle, name << 0x08, &result)) 2946 | return -EIO; 2947 | 2948 | return result; 2949 | } 2950 | 2951 | /* G sensor sys interface */ 2952 | static ssize_t sony_nc_gsensor_type_show(struct device *dev, 2953 | struct device_attribute *attr, char *buffer) 2954 | { 2955 | ssize_t count = 0; 2956 | unsigned int result; 2957 | 2958 | if (sony_call_snc_handle(sony_gsensor->handle, 0x0200, &result)) 2959 | return -EIO; 2960 | 2961 | count = snprintf(buffer, PAGE_SIZE, "%d\n", (result >> 0x03) & 0x03); 2962 | 2963 | return count; 2964 | } 2965 | 2966 | static ssize_t sony_nc_gsensor_type_store(struct device *dev, 2967 | struct device_attribute *attr, 2968 | const char *buffer, size_t count) 2969 | { 2970 | /* 2971 | * axis out type control file: 2972 | * 0: raw values, 1: acc values 2: threshold values 2973 | */ 2974 | unsigned int result; 2975 | unsigned long value; 2976 | 2977 | /* sanity checks and conversion */ 2978 | if (count > 31 || strict_strtoul(buffer, 10, &value) || value > 2) 2979 | return -EINVAL; 2980 | 2981 | value <<= 0x03; 2982 | 2983 | /* retrieve the current state / settings */ 2984 | if (sony_call_snc_handle(sony_gsensor->handle, 0x0200, &result)) 2985 | return -EIO; 2986 | 2987 | if ((result & 0x18) != value) { 2988 | /* the last 3 bits need to be preserved */ 2989 | value |= (result & 0x07); 2990 | 2991 | if (sony_call_snc_handle(sony_gsensor->handle, 2992 | (value << 0x10) | 0x0300, &result)) 2993 | return -EIO; 2994 | } 2995 | 2996 | return count; 2997 | } 2998 | 2999 | static ssize_t sony_nc_gsensor_axis_show(struct device *dev, 3000 | struct device_attribute *attr, char *buffer) 3001 | { 3002 | ssize_t count = 0; 3003 | unsigned int result; 3004 | enum axis arg; 3005 | 3006 | /* file being read for axis selection */ 3007 | if (!strcmp(attr->attr.name, "gsensor_xval")) 3008 | arg = X_AXIS; 3009 | else if (!strcmp(attr->attr.name, "gsensor_yval")) 3010 | arg = Y_AXIS; 3011 | else if (!strcmp(attr->attr.name, "gsensor_zval")) 3012 | arg = Z_AXIS; 3013 | else 3014 | return count; 3015 | 3016 | result = sony_nc_gsensor_axis_get(arg); 3017 | if (result < 0) 3018 | return -EIO; 3019 | 3020 | count = snprintf(buffer, PAGE_SIZE, "%d\n", result); 3021 | 3022 | return count; 3023 | } 3024 | 3025 | static ssize_t sony_nc_gsensor_status_show(struct device *dev, 3026 | struct device_attribute *attr, char *buffer) 3027 | { 3028 | ssize_t count = 0; 3029 | unsigned int result; 3030 | 3031 | if (sony_gsensor->handle == 0x0134) { 3032 | if (sony_call_snc_handle(sony_gsensor->handle, 0x0200, 3033 | &result)) 3034 | return -EIO; 3035 | 3036 | result = !!(result & 0x04); 3037 | } else { 3038 | if (sony_call_snc_handle(sony_gsensor->handle, 0x0400, 3039 | &result)) 3040 | return -EIO; 3041 | 3042 | result &= 0x01; 3043 | } 3044 | 3045 | count = snprintf(buffer, PAGE_SIZE, "%d\n", result); 3046 | 3047 | return count; 3048 | } 3049 | 3050 | static ssize_t sony_nc_gsensor_status_store(struct device *dev, 3051 | struct device_attribute *attr, 3052 | const char *buffer, size_t count) 3053 | { 3054 | int ret; 3055 | unsigned long value; 3056 | 3057 | if (count > 31) 3058 | return -EINVAL; 3059 | if (strict_strtoul(buffer, 10, &value) || value > 1) 3060 | return -EINVAL; 3061 | 3062 | ret = sony_nc_gsensor_status_set(value); 3063 | if (ret) 3064 | return ret; 3065 | 3066 | return count; 3067 | } 3068 | 3069 | static ssize_t sony_nc_gsensor_sensitivity_show(struct device *dev, 3070 | struct device_attribute *attr, char *buffer) 3071 | { 3072 | ssize_t count = 0; 3073 | unsigned int result; 3074 | 3075 | if (sony_call_snc_handle(sony_gsensor->handle, 0x0200, &result)) 3076 | return -EINVAL; 3077 | 3078 | count = snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x03); 3079 | return count; 3080 | } 3081 | 3082 | static ssize_t sony_nc_gsensor_sensitivity_store(struct device *dev, 3083 | struct device_attribute *attr, 3084 | const char *buffer, size_t count) 3085 | { 3086 | unsigned int result; 3087 | unsigned long value; 3088 | 3089 | if (count > 31) 3090 | return -EINVAL; 3091 | if (strict_strtoul(buffer, 10, &value) || value > 2) 3092 | return -EINVAL; 3093 | 3094 | /* retrieve the other parameters to be stored as well */ 3095 | if (sony_call_snc_handle(sony_gsensor->handle, 0x0200, &result)) 3096 | return -EIO; 3097 | value |= (result & 0x1C); /* preserve only the needed bits */ 3098 | 3099 | if (sony_call_snc_handle(sony_gsensor->handle, (value << 0x10) 3100 | | 0x0300, &result)) 3101 | return -EIO; 3102 | 3103 | return count; 3104 | } 3105 | 3106 | static int sony_nc_gsensor_setup(struct platform_device *pd, 3107 | unsigned int handle) 3108 | { 3109 | int i, enable, support; 3110 | 3111 | sony_gsensor = kzalloc(sizeof(struct sony_gsensor_device), GFP_KERNEL); 3112 | if (!sony_gsensor) 3113 | return -ENOMEM; 3114 | 3115 | sony_gsensor->handle = handle; 3116 | sony_gsensor->attrs_num = handle == 0x0134 ? 6 : 1; 3117 | 3118 | sony_gsensor->attrs = kzalloc(sizeof(struct device_attribute) 3119 | * sony_gsensor->attrs_num, GFP_KERNEL); 3120 | if (!sony_gsensor->attrs) 3121 | goto memerror; 3122 | 3123 | /* check the storing device support */ 3124 | if (sony_nc_gsensor_support_get(&support)) 3125 | return -EIO; 3126 | 3127 | /* enable the HDD protection and notification by default 3128 | when hardware driven protection is possible */ 3129 | enable = support ? 1 : force_shock_notifications; 3130 | if (sony_nc_gsensor_status_set(enable)) 3131 | if (enable) 3132 | pr_warn("failed to enable the HDD shock protection\n"); 3133 | 3134 | /* activation control */ 3135 | sysfs_attr_init(&sony_gsensor->attrs[0].attr); 3136 | sony_gsensor->attrs[0].attr.name = "gsensor_protection"; 3137 | sony_gsensor->attrs[0].attr.mode = S_IRUGO | S_IWUSR; 3138 | sony_gsensor->attrs[0].show = sony_nc_gsensor_status_show; 3139 | sony_gsensor->attrs[0].store = sony_nc_gsensor_status_store; 3140 | 3141 | if (sony_gsensor->attrs_num > 1) { 3142 | /* sensitivity selection */ 3143 | sysfs_attr_init(&sony_gsensor->attrs[1].attr); 3144 | sony_gsensor->attrs[1].attr.name = "gsensor_sensitivity"; 3145 | sony_gsensor->attrs[1].attr.mode = S_IRUGO | S_IWUSR; 3146 | sony_gsensor->attrs[1].show = sony_nc_gsensor_sensitivity_show; 3147 | sony_gsensor->attrs[1].store = 3148 | sony_nc_gsensor_sensitivity_store; 3149 | /* x/y/z output selection */ 3150 | sysfs_attr_init(&sony_gsensor->attrs[2].attr); 3151 | sony_gsensor->attrs[2].attr.name = "gsensor_val_type"; 3152 | sony_gsensor->attrs[2].attr.mode = S_IRUGO | S_IWUSR; 3153 | sony_gsensor->attrs[2].show = sony_nc_gsensor_type_show; 3154 | sony_gsensor->attrs[2].store = sony_nc_gsensor_type_store; 3155 | 3156 | sysfs_attr_init(&sony_gsensor->attrs[3].attr); 3157 | sony_gsensor->attrs[3].attr.name = "gsensor_xval"; 3158 | sony_gsensor->attrs[3].attr.mode = S_IRUGO; 3159 | sony_gsensor->attrs[3].show = sony_nc_gsensor_axis_show; 3160 | 3161 | sysfs_attr_init(&sony_gsensor->attrs[4].attr); 3162 | sony_gsensor->attrs[4].attr.name = "gsensor_yval"; 3163 | sony_gsensor->attrs[4].attr.mode = S_IRUGO; 3164 | sony_gsensor->attrs[4].show = sony_nc_gsensor_axis_show; 3165 | 3166 | sysfs_attr_init(&sony_gsensor->attrs[5].attr); 3167 | sony_gsensor->attrs[5].attr.name = "gsensor_zval"; 3168 | sony_gsensor->attrs[5].attr.mode = S_IRUGO; 3169 | sony_gsensor->attrs[5].show = sony_nc_gsensor_axis_show; 3170 | } 3171 | 3172 | for (i = 0; i < sony_gsensor->attrs_num; i++) { 3173 | if (device_create_file(&pd->dev, &sony_gsensor->attrs[i])) 3174 | goto attrserror; 3175 | } 3176 | 3177 | return 0; 3178 | 3179 | attrserror: 3180 | for (; i > 0; i--) 3181 | device_remove_file(&pd->dev, &sony_gsensor->attrs[i]); 3182 | 3183 | kfree(sony_gsensor->attrs); 3184 | memerror: 3185 | kfree(sony_gsensor); 3186 | sony_gsensor = NULL; 3187 | 3188 | return -1; 3189 | } 3190 | 3191 | static int sony_nc_gsensor_cleanup(struct platform_device *pd) 3192 | { 3193 | if (sony_gsensor) { 3194 | unsigned int i, result, reg; 3195 | 3196 | for (i = 0; i < sony_gsensor->attrs_num; i++) 3197 | device_remove_file(&pd->dev, &sony_gsensor->attrs[i]); 3198 | 3199 | /* disable the event generation, 3200 | * preserve any other setting 3201 | */ 3202 | reg = sony_gsensor->handle == 0x0134 ? 0x0100 : 0x0000; 3203 | 3204 | sony_call_snc_handle(sony_gsensor->handle, reg, &result); 3205 | 3206 | kfree(sony_gsensor->attrs); 3207 | kfree(sony_gsensor); 3208 | sony_gsensor = NULL; 3209 | } 3210 | 3211 | return 0; 3212 | } 3213 | /* end G sensor code */ 3214 | 3215 | static struct sony_battcare_data { 3216 | unsigned int handle; 3217 | struct device_attribute attrs[2]; 3218 | } *sony_battcare; 3219 | 3220 | static ssize_t sony_nc_battery_care_limit_store(struct device *dev, 3221 | struct device_attribute *attr, 3222 | const char *buffer, size_t count) 3223 | { 3224 | unsigned int result, cmd; 3225 | unsigned long value; 3226 | 3227 | if (count > 31) 3228 | return -EINVAL; 3229 | if (strict_strtoul(buffer, 10, &value)) 3230 | return -EINVAL; 3231 | 3232 | /* limit values (2 bits): 3233 | * 00 - none 3234 | * 01 - 80% 3235 | * 10 - 50% 3236 | * 11 - 100% 3237 | * 3238 | * bit 0: 0 disable BCL, 1 enable BCL 3239 | * bit 1: 1 tell to store the battery limit (see bits 6,7) too 3240 | * bits 2,3: reserved 3241 | * bits 4,5: store the limit into the EC 3242 | * bits 6,7: store the limit into the battery 3243 | */ 3244 | 3245 | /* 3246 | * handle 0x0115 should allow storing on battery too; 3247 | * handle 0x0136 same as 0x0115 + health status; 3248 | * handle 0x013f, same as 0x0136 but no storing on the battery 3249 | * 3250 | * Store only inside the EC for now, regardless the handle number 3251 | */ 3252 | switch (value) { 3253 | case 0: /* disable */ 3254 | cmd = 0x00; 3255 | break; 3256 | case 1: /* enable, 80% charge limit */ 3257 | cmd = 0x11; 3258 | break; 3259 | case 2: /* enable, 50% charge limit */ 3260 | cmd = 0x21; 3261 | break; 3262 | default: 3263 | return -EINVAL; 3264 | } 3265 | 3266 | if (sony_call_snc_handle(sony_battcare->handle, (cmd << 0x10) | 0x0100, 3267 | &result)) 3268 | return -EIO; 3269 | 3270 | return count; 3271 | } 3272 | 3273 | static ssize_t sony_nc_battery_care_limit_show(struct device *dev, 3274 | struct device_attribute *attr, char *buffer) 3275 | { 3276 | ssize_t count = 0; 3277 | unsigned int result, status; 3278 | 3279 | if (sony_call_snc_handle(sony_battcare->handle, 0x0000, &result)) 3280 | return -EIO; 3281 | 3282 | /* if disabled 0, else take the limit bits */ 3283 | status = !(result & 0x01) ? 0 : ((result & 0x30) >> 0x04); 3284 | 3285 | count = snprintf(buffer, PAGE_SIZE, "%d\n", status); 3286 | return count; 3287 | } 3288 | 3289 | static ssize_t sony_nc_battery_care_health_show(struct device *dev, 3290 | struct device_attribute *attr, char *buffer) 3291 | { 3292 | ssize_t count = 0; 3293 | unsigned int health; 3294 | 3295 | if (sony_call_snc_handle(sony_battcare->handle, 0x0200, &health)) 3296 | return -EIO; 3297 | 3298 | count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff); 3299 | 3300 | return count; 3301 | } 3302 | 3303 | static int sony_nc_battery_care_setup(struct platform_device *pd, 3304 | unsigned int handle) 3305 | { 3306 | sony_battcare = kzalloc(sizeof(struct sony_battcare_data), GFP_KERNEL); 3307 | if (!sony_battcare) 3308 | return -ENOMEM; 3309 | 3310 | sony_battcare->handle = handle; 3311 | 3312 | sysfs_attr_init(&sony_battcare->attrs[0].attr); 3313 | sony_battcare->attrs[0].attr.name = "battery_care_limiter"; 3314 | sony_battcare->attrs[0].attr.mode = S_IRUGO | S_IWUSR; 3315 | sony_battcare->attrs[0].show = sony_nc_battery_care_limit_show; 3316 | sony_battcare->attrs[0].store = sony_nc_battery_care_limit_store; 3317 | 3318 | if (device_create_file(&pd->dev, &sony_battcare->attrs[0])) 3319 | goto outkzalloc; 3320 | 3321 | if (handle == 0x0115) /* no health indication */ 3322 | return 0; 3323 | 3324 | sysfs_attr_init(&sony_battcare->attrs[1].attr); 3325 | sony_battcare->attrs[1].attr.name = "battery_care_health"; 3326 | sony_battcare->attrs[1].attr.mode = S_IRUGO; 3327 | sony_battcare->attrs[1].show = sony_nc_battery_care_health_show; 3328 | 3329 | if (device_create_file(&pd->dev, &sony_battcare->attrs[1])) 3330 | goto outlimiter; 3331 | 3332 | return 0; 3333 | 3334 | outlimiter: 3335 | device_remove_file(&pd->dev, &sony_battcare->attrs[0]); 3336 | outkzalloc: 3337 | kfree(sony_battcare); 3338 | sony_battcare = NULL; 3339 | 3340 | return -1; 3341 | } 3342 | 3343 | static int sony_nc_battery_care_cleanup(struct platform_device *pd) 3344 | { 3345 | if (sony_battcare) { 3346 | device_remove_file(&pd->dev, &sony_battcare->attrs[0]); 3347 | if (sony_battcare->handle != 0x0115) 3348 | device_remove_file(&pd->dev, &sony_battcare->attrs[1]); 3349 | 3350 | kfree(sony_battcare); 3351 | sony_battcare = NULL; 3352 | } 3353 | 3354 | return 0; 3355 | } 3356 | 3357 | static struct sony_thermal_data { 3358 | unsigned int mode; 3359 | unsigned int profiles; 3360 | struct device_attribute mode_attr; 3361 | struct device_attribute profiles_attr; 3362 | } *sony_thermal; 3363 | 3364 | static int sony_nc_thermal_mode_set(unsigned int profile) 3365 | { 3366 | unsigned int cmd, result; 3367 | 3368 | /* to avoid the 1 value hole when only 2 profiles are available */ 3369 | switch (profile) { 3370 | case 1: /* performance */ 3371 | cmd = 2; 3372 | break; 3373 | case 2: /* silent */ 3374 | cmd = 1; 3375 | break; 3376 | default: /* balanced */ 3377 | cmd = 0; 3378 | break; 3379 | } 3380 | 3381 | if (sony_call_snc_handle(0x0122, cmd << 0x10 | 0x0200, &result)) 3382 | return -EIO; 3383 | 3384 | sony_thermal->mode = profile; 3385 | 3386 | return 0; 3387 | } 3388 | 3389 | static int sony_nc_thermal_mode_get(unsigned int *profile) 3390 | { 3391 | unsigned int result; 3392 | 3393 | if (sony_call_snc_handle(0x0122, 0x0100, &result)) 3394 | return -EIO; 3395 | 3396 | /* to avoid the 1 value hole when only 2 profiles are available */ 3397 | switch (result & 0xff) { 3398 | case 2: /* performance */ 3399 | *profile = 1; 3400 | break; 3401 | case 1: /* silent */ 3402 | *profile = 2; 3403 | break; 3404 | default: /* balanced */ 3405 | *profile = 0; 3406 | break; 3407 | } 3408 | 3409 | return 0; 3410 | } 3411 | 3412 | static ssize_t sony_nc_thermal_profiles_show(struct device *dev, 3413 | struct device_attribute *attr, char *buffer) 3414 | { 3415 | return snprintf(buffer, PAGE_SIZE, "%u\n", sony_thermal->profiles); 3416 | } 3417 | 3418 | static ssize_t sony_nc_thermal_mode_store(struct device *dev, 3419 | struct device_attribute *attr, 3420 | const char *buffer, size_t count) 3421 | { 3422 | unsigned long value; 3423 | 3424 | if (count > 31) 3425 | return -EINVAL; 3426 | if (strict_strtoul(buffer, 10, &value) || 3427 | value > (sony_thermal->profiles - 1)) 3428 | return -EINVAL; 3429 | 3430 | if (sony_nc_thermal_mode_set(value)) 3431 | return -EIO; 3432 | 3433 | return count; 3434 | } 3435 | 3436 | static ssize_t sony_nc_thermal_mode_show(struct device *dev, 3437 | struct device_attribute *attr, char *buffer) 3438 | { 3439 | ssize_t count = 0; 3440 | unsigned int profile; 3441 | 3442 | if (sony_nc_thermal_mode_get(&profile)) 3443 | return -EIO; 3444 | 3445 | count = snprintf(buffer, PAGE_SIZE, "%d\n", profile); 3446 | 3447 | return count; 3448 | } 3449 | 3450 | static int sony_nc_thermal_setup(struct platform_device *pd) 3451 | { 3452 | sony_thermal = kzalloc(sizeof(struct sony_thermal_data), GFP_KERNEL); 3453 | if (!sony_thermal) 3454 | return -ENOMEM; 3455 | 3456 | if (sony_call_snc_handle(0x0122, 0x0000, &sony_thermal->profiles)) { 3457 | pr_warn("unable to retrieve the available profiles\n"); 3458 | goto outkzalloc; 3459 | } 3460 | 3461 | if (sony_nc_thermal_mode_get(&sony_thermal->mode)) { 3462 | pr_warn("unable to retrieve the current profile"); 3463 | goto outkzalloc; 3464 | } 3465 | 3466 | sysfs_attr_init(&sony_thermal->profiles_attr.attr); 3467 | sony_thermal->profiles_attr.attr.name = "thermal_profiles"; 3468 | sony_thermal->profiles_attr.attr.mode = S_IRUGO; 3469 | sony_thermal->profiles_attr.show = sony_nc_thermal_profiles_show; 3470 | 3471 | sysfs_attr_init(&sony_thermal->mode_attr.attr); 3472 | sony_thermal->mode_attr.attr.name = "thermal_control"; 3473 | sony_thermal->mode_attr.attr.mode = S_IRUGO | S_IWUSR; 3474 | sony_thermal->mode_attr.show = sony_nc_thermal_mode_show; 3475 | sony_thermal->mode_attr.store = sony_nc_thermal_mode_store; 3476 | 3477 | if (device_create_file(&pd->dev, &sony_thermal->profiles_attr)) 3478 | goto outkzalloc; 3479 | 3480 | if (device_create_file(&pd->dev, &sony_thermal->mode_attr)) 3481 | goto outprofiles; 3482 | 3483 | return 0; 3484 | 3485 | outprofiles: 3486 | device_remove_file(&pd->dev, &sony_thermal->profiles_attr); 3487 | outkzalloc: 3488 | kfree(sony_thermal); 3489 | sony_thermal = NULL; 3490 | return -1; 3491 | } 3492 | 3493 | static int sony_nc_thermal_cleanup(struct platform_device *pd) 3494 | { 3495 | if (sony_thermal) { 3496 | device_remove_file(&pd->dev, &sony_thermal->profiles_attr); 3497 | device_remove_file(&pd->dev, &sony_thermal->mode_attr); 3498 | kfree(sony_thermal); 3499 | sony_thermal = NULL; 3500 | } 3501 | 3502 | return 0; 3503 | } 3504 | 3505 | static void sony_nc_thermal_resume(void) 3506 | { 3507 | unsigned int status; 3508 | 3509 | sony_nc_thermal_mode_get(&status); 3510 | 3511 | if (status != sony_thermal->mode) 3512 | sony_nc_thermal_mode_set(sony_thermal->mode); 3513 | } 3514 | 3515 | static struct device_attribute *sony_lid; 3516 | 3517 | static ssize_t sony_nc_lid_resume_store(struct device *dev, 3518 | struct device_attribute *attr, 3519 | const char *buffer, size_t count) 3520 | { 3521 | unsigned int result; 3522 | unsigned long value; 3523 | 3524 | if (count > 31) 3525 | return -EINVAL; 3526 | if (strict_strtoul(buffer, 10, &value) || value > 3) 3527 | return -EINVAL; 3528 | 3529 | /* 00 <- disabled 3530 | 01 <- resume from S4 3531 | 10 <- resume from S3 3532 | 11 <- resume from S4 and S3 3533 | */ 3534 | /* we must set bit 1 and 2 (bit 0 is for S5), so shift one bit more */ 3535 | if (sony_call_snc_handle(0x0119, value << 0x11 | 0x0100, &result)) 3536 | return -EIO; 3537 | 3538 | return count; 3539 | } 3540 | 3541 | static ssize_t sony_nc_lid_resume_show(struct device *dev, 3542 | struct device_attribute *attr, char *buffer) 3543 | { 3544 | ssize_t count = 0; 3545 | unsigned int result; 3546 | 3547 | if (sony_call_snc_handle(0x0119, 0x0000, &result)) 3548 | return -EIO; 3549 | 3550 | count = snprintf(buffer, PAGE_SIZE, "%d\n", (result >> 1) & 0x03); 3551 | 3552 | return count; 3553 | } 3554 | 3555 | static int sony_nc_lid_resume_setup(struct platform_device *pd) 3556 | { 3557 | sony_lid = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); 3558 | if (!sony_lid) 3559 | return -ENOMEM; 3560 | 3561 | sysfs_attr_init(&sony_lid->attr); 3562 | sony_lid->attr.name = "lid_resume_control"; 3563 | sony_lid->attr.mode = S_IRUGO | S_IWUSR; 3564 | sony_lid->show = sony_nc_lid_resume_show; 3565 | sony_lid->store = sony_nc_lid_resume_store; 3566 | 3567 | if (device_create_file(&pd->dev, sony_lid)) { 3568 | kfree(sony_lid); 3569 | sony_lid = NULL; 3570 | return -1; 3571 | } 3572 | 3573 | return 0; 3574 | } 3575 | 3576 | static int sony_nc_lid_resume_cleanup(struct platform_device *pd) 3577 | { 3578 | if (sony_lid) { 3579 | device_remove_file(&pd->dev, sony_lid); 3580 | kfree(sony_lid); 3581 | sony_lid = NULL; 3582 | } 3583 | 3584 | return 0; 3585 | } 3586 | 3587 | static struct device_attribute *sony_hsc; 3588 | 3589 | static ssize_t sony_nc_highspeed_charging_store(struct device *dev, 3590 | struct device_attribute *attr, 3591 | const char *buffer, size_t count) 3592 | { 3593 | unsigned int result; 3594 | unsigned long value; 3595 | 3596 | if (count > 31) 3597 | return -EINVAL; 3598 | if (strict_strtoul(buffer, 10, &value) || value > 1) 3599 | return -EINVAL; 3600 | 3601 | if (sony_call_snc_handle(0x0131, value << 0x10 | 0x0200, &result)) 3602 | return -EIO; 3603 | 3604 | return count; 3605 | } 3606 | 3607 | static ssize_t sony_nc_highspeed_charging_show(struct device *dev, 3608 | struct device_attribute *attr, char *buffer) 3609 | { 3610 | ssize_t count = 0; 3611 | unsigned int result; 3612 | 3613 | if (sony_call_snc_handle(0x0131, 0x0100, &result)) 3614 | return -EIO; 3615 | 3616 | count = snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); 3617 | 3618 | return count; 3619 | } 3620 | 3621 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd) 3622 | { 3623 | unsigned int result; 3624 | 3625 | if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) { 3626 | pr_info("no High Speed Charging capability found\n"); 3627 | return 0; 3628 | } 3629 | 3630 | sony_hsc = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); 3631 | if (!sony_hsc) 3632 | return -ENOMEM; 3633 | 3634 | sysfs_attr_init(&sony_hsc->attr); 3635 | sony_hsc->attr.name = "battery_highspeed_charging"; 3636 | sony_hsc->attr.mode = S_IRUGO | S_IWUSR; 3637 | sony_hsc->show = sony_nc_highspeed_charging_show; 3638 | sony_hsc->store = sony_nc_highspeed_charging_store; 3639 | 3640 | if (device_create_file(&pd->dev, sony_hsc)) { 3641 | kfree(sony_hsc); 3642 | sony_hsc = NULL; 3643 | return -1; 3644 | } 3645 | 3646 | return 0; 3647 | } 3648 | 3649 | static int sony_nc_highspeed_charging_cleanup(struct platform_device *pd) 3650 | { 3651 | if (sony_hsc) { 3652 | device_remove_file(&pd->dev, sony_hsc); 3653 | kfree(sony_hsc); 3654 | sony_hsc = NULL; 3655 | } 3656 | 3657 | return 0; 3658 | } 3659 | 3660 | static struct sony_tpad_device { 3661 | unsigned int handle; 3662 | struct device_attribute attr; 3663 | } *sony_tpad; 3664 | 3665 | static ssize_t sony_nc_touchpad_store(struct device *dev, 3666 | struct device_attribute *attr, 3667 | const char *buffer, size_t count) 3668 | { 3669 | unsigned int result; 3670 | unsigned long value; 3671 | 3672 | if (count > 31) 3673 | return -EINVAL; 3674 | if (strict_strtoul(buffer, 10, &value) || value > 1) 3675 | return -EINVAL; 3676 | 3677 | /* sysfs: 0 disabled, 1 enabled; EC: 0 enabled, 1 disabled */ 3678 | if (sony_call_snc_handle(sony_tpad->handle, 3679 | (!value << 0x10) | 0x100, &result)) 3680 | return -EIO; 3681 | 3682 | return count; 3683 | } 3684 | 3685 | static ssize_t sony_nc_touchpad_show(struct device *dev, 3686 | struct device_attribute *attr, char *buffer) 3687 | { 3688 | ssize_t count = 0; 3689 | unsigned int result; 3690 | 3691 | if (sony_call_snc_handle(sony_tpad->handle, 0x000, &result)) 3692 | return -EINVAL; 3693 | 3694 | /* 1 tpad off, 0 tpad on */ 3695 | count = snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01)); 3696 | return count; 3697 | } 3698 | 3699 | static int sony_nc_touchpad_setup(struct platform_device *pd, 3700 | unsigned int handle) 3701 | { 3702 | sony_tpad = kzalloc(sizeof(struct sony_tpad_device), GFP_KERNEL); 3703 | if (!sony_tpad) 3704 | return -ENOMEM; 3705 | 3706 | sony_tpad->handle = handle; 3707 | 3708 | sysfs_attr_init(&sony_tpad->attr.attr); 3709 | sony_tpad->attr.attr.name = "touchpad"; 3710 | sony_tpad->attr.attr.mode = S_IRUGO | S_IWUSR; 3711 | sony_tpad->attr.show = sony_nc_touchpad_show; 3712 | sony_tpad->attr.store = sony_nc_touchpad_store; 3713 | 3714 | if (device_create_file(&pd->dev, &sony_tpad->attr)) { 3715 | kfree(sony_tpad); 3716 | sony_tpad = NULL; 3717 | return -1; 3718 | } 3719 | 3720 | return 0; 3721 | } 3722 | 3723 | static int sony_nc_touchpad_cleanup(struct platform_device *pd) 3724 | { 3725 | if (sony_tpad) { 3726 | device_remove_file(&pd->dev, &sony_tpad->attr); 3727 | kfree(sony_tpad); 3728 | sony_tpad = NULL; 3729 | } 3730 | 3731 | return 0; 3732 | } 3733 | 3734 | #define SONY_FAN_HANDLE 0x0149 3735 | #define FAN_SPEEDS_NUM 4 /* leave some more room */ 3736 | #define FAN_ATTRS_NUM 3 3737 | static struct sony_fan_device { 3738 | unsigned int speeds_num; 3739 | unsigned int speeds[4]; 3740 | struct device_attribute attrs[3]; 3741 | } *sony_fan; 3742 | 3743 | static ssize_t sony_nc_fan_control_store(struct device *dev, 3744 | struct device_attribute *attr, 3745 | const char *buffer, size_t count) 3746 | { 3747 | unsigned int result; 3748 | unsigned long value; 3749 | 3750 | if (count > 31) 3751 | return -EINVAL; 3752 | if (strict_strtoul(buffer, 10, &value) 3753 | || value > sony_fan->speeds_num) 3754 | return -EINVAL; 3755 | 3756 | if (sony_call_snc_handle(SONY_FAN_HANDLE, 3757 | (value << 0x10) | 0x0200, &result)) 3758 | return -EIO; 3759 | 3760 | return count; 3761 | } 3762 | 3763 | static ssize_t sony_nc_fan_control_show(struct device *dev, 3764 | struct device_attribute *attr, char *buffer) 3765 | { 3766 | ssize_t count = 0; 3767 | unsigned int result; 3768 | 3769 | if (sony_call_snc_handle(SONY_FAN_HANDLE, 0x0100, &result)) 3770 | return -EINVAL; 3771 | 3772 | count = snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff); 3773 | return count; 3774 | } 3775 | 3776 | static ssize_t sony_nc_fan_profiles_show(struct device *dev, 3777 | struct device_attribute *attr, char *buffer) 3778 | { 3779 | ssize_t count = 0; 3780 | unsigned int i; 3781 | 3782 | for (i = 0; i < sony_fan->speeds_num; i++) 3783 | count += snprintf(buffer + count, PAGE_SIZE - count, 3784 | "%.4u ", sony_fan->speeds[i] * 100); 3785 | 3786 | count += snprintf(buffer + count, PAGE_SIZE - count, "\n"); 3787 | 3788 | return count; 3789 | } 3790 | 3791 | static ssize_t sony_nc_fan_speed_show(struct device *dev, 3792 | struct device_attribute *attr, char *buffer) 3793 | { 3794 | ssize_t count = 0; 3795 | unsigned int result; 3796 | 3797 | if (sony_call_snc_handle(SONY_FAN_HANDLE, 0x0300, &result)) 3798 | return -EINVAL; 3799 | 3800 | count = snprintf(buffer, PAGE_SIZE, "%d\n", 3801 | (result & 0xff) * 100); 3802 | return count; 3803 | } 3804 | 3805 | static int sony_nc_fan_setup(struct platform_device *pd) 3806 | { 3807 | int ret; 3808 | unsigned int i, found; 3809 | u8 list[FAN_SPEEDS_NUM * 2] = { 0 }; 3810 | 3811 | sony_fan = kzalloc(sizeof(struct sony_fan_device), GFP_KERNEL); 3812 | if (!sony_fan) 3813 | return -ENOMEM; 3814 | 3815 | ret = sony_call_snc_handle_buffer(SONY_FAN_HANDLE, 0x0000, 3816 | list, FAN_SPEEDS_NUM * 2); 3817 | if (ret < 0) 3818 | pr_info("unable to retrieve fan profiles table\n"); 3819 | 3820 | for (i = 0, found = 0; 3821 | list[i] != 0 && found < FAN_SPEEDS_NUM; i += 2, found++) { 3822 | 3823 | sony_fan->speeds[found] = list[i+1]; 3824 | } 3825 | sony_fan->speeds_num = found; 3826 | 3827 | sysfs_attr_init(&sony_fan->attrs[0].attr); 3828 | sony_fan->attrs[0].attr.name = "fan_speed"; 3829 | sony_fan->attrs[0].attr.mode = S_IRUGO; 3830 | sony_fan->attrs[0].show = sony_nc_fan_speed_show; 3831 | 3832 | sysfs_attr_init(&sony_fan->attrs[1].attr); 3833 | sony_fan->attrs[1].attr.name = "fan_profiles"; 3834 | sony_fan->attrs[1].attr.mode = S_IRUGO; 3835 | sony_fan->attrs[1].show = sony_nc_fan_profiles_show; 3836 | 3837 | sysfs_attr_init(&sony_fan->attrs[2].attr); 3838 | sony_fan->attrs[2].attr.name = "fan_control"; 3839 | sony_fan->attrs[2].attr.mode = S_IRUGO | S_IWUSR; 3840 | sony_fan->attrs[2].show = sony_nc_fan_control_show; 3841 | sony_fan->attrs[2].store = sony_nc_fan_control_store; 3842 | 3843 | for (i = 0; i < FAN_ATTRS_NUM; i++) { 3844 | if (device_create_file(&pd->dev, &sony_fan->attrs[i])) 3845 | goto attrserror; 3846 | } 3847 | 3848 | return 0; 3849 | 3850 | attrserror: 3851 | for (; i > 0; i--) 3852 | device_remove_file(&pd->dev, &sony_fan->attrs[i]); 3853 | 3854 | kfree(sony_fan); 3855 | sony_fan = NULL; 3856 | 3857 | return -1; 3858 | } 3859 | 3860 | static int sony_nc_fan_cleanup(struct platform_device *pd) 3861 | { 3862 | if (sony_fan) { 3863 | int i; 3864 | 3865 | for (i = 0; i < FAN_ATTRS_NUM; i++) 3866 | device_remove_file(&pd->dev, &sony_fan->attrs[i]); 3867 | 3868 | kfree(sony_fan); 3869 | sony_fan = NULL; 3870 | } 3871 | 3872 | return 0; 3873 | } 3874 | 3875 | static struct sony_odd_device { 3876 | unsigned int vendor_id; 3877 | unsigned int model_id; 3878 | struct device_attribute status_attr; 3879 | } *sony_odd; 3880 | 3881 | #if 0 3882 | static int sony_nc_odd_remove(void) 3883 | { 3884 | /* 3885 | 0 - change the link state first? 3886 | 1 - scsi lookup searching for the optical device (scsi_device *) 3887 | 2 - call int scsi_remove_device(struct scsi_device *sdev) 3888 | */ 3889 | 3890 | struct scsi_device *sdev; 3891 | struct Scsi_Host *shost; 3892 | int error = -ENXIO; 3893 | 3894 | shost = scsi_host_lookup(host); 3895 | if (!shost) 3896 | return error; 3897 | 3898 | sdev = scsi_device_lookup(shost, channel, id, lun); 3899 | if (sdev) { 3900 | scsi_remove_device(sdev); 3901 | scsi_device_put(sdev); 3902 | error = 0; 3903 | } 3904 | 3905 | scsi_host_put(shost); 3906 | 3907 | return 0; 3908 | } 3909 | #endif 3910 | 3911 | static ssize_t sony_nc_odd_status_store(struct device *dev, 3912 | struct device_attribute *attr, 3913 | const char *buffer, size_t count) 3914 | { 3915 | unsigned int result; 3916 | unsigned long value; 3917 | 3918 | if (count > 31) 3919 | return -EINVAL; 3920 | if (strict_strtoul(buffer, 10, &value) || value > 1) 3921 | return -EINVAL; 3922 | 3923 | #if 0 3924 | if (off) 3925 | sony_nc_odd_remove(); 3926 | 3927 | /* and goes on, otherwise leave */ 3928 | #endif 3929 | 3930 | /* 0x200 turn on (sysfs: 1), 0x300 turn off (sysfs: 0) */ 3931 | value = (!value << 0x08) + 0x200; 3932 | 3933 | /* the MSB have to be high */ 3934 | if (sony_call_snc_handle(0x126, (1 << 0x10) | value, &result)) 3935 | return -EIO; 3936 | 3937 | #if 0 3938 | if (on) 3939 | /* force a bus scan? */ 3940 | #endif 3941 | 3942 | return count; 3943 | } 3944 | 3945 | static ssize_t sony_nc_odd_status_show(struct device *dev, 3946 | struct device_attribute *attr, char *buffer) 3947 | { 3948 | ssize_t count = 0; 3949 | unsigned int result; 3950 | 3951 | if (sony_call_snc_handle(0x126, 0x100, &result)) 3952 | return -EINVAL; 3953 | 3954 | count = snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); 3955 | return count; 3956 | } 3957 | 3958 | static int sony_nc_odd_setup(struct platform_device *pd) 3959 | { 3960 | #define ODD_TAB_SIZE 32 3961 | u8 list[ODD_TAB_SIZE] = { 0 }; 3962 | int ret = 0; 3963 | int found = 0; 3964 | int i = 0; 3965 | unsigned int vendor = 0; 3966 | unsigned int model = 0; 3967 | u16 word = 0; 3968 | 3969 | ret = sony_call_snc_handle_buffer(0x126, 0x0000, list, ODD_TAB_SIZE); 3970 | if (ret < 0) { 3971 | pr_info("unable to retrieve the odd table\n"); 3972 | return -EIO; 3973 | } 3974 | 3975 | /* parse the table looking for optical devices */ 3976 | do { 3977 | word = (list[i+1] << 8) | list[i]; 3978 | 3979 | if (word == 1) { /* 1 DWord device data following */ 3980 | vendor = (list[i+3] << 8) | list[i+2]; 3981 | model = (list[i+5] << 8) | list[i+4]; 3982 | found++; 3983 | i += 6; 3984 | } else { 3985 | i += 2; 3986 | } 3987 | } while (word != 0xff00); 3988 | 3989 | if (found) 3990 | dprintk("one optical device found, connected to: %x:%x\n", 3991 | vendor, model); 3992 | else 3993 | return 0; 3994 | 3995 | sony_odd = kzalloc(sizeof(*sony_odd), GFP_KERNEL); 3996 | if (!sony_odd) 3997 | return -ENOMEM; 3998 | 3999 | sony_odd->vendor_id = vendor; 4000 | sony_odd->model_id = model; 4001 | 4002 | sysfs_attr_init(&sony_odd->status_attr.attr); 4003 | sony_odd->status_attr.attr.name = "odd_power"; 4004 | sony_odd->status_attr.attr.mode = S_IRUGO | S_IWUSR; 4005 | sony_odd->status_attr.show = sony_nc_odd_status_show; 4006 | sony_odd->status_attr.store = sony_nc_odd_status_store; 4007 | 4008 | if (device_create_file(&pd->dev, &sony_odd->status_attr)) { 4009 | kfree(sony_odd); 4010 | sony_odd = NULL; 4011 | return -1; 4012 | } 4013 | 4014 | return 0; 4015 | } 4016 | 4017 | static int sony_nc_odd_cleanup(struct platform_device *pd) 4018 | { 4019 | if (sony_odd) { 4020 | device_remove_file(&pd->dev, &sony_odd->status_attr); 4021 | kfree(sony_odd); 4022 | sony_odd = NULL; 4023 | } 4024 | 4025 | return 0; 4026 | } 4027 | 4028 | /* 4029 | * Backlight device 4030 | */ 4031 | static struct backlight_device *sony_backlight_device; 4032 | 4033 | static int sony_backlight_update_status(struct backlight_device *bd) 4034 | { 4035 | return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", 4036 | bd->props.brightness + 1, NULL); 4037 | } 4038 | 4039 | static int sony_backlight_get_brightness(struct backlight_device *bd) 4040 | { 4041 | unsigned int value; 4042 | 4043 | if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) 4044 | return 0; 4045 | /* brightness levels are 1-based, while backlight ones are 0-based */ 4046 | return value - 1; 4047 | } 4048 | 4049 | static const struct backlight_ops sony_backlight_ops = { 4050 | .options = BL_CORE_SUSPENDRESUME, 4051 | .update_status = sony_backlight_update_status, 4052 | .get_brightness = sony_backlight_get_brightness, 4053 | }; 4054 | static const struct backlight_ops sony_als_backlight_ops = { 4055 | .options = BL_CORE_SUSPENDRESUME, 4056 | .update_status = sony_nc_als_update_status, 4057 | .get_brightness = sony_nc_als_get_brightness, 4058 | }; 4059 | 4060 | static void sony_nc_backlight_setup(void) 4061 | { 4062 | acpi_handle unused; 4063 | int max_brightness = 0; 4064 | const struct backlight_ops *ops = NULL; 4065 | struct backlight_properties props; 4066 | 4067 | /* do not use SNC GBRT/SBRT controls along with the ALS */ 4068 | if (sony_als) { 4069 | /* ALS based backlight device */ 4070 | ops = &sony_als_backlight_ops; 4071 | max_brightness = sony_als->levels_num - 1; 4072 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", 4073 | &unused))) { 4074 | ops = &sony_backlight_ops; 4075 | max_brightness = SONY_MAX_BRIGHTNESS - 1; 4076 | } else { 4077 | return; 4078 | } 4079 | 4080 | memset(&props, 0, sizeof(struct backlight_properties)); 4081 | props.type = BACKLIGHT_PLATFORM; 4082 | props.max_brightness = max_brightness; 4083 | sony_backlight_device = backlight_device_register("sony", NULL, NULL, 4084 | ops, &props); 4085 | 4086 | if (IS_ERR(sony_backlight_device)) { 4087 | pr_warn("unable to register backlight device\n"); 4088 | sony_backlight_device = NULL; 4089 | } else { 4090 | sony_backlight_device->props.brightness = 4091 | ops->get_brightness(sony_backlight_device); 4092 | } 4093 | } 4094 | 4095 | static void sony_nc_backlight_cleanup(void) 4096 | { 4097 | if (sony_backlight_device) 4098 | backlight_device_unregister(sony_backlight_device); 4099 | } 4100 | 4101 | static void sony_nc_tests_resume(void) 4102 | { 4103 | return; 4104 | } 4105 | 4106 | /* place here some unknown handles, we might want to see some output */ 4107 | static void sony_nc_tests_setup(void) 4108 | { 4109 | int ret; 4110 | unsigned int result; 4111 | 4112 | result = 0; 4113 | ret = sony_call_snc_handle(0x114, 0x000, &result); 4114 | if (!ret) 4115 | pr_info("handle 0x114 returned: %x\n", result & 0xff); 4116 | 4117 | result = 0; 4118 | ret = sony_call_snc_handle(0x139, 0x0000, &result); 4119 | if (!ret) 4120 | pr_info("handle 0x139+00 returned: %x\n", result & 0xffff); 4121 | 4122 | result = 0; 4123 | ret = sony_call_snc_handle(0x139, 0x0100, &result); 4124 | if (!ret) 4125 | pr_info("handle 0x139+01 returned: %x\n", result & 0xffff); 4126 | 4127 | return; 4128 | } 4129 | 4130 | static void sony_nc_snc_setup_handles(struct platform_device *pd) 4131 | { 4132 | unsigned int i; 4133 | 4134 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { 4135 | int ret = 0; 4136 | int unsigned handle = handles->cap[i]; 4137 | 4138 | if (!handle) 4139 | continue; 4140 | 4141 | dprintk("looking at handle 0x%.4x\n", handle); 4142 | 4143 | switch (handle) { 4144 | case 0x0100: 4145 | case 0x0127: 4146 | case 0x0101: 4147 | case 0x0102: 4148 | ret = sony_nc_function_setup(handle); 4149 | break; 4150 | case 0x0105: 4151 | case 0x0148: /* same as 0x0105 + Fn-F1 combo */ 4152 | ret = sony_nc_touchpad_setup(pd, handle); 4153 | break; 4154 | case 0x0115: 4155 | case 0x0136: 4156 | case 0x013f: 4157 | ret = sony_nc_battery_care_setup(pd, handle); 4158 | break; 4159 | case 0x0119: 4160 | ret = sony_nc_lid_resume_setup(pd); 4161 | break; 4162 | case 0x0122: 4163 | ret = sony_nc_thermal_setup(pd); 4164 | break; 4165 | case 0x0126: 4166 | ret = sony_nc_odd_setup(pd); 4167 | break; 4168 | case 0x0137: 4169 | case 0x0143: 4170 | ret = sony_nc_kbd_backlight_setup(pd, handle); 4171 | case 0x012f: /* no keyboard backlight */ 4172 | ret = sony_nc_als_setup(pd, handle); 4173 | break; 4174 | case 0x0131: 4175 | ret = sony_nc_highspeed_charging_setup(pd); 4176 | break; 4177 | case 0x0134: 4178 | case 0x0147: 4179 | ret = sony_nc_gsensor_setup(pd, handle); 4180 | break; 4181 | case 0x0149: 4182 | ret = sony_nc_fan_setup(pd); 4183 | break; 4184 | case 0x0124: 4185 | case 0x0135: 4186 | ret = sony_nc_rfkill_setup(sony_nc_acpi_device, handle); 4187 | break; 4188 | default: 4189 | continue; 4190 | } 4191 | 4192 | if (ret < 0) { 4193 | pr_warn("handle 0x%.4x setup failed (ret: %i)", 4194 | handle, ret); 4195 | } else { 4196 | dprintk("handle 0x%.4x setup completed\n", handle); 4197 | } 4198 | } 4199 | 4200 | if (debug) 4201 | sony_nc_tests_setup(); 4202 | } 4203 | 4204 | static void sony_nc_snc_cleanup_handles(struct platform_device *pd) 4205 | { 4206 | unsigned int i; 4207 | 4208 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { 4209 | 4210 | int unsigned handle = handles->cap[i]; 4211 | 4212 | if (!handle) 4213 | continue; 4214 | 4215 | dprintk("looking at handle 0x%.4x\n", handle); 4216 | 4217 | switch (handle) { 4218 | case 0x0105: 4219 | case 0x0148: 4220 | sony_nc_touchpad_cleanup(pd); 4221 | break; 4222 | case 0x0115: 4223 | case 0x0136: 4224 | case 0x013f: 4225 | sony_nc_battery_care_cleanup(pd); 4226 | break; 4227 | case 0x0119: 4228 | sony_nc_lid_resume_cleanup(pd); 4229 | break; 4230 | case 0x0122: 4231 | sony_nc_thermal_cleanup(pd); 4232 | break; 4233 | case 0x0126: 4234 | sony_nc_odd_cleanup(pd); 4235 | break; 4236 | case 0x0137: 4237 | case 0x0143: 4238 | sony_nc_kbd_backlight_cleanup(pd); 4239 | case 0x012f: 4240 | sony_nc_als_cleanup(pd); 4241 | break; 4242 | case 0x0131: 4243 | sony_nc_highspeed_charging_cleanup(pd); 4244 | break; 4245 | case 0x0134: 4246 | case 0x0147: 4247 | sony_nc_gsensor_cleanup(pd); 4248 | break; 4249 | case 0x0149: 4250 | sony_nc_fan_cleanup(pd); 4251 | break; 4252 | case 0x0124: 4253 | case 0x0135: 4254 | sony_nc_rfkill_cleanup(); 4255 | break; 4256 | default: 4257 | continue; 4258 | } 4259 | 4260 | dprintk("handle 0x%.4x deconfigured\n", handle); 4261 | } 4262 | } 4263 | 4264 | static int sony_nc_snc_setup(struct platform_device *pd) 4265 | { 4266 | unsigned int i, string[4], bitmask, result; 4267 | 4268 | for (i = 0; i < 4; i++) { 4269 | if (acpi_callsetfunc(sony_nc_acpi_handle, 4270 | "SN00", i, &string[i])) 4271 | return -EIO; 4272 | } 4273 | if (strncmp("SncSupported", (char *) string, 0x10)) { 4274 | pr_info("SNC device present but not supported by hardware"); 4275 | return -1; 4276 | } 4277 | 4278 | if (!acpi_callsetfunc(sony_nc_acpi_handle, "SN00", 0x04, &result)) { 4279 | unsigned int model, i; 4280 | for (model = 0, i = 0; i < 4; i++) 4281 | model |= ((result >> (i * 8)) & 0xff) << ((3 - i) * 8); 4282 | pr_info("found Vaio model ID: %u\n", model); 4283 | } 4284 | 4285 | /* retrieve the implemented offsets mask */ 4286 | if (acpi_callsetfunc(sony_nc_acpi_handle, "SN00", 0x10, &bitmask)) 4287 | return -EIO; 4288 | 4289 | /* retrieve the available handles, otherwise return */ 4290 | if (sony_nc_handles_setup(pd)) 4291 | return -2; 4292 | 4293 | /* setup found handles here */ 4294 | sony_nc_snc_setup_handles(pd); 4295 | 4296 | /* Enable all events for the found handles, otherwise return */ 4297 | if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", bitmask, &result)) 4298 | return -EIO; 4299 | 4300 | /* check for SN05 presence? */ 4301 | 4302 | return 0; 4303 | } 4304 | 4305 | static int sony_nc_snc_cleanup(struct platform_device *pd) 4306 | { 4307 | unsigned int result, bitmask; 4308 | 4309 | /* retrieve the event enabled handles */ 4310 | acpi_callgetfunc(sony_nc_acpi_handle, "SN01", &bitmask); 4311 | 4312 | /* disable the event generation for every handle */ 4313 | acpi_callsetfunc(sony_nc_acpi_handle, "SN03", bitmask, &result); 4314 | 4315 | /* cleanup handles here */ 4316 | sony_nc_snc_cleanup_handles(pd); 4317 | 4318 | sony_nc_handles_cleanup(pd); 4319 | 4320 | return 0; 4321 | } 4322 | 4323 | static int sony_nc_snc_resume(void) 4324 | { 4325 | unsigned int i, result, bitmask; 4326 | 4327 | /* retrieve the implemented offsets mask */ 4328 | if (acpi_callsetfunc(sony_nc_acpi_handle, "SN00", 0x10, &bitmask)) 4329 | return -EIO; 4330 | 4331 | /* Enable all events, otherwise return */ 4332 | if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", bitmask, &result)) 4333 | return -EIO; 4334 | 4335 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { 4336 | int unsigned handle = handles->cap[i]; 4337 | 4338 | if (!handle) 4339 | continue; 4340 | 4341 | dprintk("looking at handle 0x%.4x\n", handle); 4342 | 4343 | switch (handle) { 4344 | case 0x0100: 4345 | case 0x0127: 4346 | case 0x0101: 4347 | case 0x0102: 4348 | sony_nc_function_setup(handle); 4349 | break; 4350 | case 0x0122: 4351 | sony_nc_thermal_resume(); 4352 | break; 4353 | case 0x0124: 4354 | case 0x0135: 4355 | /* re-read rfkill state */ 4356 | sony_nc_rfkill_update(); 4357 | break; 4358 | case 0x0137: /* kbd + als */ 4359 | case 0x0143: 4360 | sony_nc_kbd_backlight_resume(); 4361 | case 0x012f: /* als only */ 4362 | sony_nc_als_resume(); 4363 | break; 4364 | default: 4365 | continue; 4366 | } 4367 | 4368 | dprintk("handle 0x%.4x updated\n", handle); 4369 | } 4370 | 4371 | if (debug) 4372 | sony_nc_tests_resume(); 4373 | 4374 | return 0; 4375 | } 4376 | 4377 | /* 4378 | * ACPI callbacks 4379 | */ 4380 | static acpi_status sony_walk_callback(acpi_handle handle, u32 level, 4381 | void *context, void **return_value) 4382 | { 4383 | struct acpi_device_info *info; 4384 | 4385 | if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { 4386 | pr_warn("method: name: %4.4s, args %X\n", 4387 | (char *)&info->name, info->param_count); 4388 | 4389 | kfree(info); 4390 | } 4391 | 4392 | return AE_OK; 4393 | } 4394 | 4395 | #define EV_HOTKEYS 1 4396 | #define EV_RFKILL 2 4397 | #define EV_ALS 3 4398 | #define EV_GSENSOR 4 4399 | #define EV_HGFX 5 4400 | static void sony_nc_notify(struct acpi_device *device, u32 event) 4401 | { 4402 | u8 ev = 0; 4403 | int value = 0; 4404 | char *env[2] = { NULL }; 4405 | 4406 | dprintk("sony_nc_notify, event: 0x%.2x\n", event); 4407 | 4408 | /* handles related events */ 4409 | if (event >= 0x90) { 4410 | unsigned int result = 0, handle = 0; 4411 | 4412 | /* the event should corrispond to the offset of the method */ 4413 | unsigned int offset = event - 0x90; 4414 | 4415 | handle = handles->cap[offset]; 4416 | switch (handle) { 4417 | /* list of handles known for generating events */ 4418 | case 0x0100: 4419 | case 0x0127: 4420 | /* hotkey event, a key has been pressed, retrieve it */ 4421 | value = sony_nc_hotkeys_decode(handle); 4422 | if (value > 0) /* known event */ 4423 | sony_laptop_report_input_event(value); 4424 | else /* restore the original event */ 4425 | value = event; 4426 | 4427 | ev = EV_HOTKEYS; 4428 | break; 4429 | 4430 | case 0x0143: 4431 | sony_call_snc_handle(handle, 0x2000, &result); 4432 | /* event reasons are reverted */ 4433 | value = (result & 0x03) == 1 ? 2 : 1; 4434 | dprintk("sony_nc_notify, ALS event received (reason:" 4435 | " %s change)\n", value == 1 ? "light" : 4436 | "backlight"); 4437 | 4438 | env[0] = (value == 1) ? "ALS=1" : "ALS=2"; 4439 | kobject_uevent_env(&device->dev.kobj, KOBJ_CHANGE, env); 4440 | 4441 | ev = EV_ALS; 4442 | break; 4443 | 4444 | case 0x012f: 4445 | case 0x0137: 4446 | sony_call_snc_handle(handle, 0x0800, &result); 4447 | value = result & 0x03; 4448 | dprintk("sony_nc_notify, ALS event received (reason:" 4449 | " %s change)\n", value == 1 ? "light" : 4450 | "backlight"); 4451 | if (value == 1) /* lighting change reason */ 4452 | sony_nc_als_event_handler(); 4453 | 4454 | env[0] = (value == 1) ? "ALS=1" : "ALS=2"; 4455 | kobject_uevent_env(&device->dev.kobj, KOBJ_CHANGE, env); 4456 | 4457 | ev = EV_ALS; 4458 | break; 4459 | 4460 | case 0x0124: 4461 | case 0x0135: 4462 | sony_call_snc_handle(handle, 0x0100, &result); 4463 | result &= 0x03; 4464 | dprintk("sony_nc_notify, RFKILL event received " 4465 | "(reason: %s)\n", result == 1 ? 4466 | "switch state changed" : "battery"); 4467 | 4468 | if (result == 1) { /* hw swtich event */ 4469 | sony_nc_rfkill_update(); 4470 | value = sony_nc_get_rfkill_hwblock(); 4471 | } else if (result == 2) { /* battery event */ 4472 | /* we might need to change the WWAN rfkill 4473 | state when the battery state changes 4474 | */ 4475 | sony_nc_rfkill_update_wwan(); 4476 | return; 4477 | } 4478 | 4479 | ev = EV_RFKILL; 4480 | break; 4481 | 4482 | case 0x0134: 4483 | case 0x0147: 4484 | ev = 4; 4485 | value = EV_GSENSOR; 4486 | /* hdd protection event, notify userspace */ 4487 | 4488 | env[0] = "HDD_SHOCK=1"; 4489 | kobject_uevent_env(&device->dev.kobj, KOBJ_CHANGE, env); 4490 | 4491 | break; 4492 | 4493 | case 0x0128: 4494 | case 0x0146: 4495 | /* Hybrid GFX switching, 1 */ 4496 | sony_call_snc_handle(handle, 0x0000, &result); 4497 | dprintk("sony_nc_notify, Hybrid GFX event received " 4498 | "(reason: %s)\n", (result & 0x01) ? 4499 | "switch position change" : "unknown"); 4500 | 4501 | /* verify the switch state 4502 | (1: discrete GFX, 0: integrated GFX)*/ 4503 | result = 0; 4504 | sony_call_snc_handle(handle, 0x0100, &result); 4505 | 4506 | /* sony_laptop_report_input_event(); */ 4507 | 4508 | ev = EV_HGFX; 4509 | value = result & 0xff; 4510 | break; 4511 | 4512 | default: 4513 | value = event; 4514 | dprintk("Unknowk event for handle: 0x%x\n", handle); 4515 | break; 4516 | } 4517 | 4518 | /* clear the event (and the event reason when present) */ 4519 | acpi_callsetfunc(sony_nc_acpi_handle, "SN05", 1 << offset, 4520 | &result); 4521 | } else { 4522 | ev = 1; 4523 | sony_laptop_report_input_event(event); 4524 | } 4525 | 4526 | acpi_bus_generate_proc_event(device, ev, value); 4527 | acpi_bus_generate_netlink_event(device->pnp.device_class, 4528 | dev_name(&device->dev), ev, value); 4529 | } 4530 | 4531 | static int sony_nc_add(struct acpi_device *device) 4532 | { 4533 | acpi_status status; 4534 | int result = 0; 4535 | acpi_handle handle; 4536 | struct sony_nc_value *item; 4537 | 4538 | pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); 4539 | 4540 | sony_nc_acpi_device = device; 4541 | strcpy(acpi_device_class(device), "sony/hotkey"); 4542 | 4543 | sony_nc_acpi_handle = device->handle; 4544 | 4545 | /* read device status */ 4546 | result = acpi_bus_get_status(device); 4547 | /* bail IFF the above call was successful 4548 | and the device is not present */ 4549 | if (!result && !device->status.present) { 4550 | dprintk("Device not present\n"); 4551 | result = -ENODEV; 4552 | goto outwalk; 4553 | } 4554 | 4555 | result = sony_pf_add(); 4556 | if (result) 4557 | goto outpresent; 4558 | 4559 | if (debug) { 4560 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, 4561 | sony_nc_acpi_handle, 1, sony_walk_callback, 4562 | NULL, NULL, NULL); 4563 | if (ACPI_FAILURE(status)) { 4564 | pr_warn("unable to walk acpi resources\n"); 4565 | result = -ENODEV; 4566 | goto outpresent; 4567 | } 4568 | } 4569 | 4570 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", 4571 | &handle))) { 4572 | if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) 4573 | dprintk("ECON Method failed\n"); 4574 | } 4575 | 4576 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", 4577 | &handle))) { 4578 | dprintk("Doing SNC setup\n"); 4579 | 4580 | if (sony_nc_snc_setup(sony_pf_device)) 4581 | goto outsnc; 4582 | } 4583 | 4584 | /* setup input devices and helper fifo */ 4585 | result = sony_laptop_setup_input(device); 4586 | if (result) { 4587 | pr_err("Unable to create input devices\n"); 4588 | goto outsnc; 4589 | } 4590 | 4591 | if (acpi_video_backlight_support()) { 4592 | pr_info("brightness ignored, must be " 4593 | "controlled by ACPI video driver\n"); 4594 | } else { 4595 | sony_nc_backlight_setup(); 4596 | } 4597 | 4598 | /* create sony_pf sysfs attributes related to the SNC device */ 4599 | for (item = sony_nc_values; item->name; ++item) { 4600 | 4601 | if (!debug && item->debug) 4602 | continue; 4603 | 4604 | /* find the available acpiget as described in the DSDT */ 4605 | for (; item->acpiget && *item->acpiget; ++item->acpiget) { 4606 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, 4607 | *item->acpiget, 4608 | &handle))) { 4609 | dprintk("Found %s getter: %s\n", 4610 | item->name, *item->acpiget); 4611 | item->devattr.attr.mode |= S_IRUGO; 4612 | break; 4613 | } 4614 | } 4615 | 4616 | /* find the available acpiset as described in the DSDT */ 4617 | for (; item->acpiset && *item->acpiset; ++item->acpiset) { 4618 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, 4619 | *item->acpiset, 4620 | &handle))) { 4621 | dprintk("Found %s setter: %s\n", 4622 | item->name, *item->acpiset); 4623 | item->devattr.attr.mode |= S_IWUSR; 4624 | break; 4625 | } 4626 | } 4627 | 4628 | if (item->devattr.attr.mode != 0) { 4629 | result = 4630 | device_create_file(&sony_pf_device->dev, 4631 | &item->devattr); 4632 | if (result) 4633 | goto out_sysfs; 4634 | } 4635 | } 4636 | 4637 | return 0; 4638 | 4639 | out_sysfs: 4640 | for (item = sony_nc_values; item->name; ++item) 4641 | device_remove_file(&sony_pf_device->dev, &item->devattr); 4642 | 4643 | sony_nc_backlight_cleanup(); 4644 | 4645 | sony_laptop_remove_input(); 4646 | 4647 | outsnc: 4648 | sony_nc_snc_cleanup(sony_pf_device); 4649 | 4650 | outpresent: 4651 | sony_pf_remove(); 4652 | 4653 | outwalk: 4654 | return result; 4655 | } 4656 | 4657 | static int sony_nc_remove(struct acpi_device *device, int type) 4658 | { 4659 | struct sony_nc_value *item; 4660 | 4661 | sony_nc_backlight_cleanup(); 4662 | 4663 | sony_nc_acpi_device = NULL; 4664 | 4665 | for (item = sony_nc_values; item->name; ++item) 4666 | device_remove_file(&sony_pf_device->dev, &item->devattr); 4667 | 4668 | sony_nc_snc_cleanup(sony_pf_device); 4669 | sony_pf_remove(); 4670 | sony_laptop_remove_input(); 4671 | dprintk(SONY_NC_DRIVER_NAME " removed.\n"); 4672 | 4673 | return 0; 4674 | } 4675 | 4676 | static int sony_nc_resume(struct acpi_device *device) 4677 | { 4678 | struct sony_nc_value *item; 4679 | acpi_handle handle; 4680 | 4681 | for (item = sony_nc_values; item->name; item++) { 4682 | int ret; 4683 | 4684 | if (!item->valid) 4685 | continue; 4686 | ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, 4687 | item->value, NULL); 4688 | if (ret < 0) { 4689 | pr_err("%s: %d\n", __func__, ret); 4690 | break; 4691 | } 4692 | } 4693 | 4694 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", 4695 | &handle))) { 4696 | if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) 4697 | dprintk("ECON Method failed\n"); 4698 | } 4699 | 4700 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", 4701 | &handle))) { 4702 | dprintk("Doing SNC setup\n"); 4703 | 4704 | sony_nc_snc_resume(); 4705 | } 4706 | 4707 | /* set the last requested brightness level */ 4708 | if (sony_backlight_device && 4709 | sony_backlight_ops.update_status(sony_backlight_device) < 0) 4710 | pr_warn("unable to restore brightness level\n"); 4711 | 4712 | return 0; 4713 | } 4714 | 4715 | static const struct acpi_device_id sony_device_ids[] = { 4716 | {SONY_NC_HID, 0}, 4717 | {SONY_PIC_HID, 0}, 4718 | {"", 0}, 4719 | }; 4720 | MODULE_DEVICE_TABLE(acpi, sony_device_ids); 4721 | 4722 | static const struct acpi_device_id sony_nc_device_ids[] = { 4723 | {SONY_NC_HID, 0}, 4724 | {"", 0}, 4725 | }; 4726 | 4727 | static struct acpi_driver sony_nc_driver = { 4728 | .name = SONY_NC_DRIVER_NAME, 4729 | .class = SONY_NC_CLASS, 4730 | .ids = sony_nc_device_ids, 4731 | .owner = THIS_MODULE, 4732 | .ops = { 4733 | .add = sony_nc_add, 4734 | .remove = sony_nc_remove, 4735 | .resume = sony_nc_resume, 4736 | .notify = sony_nc_notify, 4737 | }, 4738 | }; 4739 | 4740 | /*********** SPIC (SNY6001) Device ***********/ 4741 | 4742 | #define SONYPI_DEVICE_TYPE1 0x00000001 4743 | #define SONYPI_DEVICE_TYPE2 0x00000002 4744 | #define SONYPI_DEVICE_TYPE3 0x00000004 4745 | 4746 | #define SONYPI_TYPE1_OFFSET 0x04 4747 | #define SONYPI_TYPE2_OFFSET 0x12 4748 | #define SONYPI_TYPE3_OFFSET 0x12 4749 | 4750 | struct sony_pic_ioport { 4751 | struct acpi_resource_io io1; 4752 | struct acpi_resource_io io2; 4753 | struct list_head list; 4754 | }; 4755 | 4756 | struct sony_pic_irq { 4757 | struct acpi_resource_irq irq; 4758 | struct list_head list; 4759 | }; 4760 | 4761 | struct sonypi_eventtypes { 4762 | u8 data; 4763 | unsigned long mask; 4764 | struct sonypi_event *events; 4765 | }; 4766 | 4767 | struct sony_pic_dev { 4768 | struct acpi_device *acpi_dev; 4769 | struct sony_pic_irq *cur_irq; 4770 | struct sony_pic_ioport *cur_ioport; 4771 | struct list_head interrupts; 4772 | struct list_head ioports; 4773 | struct mutex lock; 4774 | struct sonypi_eventtypes *event_types; 4775 | int (*handle_irq)(const u8, const u8); 4776 | int model; 4777 | u16 evport_offset; 4778 | u8 camera_power; 4779 | u8 bluetooth_power; 4780 | u8 wwan_power; 4781 | }; 4782 | 4783 | static struct sony_pic_dev spic_dev = { 4784 | .interrupts = LIST_HEAD_INIT(spic_dev.interrupts), 4785 | .ioports = LIST_HEAD_INIT(spic_dev.ioports), 4786 | }; 4787 | 4788 | static int spic_drv_registered; 4789 | 4790 | /* Event masks */ 4791 | #define SONYPI_JOGGER_MASK 0x00000001 4792 | #define SONYPI_CAPTURE_MASK 0x00000002 4793 | #define SONYPI_FNKEY_MASK 0x00000004 4794 | #define SONYPI_BLUETOOTH_MASK 0x00000008 4795 | #define SONYPI_PKEY_MASK 0x00000010 4796 | #define SONYPI_BACK_MASK 0x00000020 4797 | #define SONYPI_HELP_MASK 0x00000040 4798 | #define SONYPI_LID_MASK 0x00000080 4799 | #define SONYPI_ZOOM_MASK 0x00000100 4800 | #define SONYPI_THUMBPHRASE_MASK 0x00000200 4801 | #define SONYPI_MEYE_MASK 0x00000400 4802 | #define SONYPI_MEMORYSTICK_MASK 0x00000800 4803 | #define SONYPI_BATTERY_MASK 0x00001000 4804 | #define SONYPI_WIRELESS_MASK 0x00002000 4805 | 4806 | struct sonypi_event { 4807 | u8 data; 4808 | u8 event; 4809 | }; 4810 | 4811 | /* The set of possible button release events */ 4812 | static struct sonypi_event sonypi_releaseev[] = { 4813 | { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED }, 4814 | { 0, 0 } 4815 | }; 4816 | 4817 | /* The set of possible jogger events */ 4818 | static struct sonypi_event sonypi_joggerev[] = { 4819 | { 0x1f, SONYPI_EVENT_JOGDIAL_UP }, 4820 | { 0x01, SONYPI_EVENT_JOGDIAL_DOWN }, 4821 | { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED }, 4822 | { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED }, 4823 | { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP }, 4824 | { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN }, 4825 | { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED }, 4826 | { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED }, 4827 | { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP }, 4828 | { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN }, 4829 | { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED }, 4830 | { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED }, 4831 | { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED }, 4832 | { 0, 0 } 4833 | }; 4834 | 4835 | /* The set of possible capture button events */ 4836 | static struct sonypi_event sonypi_captureev[] = { 4837 | { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED }, 4838 | { 0x07, SONYPI_EVENT_CAPTURE_PRESSED }, 4839 | { 0x40, SONYPI_EVENT_CAPTURE_PRESSED }, 4840 | { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED }, 4841 | { 0, 0 } 4842 | }; 4843 | 4844 | /* The set of possible fnkeys events */ 4845 | static struct sonypi_event sonypi_fnkeyev[] = { 4846 | { 0x10, SONYPI_EVENT_FNKEY_ESC }, 4847 | { 0x11, SONYPI_EVENT_FNKEY_F1 }, 4848 | { 0x12, SONYPI_EVENT_FNKEY_F2 }, 4849 | { 0x13, SONYPI_EVENT_FNKEY_F3 }, 4850 | { 0x14, SONYPI_EVENT_FNKEY_F4 }, 4851 | { 0x15, SONYPI_EVENT_FNKEY_F5 }, 4852 | { 0x16, SONYPI_EVENT_FNKEY_F6 }, 4853 | { 0x17, SONYPI_EVENT_FNKEY_F7 }, 4854 | { 0x18, SONYPI_EVENT_FNKEY_F8 }, 4855 | { 0x19, SONYPI_EVENT_FNKEY_F9 }, 4856 | { 0x1a, SONYPI_EVENT_FNKEY_F10 }, 4857 | { 0x1b, SONYPI_EVENT_FNKEY_F11 }, 4858 | { 0x1c, SONYPI_EVENT_FNKEY_F12 }, 4859 | { 0x1f, SONYPI_EVENT_FNKEY_RELEASED }, 4860 | { 0x21, SONYPI_EVENT_FNKEY_1 }, 4861 | { 0x22, SONYPI_EVENT_FNKEY_2 }, 4862 | { 0x31, SONYPI_EVENT_FNKEY_D }, 4863 | { 0x32, SONYPI_EVENT_FNKEY_E }, 4864 | { 0x33, SONYPI_EVENT_FNKEY_F }, 4865 | { 0x34, SONYPI_EVENT_FNKEY_S }, 4866 | { 0x35, SONYPI_EVENT_FNKEY_B }, 4867 | { 0x36, SONYPI_EVENT_FNKEY_ONLY }, 4868 | { 0, 0 } 4869 | }; 4870 | 4871 | /* The set of possible program key events */ 4872 | static struct sonypi_event sonypi_pkeyev[] = { 4873 | { 0x01, SONYPI_EVENT_PKEY_P1 }, 4874 | { 0x02, SONYPI_EVENT_PKEY_P2 }, 4875 | { 0x04, SONYPI_EVENT_PKEY_P3 }, 4876 | { 0x20, SONYPI_EVENT_PKEY_P1 }, 4877 | { 0, 0 } 4878 | }; 4879 | 4880 | /* The set of possible bluetooth events */ 4881 | static struct sonypi_event sonypi_blueev[] = { 4882 | { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED }, 4883 | { 0x59, SONYPI_EVENT_BLUETOOTH_ON }, 4884 | { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF }, 4885 | { 0, 0 } 4886 | }; 4887 | 4888 | /* The set of possible wireless events */ 4889 | static struct sonypi_event sonypi_wlessev[] = { 4890 | { 0x59, SONYPI_EVENT_IGNORE }, 4891 | { 0x5a, SONYPI_EVENT_IGNORE }, 4892 | { 0, 0 } 4893 | }; 4894 | 4895 | /* The set of possible back button events */ 4896 | static struct sonypi_event sonypi_backev[] = { 4897 | { 0x20, SONYPI_EVENT_BACK_PRESSED }, 4898 | { 0, 0 } 4899 | }; 4900 | 4901 | /* The set of possible help button events */ 4902 | static struct sonypi_event sonypi_helpev[] = { 4903 | { 0x3b, SONYPI_EVENT_HELP_PRESSED }, 4904 | { 0, 0 } 4905 | }; 4906 | 4907 | 4908 | /* The set of possible lid events */ 4909 | static struct sonypi_event sonypi_lidev[] = { 4910 | { 0x51, SONYPI_EVENT_LID_CLOSED }, 4911 | { 0x50, SONYPI_EVENT_LID_OPENED }, 4912 | { 0, 0 } 4913 | }; 4914 | 4915 | /* The set of possible zoom events */ 4916 | static struct sonypi_event sonypi_zoomev[] = { 4917 | { 0x39, SONYPI_EVENT_ZOOM_PRESSED }, 4918 | { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED }, 4919 | { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED }, 4920 | { 0x04, SONYPI_EVENT_ZOOM_PRESSED }, 4921 | { 0, 0 } 4922 | }; 4923 | 4924 | /* The set of possible thumbphrase events */ 4925 | static struct sonypi_event sonypi_thumbphraseev[] = { 4926 | { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED }, 4927 | { 0, 0 } 4928 | }; 4929 | 4930 | /* The set of possible motioneye camera events */ 4931 | static struct sonypi_event sonypi_meyeev[] = { 4932 | { 0x00, SONYPI_EVENT_MEYE_FACE }, 4933 | { 0x01, SONYPI_EVENT_MEYE_OPPOSITE }, 4934 | { 0, 0 } 4935 | }; 4936 | 4937 | /* The set of possible memorystick events */ 4938 | static struct sonypi_event sonypi_memorystickev[] = { 4939 | { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT }, 4940 | { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT }, 4941 | { 0, 0 } 4942 | }; 4943 | 4944 | /* The set of possible battery events */ 4945 | static struct sonypi_event sonypi_batteryev[] = { 4946 | { 0x20, SONYPI_EVENT_BATTERY_INSERT }, 4947 | { 0x30, SONYPI_EVENT_BATTERY_REMOVE }, 4948 | { 0, 0 } 4949 | }; 4950 | 4951 | /* The set of possible volume events */ 4952 | static struct sonypi_event sonypi_volumeev[] = { 4953 | { 0x01, SONYPI_EVENT_VOLUME_INC_PRESSED }, 4954 | { 0x02, SONYPI_EVENT_VOLUME_DEC_PRESSED }, 4955 | { 0, 0 } 4956 | }; 4957 | 4958 | /* The set of possible brightness events */ 4959 | static struct sonypi_event sonypi_brightnessev[] = { 4960 | { 0x80, SONYPI_EVENT_BRIGHTNESS_PRESSED }, 4961 | { 0, 0 } 4962 | }; 4963 | 4964 | static struct sonypi_eventtypes type1_events[] = { 4965 | { 0, 0xffffffff, sonypi_releaseev }, 4966 | { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev }, 4967 | { 0x30, SONYPI_LID_MASK, sonypi_lidev }, 4968 | { 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev }, 4969 | { 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev }, 4970 | { 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, 4971 | { 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, 4972 | { 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev }, 4973 | { 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, 4974 | { 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev }, 4975 | { 0 }, 4976 | }; 4977 | static struct sonypi_eventtypes type2_events[] = { 4978 | { 0, 0xffffffff, sonypi_releaseev }, 4979 | { 0x38, SONYPI_LID_MASK, sonypi_lidev }, 4980 | { 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev }, 4981 | { 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev }, 4982 | { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, 4983 | { 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, 4984 | { 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev }, 4985 | { 0x11, SONYPI_BACK_MASK, sonypi_backev }, 4986 | { 0x21, SONYPI_HELP_MASK, sonypi_helpev }, 4987 | { 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev }, 4988 | { 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev }, 4989 | { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, 4990 | { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, 4991 | { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, 4992 | { 0 }, 4993 | }; 4994 | static struct sonypi_eventtypes type3_events[] = { 4995 | { 0, 0xffffffff, sonypi_releaseev }, 4996 | { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, 4997 | { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev }, 4998 | { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, 4999 | { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, 5000 | { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, 5001 | { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev }, 5002 | { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev }, 5003 | { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev }, 5004 | { 0x05, SONYPI_PKEY_MASK, sonypi_volumeev }, 5005 | { 0x05, SONYPI_PKEY_MASK, sonypi_brightnessev }, 5006 | { 0 }, 5007 | }; 5008 | 5009 | /* low level spic calls */ 5010 | #define ITERATIONS_LONG 10000 5011 | #define ITERATIONS_SHORT 10 5012 | #define wait_on_command(command, iterations) { \ 5013 | unsigned int n = iterations; \ 5014 | while (--n && (command)) \ 5015 | udelay(1); \ 5016 | if (!n) \ 5017 | dprintk("command failed at %s : %s (line %d)\n", \ 5018 | __FILE__, __func__, __LINE__); \ 5019 | } 5020 | 5021 | static u8 sony_pic_call1(u8 dev) 5022 | { 5023 | u8 v1, v2; 5024 | 5025 | wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, 5026 | ITERATIONS_LONG); 5027 | outb(dev, spic_dev.cur_ioport->io1.minimum + 4); 5028 | v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4); 5029 | v2 = inb_p(spic_dev.cur_ioport->io1.minimum); 5030 | dprintk("sony_pic_call1(0x%.2x): 0x%.4x\n", dev, (v2 << 8) | v1); 5031 | return v2; 5032 | } 5033 | 5034 | static u8 sony_pic_call2(u8 dev, u8 fn) 5035 | { 5036 | u8 v1; 5037 | 5038 | wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, 5039 | ITERATIONS_LONG); 5040 | outb(dev, spic_dev.cur_ioport->io1.minimum + 4); 5041 | wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, 5042 | ITERATIONS_LONG); 5043 | outb(fn, spic_dev.cur_ioport->io1.minimum); 5044 | v1 = inb_p(spic_dev.cur_ioport->io1.minimum); 5045 | dprintk("sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x\n", dev, fn, v1); 5046 | return v1; 5047 | } 5048 | 5049 | static u8 sony_pic_call3(u8 dev, u8 fn, u8 v) 5050 | { 5051 | u8 v1; 5052 | 5053 | wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, 5054 | ITERATIONS_LONG); 5055 | outb(dev, spic_dev.cur_ioport->io1.minimum + 4); 5056 | wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, 5057 | ITERATIONS_LONG); 5058 | outb(fn, spic_dev.cur_ioport->io1.minimum); 5059 | wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, 5060 | ITERATIONS_LONG); 5061 | outb(v, spic_dev.cur_ioport->io1.minimum); 5062 | v1 = inb_p(spic_dev.cur_ioport->io1.minimum); 5063 | dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n", 5064 | dev, fn, v, v1); 5065 | return v1; 5066 | } 5067 | 5068 | /* 5069 | * minidrivers for SPIC models 5070 | */ 5071 | static int type3_handle_irq(const u8 data_mask, const u8 ev) 5072 | { 5073 | /* 5074 | * 0x31 could mean we have to take some extra action and wait for 5075 | * the next irq for some Type3 models, it will generate a new 5076 | * irq and we can read new data from the device: 5077 | * - 0x5c and 0x5f requires 0xA0 5078 | * - 0x61 requires 0xB3 5079 | */ 5080 | if (data_mask == 0x31) { 5081 | if (ev == 0x5c || ev == 0x5f) 5082 | sony_pic_call1(0xA0); 5083 | else if (ev == 0x61) 5084 | sony_pic_call1(0xB3); 5085 | return 0; 5086 | } 5087 | return 1; 5088 | } 5089 | 5090 | static void sony_pic_detect_device_type(struct sony_pic_dev *dev) 5091 | { 5092 | struct pci_dev *pcidev; 5093 | 5094 | pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, 5095 | PCI_DEVICE_ID_INTEL_82371AB_3, NULL); 5096 | if (pcidev) { 5097 | dev->model = SONYPI_DEVICE_TYPE1; 5098 | dev->evport_offset = SONYPI_TYPE1_OFFSET; 5099 | dev->event_types = type1_events; 5100 | goto out; 5101 | } 5102 | 5103 | pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, 5104 | PCI_DEVICE_ID_INTEL_ICH6_1, NULL); 5105 | if (pcidev) { 5106 | dev->model = SONYPI_DEVICE_TYPE2; 5107 | dev->evport_offset = SONYPI_TYPE2_OFFSET; 5108 | dev->event_types = type2_events; 5109 | goto out; 5110 | } 5111 | 5112 | pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, 5113 | PCI_DEVICE_ID_INTEL_ICH7_1, NULL); 5114 | if (pcidev) { 5115 | dev->model = SONYPI_DEVICE_TYPE3; 5116 | dev->handle_irq = type3_handle_irq; 5117 | dev->evport_offset = SONYPI_TYPE3_OFFSET; 5118 | dev->event_types = type3_events; 5119 | goto out; 5120 | } 5121 | 5122 | pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, 5123 | PCI_DEVICE_ID_INTEL_ICH8_4, NULL); 5124 | if (pcidev) { 5125 | dev->model = SONYPI_DEVICE_TYPE3; 5126 | dev->handle_irq = type3_handle_irq; 5127 | dev->evport_offset = SONYPI_TYPE3_OFFSET; 5128 | dev->event_types = type3_events; 5129 | goto out; 5130 | } 5131 | 5132 | pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, 5133 | PCI_DEVICE_ID_INTEL_ICH9_1, NULL); 5134 | if (pcidev) { 5135 | dev->model = SONYPI_DEVICE_TYPE3; 5136 | dev->handle_irq = type3_handle_irq; 5137 | dev->evport_offset = SONYPI_TYPE3_OFFSET; 5138 | dev->event_types = type3_events; 5139 | goto out; 5140 | } 5141 | 5142 | /* default */ 5143 | dev->model = SONYPI_DEVICE_TYPE2; 5144 | dev->evport_offset = SONYPI_TYPE2_OFFSET; 5145 | dev->event_types = type2_events; 5146 | 5147 | out: 5148 | if (pcidev) 5149 | pci_dev_put(pcidev); 5150 | 5151 | pr_info("detected Type%d model\n", 5152 | dev->model == SONYPI_DEVICE_TYPE1 ? 1 : 5153 | dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); 5154 | } 5155 | 5156 | /* camera tests and poweron/poweroff */ 5157 | #define SONYPI_CAMERA_PICTURE 5 5158 | #define SONYPI_CAMERA_CONTROL 0x10 5159 | 5160 | #define SONYPI_CAMERA_BRIGHTNESS 0 5161 | #define SONYPI_CAMERA_CONTRAST 1 5162 | #define SONYPI_CAMERA_HUE 2 5163 | #define SONYPI_CAMERA_COLOR 3 5164 | #define SONYPI_CAMERA_SHARPNESS 4 5165 | 5166 | #define SONYPI_CAMERA_EXPOSURE_MASK 0xC 5167 | #define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3 5168 | #define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30 5169 | #define SONYPI_CAMERA_MUTE_MASK 0x40 5170 | 5171 | /* the rest don't need a loop until not 0xff */ 5172 | #define SONYPI_CAMERA_AGC 6 5173 | #define SONYPI_CAMERA_AGC_MASK 0x30 5174 | #define SONYPI_CAMERA_SHUTTER_MASK 0x7 5175 | 5176 | #define SONYPI_CAMERA_SHUTDOWN_REQUEST 7 5177 | #define SONYPI_CAMERA_CONTROL 0x10 5178 | 5179 | #define SONYPI_CAMERA_STATUS 7 5180 | #define SONYPI_CAMERA_STATUS_READY 0x2 5181 | #define SONYPI_CAMERA_STATUS_POSITION 0x4 5182 | 5183 | #define SONYPI_DIRECTION_BACKWARDS 0x4 5184 | 5185 | #define SONYPI_CAMERA_REVISION 8 5186 | #define SONYPI_CAMERA_ROMVERSION 9 5187 | 5188 | static int __sony_pic_camera_ready(void) 5189 | { 5190 | u8 v; 5191 | 5192 | v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS); 5193 | return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY)); 5194 | } 5195 | 5196 | static int __sony_pic_camera_off(void) 5197 | { 5198 | if (!camera) { 5199 | pr_warn("camera control not enabled\n"); 5200 | return -ENODEV; 5201 | } 5202 | 5203 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, 5204 | SONYPI_CAMERA_MUTE_MASK), 5205 | ITERATIONS_SHORT); 5206 | 5207 | if (spic_dev.camera_power) { 5208 | sony_pic_call2(0x91, 0); 5209 | spic_dev.camera_power = 0; 5210 | } 5211 | return 0; 5212 | } 5213 | 5214 | static int __sony_pic_camera_on(void) 5215 | { 5216 | int i, j, x; 5217 | 5218 | if (!camera) { 5219 | pr_warn("camera control not enabled\n"); 5220 | return -ENODEV; 5221 | } 5222 | 5223 | if (spic_dev.camera_power) 5224 | return 0; 5225 | 5226 | for (j = 5; j > 0; j--) { 5227 | 5228 | for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++) 5229 | msleep(10); 5230 | sony_pic_call1(0x93); 5231 | 5232 | for (i = 400; i > 0; i--) { 5233 | if (__sony_pic_camera_ready()) 5234 | break; 5235 | msleep(10); 5236 | } 5237 | if (i) 5238 | break; 5239 | } 5240 | 5241 | if (j == 0) { 5242 | pr_warn("failed to power on camera\n"); 5243 | return -ENODEV; 5244 | } 5245 | 5246 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL, 5247 | 0x5a), 5248 | ITERATIONS_SHORT); 5249 | 5250 | spic_dev.camera_power = 1; 5251 | return 0; 5252 | } 5253 | 5254 | /* External camera command (exported to the motion eye v4l driver) */ 5255 | int sony_pic_camera_command(int command, u8 value) 5256 | { 5257 | if (!camera) 5258 | return -EIO; 5259 | 5260 | mutex_lock(&spic_dev.lock); 5261 | 5262 | switch (command) { 5263 | case SONY_PIC_COMMAND_SETCAMERA: 5264 | if (value) 5265 | __sony_pic_camera_on(); 5266 | else 5267 | __sony_pic_camera_off(); 5268 | break; 5269 | case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS: 5270 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, 5271 | value), ITERATIONS_SHORT); 5272 | break; 5273 | case SONY_PIC_COMMAND_SETCAMERACONTRAST: 5274 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, 5275 | value), ITERATIONS_SHORT); 5276 | break; 5277 | case SONY_PIC_COMMAND_SETCAMERAHUE: 5278 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value), 5279 | ITERATIONS_SHORT); 5280 | break; 5281 | case SONY_PIC_COMMAND_SETCAMERACOLOR: 5282 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, 5283 | value), ITERATIONS_SHORT); 5284 | break; 5285 | case SONY_PIC_COMMAND_SETCAMERASHARPNESS: 5286 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, 5287 | value), ITERATIONS_SHORT); 5288 | break; 5289 | case SONY_PIC_COMMAND_SETCAMERAPICTURE: 5290 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, 5291 | value), ITERATIONS_SHORT); 5292 | break; 5293 | case SONY_PIC_COMMAND_SETCAMERAAGC: 5294 | wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value), 5295 | ITERATIONS_SHORT); 5296 | break; 5297 | default: 5298 | pr_err("sony_pic_camera_command invalid: %d\n", command); 5299 | break; 5300 | } 5301 | mutex_unlock(&spic_dev.lock); 5302 | return 0; 5303 | } 5304 | EXPORT_SYMBOL(sony_pic_camera_command); 5305 | 5306 | /* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */ 5307 | static void __sony_pic_set_wwanpower(u8 state) 5308 | { 5309 | state = !!state; 5310 | if (spic_dev.wwan_power == state) 5311 | return; 5312 | sony_pic_call2(0xB0, state); 5313 | sony_pic_call1(0x82); 5314 | spic_dev.wwan_power = state; 5315 | } 5316 | 5317 | static ssize_t sony_pic_wwanpower_store(struct device *dev, 5318 | struct device_attribute *attr, 5319 | const char *buffer, size_t count) 5320 | { 5321 | unsigned long value; 5322 | if (count > 31) 5323 | return -EINVAL; 5324 | 5325 | if (strict_strtoul(buffer, 10, &value)) 5326 | return -EINVAL; 5327 | 5328 | mutex_lock(&spic_dev.lock); 5329 | __sony_pic_set_wwanpower(value); 5330 | mutex_unlock(&spic_dev.lock); 5331 | 5332 | return count; 5333 | } 5334 | 5335 | static ssize_t sony_pic_wwanpower_show(struct device *dev, 5336 | struct device_attribute *attr, char *buffer) 5337 | { 5338 | ssize_t count; 5339 | mutex_lock(&spic_dev.lock); 5340 | count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power); 5341 | mutex_unlock(&spic_dev.lock); 5342 | return count; 5343 | } 5344 | 5345 | /* bluetooth subsystem power state */ 5346 | static void __sony_pic_set_bluetoothpower(u8 state) 5347 | { 5348 | state = !!state; 5349 | if (spic_dev.bluetooth_power == state) 5350 | return; 5351 | sony_pic_call2(0x96, state); 5352 | sony_pic_call1(0x82); 5353 | spic_dev.bluetooth_power = state; 5354 | } 5355 | 5356 | static ssize_t sony_pic_bluetoothpower_store(struct device *dev, 5357 | struct device_attribute *attr, 5358 | const char *buffer, size_t count) 5359 | { 5360 | unsigned long value; 5361 | if (count > 31) 5362 | return -EINVAL; 5363 | 5364 | if (strict_strtoul(buffer, 10, &value)) 5365 | return -EINVAL; 5366 | 5367 | mutex_lock(&spic_dev.lock); 5368 | __sony_pic_set_bluetoothpower(value); 5369 | mutex_unlock(&spic_dev.lock); 5370 | 5371 | return count; 5372 | } 5373 | 5374 | static ssize_t sony_pic_bluetoothpower_show(struct device *dev, 5375 | struct device_attribute *attr, char *buffer) 5376 | { 5377 | ssize_t count = 0; 5378 | mutex_lock(&spic_dev.lock); 5379 | count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power); 5380 | mutex_unlock(&spic_dev.lock); 5381 | return count; 5382 | } 5383 | 5384 | /* fan speed */ 5385 | /* FAN0 information (reverse engineered from ACPI tables) */ 5386 | #define SONY_PIC_FAN0_STATUS 0x93 5387 | static int sony_pic_set_fanspeed(unsigned long value) 5388 | { 5389 | return ec_write(SONY_PIC_FAN0_STATUS, value); 5390 | } 5391 | 5392 | static int sony_pic_get_fanspeed(u8 *value) 5393 | { 5394 | return ec_read(SONY_PIC_FAN0_STATUS, value); 5395 | } 5396 | 5397 | static ssize_t sony_pic_fanspeed_store(struct device *dev, 5398 | struct device_attribute *attr, 5399 | const char *buffer, size_t count) 5400 | { 5401 | unsigned long value; 5402 | if (count > 31) 5403 | return -EINVAL; 5404 | 5405 | if (strict_strtoul(buffer, 10, &value)) 5406 | return -EINVAL; 5407 | 5408 | if (sony_pic_set_fanspeed(value)) 5409 | return -EIO; 5410 | 5411 | return count; 5412 | } 5413 | 5414 | static ssize_t sony_pic_fanspeed_show(struct device *dev, 5415 | struct device_attribute *attr, char *buffer) 5416 | { 5417 | u8 value = 0; 5418 | if (sony_pic_get_fanspeed(&value)) 5419 | return -EIO; 5420 | 5421 | return snprintf(buffer, PAGE_SIZE, "%d\n", value); 5422 | } 5423 | 5424 | #define SPIC_ATTR(_name, _mode) \ 5425 | struct device_attribute spic_attr_##_name = __ATTR(_name, \ 5426 | _mode, sony_pic_## _name ##_show, \ 5427 | sony_pic_## _name ##_store) 5428 | 5429 | static SPIC_ATTR(bluetoothpower, 0644); 5430 | static SPIC_ATTR(wwanpower, 0644); 5431 | static SPIC_ATTR(fanspeed, 0644); 5432 | 5433 | static struct attribute *spic_attributes[] = { 5434 | &spic_attr_bluetoothpower.attr, 5435 | &spic_attr_wwanpower.attr, 5436 | &spic_attr_fanspeed.attr, 5437 | NULL 5438 | }; 5439 | 5440 | static struct attribute_group spic_attribute_group = { 5441 | .attrs = spic_attributes 5442 | }; 5443 | 5444 | /******** SONYPI compatibility **********/ 5445 | #ifdef CONFIG_SONYPI_COMPAT 5446 | 5447 | /* battery / brightness / temperature addresses */ 5448 | #define SONYPI_BAT_FLAGS 0x81 5449 | #define SONYPI_LCD_LIGHT 0x96 5450 | #define SONYPI_BAT1_PCTRM 0xa0 5451 | #define SONYPI_BAT1_LEFT 0xa2 5452 | #define SONYPI_BAT1_MAXRT 0xa4 5453 | #define SONYPI_BAT2_PCTRM 0xa8 5454 | #define SONYPI_BAT2_LEFT 0xaa 5455 | #define SONYPI_BAT2_MAXRT 0xac 5456 | #define SONYPI_BAT1_MAXTK 0xb0 5457 | #define SONYPI_BAT1_FULL 0xb2 5458 | #define SONYPI_BAT2_MAXTK 0xb8 5459 | #define SONYPI_BAT2_FULL 0xba 5460 | #define SONYPI_TEMP_STATUS 0xC1 5461 | 5462 | struct sonypi_compat_s { 5463 | struct fasync_struct *fifo_async; 5464 | struct kfifo fifo; 5465 | spinlock_t fifo_lock; 5466 | wait_queue_head_t fifo_proc_list; 5467 | atomic_t open_count; 5468 | }; 5469 | static struct sonypi_compat_s sonypi_compat = { 5470 | .open_count = ATOMIC_INIT(0), 5471 | }; 5472 | 5473 | static int sonypi_misc_fasync(int fd, struct file *filp, int on) 5474 | { 5475 | return fasync_helper(fd, filp, on, &sonypi_compat.fifo_async); 5476 | } 5477 | 5478 | static int sonypi_misc_release(struct inode *inode, struct file *file) 5479 | { 5480 | atomic_dec(&sonypi_compat.open_count); 5481 | return 0; 5482 | } 5483 | 5484 | static int sonypi_misc_open(struct inode *inode, struct file *file) 5485 | { 5486 | /* Flush input queue on first open */ 5487 | unsigned long flags; 5488 | 5489 | spin_lock_irqsave(&sonypi_compat.fifo_lock, flags); 5490 | 5491 | if (atomic_inc_return(&sonypi_compat.open_count) == 1) 5492 | kfifo_reset(&sonypi_compat.fifo); 5493 | 5494 | spin_unlock_irqrestore(&sonypi_compat.fifo_lock, flags); 5495 | 5496 | return 0; 5497 | } 5498 | 5499 | static ssize_t sonypi_misc_read(struct file *file, char __user *buf, 5500 | size_t count, loff_t *pos) 5501 | { 5502 | ssize_t ret; 5503 | unsigned char c; 5504 | 5505 | if ((kfifo_len(&sonypi_compat.fifo) == 0) && 5506 | (file->f_flags & O_NONBLOCK)) 5507 | return -EAGAIN; 5508 | 5509 | ret = wait_event_interruptible(sonypi_compat.fifo_proc_list, 5510 | kfifo_len(&sonypi_compat.fifo) != 0); 5511 | if (ret) 5512 | return ret; 5513 | 5514 | while (ret < count && 5515 | (kfifo_out_locked(&sonypi_compat.fifo, &c, sizeof(c), 5516 | &sonypi_compat.fifo_lock) == sizeof(c))) { 5517 | if (put_user(c, buf++)) 5518 | return -EFAULT; 5519 | ret++; 5520 | } 5521 | 5522 | if (ret > 0) { 5523 | struct inode *inode = file->f_path.dentry->d_inode; 5524 | inode->i_atime = current_fs_time(inode->i_sb); 5525 | } 5526 | 5527 | return ret; 5528 | } 5529 | 5530 | static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait) 5531 | { 5532 | poll_wait(file, &sonypi_compat.fifo_proc_list, wait); 5533 | if (kfifo_len(&sonypi_compat.fifo)) 5534 | return POLLIN | POLLRDNORM; 5535 | return 0; 5536 | } 5537 | 5538 | static int ec_read16(u8 addr, u16 *value) 5539 | { 5540 | u8 val_lb, val_hb; 5541 | if (ec_read(addr, &val_lb)) 5542 | return -1; 5543 | if (ec_read(addr + 1, &val_hb)) 5544 | return -1; 5545 | *value = val_lb | (val_hb << 8); 5546 | return 0; 5547 | } 5548 | 5549 | static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, 5550 | unsigned long arg) 5551 | { 5552 | int ret = 0; 5553 | void __user *argp = (void __user *)arg; 5554 | u8 val8; 5555 | u16 val16; 5556 | unsigned int value; 5557 | 5558 | mutex_lock(&spic_dev.lock); 5559 | switch (cmd) { 5560 | case SONYPI_IOCGBRT: 5561 | if (sony_backlight_device == NULL) { 5562 | ret = -EIO; 5563 | break; 5564 | } 5565 | if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) { 5566 | ret = -EIO; 5567 | break; 5568 | } 5569 | val8 = ((value & 0xff) - 1) << 5; 5570 | if (copy_to_user(argp, &val8, sizeof(val8))) 5571 | ret = -EFAULT; 5572 | break; 5573 | case SONYPI_IOCSBRT: 5574 | if (sony_backlight_device == NULL) { 5575 | ret = -EIO; 5576 | break; 5577 | } 5578 | if (copy_from_user(&val8, argp, sizeof(val8))) { 5579 | ret = -EFAULT; 5580 | break; 5581 | } 5582 | if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", 5583 | (val8 >> 5) + 1, NULL)) { 5584 | ret = -EIO; 5585 | break; 5586 | } 5587 | /* sync the backlight device status */ 5588 | sony_backlight_device->props.brightness = 5589 | sony_backlight_get_brightness(sony_backlight_device); 5590 | break; 5591 | case SONYPI_IOCGBAT1CAP: 5592 | if (ec_read16(SONYPI_BAT1_FULL, &val16)) { 5593 | ret = -EIO; 5594 | break; 5595 | } 5596 | if (copy_to_user(argp, &val16, sizeof(val16))) 5597 | ret = -EFAULT; 5598 | break; 5599 | case SONYPI_IOCGBAT1REM: 5600 | if (ec_read16(SONYPI_BAT1_LEFT, &val16)) { 5601 | ret = -EIO; 5602 | break; 5603 | } 5604 | if (copy_to_user(argp, &val16, sizeof(val16))) 5605 | ret = -EFAULT; 5606 | break; 5607 | case SONYPI_IOCGBAT2CAP: 5608 | if (ec_read16(SONYPI_BAT2_FULL, &val16)) { 5609 | ret = -EIO; 5610 | break; 5611 | } 5612 | if (copy_to_user(argp, &val16, sizeof(val16))) 5613 | ret = -EFAULT; 5614 | break; 5615 | case SONYPI_IOCGBAT2REM: 5616 | if (ec_read16(SONYPI_BAT2_LEFT, &val16)) { 5617 | ret = -EIO; 5618 | break; 5619 | } 5620 | if (copy_to_user(argp, &val16, sizeof(val16))) 5621 | ret = -EFAULT; 5622 | break; 5623 | case SONYPI_IOCGBATFLAGS: 5624 | if (ec_read(SONYPI_BAT_FLAGS, &val8)) { 5625 | ret = -EIO; 5626 | break; 5627 | } 5628 | val8 &= 0x07; 5629 | if (copy_to_user(argp, &val8, sizeof(val8))) 5630 | ret = -EFAULT; 5631 | break; 5632 | case SONYPI_IOCGBLUE: 5633 | val8 = spic_dev.bluetooth_power; 5634 | if (copy_to_user(argp, &val8, sizeof(val8))) 5635 | ret = -EFAULT; 5636 | break; 5637 | case SONYPI_IOCSBLUE: 5638 | if (copy_from_user(&val8, argp, sizeof(val8))) { 5639 | ret = -EFAULT; 5640 | break; 5641 | } 5642 | __sony_pic_set_bluetoothpower(val8); 5643 | break; 5644 | /* FAN Controls */ 5645 | case SONYPI_IOCGFAN: 5646 | if (sony_pic_get_fanspeed(&val8)) { 5647 | ret = -EIO; 5648 | break; 5649 | } 5650 | if (copy_to_user(argp, &val8, sizeof(val8))) 5651 | ret = -EFAULT; 5652 | break; 5653 | case SONYPI_IOCSFAN: 5654 | if (copy_from_user(&val8, argp, sizeof(val8))) { 5655 | ret = -EFAULT; 5656 | break; 5657 | } 5658 | if (sony_pic_set_fanspeed(val8)) 5659 | ret = -EIO; 5660 | break; 5661 | /* GET Temperature (useful under APM) */ 5662 | case SONYPI_IOCGTEMP: 5663 | if (ec_read(SONYPI_TEMP_STATUS, &val8)) { 5664 | ret = -EIO; 5665 | break; 5666 | } 5667 | if (copy_to_user(argp, &val8, sizeof(val8))) 5668 | ret = -EFAULT; 5669 | break; 5670 | default: 5671 | ret = -EINVAL; 5672 | } 5673 | mutex_unlock(&spic_dev.lock); 5674 | return ret; 5675 | } 5676 | 5677 | static const struct file_operations sonypi_misc_fops = { 5678 | .owner = THIS_MODULE, 5679 | .read = sonypi_misc_read, 5680 | .poll = sonypi_misc_poll, 5681 | .open = sonypi_misc_open, 5682 | .release = sonypi_misc_release, 5683 | .fasync = sonypi_misc_fasync, 5684 | .unlocked_ioctl = sonypi_misc_ioctl, 5685 | .llseek = noop_llseek, 5686 | }; 5687 | 5688 | static struct miscdevice sonypi_misc_device = { 5689 | .minor = MISC_DYNAMIC_MINOR, 5690 | .name = "sonypi", 5691 | .fops = &sonypi_misc_fops, 5692 | }; 5693 | 5694 | static void sonypi_compat_report_event(u8 event) 5695 | { 5696 | kfifo_in_locked(&sonypi_compat.fifo, (unsigned char *)&event, 5697 | sizeof(event), &sonypi_compat.fifo_lock); 5698 | kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN); 5699 | wake_up_interruptible(&sonypi_compat.fifo_proc_list); 5700 | } 5701 | 5702 | static int sonypi_compat_init(void) 5703 | { 5704 | int error; 5705 | 5706 | spin_lock_init(&sonypi_compat.fifo_lock); 5707 | error = 5708 | kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); 5709 | if (error) { 5710 | pr_err("kfifo_alloc failed\n"); 5711 | return error; 5712 | } 5713 | 5714 | init_waitqueue_head(&sonypi_compat.fifo_proc_list); 5715 | 5716 | if (minor != -1) 5717 | sonypi_misc_device.minor = minor; 5718 | error = misc_register(&sonypi_misc_device); 5719 | if (error) { 5720 | pr_err("misc_register failed\n"); 5721 | goto err_free_kfifo; 5722 | } 5723 | if (minor == -1) 5724 | pr_info("device allocated minor is %d\n", 5725 | sonypi_misc_device.minor); 5726 | 5727 | return 0; 5728 | 5729 | err_free_kfifo: 5730 | kfifo_free(&sonypi_compat.fifo); 5731 | return error; 5732 | } 5733 | 5734 | static void sonypi_compat_exit(void) 5735 | { 5736 | misc_deregister(&sonypi_misc_device); 5737 | kfifo_free(&sonypi_compat.fifo); 5738 | } 5739 | #else 5740 | static int sonypi_compat_init(void) { return 0; } 5741 | static void sonypi_compat_exit(void) { } 5742 | static void sonypi_compat_report_event(u8 event) { } 5743 | #endif /* CONFIG_SONYPI_COMPAT */ 5744 | 5745 | /* 5746 | * ACPI callbacks 5747 | */ 5748 | static acpi_status 5749 | sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) 5750 | { 5751 | u32 i; 5752 | struct sony_pic_dev *dev = (struct sony_pic_dev *)context; 5753 | 5754 | switch (resource->type) { 5755 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: 5756 | { 5757 | /* start IO enumeration */ 5758 | struct sony_pic_ioport *ioport = 5759 | kzalloc(sizeof(*ioport), GFP_KERNEL); 5760 | if (!ioport) 5761 | return AE_ERROR; 5762 | 5763 | list_add(&ioport->list, &dev->ioports); 5764 | return AE_OK; 5765 | } 5766 | 5767 | case ACPI_RESOURCE_TYPE_END_DEPENDENT: 5768 | /* end IO enumeration */ 5769 | return AE_OK; 5770 | 5771 | case ACPI_RESOURCE_TYPE_IRQ: 5772 | { 5773 | struct acpi_resource_irq *p = &resource->data.irq; 5774 | struct sony_pic_irq *interrupt = NULL; 5775 | if (!p || !p->interrupt_count) { 5776 | /* 5777 | * IRQ descriptors may have no IRQ# bits set, 5778 | * particularly those those w/ _STA disabled 5779 | */ 5780 | dprintk("Blank IRQ resource\n"); 5781 | return AE_OK; 5782 | } 5783 | for (i = 0; i < p->interrupt_count; i++) { 5784 | if (!p->interrupts[i]) { 5785 | pr_warn("Invalid IRQ %d\n", 5786 | p->interrupts[i]); 5787 | continue; 5788 | } 5789 | interrupt = kzalloc(sizeof(*interrupt), 5790 | GFP_KERNEL); 5791 | if (!interrupt) 5792 | return AE_ERROR; 5793 | 5794 | list_add(&interrupt->list, &dev->interrupts); 5795 | interrupt->irq.triggering = p->triggering; 5796 | interrupt->irq.polarity = p->polarity; 5797 | interrupt->irq.sharable = p->sharable; 5798 | interrupt->irq.interrupt_count = 1; 5799 | interrupt->irq.interrupts[0] = p->interrupts[i]; 5800 | } 5801 | return AE_OK; 5802 | } 5803 | case ACPI_RESOURCE_TYPE_IO: 5804 | { 5805 | struct acpi_resource_io *io = &resource->data.io; 5806 | struct sony_pic_ioport *ioport = 5807 | list_first_entry(&dev->ioports, 5808 | struct sony_pic_ioport, list); 5809 | if (!io) { 5810 | dprintk("Blank IO resource\n"); 5811 | return AE_OK; 5812 | } 5813 | 5814 | if (!ioport->io1.minimum) { 5815 | memcpy(&ioport->io1, io, sizeof(*io)); 5816 | dprintk("IO1 at 0x%.4x (0x%.2x)\n", 5817 | ioport->io1.minimum, 5818 | ioport->io1.address_length); 5819 | } else if (!ioport->io2.minimum) { 5820 | memcpy(&ioport->io2, io, sizeof(*io)); 5821 | dprintk("IO2 at 0x%.4x (0x%.2x)\n", 5822 | ioport->io2.minimum, 5823 | ioport->io2.address_length); 5824 | } else { 5825 | pr_err("Unknown SPIC Type, " 5826 | "more than 2 IO Ports\n"); 5827 | return AE_ERROR; 5828 | } 5829 | return AE_OK; 5830 | } 5831 | default: 5832 | dprintk("Resource %d isn't an IRQ nor an IO port\n", 5833 | resource->type); 5834 | 5835 | case ACPI_RESOURCE_TYPE_END_TAG: 5836 | return AE_OK; 5837 | } 5838 | return AE_CTRL_TERMINATE; 5839 | } 5840 | 5841 | static int sony_pic_possible_resources(struct acpi_device *device) 5842 | { 5843 | int result = 0; 5844 | acpi_status status = AE_OK; 5845 | 5846 | if (!device) 5847 | return -EINVAL; 5848 | 5849 | /* get device status */ 5850 | /* see acpi_pci_link_get_current acpi_pci_link_get_possible */ 5851 | dprintk("Evaluating _STA\n"); 5852 | result = acpi_bus_get_status(device); 5853 | if (result) { 5854 | pr_warn("Unable to read status\n"); 5855 | goto end; 5856 | } 5857 | 5858 | if (!device->status.enabled) 5859 | dprintk("Device disabled\n"); 5860 | else 5861 | dprintk("Device enabled\n"); 5862 | 5863 | /* 5864 | * Query and parse 'method' 5865 | */ 5866 | dprintk("Evaluating %s\n", METHOD_NAME__PRS); 5867 | status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, 5868 | sony_pic_read_possible_resource, &spic_dev); 5869 | if (ACPI_FAILURE(status)) { 5870 | pr_warn("Failure evaluating %s\n", METHOD_NAME__PRS); 5871 | result = -ENODEV; 5872 | } 5873 | end: 5874 | return result; 5875 | } 5876 | 5877 | /* 5878 | * Disable the spic device by calling its _DIS method 5879 | */ 5880 | static int sony_pic_disable(struct acpi_device *device) 5881 | { 5882 | acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL, 5883 | NULL); 5884 | 5885 | if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND) 5886 | return -ENXIO; 5887 | 5888 | dprintk("Device disabled\n"); 5889 | return 0; 5890 | } 5891 | 5892 | 5893 | /* 5894 | * Based on drivers/acpi/pci_link.c:acpi_pci_link_set 5895 | * 5896 | * Call _SRS to set current resources 5897 | */ 5898 | static int sony_pic_enable(struct acpi_device *device, 5899 | struct sony_pic_ioport *ioport, struct sony_pic_irq *irq) 5900 | { 5901 | acpi_status status; 5902 | int result = 0; 5903 | /* Type 1 resource layout is: 5904 | * IO 5905 | * IO 5906 | * IRQNoFlags 5907 | * End 5908 | * 5909 | * Type 2 and 3 resource layout is: 5910 | * IO 5911 | * IRQNoFlags 5912 | * End 5913 | */ 5914 | struct { 5915 | struct acpi_resource res1; 5916 | struct acpi_resource res2; 5917 | struct acpi_resource res3; 5918 | struct acpi_resource res4; 5919 | } *resource; 5920 | struct acpi_buffer buffer = { 0, NULL }; 5921 | 5922 | if (!ioport || !irq) 5923 | return -EINVAL; 5924 | 5925 | /* init acpi_buffer */ 5926 | resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL); 5927 | if (!resource) 5928 | return -ENOMEM; 5929 | 5930 | buffer.length = sizeof(*resource) + 1; 5931 | buffer.pointer = resource; 5932 | 5933 | /* setup Type 1 resources */ 5934 | if (spic_dev.model == SONYPI_DEVICE_TYPE1) { 5935 | 5936 | /* setup io resources */ 5937 | resource->res1.type = ACPI_RESOURCE_TYPE_IO; 5938 | resource->res1.length = sizeof(struct acpi_resource); 5939 | memcpy(&resource->res1.data.io, &ioport->io1, 5940 | sizeof(struct acpi_resource_io)); 5941 | 5942 | resource->res2.type = ACPI_RESOURCE_TYPE_IO; 5943 | resource->res2.length = sizeof(struct acpi_resource); 5944 | memcpy(&resource->res2.data.io, &ioport->io2, 5945 | sizeof(struct acpi_resource_io)); 5946 | 5947 | /* setup irq resource */ 5948 | resource->res3.type = ACPI_RESOURCE_TYPE_IRQ; 5949 | resource->res3.length = sizeof(struct acpi_resource); 5950 | memcpy(&resource->res3.data.irq, &irq->irq, 5951 | sizeof(struct acpi_resource_irq)); 5952 | /* we requested a shared irq */ 5953 | resource->res3.data.irq.sharable = ACPI_SHARED; 5954 | 5955 | resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG; 5956 | 5957 | } 5958 | /* setup Type 2/3 resources */ 5959 | else { 5960 | /* setup io resource */ 5961 | resource->res1.type = ACPI_RESOURCE_TYPE_IO; 5962 | resource->res1.length = sizeof(struct acpi_resource); 5963 | memcpy(&resource->res1.data.io, &ioport->io1, 5964 | sizeof(struct acpi_resource_io)); 5965 | 5966 | /* setup irq resource */ 5967 | resource->res2.type = ACPI_RESOURCE_TYPE_IRQ; 5968 | resource->res2.length = sizeof(struct acpi_resource); 5969 | memcpy(&resource->res2.data.irq, &irq->irq, 5970 | sizeof(struct acpi_resource_irq)); 5971 | /* we requested a shared irq */ 5972 | resource->res2.data.irq.sharable = ACPI_SHARED; 5973 | 5974 | resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG; 5975 | } 5976 | 5977 | /* Attempt to set the resource */ 5978 | dprintk("Evaluating _SRS\n"); 5979 | status = acpi_set_current_resources(device->handle, &buffer); 5980 | 5981 | /* check for total failure */ 5982 | if (ACPI_FAILURE(status)) { 5983 | pr_err("Error evaluating _SRS\n"); 5984 | result = -ENODEV; 5985 | goto end; 5986 | } 5987 | 5988 | /* Necessary device initializations calls (from sonypi) */ 5989 | sony_pic_call1(0x82); 5990 | sony_pic_call2(0x81, 0xff); 5991 | sony_pic_call1(compat ? 0x92 : 0x82); 5992 | 5993 | end: 5994 | kfree(resource); 5995 | return result; 5996 | } 5997 | 5998 | /***************** 5999 | * 6000 | * ISR: some event is available 6001 | * 6002 | *****************/ 6003 | static irqreturn_t sony_pic_irq(int irq, void *dev_id) 6004 | { 6005 | int i, j; 6006 | u8 ev = 0; 6007 | u8 data_mask = 0; 6008 | u8 device_event = 0; 6009 | 6010 | struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id; 6011 | 6012 | ev = inb_p(dev->cur_ioport->io1.minimum); 6013 | if (dev->cur_ioport->io2.minimum) 6014 | data_mask = inb_p(dev->cur_ioport->io2.minimum); 6015 | else 6016 | data_mask = inb_p(dev->cur_ioport->io1.minimum + 6017 | dev->evport_offset); 6018 | 6019 | dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", 6020 | ev, data_mask, dev->cur_ioport->io1.minimum, 6021 | dev->evport_offset); 6022 | 6023 | if (ev == 0x00 || ev == 0xff) 6024 | return IRQ_HANDLED; 6025 | 6026 | for (i = 0; dev->event_types[i].mask; i++) { 6027 | 6028 | if ((data_mask & dev->event_types[i].data) != 6029 | dev->event_types[i].data) 6030 | continue; 6031 | 6032 | if (!(mask & dev->event_types[i].mask)) 6033 | continue; 6034 | 6035 | for (j = 0; dev->event_types[i].events[j].event; j++) { 6036 | if (ev == dev->event_types[i].events[j].data) { 6037 | device_event = 6038 | dev->event_types[i].events[j].event; 6039 | /* some events may require ignoring */ 6040 | if (!device_event) 6041 | return IRQ_HANDLED; 6042 | goto found; 6043 | } 6044 | } 6045 | } 6046 | /* Still not able to decode the event try to pass 6047 | * it over to the minidriver 6048 | */ 6049 | if (dev->handle_irq && dev->handle_irq(data_mask, ev) == 0) 6050 | return IRQ_HANDLED; 6051 | 6052 | dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", 6053 | ev, data_mask, dev->cur_ioport->io1.minimum, 6054 | dev->evport_offset); 6055 | return IRQ_HANDLED; 6056 | 6057 | found: 6058 | sony_laptop_report_input_event(device_event); 6059 | acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event); 6060 | sonypi_compat_report_event(device_event); 6061 | return IRQ_HANDLED; 6062 | } 6063 | 6064 | /***************** 6065 | * 6066 | * ACPI driver 6067 | * 6068 | *****************/ 6069 | static int sony_pic_remove(struct acpi_device *device, int type) 6070 | { 6071 | struct sony_pic_ioport *io, *tmp_io; 6072 | struct sony_pic_irq *irq, *tmp_irq; 6073 | 6074 | if (sony_pic_disable(device)) { 6075 | pr_err("Couldn't disable device\n"); 6076 | return -ENXIO; 6077 | } 6078 | 6079 | free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); 6080 | release_region(spic_dev.cur_ioport->io1.minimum, 6081 | spic_dev.cur_ioport->io1.address_length); 6082 | if (spic_dev.cur_ioport->io2.minimum) 6083 | release_region(spic_dev.cur_ioport->io2.minimum, 6084 | spic_dev.cur_ioport->io2.address_length); 6085 | 6086 | sonypi_compat_exit(); 6087 | 6088 | sony_laptop_remove_input(); 6089 | 6090 | /* pf attrs */ 6091 | sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group); 6092 | sony_pf_remove(); 6093 | 6094 | list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { 6095 | list_del(&io->list); 6096 | kfree(io); 6097 | } 6098 | list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) { 6099 | list_del(&irq->list); 6100 | kfree(irq); 6101 | } 6102 | spic_dev.cur_ioport = NULL; 6103 | spic_dev.cur_irq = NULL; 6104 | 6105 | dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); 6106 | return 0; 6107 | } 6108 | 6109 | static int sony_pic_add(struct acpi_device *device) 6110 | { 6111 | int result; 6112 | struct sony_pic_ioport *io, *tmp_io; 6113 | struct sony_pic_irq *irq, *tmp_irq; 6114 | 6115 | pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); 6116 | 6117 | spic_dev.acpi_dev = device; 6118 | strcpy(acpi_device_class(device), "sony/hotkey"); 6119 | sony_pic_detect_device_type(&spic_dev); 6120 | mutex_init(&spic_dev.lock); 6121 | 6122 | /* read _PRS resources */ 6123 | result = sony_pic_possible_resources(device); 6124 | if (result) { 6125 | pr_err("Unable to read possible resources\n"); 6126 | goto err_free_resources; 6127 | } 6128 | 6129 | /* setup input devices and helper fifo */ 6130 | result = sony_laptop_setup_input(device); 6131 | if (result) { 6132 | pr_err("Unable to create input devices\n"); 6133 | goto err_free_resources; 6134 | } 6135 | 6136 | if (sonypi_compat_init()) 6137 | goto err_remove_input; 6138 | 6139 | /* request io port */ 6140 | list_for_each_entry_reverse(io, &spic_dev.ioports, list) { 6141 | if (request_region(io->io1.minimum, io->io1.address_length, 6142 | "Sony Programmable I/O Device")) { 6143 | dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n", 6144 | io->io1.minimum, io->io1.maximum, 6145 | io->io1.address_length); 6146 | /* Type 1 have 2 ioports */ 6147 | if (io->io2.minimum) { 6148 | if (request_region(io->io2.minimum, 6149 | io->io2.address_length, 6150 | "Sony Programmable I/O Device")) { 6151 | dprintk("I/O port2: 0x%.4x (0x%.4x) " 6152 | "+ 0x%.2x\n", 6153 | io->io2.minimum, 6154 | io->io2.maximum, 6155 | io->io2.address_length); 6156 | spic_dev.cur_ioport = io; 6157 | break; 6158 | } else { 6159 | dprintk("Unable to get I/O port2: " 6160 | "0x%.4x (0x%.4x) " 6161 | "+ 0x%.2x\n", 6162 | io->io2.minimum, 6163 | io->io2.maximum, 6164 | io->io2.address_length); 6165 | release_region(io->io1.minimum, 6166 | io->io1.address_length); 6167 | } 6168 | } else { 6169 | spic_dev.cur_ioport = io; 6170 | break; 6171 | } 6172 | } 6173 | } 6174 | if (!spic_dev.cur_ioport) { 6175 | pr_err("Failed to request_region\n"); 6176 | result = -ENODEV; 6177 | goto err_remove_compat; 6178 | } 6179 | 6180 | /* request IRQ */ 6181 | list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { 6182 | if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, 6183 | IRQF_DISABLED, "sony-laptop", &spic_dev)) { 6184 | dprintk("IRQ: %d - triggering: %d - " 6185 | "polarity: %d - shr: %d\n", 6186 | irq->irq.interrupts[0], 6187 | irq->irq.triggering, 6188 | irq->irq.polarity, 6189 | irq->irq.sharable); 6190 | spic_dev.cur_irq = irq; 6191 | break; 6192 | } 6193 | } 6194 | if (!spic_dev.cur_irq) { 6195 | pr_err("Failed to request_irq\n"); 6196 | result = -ENODEV; 6197 | goto err_release_region; 6198 | } 6199 | 6200 | /* set resource status _SRS */ 6201 | result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); 6202 | if (result) { 6203 | pr_err("Couldn't enable device\n"); 6204 | goto err_free_irq; 6205 | } 6206 | 6207 | spic_dev.bluetooth_power = -1; 6208 | /* create device attributes */ 6209 | result = sony_pf_add(); 6210 | if (result) 6211 | goto err_disable_device; 6212 | 6213 | result = sysfs_create_group(&sony_pf_device->dev.kobj, 6214 | &spic_attribute_group); 6215 | if (result) 6216 | goto err_remove_pf; 6217 | 6218 | return 0; 6219 | 6220 | err_remove_pf: 6221 | sony_pf_remove(); 6222 | 6223 | err_disable_device: 6224 | sony_pic_disable(device); 6225 | 6226 | err_free_irq: 6227 | free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); 6228 | 6229 | err_release_region: 6230 | release_region(spic_dev.cur_ioport->io1.minimum, 6231 | spic_dev.cur_ioport->io1.address_length); 6232 | if (spic_dev.cur_ioport->io2.minimum) 6233 | release_region(spic_dev.cur_ioport->io2.minimum, 6234 | spic_dev.cur_ioport->io2.address_length); 6235 | 6236 | err_remove_compat: 6237 | sonypi_compat_exit(); 6238 | 6239 | err_remove_input: 6240 | sony_laptop_remove_input(); 6241 | 6242 | err_free_resources: 6243 | list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { 6244 | list_del(&io->list); 6245 | kfree(io); 6246 | } 6247 | list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) { 6248 | list_del(&irq->list); 6249 | kfree(irq); 6250 | } 6251 | spic_dev.cur_ioport = NULL; 6252 | spic_dev.cur_irq = NULL; 6253 | 6254 | return result; 6255 | } 6256 | 6257 | static int sony_pic_suspend(struct acpi_device *device, pm_message_t state) 6258 | { 6259 | if (sony_pic_disable(device)) 6260 | return -ENXIO; 6261 | return 0; 6262 | } 6263 | 6264 | static int sony_pic_resume(struct acpi_device *device) 6265 | { 6266 | sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); 6267 | return 0; 6268 | } 6269 | 6270 | static const struct acpi_device_id sony_pic_device_ids[] = { 6271 | {SONY_PIC_HID, 0}, 6272 | {"", 0}, 6273 | }; 6274 | 6275 | static struct acpi_driver sony_pic_driver = { 6276 | .name = SONY_PIC_DRIVER_NAME, 6277 | .class = SONY_PIC_CLASS, 6278 | .ids = sony_pic_device_ids, 6279 | .owner = THIS_MODULE, 6280 | .ops = { 6281 | .add = sony_pic_add, 6282 | .remove = sony_pic_remove, 6283 | .suspend = sony_pic_suspend, 6284 | .resume = sony_pic_resume, 6285 | }, 6286 | }; 6287 | 6288 | static struct dmi_system_id __initdata sonypi_dmi_table[] = { 6289 | { 6290 | .ident = "Sony Vaio", 6291 | .matches = { 6292 | DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 6293 | DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"), 6294 | }, 6295 | }, 6296 | { 6297 | .ident = "Sony Vaio", 6298 | .matches = { 6299 | DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 6300 | DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"), 6301 | }, 6302 | }, 6303 | { } 6304 | }; 6305 | 6306 | static int __init sony_laptop_init(void) 6307 | { 6308 | int result; 6309 | 6310 | if (!no_spic && dmi_check_system(sonypi_dmi_table)) { 6311 | result = acpi_bus_register_driver(&sony_pic_driver); 6312 | if (result) { 6313 | pr_err("Unable to register SPIC driver\n"); 6314 | goto out; 6315 | } 6316 | spic_drv_registered = 1; 6317 | } 6318 | 6319 | result = acpi_bus_register_driver(&sony_nc_driver); 6320 | if (result) { 6321 | pr_err("Unable to register SNC driver\n"); 6322 | goto out_unregister_pic; 6323 | } 6324 | 6325 | return 0; 6326 | 6327 | out_unregister_pic: 6328 | if (spic_drv_registered) 6329 | acpi_bus_unregister_driver(&sony_pic_driver); 6330 | out: 6331 | return result; 6332 | } 6333 | 6334 | static void __exit sony_laptop_exit(void) 6335 | { 6336 | acpi_bus_unregister_driver(&sony_nc_driver); 6337 | if (spic_drv_registered) 6338 | acpi_bus_unregister_driver(&sony_pic_driver); 6339 | } 6340 | 6341 | module_init(sony_laptop_init); 6342 | module_exit(sony_laptop_exit); 6343 | --------------------------------------------------------------------------------