Merge git://git.infradead.org/~dwmw2/ideapad-2.6

* git://git.infradead.org/~dwmw2/ideapad-2.6:
Call acpi_video_register() in intel_opregion_init() failure path
ideapad: Only allow camera state to be set to 0 or 1
ideapad: Stop using global variables
Add Lenovo ideapad driver

+315
+1
drivers/gpu/drm/i915/i915_opregion.c
··· 535 535 err_out: 536 536 iounmap(opregion->header); 537 537 opregion->header = NULL; 538 + acpi_video_register(); 538 539 return err; 539 540 } 540 541
+7
drivers/platform/x86/Kconfig
··· 219 219 ---help--- 220 220 Build the sonypi driver compatibility code into the sony-laptop driver. 221 221 222 + config IDEAPAD_ACPI 223 + tristate "Lenovo IdeaPad ACPI Laptop Extras" 224 + depends on ACPI 225 + depends on RFKILL 226 + help 227 + This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. 228 + 222 229 config THINKPAD_ACPI 223 230 tristate "ThinkPad ACPI Laptop Extras" 224 231 depends on ACPI
+1
drivers/platform/x86/Makefile
··· 15 15 obj-$(CONFIG_HP_WMI) += hp-wmi.o 16 16 obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o 17 17 obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o 18 + obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o 18 19 obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 19 20 obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o 20 21 obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
+306
drivers/platform/x86/ideapad_acpi.c
··· 1 + /* 2 + * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras 3 + * 4 + * Copyright © 2010 Intel Corporation 5 + * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License as published by 9 + * the Free Software Foundation; either version 2 of the License, or 10 + * (at your option) any later version. 11 + * 12 + * This program is distributed in the hope that it will be useful, 13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 + * GNU General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 + * 02110-1301, USA. 21 + */ 22 + 23 + #include <linux/kernel.h> 24 + #include <linux/module.h> 25 + #include <linux/init.h> 26 + #include <linux/types.h> 27 + #include <acpi/acpi_bus.h> 28 + #include <acpi/acpi_drivers.h> 29 + #include <linux/rfkill.h> 30 + 31 + #define IDEAPAD_DEV_CAMERA 0 32 + #define IDEAPAD_DEV_WLAN 1 33 + #define IDEAPAD_DEV_BLUETOOTH 2 34 + #define IDEAPAD_DEV_3G 3 35 + #define IDEAPAD_DEV_KILLSW 4 36 + 37 + struct ideapad_private { 38 + struct rfkill *rfk[5]; 39 + }; 40 + 41 + static struct { 42 + char *name; 43 + int type; 44 + } ideapad_rfk_data[] = { 45 + /* camera has no rfkill */ 46 + { "ideapad_wlan", RFKILL_TYPE_WLAN }, 47 + { "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH }, 48 + { "ideapad_3g", RFKILL_TYPE_WWAN }, 49 + { "ideapad_killsw", RFKILL_TYPE_WLAN } 50 + }; 51 + 52 + static int ideapad_dev_exists(int device) 53 + { 54 + acpi_status status; 55 + union acpi_object in_param; 56 + struct acpi_object_list input = { 1, &in_param }; 57 + struct acpi_buffer output; 58 + union acpi_object out_obj; 59 + 60 + output.length = sizeof(out_obj); 61 + output.pointer = &out_obj; 62 + 63 + in_param.type = ACPI_TYPE_INTEGER; 64 + in_param.integer.value = device + 1; 65 + 66 + status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output); 67 + if (ACPI_FAILURE(status)) { 68 + printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status); 69 + return -ENODEV; 70 + } 71 + if (out_obj.type != ACPI_TYPE_INTEGER) { 72 + printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n"); 73 + return -ENODEV; 74 + } 75 + return out_obj.integer.value; 76 + } 77 + 78 + static int ideapad_dev_get_state(int device) 79 + { 80 + acpi_status status; 81 + union acpi_object in_param; 82 + struct acpi_object_list input = { 1, &in_param }; 83 + struct acpi_buffer output; 84 + union acpi_object out_obj; 85 + 86 + output.length = sizeof(out_obj); 87 + output.pointer = &out_obj; 88 + 89 + in_param.type = ACPI_TYPE_INTEGER; 90 + in_param.integer.value = device + 1; 91 + 92 + status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output); 93 + if (ACPI_FAILURE(status)) { 94 + printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status); 95 + return -ENODEV; 96 + } 97 + if (out_obj.type != ACPI_TYPE_INTEGER) { 98 + printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n"); 99 + return -ENODEV; 100 + } 101 + return out_obj.integer.value; 102 + } 103 + 104 + static int ideapad_dev_set_state(int device, int state) 105 + { 106 + acpi_status status; 107 + union acpi_object in_params[2]; 108 + struct acpi_object_list input = { 2, in_params }; 109 + 110 + in_params[0].type = ACPI_TYPE_INTEGER; 111 + in_params[0].integer.value = device + 1; 112 + in_params[1].type = ACPI_TYPE_INTEGER; 113 + in_params[1].integer.value = state; 114 + 115 + status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL); 116 + if (ACPI_FAILURE(status)) { 117 + printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status); 118 + return -ENODEV; 119 + } 120 + return 0; 121 + } 122 + static ssize_t show_ideapad_cam(struct device *dev, 123 + struct device_attribute *attr, 124 + char *buf) 125 + { 126 + int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA); 127 + if (state < 0) 128 + return state; 129 + 130 + return sprintf(buf, "%d\n", state); 131 + } 132 + 133 + static ssize_t store_ideapad_cam(struct device *dev, 134 + struct device_attribute *attr, 135 + const char *buf, size_t count) 136 + { 137 + int ret, state; 138 + 139 + if (!count) 140 + return 0; 141 + if (sscanf(buf, "%i", &state) != 1) 142 + return -EINVAL; 143 + ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state); 144 + if (ret < 0) 145 + return ret; 146 + return count; 147 + } 148 + 149 + static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); 150 + 151 + static int ideapad_rfk_set(void *data, bool blocked) 152 + { 153 + int device = (unsigned long)data; 154 + 155 + if (device == IDEAPAD_DEV_KILLSW) 156 + return -EINVAL; 157 + return ideapad_dev_set_state(device, !blocked); 158 + } 159 + 160 + static struct rfkill_ops ideapad_rfk_ops = { 161 + .set_block = ideapad_rfk_set, 162 + }; 163 + 164 + static void ideapad_sync_rfk_state(struct acpi_device *adevice) 165 + { 166 + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 167 + int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW); 168 + int i; 169 + 170 + rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked); 171 + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) 172 + if (priv->rfk[i]) 173 + rfkill_set_hw_state(priv->rfk[i], hw_blocked); 174 + if (hw_blocked) 175 + return; 176 + 177 + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) 178 + if (priv->rfk[i]) 179 + rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i)); 180 + } 181 + 182 + static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) 183 + { 184 + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 185 + int ret; 186 + 187 + priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev, 188 + ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops, 189 + (void *)(long)dev); 190 + if (!priv->rfk[dev]) 191 + return -ENOMEM; 192 + 193 + ret = rfkill_register(priv->rfk[dev]); 194 + if (ret) { 195 + rfkill_destroy(priv->rfk[dev]); 196 + return ret; 197 + } 198 + return 0; 199 + } 200 + 201 + static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) 202 + { 203 + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 204 + 205 + if (!priv->rfk[dev]) 206 + return; 207 + 208 + rfkill_unregister(priv->rfk[dev]); 209 + rfkill_destroy(priv->rfk[dev]); 210 + } 211 + 212 + static const struct acpi_device_id ideapad_device_ids[] = { 213 + { "VPC2004", 0}, 214 + { "", 0}, 215 + }; 216 + MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); 217 + 218 + static int ideapad_acpi_add(struct acpi_device *adevice) 219 + { 220 + int i; 221 + int devs_present[5]; 222 + struct ideapad_private *priv; 223 + 224 + for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { 225 + devs_present[i] = ideapad_dev_exists(i); 226 + if (devs_present[i] < 0) 227 + return devs_present[i]; 228 + } 229 + 230 + /* The hardware switch is always present */ 231 + devs_present[IDEAPAD_DEV_KILLSW] = 1; 232 + 233 + priv = kzalloc(sizeof(*priv), GFP_KERNEL); 234 + if (!priv) 235 + return -ENOMEM; 236 + 237 + if (devs_present[IDEAPAD_DEV_CAMERA]) { 238 + int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); 239 + if (ret) { 240 + kfree(priv); 241 + return ret; 242 + } 243 + } 244 + 245 + dev_set_drvdata(&adevice->dev, priv); 246 + for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { 247 + if (!devs_present[i]) 248 + continue; 249 + 250 + ideapad_register_rfkill(adevice, i); 251 + } 252 + ideapad_sync_rfk_state(adevice); 253 + return 0; 254 + } 255 + 256 + static int ideapad_acpi_remove(struct acpi_device *adevice, int type) 257 + { 258 + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 259 + int i; 260 + 261 + device_remove_file(&adevice->dev, &dev_attr_camera_power); 262 + 263 + for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) 264 + ideapad_unregister_rfkill(adevice, i); 265 + 266 + dev_set_drvdata(&adevice->dev, NULL); 267 + kfree(priv); 268 + return 0; 269 + } 270 + 271 + static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) 272 + { 273 + ideapad_sync_rfk_state(adevice); 274 + } 275 + 276 + static struct acpi_driver ideapad_acpi_driver = { 277 + .name = "ideapad_acpi", 278 + .class = "IdeaPad", 279 + .ids = ideapad_device_ids, 280 + .ops.add = ideapad_acpi_add, 281 + .ops.remove = ideapad_acpi_remove, 282 + .ops.notify = ideapad_acpi_notify, 283 + .owner = THIS_MODULE, 284 + }; 285 + 286 + 287 + static int __init ideapad_acpi_module_init(void) 288 + { 289 + acpi_bus_register_driver(&ideapad_acpi_driver); 290 + 291 + return 0; 292 + } 293 + 294 + 295 + static void __exit ideapad_acpi_module_exit(void) 296 + { 297 + acpi_bus_unregister_driver(&ideapad_acpi_driver); 298 + 299 + } 300 + 301 + MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 302 + MODULE_DESCRIPTION("IdeaPad ACPI Extras"); 303 + MODULE_LICENSE("GPL"); 304 + 305 + module_init(ideapad_acpi_module_init); 306 + module_exit(ideapad_acpi_module_exit);