[PATCH] Rewritten backlight infrastructure for portable Apple computers
[linux-2.6] / drivers / video / fbsysfs.c
1 /*
2  * fbsysfs.c - framebuffer device class and attributes
3  *
4  * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
5  * 
6  *      This program is free software you can redistribute it and/or
7  *      modify it under the terms of the GNU General Public License
8  *      as published by the Free Software Foundation; either version
9  *      2 of the License, or (at your option) any later version.
10  */
11
12 /*
13  * Note:  currently there's only stubs for framebuffer_alloc and
14  * framebuffer_release here.  The reson for that is that until all drivers
15  * are converted to use it a sysfsification will open OOPSable races.
16  */
17
18 #include <linux/kernel.h>
19 #include <linux/fb.h>
20 #include <linux/console.h>
21 #include <linux/module.h>
22
23 /**
24  * framebuffer_alloc - creates a new frame buffer info structure
25  *
26  * @size: size of driver private data, can be zero
27  * @dev: pointer to the device for this fb, this can be NULL
28  *
29  * Creates a new frame buffer info structure. Also reserves @size bytes
30  * for driver private data (info->par). info->par (if any) will be
31  * aligned to sizeof(long).
32  *
33  * Returns the new structure, or NULL if an error occured.
34  *
35  */
36 struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
37 {
38 #define BYTES_PER_LONG (BITS_PER_LONG/8)
39 #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
40         int fb_info_size = sizeof(struct fb_info);
41         struct fb_info *info;
42         char *p;
43
44         if (size)
45                 fb_info_size += PADDING;
46
47         p = kzalloc(fb_info_size + size, GFP_KERNEL);
48
49         if (!p)
50                 return NULL;
51
52         info = (struct fb_info *) p;
53
54         if (size)
55                 info->par = p + fb_info_size;
56
57         info->device = dev;
58
59 #ifdef CONFIG_FB_BACKLIGHT
60         mutex_init(&info->bl_mutex);
61 #endif
62
63         return info;
64 #undef PADDING
65 #undef BYTES_PER_LONG
66 }
67 EXPORT_SYMBOL(framebuffer_alloc);
68
69 /**
70  * framebuffer_release - marks the structure available for freeing
71  *
72  * @info: frame buffer info structure
73  *
74  * Drop the reference count of the class_device embedded in the
75  * framebuffer info structure.
76  *
77  */
78 void framebuffer_release(struct fb_info *info)
79 {
80         kfree(info);
81 }
82 EXPORT_SYMBOL(framebuffer_release);
83
84 static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
85 {
86         int err;
87
88         var->activate |= FB_ACTIVATE_FORCE;
89         acquire_console_sem();
90         fb_info->flags |= FBINFO_MISC_USEREVENT;
91         err = fb_set_var(fb_info, var);
92         fb_info->flags &= ~FBINFO_MISC_USEREVENT;
93         release_console_sem();
94         if (err)
95                 return err;
96         return 0;
97 }
98
99 static int mode_string(char *buf, unsigned int offset,
100                        const struct fb_videomode *mode)
101 {
102         char m = 'U';
103         if (mode->flag & FB_MODE_IS_DETAILED)
104                 m = 'D';
105         if (mode->flag & FB_MODE_IS_VESA)
106                 m = 'V';
107         if (mode->flag & FB_MODE_IS_STANDARD)
108                 m = 'S';
109         return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d-%d\n", m, mode->xres, mode->yres, mode->refresh);
110 }
111
112 static ssize_t store_mode(struct class_device *class_device, const char * buf,
113                           size_t count)
114 {
115         struct fb_info *fb_info = class_get_devdata(class_device);
116         char mstr[100];
117         struct fb_var_screeninfo var;
118         struct fb_modelist *modelist;
119         struct fb_videomode *mode;
120         struct list_head *pos;
121         size_t i;
122         int err;
123
124         memset(&var, 0, sizeof(var));
125
126         list_for_each(pos, &fb_info->modelist) {
127                 modelist = list_entry(pos, struct fb_modelist, list);
128                 mode = &modelist->mode;
129                 i = mode_string(mstr, 0, mode);
130                 if (strncmp(mstr, buf, max(count, i)) == 0) {
131
132                         var = fb_info->var;
133                         fb_videomode_to_var(&var, mode);
134                         if ((err = activate(fb_info, &var)))
135                                 return err;
136                         fb_info->mode = mode;
137                         return count;
138                 }
139         }
140         return -EINVAL;
141 }
142
143 static ssize_t show_mode(struct class_device *class_device, char *buf)
144 {
145         struct fb_info *fb_info = class_get_devdata(class_device);
146
147         if (!fb_info->mode)
148                 return 0;
149
150         return mode_string(buf, 0, fb_info->mode);
151 }
152
153 static ssize_t store_modes(struct class_device *class_device, const char * buf,
154                            size_t count)
155 {
156         struct fb_info *fb_info = class_get_devdata(class_device);
157         LIST_HEAD(old_list);
158         int i = count / sizeof(struct fb_videomode);
159
160         if (i * sizeof(struct fb_videomode) != count)
161                 return -EINVAL;
162
163         acquire_console_sem();
164         list_splice(&fb_info->modelist, &old_list);
165         fb_videomode_to_modelist((struct fb_videomode *)buf, i,
166                                  &fb_info->modelist);
167         if (fb_new_modelist(fb_info)) {
168                 fb_destroy_modelist(&fb_info->modelist);
169                 list_splice(&old_list, &fb_info->modelist);
170         } else
171                 fb_destroy_modelist(&old_list);
172
173         release_console_sem();
174
175         return 0;
176 }
177
178 static ssize_t show_modes(struct class_device *class_device, char *buf)
179 {
180         struct fb_info *fb_info = class_get_devdata(class_device);
181         unsigned int i;
182         struct list_head *pos;
183         struct fb_modelist *modelist;
184         const struct fb_videomode *mode;
185
186         i = 0;
187         list_for_each(pos, &fb_info->modelist) {
188                 modelist = list_entry(pos, struct fb_modelist, list);
189                 mode = &modelist->mode;
190                 i += mode_string(buf, i, mode);
191         }
192         return i;
193 }
194
195 static ssize_t store_bpp(struct class_device *class_device, const char * buf,
196                          size_t count)
197 {
198         struct fb_info *fb_info = class_get_devdata(class_device);
199         struct fb_var_screeninfo var;
200         char ** last = NULL;
201         int err;
202
203         var = fb_info->var;
204         var.bits_per_pixel = simple_strtoul(buf, last, 0);
205         if ((err = activate(fb_info, &var)))
206                 return err;
207         return count;
208 }
209
210 static ssize_t show_bpp(struct class_device *class_device, char *buf)
211 {
212         struct fb_info *fb_info = class_get_devdata(class_device);
213         return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
214 }
215
216 static ssize_t store_rotate(struct class_device *class_device, const char *buf,
217                             size_t count)
218 {
219         struct fb_info *fb_info = class_get_devdata(class_device);
220         struct fb_var_screeninfo var;
221         char **last = NULL;
222         int err;
223
224         var = fb_info->var;
225         var.rotate = simple_strtoul(buf, last, 0);
226
227         if ((err = activate(fb_info, &var)))
228                 return err;
229
230         return count;
231 }
232
233
234 static ssize_t show_rotate(struct class_device *class_device, char *buf)
235 {
236         struct fb_info *fb_info = class_get_devdata(class_device);
237
238         return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
239 }
240
241 static ssize_t store_con_rotate(struct class_device *class_device,
242                                 const char *buf, size_t count)
243 {
244         struct fb_info *fb_info = class_get_devdata(class_device);
245         int rotate;
246         char **last = NULL;
247
248         acquire_console_sem();
249         rotate = simple_strtoul(buf, last, 0);
250         fb_con_duit(fb_info, FB_EVENT_SET_CON_ROTATE, &rotate);
251         release_console_sem();
252         return count;
253 }
254
255 static ssize_t store_con_rotate_all(struct class_device *class_device,
256                                 const char *buf, size_t count)
257 {
258         struct fb_info *fb_info = class_get_devdata(class_device);
259         int rotate;
260         char **last = NULL;
261
262         acquire_console_sem();
263         rotate = simple_strtoul(buf, last, 0);
264         fb_con_duit(fb_info, FB_EVENT_SET_CON_ROTATE_ALL, &rotate);
265         release_console_sem();
266         return count;
267 }
268
269 static ssize_t show_con_rotate(struct class_device *class_device, char *buf)
270 {
271         struct fb_info *fb_info = class_get_devdata(class_device);
272         int rotate;
273
274         acquire_console_sem();
275         rotate = fb_con_duit(fb_info, FB_EVENT_GET_CON_ROTATE, NULL);
276         release_console_sem();
277         return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
278 }
279
280 static ssize_t store_virtual(struct class_device *class_device,
281                              const char * buf, size_t count)
282 {
283         struct fb_info *fb_info = class_get_devdata(class_device);
284         struct fb_var_screeninfo var;
285         char *last = NULL;
286         int err;
287
288         var = fb_info->var;
289         var.xres_virtual = simple_strtoul(buf, &last, 0);
290         last++;
291         if (last - buf >= count)
292                 return -EINVAL;
293         var.yres_virtual = simple_strtoul(last, &last, 0);
294
295         if ((err = activate(fb_info, &var)))
296                 return err;
297         return count;
298 }
299
300 static ssize_t show_virtual(struct class_device *class_device, char *buf)
301 {
302         struct fb_info *fb_info = class_get_devdata(class_device);
303         return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual,
304                         fb_info->var.yres_virtual);
305 }
306
307 static ssize_t show_stride(struct class_device *class_device, char *buf)
308 {
309         struct fb_info *fb_info = class_get_devdata(class_device);
310         return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length);
311 }
312
313 static ssize_t store_blank(struct class_device *class_device, const char * buf,
314                            size_t count)
315 {
316         struct fb_info *fb_info = class_get_devdata(class_device);
317         char *last = NULL;
318         int err;
319
320         acquire_console_sem();
321         fb_info->flags |= FBINFO_MISC_USEREVENT;
322         err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
323         fb_info->flags &= ~FBINFO_MISC_USEREVENT;
324         release_console_sem();
325         if (err < 0)
326                 return err;
327         return count;
328 }
329
330 static ssize_t show_blank(struct class_device *class_device, char *buf)
331 {
332 //      struct fb_info *fb_info = class_get_devdata(class_device);
333         return 0;
334 }
335
336 static ssize_t store_console(struct class_device *class_device,
337                              const char * buf, size_t count)
338 {
339 //      struct fb_info *fb_info = class_get_devdata(class_device);
340         return 0;
341 }
342
343 static ssize_t show_console(struct class_device *class_device, char *buf)
344 {
345 //      struct fb_info *fb_info = class_get_devdata(class_device);
346         return 0;
347 }
348
349 static ssize_t store_cursor(struct class_device *class_device,
350                             const char * buf, size_t count)
351 {
352 //      struct fb_info *fb_info = class_get_devdata(class_device);
353         return 0;
354 }
355
356 static ssize_t show_cursor(struct class_device *class_device, char *buf)
357 {
358 //      struct fb_info *fb_info = class_get_devdata(class_device);
359         return 0;
360 }
361
362 static ssize_t store_pan(struct class_device *class_device, const char * buf,
363                          size_t count)
364 {
365         struct fb_info *fb_info = class_get_devdata(class_device);
366         struct fb_var_screeninfo var;
367         char *last = NULL;
368         int err;
369
370         var = fb_info->var;
371         var.xoffset = simple_strtoul(buf, &last, 0);
372         last++;
373         if (last - buf >= count)
374                 return -EINVAL;
375         var.yoffset = simple_strtoul(last, &last, 0);
376
377         acquire_console_sem();
378         err = fb_pan_display(fb_info, &var);
379         release_console_sem();
380
381         if (err < 0)
382                 return err;
383         return count;
384 }
385
386 static ssize_t show_pan(struct class_device *class_device, char *buf)
387 {
388         struct fb_info *fb_info = class_get_devdata(class_device);
389         return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset,
390                         fb_info->var.xoffset);
391 }
392
393 static ssize_t show_name(struct class_device *class_device, char *buf)
394 {
395         struct fb_info *fb_info = class_get_devdata(class_device);
396
397         return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id);
398 }
399
400 static ssize_t store_fbstate(struct class_device *class_device,
401                         const char *buf, size_t count)
402 {
403         struct fb_info *fb_info = class_get_devdata(class_device);
404         u32 state;
405         char *last = NULL;
406
407         state = simple_strtoul(buf, &last, 0);
408
409         acquire_console_sem();
410         fb_set_suspend(fb_info, (int)state);
411         release_console_sem();
412
413         return count;
414 }
415
416 static ssize_t show_fbstate(struct class_device *class_device, char *buf)
417 {
418         struct fb_info *fb_info = class_get_devdata(class_device);
419         return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
420 }
421
422 #ifdef CONFIG_FB_BACKLIGHT
423 static ssize_t store_bl_curve(struct class_device *class_device,
424                 const char *buf, size_t count)
425 {
426         struct fb_info *fb_info = class_get_devdata(class_device);
427         u8 tmp_curve[FB_BACKLIGHT_LEVELS];
428         unsigned int i;
429
430         if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
431                 return -EINVAL;
432
433         for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
434                 if (sscanf(&buf[i * 24],
435                         "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
436                         &tmp_curve[i * 8 + 0],
437                         &tmp_curve[i * 8 + 1],
438                         &tmp_curve[i * 8 + 2],
439                         &tmp_curve[i * 8 + 3],
440                         &tmp_curve[i * 8 + 4],
441                         &tmp_curve[i * 8 + 5],
442                         &tmp_curve[i * 8 + 6],
443                         &tmp_curve[i * 8 + 7]) != 8)
444                         return -EINVAL;
445
446         /* If there has been an error in the input data, we won't
447          * reach this loop.
448          */
449         mutex_lock(&fb_info->bl_mutex);
450         for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
451                 fb_info->bl_curve[i] = tmp_curve[i];
452         mutex_unlock(&fb_info->bl_mutex);
453
454         return count;
455 }
456
457 static ssize_t show_bl_curve(struct class_device *class_device, char *buf)
458 {
459         struct fb_info *fb_info = class_get_devdata(class_device);
460         ssize_t len = 0;
461         unsigned int i;
462
463         mutex_lock(&fb_info->bl_mutex);
464         for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
465                 len += snprintf(&buf[len], PAGE_SIZE,
466                                 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
467                                 fb_info->bl_curve[i + 0],
468                                 fb_info->bl_curve[i + 1],
469                                 fb_info->bl_curve[i + 2],
470                                 fb_info->bl_curve[i + 3],
471                                 fb_info->bl_curve[i + 4],
472                                 fb_info->bl_curve[i + 5],
473                                 fb_info->bl_curve[i + 6],
474                                 fb_info->bl_curve[i + 7]);
475         mutex_unlock(&fb_info->bl_mutex);
476
477         return len;
478 }
479 #endif
480
481 /* When cmap is added back in it should be a binary attribute
482  * not a text one. Consideration should also be given to converting
483  * fbdev to use configfs instead of sysfs */
484 static struct class_device_attribute class_device_attrs[] = {
485         __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
486         __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
487         __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
488         __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
489         __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
490         __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
491         __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
492         __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
493         __ATTR(name, S_IRUGO, show_name, NULL),
494         __ATTR(stride, S_IRUGO, show_stride, NULL),
495         __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
496         __ATTR(con_rotate, S_IRUGO|S_IWUSR, show_con_rotate, store_con_rotate),
497         __ATTR(con_rotate_all, S_IWUSR, NULL, store_con_rotate_all),
498         __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
499 #ifdef CONFIG_FB_BACKLIGHT
500         __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
501 #endif
502 };
503
504 int fb_init_class_device(struct fb_info *fb_info)
505 {
506         unsigned int i;
507         class_set_devdata(fb_info->class_device, fb_info);
508
509         for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
510                 class_device_create_file(fb_info->class_device,
511                                          &class_device_attrs[i]);
512         return 0;
513 }
514
515 void fb_cleanup_class_device(struct fb_info *fb_info)
516 {
517         unsigned int i;
518
519         for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
520                 class_device_remove_file(fb_info->class_device,
521                                          &class_device_attrs[i]);
522 }
523
524 #ifdef CONFIG_FB_BACKLIGHT
525 /* This function generates a linear backlight curve
526  *
527  *     0: off
528  *   1-7: min
529  * 8-127: linear from min to max
530  */
531 void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
532 {
533         unsigned int i, flat, count, range = (max - min);
534
535         fb_info->bl_curve[0] = off;
536
537         for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
538                 fb_info->bl_curve[flat] = min;
539
540         count = FB_BACKLIGHT_LEVELS * 15 / 16;
541         for (i = 0; i < count; ++i)
542                 fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
543 }
544 EXPORT_SYMBOL_GPL(fb_bl_default_curve);
545 #endif