1 /*
2  *  ideapad-laptop.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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24 
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/init.h>
28 #include <linux/types.h>
29 #include <acpi/acpi_bus.h>
30 #include <acpi/acpi_drivers.h>
31 #include <linux/rfkill.h>
32 #include <linux/platform_device.h>
33 #include <linux/input.h>
34 #include <linux/input/sparse-keymap.h>
35 #include <linux/backlight.h>
36 #include <linux/fb.h>
37 
38 #define IDEAPAD_RFKILL_DEV_NUM	(3)
39 
40 #define CFG_BT_BIT	(16)
41 #define CFG_3G_BIT	(17)
42 #define CFG_WIFI_BIT	(18)
43 #define CFG_CAMERA_BIT	(19)
44 
45 struct ideapad_private {
46 	struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
47 	struct platform_device *platform_device;
48 	struct input_dev *inputdev;
49 	struct backlight_device *blightdev;
50 	unsigned long cfg;
51 };
52 
53 static acpi_handle ideapad_handle;
54 static bool no_bt_rfkill;
55 module_param(no_bt_rfkill, bool, 0444);
56 MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
57 
58 /*
59  * ACPI Helpers
60  */
61 #define IDEAPAD_EC_TIMEOUT (100) /* in ms */
62 
63 static int read_method_int(acpi_handle handle, const char *method, int *val)
64 {
65 	acpi_status status;
66 	unsigned long long result;
67 
68 	status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
69 	if (ACPI_FAILURE(status)) {
70 		*val = -1;
71 		return -1;
72 	} else {
73 		*val = result;
74 		return 0;
75 	}
76 }
77 
78 static int method_vpcr(acpi_handle handle, int cmd, int *ret)
79 {
80 	acpi_status status;
81 	unsigned long long result;
82 	struct acpi_object_list params;
83 	union acpi_object in_obj;
84 
85 	params.count = 1;
86 	params.pointer = &in_obj;
87 	in_obj.type = ACPI_TYPE_INTEGER;
88 	in_obj.integer.value = cmd;
89 
90 	status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
91 
92 	if (ACPI_FAILURE(status)) {
93 		*ret = -1;
94 		return -1;
95 	} else {
96 		*ret = result;
97 		return 0;
98 	}
99 }
100 
101 static int method_vpcw(acpi_handle handle, int cmd, int data)
102 {
103 	struct acpi_object_list params;
104 	union acpi_object in_obj[2];
105 	acpi_status status;
106 
107 	params.count = 2;
108 	params.pointer = in_obj;
109 	in_obj[0].type = ACPI_TYPE_INTEGER;
110 	in_obj[0].integer.value = cmd;
111 	in_obj[1].type = ACPI_TYPE_INTEGER;
112 	in_obj[1].integer.value = data;
113 
114 	status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
115 	if (status != AE_OK)
116 		return -1;
117 	return 0;
118 }
119 
120 static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
121 {
122 	int val;
123 	unsigned long int end_jiffies;
124 
125 	if (method_vpcw(handle, 1, cmd))
126 		return -1;
127 
128 	for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
129 	     time_before(jiffies, end_jiffies);) {
130 		schedule();
131 		if (method_vpcr(handle, 1, &val))
132 			return -1;
133 		if (val == 0) {
134 			if (method_vpcr(handle, 0, &val))
135 				return -1;
136 			*data = val;
137 			return 0;
138 		}
139 	}
140 	pr_err("timeout in read_ec_cmd\n");
141 	return -1;
142 }
143 
144 static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
145 {
146 	int val;
147 	unsigned long int end_jiffies;
148 
149 	if (method_vpcw(handle, 0, data))
150 		return -1;
151 	if (method_vpcw(handle, 1, cmd))
152 		return -1;
153 
154 	for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
155 	     time_before(jiffies, end_jiffies);) {
156 		schedule();
157 		if (method_vpcr(handle, 1, &val))
158 			return -1;
159 		if (val == 0)
160 			return 0;
161 	}
162 	pr_err("timeout in write_ec_cmd\n");
163 	return -1;
164 }
165 
166 /*
167  * sysfs
168  */
169 static ssize_t show_ideapad_cam(struct device *dev,
170 				struct device_attribute *attr,
171 				char *buf)
172 {
173 	unsigned long result;
174 
175 	if (read_ec_data(ideapad_handle, 0x1D, &result))
176 		return sprintf(buf, "-1\n");
177 	return sprintf(buf, "%lu\n", result);
178 }
179 
180 static ssize_t store_ideapad_cam(struct device *dev,
181 				 struct device_attribute *attr,
182 				 const char *buf, size_t count)
183 {
184 	int ret, state;
185 
186 	if (!count)
187 		return 0;
188 	if (sscanf(buf, "%i", &state) != 1)
189 		return -EINVAL;
190 	ret = write_ec_cmd(ideapad_handle, 0x1E, state);
191 	if (ret < 0)
192 		return ret;
193 	return count;
194 }
195 
196 static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
197 
198 static ssize_t show_ideapad_cfg(struct device *dev,
199 				struct device_attribute *attr,
200 				char *buf)
201 {
202 	struct ideapad_private *priv = dev_get_drvdata(dev);
203 
204 	return sprintf(buf, "0x%.8lX\n", priv->cfg);
205 }
206 
207 static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
208 
209 static struct attribute *ideapad_attributes[] = {
210 	&dev_attr_camera_power.attr,
211 	&dev_attr_cfg.attr,
212 	NULL
213 };
214 
215 static mode_t ideapad_is_visible(struct kobject *kobj,
216 				 struct attribute *attr,
217 				 int idx)
218 {
219 	struct device *dev = container_of(kobj, struct device, kobj);
220 	struct ideapad_private *priv = dev_get_drvdata(dev);
221 	bool supported;
222 
223 	if (attr == &dev_attr_camera_power.attr)
224 		supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
225 	else
226 		supported = true;
227 
228 	return supported ? attr->mode : 0;
229 }
230 
231 static struct attribute_group ideapad_attribute_group = {
232 	.is_visible = ideapad_is_visible,
233 	.attrs = ideapad_attributes
234 };
235 
236 /*
237  * Rfkill
238  */
239 struct ideapad_rfk_data {
240 	char *name;
241 	int cfgbit;
242 	int opcode;
243 	int type;
244 };
245 
246 const struct ideapad_rfk_data ideapad_rfk_data[] = {
247 	{ "ideapad_wlan",      CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN },
248 	{ "ideapad_bluetooth", CFG_BT_BIT,   0x17, RFKILL_TYPE_BLUETOOTH },
249 	{ "ideapad_3g",        CFG_3G_BIT,   0x20, RFKILL_TYPE_WWAN },
250 };
251 
252 static int ideapad_rfk_set(void *data, bool blocked)
253 {
254 	unsigned long opcode = (unsigned long)data;
255 
256 	return write_ec_cmd(ideapad_handle, opcode, !blocked);
257 }
258 
259 static struct rfkill_ops ideapad_rfk_ops = {
260 	.set_block = ideapad_rfk_set,
261 };
262 
263 static void ideapad_sync_rfk_state(struct acpi_device *adevice)
264 {
265 	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
266 	unsigned long hw_blocked;
267 	int i;
268 
269 	if (read_ec_data(ideapad_handle, 0x23, &hw_blocked))
270 		return;
271 	hw_blocked = !hw_blocked;
272 
273 	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
274 		if (priv->rfk[i])
275 			rfkill_set_hw_state(priv->rfk[i], hw_blocked);
276 }
277 
278 static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
279 					     int dev)
280 {
281 	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
282 	int ret;
283 	unsigned long sw_blocked;
284 
285 	if (no_bt_rfkill &&
286 	    (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
287 		/* Force to enable bluetooth when no_bt_rfkill=1 */
288 		write_ec_cmd(ideapad_handle,
289 			     ideapad_rfk_data[dev].opcode, 1);
290 		return 0;
291 	}
292 
293 	priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
294 				      ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
295 				      (void *)(long)dev);
296 	if (!priv->rfk[dev])
297 		return -ENOMEM;
298 
299 	if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1,
300 			 &sw_blocked)) {
301 		rfkill_init_sw_state(priv->rfk[dev], 0);
302 	} else {
303 		sw_blocked = !sw_blocked;
304 		rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
305 	}
306 
307 	ret = rfkill_register(priv->rfk[dev]);
308 	if (ret) {
309 		rfkill_destroy(priv->rfk[dev]);
310 		return ret;
311 	}
312 	return 0;
313 }
314 
315 static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
316 {
317 	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
318 
319 	if (!priv->rfk[dev])
320 		return;
321 
322 	rfkill_unregister(priv->rfk[dev]);
323 	rfkill_destroy(priv->rfk[dev]);
324 }
325 
326 /*
327  * Platform device
328  */
329 static int __devinit ideapad_platform_init(struct ideapad_private *priv)
330 {
331 	int result;
332 
333 	priv->platform_device = platform_device_alloc("ideapad", -1);
334 	if (!priv->platform_device)
335 		return -ENOMEM;
336 	platform_set_drvdata(priv->platform_device, priv);
337 
338 	result = platform_device_add(priv->platform_device);
339 	if (result)
340 		goto fail_platform_device;
341 
342 	result = sysfs_create_group(&priv->platform_device->dev.kobj,
343 				    &ideapad_attribute_group);
344 	if (result)
345 		goto fail_sysfs;
346 	return 0;
347 
348 fail_sysfs:
349 	platform_device_del(priv->platform_device);
350 fail_platform_device:
351 	platform_device_put(priv->platform_device);
352 	return result;
353 }
354 
355 static void ideapad_platform_exit(struct ideapad_private *priv)
356 {
357 	sysfs_remove_group(&priv->platform_device->dev.kobj,
358 			   &ideapad_attribute_group);
359 	platform_device_unregister(priv->platform_device);
360 }
361 
362 /*
363  * input device
364  */
365 static const struct key_entry ideapad_keymap[] = {
366 	{ KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } },
367 	{ KE_KEY, 0x0D, { KEY_WLAN } },
368 	{ KE_END, 0 },
369 };
370 
371 static int __devinit ideapad_input_init(struct ideapad_private *priv)
372 {
373 	struct input_dev *inputdev;
374 	int error;
375 
376 	inputdev = input_allocate_device();
377 	if (!inputdev) {
378 		pr_info("Unable to allocate input device\n");
379 		return -ENOMEM;
380 	}
381 
382 	inputdev->name = "Ideapad extra buttons";
383 	inputdev->phys = "ideapad/input0";
384 	inputdev->id.bustype = BUS_HOST;
385 	inputdev->dev.parent = &priv->platform_device->dev;
386 
387 	error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
388 	if (error) {
389 		pr_err("Unable to setup input device keymap\n");
390 		goto err_free_dev;
391 	}
392 
393 	error = input_register_device(inputdev);
394 	if (error) {
395 		pr_err("Unable to register input device\n");
396 		goto err_free_keymap;
397 	}
398 
399 	priv->inputdev = inputdev;
400 	return 0;
401 
402 err_free_keymap:
403 	sparse_keymap_free(inputdev);
404 err_free_dev:
405 	input_free_device(inputdev);
406 	return error;
407 }
408 
409 static void ideapad_input_exit(struct ideapad_private *priv)
410 {
411 	sparse_keymap_free(priv->inputdev);
412 	input_unregister_device(priv->inputdev);
413 	priv->inputdev = NULL;
414 }
415 
416 static void ideapad_input_report(struct ideapad_private *priv,
417 				 unsigned long scancode)
418 {
419 	sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
420 }
421 
422 /*
423  * backlight
424  */
425 static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
426 {
427 	unsigned long now;
428 
429 	if (read_ec_data(ideapad_handle, 0x12, &now))
430 		return -EIO;
431 	return now;
432 }
433 
434 static int ideapad_backlight_update_status(struct backlight_device *blightdev)
435 {
436 	if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness))
437 		return -EIO;
438 	if (write_ec_cmd(ideapad_handle, 0x33,
439 			 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
440 		return -EIO;
441 
442 	return 0;
443 }
444 
445 static const struct backlight_ops ideapad_backlight_ops = {
446 	.get_brightness = ideapad_backlight_get_brightness,
447 	.update_status = ideapad_backlight_update_status,
448 };
449 
450 static int ideapad_backlight_init(struct ideapad_private *priv)
451 {
452 	struct backlight_device *blightdev;
453 	struct backlight_properties props;
454 	unsigned long max, now, power;
455 
456 	if (read_ec_data(ideapad_handle, 0x11, &max))
457 		return -EIO;
458 	if (read_ec_data(ideapad_handle, 0x12, &now))
459 		return -EIO;
460 	if (read_ec_data(ideapad_handle, 0x18, &power))
461 		return -EIO;
462 
463 	memset(&props, 0, sizeof(struct backlight_properties));
464 	props.max_brightness = max;
465 	props.type = BACKLIGHT_PLATFORM;
466 	blightdev = backlight_device_register("ideapad",
467 					      &priv->platform_device->dev,
468 					      priv,
469 					      &ideapad_backlight_ops,
470 					      &props);
471 	if (IS_ERR(blightdev)) {
472 		pr_err("Could not register backlight device\n");
473 		return PTR_ERR(blightdev);
474 	}
475 
476 	priv->blightdev = blightdev;
477 	blightdev->props.brightness = now;
478 	blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
479 	backlight_update_status(blightdev);
480 
481 	return 0;
482 }
483 
484 static void ideapad_backlight_exit(struct ideapad_private *priv)
485 {
486 	if (priv->blightdev)
487 		backlight_device_unregister(priv->blightdev);
488 	priv->blightdev = NULL;
489 }
490 
491 static void ideapad_backlight_notify_power(struct ideapad_private *priv)
492 {
493 	unsigned long power;
494 	struct backlight_device *blightdev = priv->blightdev;
495 
496 	if (read_ec_data(ideapad_handle, 0x18, &power))
497 		return;
498 	blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
499 }
500 
501 static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
502 {
503 	unsigned long now;
504 
505 	/* if we control brightness via acpi video driver */
506 	if (priv->blightdev == NULL) {
507 		read_ec_data(ideapad_handle, 0x12, &now);
508 		return;
509 	}
510 
511 	backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
512 }
513 
514 /*
515  * module init/exit
516  */
517 static const struct acpi_device_id ideapad_device_ids[] = {
518 	{ "VPC2004", 0},
519 	{ "", 0},
520 };
521 MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
522 
523 static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
524 {
525 	int ret, i;
526 	unsigned long cfg;
527 	struct ideapad_private *priv;
528 
529 	if (read_method_int(adevice->handle, "_CFG", (int *)&cfg))
530 		return -ENODEV;
531 
532 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
533 	if (!priv)
534 		return -ENOMEM;
535 	dev_set_drvdata(&adevice->dev, priv);
536 	ideapad_handle = adevice->handle;
537 	priv->cfg = cfg;
538 
539 	ret = ideapad_platform_init(priv);
540 	if (ret)
541 		goto platform_failed;
542 
543 	ret = ideapad_input_init(priv);
544 	if (ret)
545 		goto input_failed;
546 
547 	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
548 		if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg))
549 			ideapad_register_rfkill(adevice, i);
550 		else
551 			priv->rfk[i] = NULL;
552 	}
553 	ideapad_sync_rfk_state(adevice);
554 
555 	if (!acpi_video_backlight_support()) {
556 		ret = ideapad_backlight_init(priv);
557 		if (ret && ret != -ENODEV)
558 			goto backlight_failed;
559 	}
560 
561 	return 0;
562 
563 backlight_failed:
564 	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
565 		ideapad_unregister_rfkill(adevice, i);
566 	ideapad_input_exit(priv);
567 input_failed:
568 	ideapad_platform_exit(priv);
569 platform_failed:
570 	kfree(priv);
571 	return ret;
572 }
573 
574 static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
575 {
576 	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
577 	int i;
578 
579 	ideapad_backlight_exit(priv);
580 	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
581 		ideapad_unregister_rfkill(adevice, i);
582 	ideapad_input_exit(priv);
583 	ideapad_platform_exit(priv);
584 	dev_set_drvdata(&adevice->dev, NULL);
585 	kfree(priv);
586 
587 	return 0;
588 }
589 
590 static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
591 {
592 	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
593 	acpi_handle handle = adevice->handle;
594 	unsigned long vpc1, vpc2, vpc_bit;
595 
596 	if (read_ec_data(handle, 0x10, &vpc1))
597 		return;
598 	if (read_ec_data(handle, 0x1A, &vpc2))
599 		return;
600 
601 	vpc1 = (vpc2 << 8) | vpc1;
602 	for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
603 		if (test_bit(vpc_bit, &vpc1)) {
604 			switch (vpc_bit) {
605 			case 9:
606 				ideapad_sync_rfk_state(adevice);
607 				break;
608 			case 4:
609 				ideapad_backlight_notify_brightness(priv);
610 				break;
611 			case 2:
612 				ideapad_backlight_notify_power(priv);
613 				break;
614 			default:
615 				ideapad_input_report(priv, vpc_bit);
616 			}
617 		}
618 	}
619 }
620 
621 static struct acpi_driver ideapad_acpi_driver = {
622 	.name = "ideapad_acpi",
623 	.class = "IdeaPad",
624 	.ids = ideapad_device_ids,
625 	.ops.add = ideapad_acpi_add,
626 	.ops.remove = ideapad_acpi_remove,
627 	.ops.notify = ideapad_acpi_notify,
628 	.owner = THIS_MODULE,
629 };
630 
631 static int __init ideapad_acpi_module_init(void)
632 {
633 	return acpi_bus_register_driver(&ideapad_acpi_driver);
634 }
635 
636 static void __exit ideapad_acpi_module_exit(void)
637 {
638 	acpi_bus_unregister_driver(&ideapad_acpi_driver);
639 }
640 
641 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
642 MODULE_DESCRIPTION("IdeaPad ACPI Extras");
643 MODULE_LICENSE("GPL");
644 
645 module_init(ideapad_acpi_module_init);
646 module_exit(ideapad_acpi_module_exit);
647