Merge branches 'release', 'asus', 'sony-laptop' and 'thinkpad' into release
[pandora-kernel.git] / arch / mips / lasat / picvue_proc.c
1 /*
2  * Picvue PVC160206 display driver
3  *
4  * Brian Murphy <brian.murphy@eicon.com>
5  *
6  */
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/errno.h>
11
12 #include <linux/proc_fs.h>
13 #include <linux/interrupt.h>
14
15 #include <linux/timer.h>
16 #include <linux/mutex.h>
17
18 #include "picvue.h"
19
20 static DEFINE_MUTEX(pvc_mutex);
21 static char pvc_lines[PVC_NLINES][PVC_LINELEN+1];
22 static int pvc_linedata[PVC_NLINES];
23 static struct proc_dir_entry *pvc_display_dir;
24 static char *pvc_linename[PVC_NLINES] = {"line1", "line2"};
25 #define DISPLAY_DIR_NAME "display"
26 static int scroll_dir, scroll_interval;
27
28 static struct timer_list timer;
29
30 static void pvc_display(unsigned long data)
31 {
32         int i;
33
34         pvc_clear();
35         for (i = 0; i < PVC_NLINES; i++)
36                 pvc_write_string(pvc_lines[i], 0, i);
37 }
38
39 static DECLARE_TASKLET(pvc_display_tasklet, &pvc_display, 0);
40
41 static int pvc_proc_read_line(char *page, char **start,
42                              off_t off, int count,
43                              int *eof, void *data)
44 {
45         char *origpage = page;
46         int lineno = *(int *)data;
47
48         if (lineno < 0 || lineno > PVC_NLINES) {
49                 printk(KERN_WARNING "proc_read_line: invalid lineno %d\n", lineno);
50                 return 0;
51         }
52
53         mutex_lock(&pvc_mutex);
54         page += sprintf(page, "%s\n", pvc_lines[lineno]);
55         mutex_unlock(&pvc_mutex);
56
57         return page - origpage;
58 }
59
60 static int pvc_proc_write_line(struct file *file, const char *buffer,
61                            unsigned long count, void *data)
62 {
63         int origcount = count;
64         int lineno = *(int *)data;
65
66         if (lineno < 0 || lineno > PVC_NLINES) {
67                 printk(KERN_WARNING "proc_write_line: invalid lineno %d\n",
68                        lineno);
69                 return origcount;
70         }
71
72         if (count > PVC_LINELEN)
73                 count = PVC_LINELEN;
74
75         if (buffer[count-1] == '\n')
76                 count--;
77
78         mutex_lock(&pvc_mutex);
79         strncpy(pvc_lines[lineno], buffer, count);
80         pvc_lines[lineno][count] = '\0';
81         mutex_unlock(&pvc_mutex);
82
83         tasklet_schedule(&pvc_display_tasklet);
84
85         return origcount;
86 }
87
88 static int pvc_proc_write_scroll(struct file *file, const char *buffer,
89                            unsigned long count, void *data)
90 {
91         int origcount = count;
92         int cmd = simple_strtol(buffer, NULL, 10);
93
94         mutex_lock(&pvc_mutex);
95         if (scroll_interval != 0)
96                 del_timer(&timer);
97
98         if (cmd == 0) {
99                 scroll_dir = 0;
100                 scroll_interval = 0;
101         } else {
102                 if (cmd < 0) {
103                         scroll_dir = -1;
104                         scroll_interval = -cmd;
105                 } else {
106                         scroll_dir = 1;
107                         scroll_interval = cmd;
108                 }
109                 add_timer(&timer);
110         }
111         mutex_unlock(&pvc_mutex);
112
113         return origcount;
114 }
115
116 static int pvc_proc_read_scroll(char *page, char **start,
117                              off_t off, int count,
118                              int *eof, void *data)
119 {
120         char *origpage = page;
121
122         mutex_lock(&pvc_mutex);
123         page += sprintf(page, "%d\n", scroll_dir * scroll_interval);
124         mutex_unlock(&pvc_mutex);
125
126         return page - origpage;
127 }
128
129
130 void pvc_proc_timerfunc(unsigned long data)
131 {
132         if (scroll_dir < 0)
133                 pvc_move(DISPLAY|RIGHT);
134         else if (scroll_dir > 0)
135                 pvc_move(DISPLAY|LEFT);
136
137         timer.expires = jiffies + scroll_interval;
138         add_timer(&timer);
139 }
140
141 static void pvc_proc_cleanup(void)
142 {
143         int i;
144         for (i = 0; i < PVC_NLINES; i++)
145                 remove_proc_entry(pvc_linename[i], pvc_display_dir);
146         remove_proc_entry("scroll", pvc_display_dir);
147         remove_proc_entry(DISPLAY_DIR_NAME, NULL);
148
149         del_timer(&timer);
150 }
151
152 static int __init pvc_proc_init(void)
153 {
154         struct proc_dir_entry *proc_entry;
155         int i;
156
157         pvc_display_dir = proc_mkdir(DISPLAY_DIR_NAME, NULL);
158         if (pvc_display_dir == NULL)
159                 goto error;
160
161         for (i = 0; i < PVC_NLINES; i++) {
162                 strcpy(pvc_lines[i], "");
163                 pvc_linedata[i] = i;
164         }
165         for (i = 0; i < PVC_NLINES; i++) {
166                 proc_entry = create_proc_entry(pvc_linename[i], 0644,
167                                                pvc_display_dir);
168                 if (proc_entry == NULL)
169                         goto error;
170
171                 proc_entry->read_proc = pvc_proc_read_line;
172                 proc_entry->write_proc = pvc_proc_write_line;
173                 proc_entry->data = &pvc_linedata[i];
174         }
175         proc_entry = create_proc_entry("scroll", 0644, pvc_display_dir);
176         if (proc_entry == NULL)
177                 goto error;
178
179         proc_entry->write_proc = pvc_proc_write_scroll;
180         proc_entry->read_proc = pvc_proc_read_scroll;
181
182         init_timer(&timer);
183         timer.function = pvc_proc_timerfunc;
184
185         return 0;
186 error:
187         pvc_proc_cleanup();
188         return -ENOMEM;
189 }
190
191 module_init(pvc_proc_init);
192 module_exit(pvc_proc_cleanup);
193 MODULE_LICENSE("GPL");