1/*
2 *  toshiba_acpi.c - Toshiba Laptop ACPI Extras
3 *
4 *
5 *  Copyright (C) 2002-2004 John Belmonte
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 *
21 *
22 *  The devolpment page for this driver is located at
23 *  http://memebeam.org/toys/ToshibaAcpiDriver.
24 *
25 *  Credits:
26 *	Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse
27 *		engineering the Windows drivers
28 *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
29 *	Rob Miller - TV out and hotkeys help
30 *
31 *
32 *  TODO
33 *
34 */
35
36#define TOSHIBA_ACPI_VERSION	"0.18"
37#define PROC_INTERFACE_VERSION	1
38
39#include <linux/kernel.h>
40#include <linux/module.h>
41#include <linux/init.h>
42#include <linux/types.h>
43#include <linux/proc_fs.h>
44#include <linux/backlight.h>
45
46#include <asm/uaccess.h>
47
48#include <acpi/acpi_drivers.h>
49
50MODULE_AUTHOR("John Belmonte");
51MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
52MODULE_LICENSE("GPL");
53
54#define MY_LOGPREFIX "toshiba_acpi: "
55#define MY_ERR KERN_ERR MY_LOGPREFIX
56#define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
57#define MY_INFO KERN_INFO MY_LOGPREFIX
58
59/* Toshiba ACPI method paths */
60#define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
61#define METHOD_HCI_1		"\\_SB_.VALD.GHCI"
62#define METHOD_HCI_2		"\\_SB_.VALZ.GHCI"
63#define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
64
65/* Toshiba HCI interface definitions
66 *
67 * HCI is Toshiba's "Hardware Control Interface" which is supposed to
68 * be uniform across all their models.  Ideally we would just call
69 * dedicated ACPI methods instead of using this primitive interface.
70 * However the ACPI methods seem to be incomplete in some areas (for
71 * example they allow setting, but not reading, the LCD brightness value),
72 * so this is still useful.
73 */
74
75#define HCI_WORDS			6
76
77/* operations */
78#define HCI_SET				0xff00
79#define HCI_GET				0xfe00
80
81/* return codes */
82#define HCI_SUCCESS			0x0000
83#define HCI_FAILURE			0x1000
84#define HCI_NOT_SUPPORTED		0x8000
85#define HCI_EMPTY			0x8c00
86
87/* registers */
88#define HCI_FAN				0x0004
89#define HCI_SYSTEM_EVENT		0x0016
90#define HCI_VIDEO_OUT			0x001c
91#define HCI_HOTKEY_EVENT		0x001e
92#define HCI_LCD_BRIGHTNESS		0x002a
93
94/* field definitions */
95#define HCI_LCD_BRIGHTNESS_BITS		3
96#define HCI_LCD_BRIGHTNESS_SHIFT	(16-HCI_LCD_BRIGHTNESS_BITS)
97#define HCI_LCD_BRIGHTNESS_LEVELS	(1 << HCI_LCD_BRIGHTNESS_BITS)
98#define HCI_VIDEO_OUT_LCD		0x1
99#define HCI_VIDEO_OUT_CRT		0x2
100#define HCI_VIDEO_OUT_TV		0x4
101
102/* utility
103 */
104
105static __inline__ void _set_bit(u32 * word, u32 mask, int value)
106{
107	*word = (*word & ~mask) | (mask * value);
108}
109
110/* acpi interface wrappers
111 */
112
113static int is_valid_acpi_path(const char *methodName)
114{
115	acpi_handle handle;
116	acpi_status status;
117
118	status = acpi_get_handle(NULL, (char *)methodName, &handle);
119	return !ACPI_FAILURE(status);
120}
121
122static int write_acpi_int(const char *methodName, int val)
123{
124	struct acpi_object_list params;
125	union acpi_object in_objs[1];
126	acpi_status status;
127
128	params.count = ARRAY_SIZE(in_objs);
129	params.pointer = in_objs;
130	in_objs[0].type = ACPI_TYPE_INTEGER;
131	in_objs[0].integer.value = val;
132
133	status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
134	return (status == AE_OK);
135}
136
137
138static const char *method_hci /*= 0*/ ;
139
140/* Perform a raw HCI call.  Here we don't care about input or output buffer
141 * format.
142 */
143static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
144{
145	struct acpi_object_list params;
146	union acpi_object in_objs[HCI_WORDS];
147	struct acpi_buffer results;
148	union acpi_object out_objs[HCI_WORDS + 1];
149	acpi_status status;
150	int i;
151
152	params.count = HCI_WORDS;
153	params.pointer = in_objs;
154	for (i = 0; i < HCI_WORDS; ++i) {
155		in_objs[i].type = ACPI_TYPE_INTEGER;
156		in_objs[i].integer.value = in[i];
157	}
158
159	results.length = sizeof(out_objs);
160	results.pointer = out_objs;
161
162	status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
163				      &results);
164	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
165		for (i = 0; i < out_objs->package.count; ++i) {
166			out[i] = out_objs->package.elements[i].integer.value;
167		}
168	}
169
170	return status;
171}
172
173/* common hci tasks (get or set one value)
174 *
175 * In addition to the ACPI status, the HCI system returns a result which
176 * may be useful (such as "not supported").
177 */
178
179static acpi_status hci_write1(u32 reg, u32 in1, u32 * result)
180{
181	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
182	u32 out[HCI_WORDS];
183	acpi_status status = hci_raw(in, out);
184	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
185	return status;
186}
187
188static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
189{
190	u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
191	u32 out[HCI_WORDS];
192	acpi_status status = hci_raw(in, out);
193	*out1 = out[2];
194	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
195	return status;
196}
197
198static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
199static struct backlight_device *toshiba_backlight_device;
200static int force_fan;
201static int last_key_event;
202static int key_event_valid;
203
204typedef struct _ProcItem {
205	const char *name;
206	char *(*read_func) (char *);
207	unsigned long (*write_func) (const char *, unsigned long);
208} ProcItem;
209
210/* proc file handlers
211 */
212
213static int
214dispatch_read(char *page, char **start, off_t off, int count, int *eof,
215	      ProcItem * item)
216{
217	char *p = page;
218	int len;
219
220	if (off == 0)
221		p = item->read_func(p);
222
223	/* ISSUE: I don't understand this code */
224	len = (p - page);
225	if (len <= off + count)
226		*eof = 1;
227	*start = page + off;
228	len -= off;
229	if (len > count)
230		len = count;
231	if (len < 0)
232		len = 0;
233	return len;
234}
235
236static int
237dispatch_write(struct file *file, const char __user * buffer,
238	       unsigned long count, ProcItem * item)
239{
240	int result;
241	char *tmp_buffer;
242
243	/* Arg buffer points to userspace memory, which can't be accessed
244	 * directly.  Since we're making a copy, zero-terminate the
245	 * destination so that sscanf can be used on it safely.
246	 */
247	tmp_buffer = kmalloc(count + 1, GFP_KERNEL);
248	if (!tmp_buffer)
249		return -ENOMEM;
250
251	if (copy_from_user(tmp_buffer, buffer, count)) {
252		result = -EFAULT;
253	} else {
254		tmp_buffer[count] = 0;
255		result = item->write_func(tmp_buffer, count);
256	}
257	kfree(tmp_buffer);
258	return result;
259}
260
261static int get_lcd(struct backlight_device *bd)
262{
263	u32 hci_result;
264	u32 value;
265
266	hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
267	if (hci_result == HCI_SUCCESS) {
268		return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
269	} else
270		return -EFAULT;
271}
272
273static char *read_lcd(char *p)
274{
275	int value = get_lcd(NULL);
276
277	if (value >= 0) {
278		p += sprintf(p, "brightness:              %d\n", value);
279		p += sprintf(p, "brightness_levels:       %d\n",
280			     HCI_LCD_BRIGHTNESS_LEVELS);
281	} else {
282		printk(MY_ERR "Error reading LCD brightness\n");
283	}
284
285	return p;
286}
287
288static int set_lcd(int value)
289{
290	u32 hci_result;
291
292	value = value << HCI_LCD_BRIGHTNESS_SHIFT;
293	hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
294	if (hci_result != HCI_SUCCESS)
295		return -EFAULT;
296
297	return 0;
298}
299
300static int set_lcd_status(struct backlight_device *bd)
301{
302	return set_lcd(bd->props.brightness);
303}
304
305static unsigned long write_lcd(const char *buffer, unsigned long count)
306{
307	int value;
308	int ret;
309
310	if (sscanf(buffer, " brightness : %i", &value) == 1 &&
311	    value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
312		ret = set_lcd(value);
313		if (ret == 0)
314			ret = count;
315	} else {
316		ret = -EINVAL;
317	}
318	return ret;
319}
320
321static char *read_video(char *p)
322{
323	u32 hci_result;
324	u32 value;
325
326	hci_read1(HCI_VIDEO_OUT, &value, &hci_result);
327	if (hci_result == HCI_SUCCESS) {
328		int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
329		int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
330		int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
331		p += sprintf(p, "lcd_out:                 %d\n", is_lcd);
332		p += sprintf(p, "crt_out:                 %d\n", is_crt);
333		p += sprintf(p, "tv_out:                  %d\n", is_tv);
334	} else {
335		printk(MY_ERR "Error reading video out status\n");
336	}
337
338	return p;
339}
340
341static unsigned long write_video(const char *buffer, unsigned long count)
342{
343	int value;
344	int remain = count;
345	int lcd_out = -1;
346	int crt_out = -1;
347	int tv_out = -1;
348	u32 hci_result;
349	int video_out;
350
351	/* scan expression.  Multiple expressions may be delimited with ;
352	 *
353	 *  NOTE: to keep scanning simple, invalid fields are ignored
354	 */
355	while (remain) {
356		if (sscanf(buffer, " lcd_out : %i", &value) == 1)
357			lcd_out = value & 1;
358		else if (sscanf(buffer, " crt_out : %i", &value) == 1)
359			crt_out = value & 1;
360		else if (sscanf(buffer, " tv_out : %i", &value) == 1)
361			tv_out = value & 1;
362		/* advance to one character past the next ; */
363		do {
364			++buffer;
365			--remain;
366		}
367		while (remain && *(buffer - 1) != ';');
368	}
369
370	hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result);
371	if (hci_result == HCI_SUCCESS) {
372		int new_video_out = video_out;
373		if (lcd_out != -1)
374			_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
375		if (crt_out != -1)
376			_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
377		if (tv_out != -1)
378			_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
379		/* To avoid unnecessary video disruption, only write the new
380		 * video setting if something changed. */
381		if (new_video_out != video_out)
382			write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
383	} else {
384		return -EFAULT;
385	}
386
387	return count;
388}
389
390static char *read_fan(char *p)
391{
392	u32 hci_result;
393	u32 value;
394
395	hci_read1(HCI_FAN, &value, &hci_result);
396	if (hci_result == HCI_SUCCESS) {
397		p += sprintf(p, "running:                 %d\n", (value > 0));
398		p += sprintf(p, "force_on:                %d\n", force_fan);
399	} else {
400		printk(MY_ERR "Error reading fan status\n");
401	}
402
403	return p;
404}
405
406static unsigned long write_fan(const char *buffer, unsigned long count)
407{
408	int value;
409	u32 hci_result;
410
411	if (sscanf(buffer, " force_on : %i", &value) == 1 &&
412	    value >= 0 && value <= 1) {
413		hci_write1(HCI_FAN, value, &hci_result);
414		if (hci_result != HCI_SUCCESS)
415			return -EFAULT;
416		else
417			force_fan = value;
418	} else {
419		return -EINVAL;
420	}
421
422	return count;
423}
424
425static char *read_keys(char *p)
426{
427	u32 hci_result;
428	u32 value;
429
430	if (!key_event_valid) {
431		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
432		if (hci_result == HCI_SUCCESS) {
433			key_event_valid = 1;
434			last_key_event = value;
435		} else if (hci_result == HCI_EMPTY) {
436			/* better luck next time */
437		} else if (hci_result == HCI_NOT_SUPPORTED) {
438			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
439			printk(MY_NOTICE "Re-enabled hotkeys\n");
440		} else {
441			printk(MY_ERR "Error reading hotkey status\n");
442			goto end;
443		}
444	}
445
446	p += sprintf(p, "hotkey_ready:            %d\n", key_event_valid);
447	p += sprintf(p, "hotkey:                  0x%04x\n", last_key_event);
448
449      end:
450	return p;
451}
452
453static unsigned long write_keys(const char *buffer, unsigned long count)
454{
455	int value;
456
457	if (sscanf(buffer, " hotkey_ready : %i", &value) == 1 && value == 0) {
458		key_event_valid = 0;
459	} else {
460		return -EINVAL;
461	}
462
463	return count;
464}
465
466static char *read_version(char *p)
467{
468	p += sprintf(p, "driver:                  %s\n", TOSHIBA_ACPI_VERSION);
469	p += sprintf(p, "proc_interface:          %d\n",
470		     PROC_INTERFACE_VERSION);
471	return p;
472}
473
474/* proc and module init
475 */
476
477#define PROC_TOSHIBA		"toshiba"
478
479static ProcItem proc_items[] = {
480	{"lcd", read_lcd, write_lcd},
481	{"video", read_video, write_video},
482	{"fan", read_fan, write_fan},
483	{"keys", read_keys, write_keys},
484	{"version", read_version, NULL},
485	{NULL}
486};
487
488static acpi_status __init add_device(void)
489{
490	struct proc_dir_entry *proc;
491	ProcItem *item;
492
493	for (item = proc_items; item->name; ++item) {
494		proc = create_proc_read_entry(item->name,
495					      S_IFREG | S_IRUGO | S_IWUSR,
496					      toshiba_proc_dir,
497					      (read_proc_t *) dispatch_read,
498					      item);
499		if (proc)
500			proc->owner = THIS_MODULE;
501		if (proc && item->write_func)
502			proc->write_proc = (write_proc_t *) dispatch_write;
503	}
504
505	return AE_OK;
506}
507
508static acpi_status remove_device(void)
509{
510	ProcItem *item;
511
512	for (item = proc_items; item->name; ++item)
513		remove_proc_entry(item->name, toshiba_proc_dir);
514	return AE_OK;
515}
516
517static struct backlight_ops toshiba_backlight_data = {
518        .get_brightness = get_lcd,
519        .update_status  = set_lcd_status,
520};
521
522static void toshiba_acpi_exit(void)
523{
524	if (toshiba_backlight_device)
525		backlight_device_unregister(toshiba_backlight_device);
526
527	remove_device();
528
529	if (toshiba_proc_dir)
530		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
531
532	return;
533}
534
535static int __init toshiba_acpi_init(void)
536{
537	acpi_status status = AE_OK;
538	u32 hci_result;
539
540	if (acpi_disabled)
541		return -ENODEV;
542
543	/* simple device detection: look for HCI method */
544	if (is_valid_acpi_path(METHOD_HCI_1))
545		method_hci = METHOD_HCI_1;
546	else if (is_valid_acpi_path(METHOD_HCI_2))
547		method_hci = METHOD_HCI_2;
548	else
549		return -ENODEV;
550
551	printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
552	       TOSHIBA_ACPI_VERSION);
553	printk(MY_INFO "    HCI method: %s\n", method_hci);
554
555	force_fan = 0;
556	key_event_valid = 0;
557
558	/* enable event fifo */
559	hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
560
561	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
562	if (!toshiba_proc_dir) {
563		status = AE_ERROR;
564	} else {
565		toshiba_proc_dir->owner = THIS_MODULE;
566		status = add_device();
567		if (ACPI_FAILURE(status))
568			remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
569	}
570
571	toshiba_backlight_device = backlight_device_register("toshiba",NULL,
572						NULL,
573						&toshiba_backlight_data);
574        if (IS_ERR(toshiba_backlight_device)) {
575		printk(KERN_ERR "Could not register toshiba backlight device\n");
576		toshiba_backlight_device = NULL;
577		toshiba_acpi_exit();
578	}
579        toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
580
581	return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
582}
583
584module_init(toshiba_acpi_init);
585module_exit(toshiba_acpi_exit);
586