mm: thp: set the accessed flag for old pages on access fault
[pandora-kernel.git] / drivers / video / cobalt_lcdfb.c
1 /*
2  *  Cobalt server LCD frame buffer driver.
3  *
4  *  Copyright (C) 2008  Yoichi Yuasa <yuasa@linux-mips.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 #include <linux/delay.h>
21 #include <linux/fb.h>
22 #include <linux/init.h>
23 #include <linux/io.h>
24 #include <linux/ioport.h>
25 #include <linux/uaccess.h>
26 #include <linux/platform_device.h>
27 #include <linux/module.h>
28
29 /*
30  * Cursor position address
31  * \X  0    1    2  ...  14   15
32  * Y+----+----+----+---+----+----+
33  * 0|0x00|0x01|0x02|...|0x0e|0x0f|
34  *  +----+----+----+---+----+----+
35  * 1|0x40|0x41|0x42|...|0x4e|0x4f|
36  *  +----+----+----+---+----+----+
37  */
38 #define LCD_DATA_REG_OFFSET     0x10
39 #define LCD_XRES_MAX            16
40 #define LCD_YRES_MAX            2
41 #define LCD_CHARS_MAX           32
42
43 #define LCD_CLEAR               0x01
44 #define LCD_CURSOR_MOVE_HOME    0x02
45 #define LCD_RESET               0x06
46 #define LCD_OFF                 0x08
47 #define LCD_CURSOR_OFF          0x0c
48 #define LCD_CURSOR_BLINK_OFF    0x0e
49 #define LCD_CURSOR_ON           0x0f
50 #define LCD_ON                  LCD_CURSOR_ON
51 #define LCD_CURSOR_MOVE_LEFT    0x10
52 #define LCD_CURSOR_MOVE_RIGHT   0x14
53 #define LCD_DISPLAY_LEFT        0x18
54 #define LCD_DISPLAY_RIGHT       0x1c
55 #define LCD_PRERESET            0x3f    /* execute 4 times continuously */
56 #define LCD_BUSY                0x80
57
58 #define LCD_GRAPHIC_MODE        0x40
59 #define LCD_TEXT_MODE           0x80
60 #define LCD_CUR_POS_MASK        0x7f
61
62 #define LCD_CUR_POS(x)          ((x) & LCD_CUR_POS_MASK)
63 #define LCD_TEXT_POS(x)         ((x) | LCD_TEXT_MODE)
64
65 static inline void lcd_write_control(struct fb_info *info, u8 control)
66 {
67         writel((u32)control << 24, info->screen_base);
68 }
69
70 static inline u8 lcd_read_control(struct fb_info *info)
71 {
72         return readl(info->screen_base) >> 24;
73 }
74
75 static inline void lcd_write_data(struct fb_info *info, u8 data)
76 {
77         writel((u32)data << 24, info->screen_base + LCD_DATA_REG_OFFSET);
78 }
79
80 static inline u8 lcd_read_data(struct fb_info *info)
81 {
82         return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24;
83 }
84
85 static int lcd_busy_wait(struct fb_info *info)
86 {
87         u8 val = 0;
88         int timeout = 10, retval = 0;
89
90         do {
91                 val = lcd_read_control(info);
92                 val &= LCD_BUSY;
93                 if (val != LCD_BUSY)
94                         break;
95
96                 if (msleep_interruptible(1))
97                         return -EINTR;
98
99                 timeout--;
100         } while (timeout);
101
102         if (val == LCD_BUSY)
103                 retval = -EBUSY;
104
105         return retval;
106 }
107
108 static void lcd_clear(struct fb_info *info)
109 {
110         int i;
111
112         for (i = 0; i < 4; i++) {
113                 udelay(150);
114
115                 lcd_write_control(info, LCD_PRERESET);
116         }
117
118         udelay(150);
119
120         lcd_write_control(info, LCD_CLEAR);
121
122         udelay(150);
123
124         lcd_write_control(info, LCD_RESET);
125 }
126
127 static struct fb_fix_screeninfo cobalt_lcdfb_fix __devinitdata = {
128         .id             = "cobalt-lcd",
129         .type           = FB_TYPE_TEXT,
130         .type_aux       = FB_AUX_TEXT_MDA,
131         .visual         = FB_VISUAL_MONO01,
132         .line_length    = LCD_XRES_MAX,
133         .accel          = FB_ACCEL_NONE,
134 };
135
136 static ssize_t cobalt_lcdfb_read(struct fb_info *info, char __user *buf,
137                                  size_t count, loff_t *ppos)
138 {
139         char src[LCD_CHARS_MAX];
140         unsigned long pos;
141         int len, retval = 0;
142
143         pos = *ppos;
144         if (pos >= LCD_CHARS_MAX || count == 0)
145                 return 0;
146
147         if (count > LCD_CHARS_MAX)
148                 count = LCD_CHARS_MAX;
149
150         if (pos + count > LCD_CHARS_MAX)
151                 count = LCD_CHARS_MAX - pos;
152
153         for (len = 0; len < count; len++) {
154                 retval = lcd_busy_wait(info);
155                 if (retval < 0)
156                         break;
157
158                 lcd_write_control(info, LCD_TEXT_POS(pos));
159
160                 retval = lcd_busy_wait(info);
161                 if (retval < 0)
162                         break;
163
164                 src[len] = lcd_read_data(info);
165                 if (pos == 0x0f)
166                         pos = 0x40;
167                 else
168                         pos++;
169         }
170
171         if (retval < 0 && signal_pending(current))
172                 return -ERESTARTSYS;
173
174         if (copy_to_user(buf, src, len))
175                 return -EFAULT;
176
177         *ppos += len;
178
179         return len;
180 }
181
182 static ssize_t cobalt_lcdfb_write(struct fb_info *info, const char __user *buf,
183                                   size_t count, loff_t *ppos)
184 {
185         char dst[LCD_CHARS_MAX];
186         unsigned long pos;
187         int len, retval = 0;
188
189         pos = *ppos;
190         if (pos >= LCD_CHARS_MAX || count == 0)
191                 return 0;
192
193         if (count > LCD_CHARS_MAX)
194                 count = LCD_CHARS_MAX;
195
196         if (pos + count > LCD_CHARS_MAX)
197                 count = LCD_CHARS_MAX - pos;
198
199         if (copy_from_user(dst, buf, count))
200                 return -EFAULT;
201
202         for (len = 0; len < count; len++) {
203                 retval = lcd_busy_wait(info);
204                 if (retval < 0)
205                         break;
206
207                 lcd_write_control(info, LCD_TEXT_POS(pos));
208
209                 retval = lcd_busy_wait(info);
210                 if (retval < 0)
211                         break;
212
213                 lcd_write_data(info, dst[len]);
214                 if (pos == 0x0f)
215                         pos = 0x40;
216                 else
217                         pos++;
218         }
219
220         if (retval < 0 && signal_pending(current))
221                 return -ERESTARTSYS;
222
223         *ppos += len;
224
225         return len;
226 }
227
228 static int cobalt_lcdfb_blank(int blank_mode, struct fb_info *info)
229 {
230         int retval;
231
232         retval = lcd_busy_wait(info);
233         if (retval < 0)
234                 return retval;
235
236         switch (blank_mode) {
237         case FB_BLANK_UNBLANK:
238                 lcd_write_control(info, LCD_ON);
239                 break;
240         default:
241                 lcd_write_control(info, LCD_OFF);
242                 break;
243         }
244
245         return 0;
246 }
247
248 static int cobalt_lcdfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
249 {
250         u32 x, y;
251         int retval;
252
253         switch (cursor->set) {
254         case FB_CUR_SETPOS:
255                 x = cursor->image.dx;
256                 y = cursor->image.dy;
257                 if (x >= LCD_XRES_MAX || y >= LCD_YRES_MAX)
258                         return -EINVAL;
259
260                 retval = lcd_busy_wait(info);
261                 if (retval < 0)
262                         return retval;
263
264                 lcd_write_control(info,
265                                   LCD_TEXT_POS(info->fix.line_length * y + x));
266                 break;
267         default:
268                 return -EINVAL;
269         }
270
271         retval = lcd_busy_wait(info);
272         if (retval < 0)
273                 return retval;
274
275         if (cursor->enable)
276                 lcd_write_control(info, LCD_CURSOR_ON);
277         else
278                 lcd_write_control(info, LCD_CURSOR_OFF);
279
280         return 0;
281 }
282
283 static struct fb_ops cobalt_lcd_fbops = {
284         .owner          = THIS_MODULE,
285         .fb_read        = cobalt_lcdfb_read,
286         .fb_write       = cobalt_lcdfb_write,
287         .fb_blank       = cobalt_lcdfb_blank,
288         .fb_cursor      = cobalt_lcdfb_cursor,
289 };
290
291 static int __devinit cobalt_lcdfb_probe(struct platform_device *dev)
292 {
293         struct fb_info *info;
294         struct resource *res;
295         int retval;
296
297         info = framebuffer_alloc(0, &dev->dev);
298         if (!info)
299                 return -ENOMEM;
300
301         res = platform_get_resource(dev, IORESOURCE_MEM, 0);
302         if (!res) {
303                 framebuffer_release(info);
304                 return -EBUSY;
305         }
306
307         info->screen_size = resource_size(res);
308         info->screen_base = ioremap(res->start, info->screen_size);
309         info->fbops = &cobalt_lcd_fbops;
310         info->fix = cobalt_lcdfb_fix;
311         info->fix.smem_start = res->start;
312         info->fix.smem_len = info->screen_size;
313         info->pseudo_palette = NULL;
314         info->par = NULL;
315         info->flags = FBINFO_DEFAULT;
316
317         retval = register_framebuffer(info);
318         if (retval < 0) {
319                 iounmap(info->screen_base);
320                 framebuffer_release(info);
321                 return retval;
322         }
323
324         platform_set_drvdata(dev, info);
325
326         lcd_clear(info);
327
328         printk(KERN_INFO "fb%d: Cobalt server LCD frame buffer device\n",
329                 info->node);
330
331         return 0;
332 }
333
334 static int __devexit cobalt_lcdfb_remove(struct platform_device *dev)
335 {
336         struct fb_info *info;
337
338         info = platform_get_drvdata(dev);
339         if (info) {
340                 iounmap(info->screen_base);
341                 unregister_framebuffer(info);
342                 framebuffer_release(info);
343         }
344
345         return 0;
346 }
347
348 static struct platform_driver cobalt_lcdfb_driver = {
349         .probe  = cobalt_lcdfb_probe,
350         .remove = __devexit_p(cobalt_lcdfb_remove),
351         .driver = {
352                 .name   = "cobalt-lcd",
353                 .owner  = THIS_MODULE,
354         },
355 };
356
357 static int __init cobalt_lcdfb_init(void)
358 {
359         return platform_driver_register(&cobalt_lcdfb_driver);
360 }
361
362 static void __exit cobalt_lcdfb_exit(void)
363 {
364         platform_driver_unregister(&cobalt_lcdfb_driver);
365 }
366
367 module_init(cobalt_lcdfb_init);
368 module_exit(cobalt_lcdfb_exit);
369
370 MODULE_LICENSE("GPL v2");
371 MODULE_AUTHOR("Yoichi Yuasa");
372 MODULE_DESCRIPTION("Cobalt server LCD frame buffer driver");