aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/backlight
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/backlight
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/video/backlight')
-rw-r--r--drivers/video/backlight/Kconfig52
-rw-r--r--drivers/video/backlight/Makefile5
-rw-r--r--drivers/video/backlight/backlight.c264
-rw-r--r--drivers/video/backlight/corgi_bl.c198
-rw-r--r--drivers/video/backlight/lcd.c263
5 files changed, 782 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
new file mode 100644
index 000000000000..996d543d6609
--- /dev/null
+++ b/drivers/video/backlight/Kconfig
@@ -0,0 +1,52 @@
1#
2# Backlight & LCD drivers configuration
3#
4
5menuconfig BACKLIGHT_LCD_SUPPORT
6 bool "Backlight & LCD device support"
7 help
8 Enable this to be able to choose the drivers for controlling the
9 backlight and the LCD panel on some platforms, for example on PDAs.
10
11config BACKLIGHT_CLASS_DEVICE
12 tristate "Lowlevel Backlight controls"
13 depends on BACKLIGHT_LCD_SUPPORT
14 default m
15 help
16 This framework adds support for low-level control of the LCD
17 backlight. This includes support for brightness and power.
18
19 To have support for your specific LCD panel you will have to
20 select the proper drivers which depend on this option.
21
22config BACKLIGHT_DEVICE
23 bool
24 depends on BACKLIGHT_CLASS_DEVICE
25 default y
26
27config LCD_CLASS_DEVICE
28 tristate "Lowlevel LCD controls"
29 depends on BACKLIGHT_LCD_SUPPORT
30 default m
31 help
32 This framework adds support for low-level control of LCD.
33 Some framebuffer devices connect to platform-specific LCD modules
34 in order to have a platform-specific way to control the flat panel
35 (contrast and applying power to the LCD (not to the backlight!)).
36
37 To have support for your specific LCD panel you will have to
38 select the proper drivers which depend on this option.
39
40config LCD_DEVICE
41 bool
42 depends on LCD_CLASS_DEVICE
43 default y
44
45config BACKLIGHT_CORGI
46 tristate "Sharp Corgi Backlight Driver (SL-C7xx Series)"
47 depends on BACKLIGHT_DEVICE && PXA_SHARPSL
48 default y
49 help
50 If you have a Sharp Zaurus SL-C7xx, say y to enable the
51 backlight driver.
52
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
new file mode 100644
index 000000000000..9aae884475be
--- /dev/null
+++ b/drivers/video/backlight/Makefile
@@ -0,0 +1,5 @@
1# Backlight & LCD drivers
2
3obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
4obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
5obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
new file mode 100644
index 000000000000..acc81cb01d56
--- /dev/null
+++ b/drivers/video/backlight/backlight.c
@@ -0,0 +1,264 @@
1/*
2 * Backlight Lowlevel Control Abstraction
3 *
4 * Copyright (C) 2003,2004 Hewlett-Packard Company
5 *
6 */
7
8#include <linux/version.h>
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/device.h>
12#include <linux/backlight.h>
13#include <linux/notifier.h>
14#include <linux/ctype.h>
15#include <linux/err.h>
16#include <linux/fb.h>
17#include <asm/bug.h>
18
19static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
20{
21 int rc;
22 struct backlight_device *bd = to_backlight_device(cdev);
23
24 down(&bd->sem);
25 if (likely(bd->props && bd->props->get_power))
26 rc = sprintf(buf, "%d\n", bd->props->get_power(bd));
27 else
28 rc = -ENXIO;
29 up(&bd->sem);
30
31 return rc;
32}
33
34static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count)
35{
36 int rc, power;
37 char *endp;
38 struct backlight_device *bd = to_backlight_device(cdev);
39
40 power = simple_strtoul(buf, &endp, 0);
41 if (*endp && !isspace(*endp))
42 return -EINVAL;
43
44 down(&bd->sem);
45 if (likely(bd->props && bd->props->set_power)) {
46 pr_debug("backlight: set power to %d\n", power);
47 bd->props->set_power(bd, power);
48 rc = count;
49 } else
50 rc = -ENXIO;
51 up(&bd->sem);
52
53 return rc;
54}
55
56static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
57{
58 int rc;
59 struct backlight_device *bd = to_backlight_device(cdev);
60
61 down(&bd->sem);
62 if (likely(bd->props && bd->props->get_brightness))
63 rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd));
64 else
65 rc = -ENXIO;
66 up(&bd->sem);
67
68 return rc;
69}
70
71static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count)
72{
73 int rc, brightness;
74 char *endp;
75 struct backlight_device *bd = to_backlight_device(cdev);
76
77 brightness = simple_strtoul(buf, &endp, 0);
78 if (*endp && !isspace(*endp))
79 return -EINVAL;
80
81 down(&bd->sem);
82 if (likely(bd->props && bd->props->set_brightness)) {
83 pr_debug("backlight: set brightness to %d\n", brightness);
84 bd->props->set_brightness(bd, brightness);
85 rc = count;
86 } else
87 rc = -ENXIO;
88 up(&bd->sem);
89
90 return rc;
91}
92
93static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf)
94{
95 int rc;
96 struct backlight_device *bd = to_backlight_device(cdev);
97
98 down(&bd->sem);
99 if (likely(bd->props))
100 rc = sprintf(buf, "%d\n", bd->props->max_brightness);
101 else
102 rc = -ENXIO;
103 up(&bd->sem);
104
105 return rc;
106}
107
108static void backlight_class_release(struct class_device *dev)
109{
110 struct backlight_device *bd = to_backlight_device(dev);
111 kfree(bd);
112}
113
114static struct class backlight_class = {
115 .name = "backlight",
116 .release = backlight_class_release,
117};
118
119#define DECLARE_ATTR(_name,_mode,_show,_store) \
120{ \
121 .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \
122 .show = _show, \
123 .store = _store, \
124}
125
126static struct class_device_attribute bl_class_device_attributes[] = {
127 DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power),
128 DECLARE_ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness),
129 DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
130};
131
132/* This callback gets called when something important happens inside a
133 * framebuffer driver. We're looking if that important event is blanking,
134 * and if it is, we're switching backlight power as well ...
135 */
136static int fb_notifier_callback(struct notifier_block *self,
137 unsigned long event, void *data)
138{
139 struct backlight_device *bd;
140 struct fb_event *evdata =(struct fb_event *)data;
141
142 /* If we aren't interested in this event, skip it immediately ... */
143 if (event != FB_EVENT_BLANK)
144 return 0;
145
146 bd = container_of(self, struct backlight_device, fb_notif);
147 down(&bd->sem);
148 if (bd->props)
149 if (!bd->props->check_fb || bd->props->check_fb(evdata->info))
150 bd->props->set_power(bd, *(int *)evdata->data);
151 up(&bd->sem);
152 return 0;
153}
154
155/**
156 * backlight_device_register - create and register a new object of
157 * backlight_device class.
158 * @name: the name of the new object(must be the same as the name of the
159 * respective framebuffer device).
160 * @devdata: an optional pointer to be stored in the class_device. The
161 * methods may retrieve it by using class_get_devdata(&bd->class_dev).
162 * @bp: the backlight properties structure.
163 *
164 * Creates and registers new backlight class_device. Returns either an
165 * ERR_PTR() or a pointer to the newly allocated device.
166 */
167struct backlight_device *backlight_device_register(const char *name, void *devdata,
168 struct backlight_properties *bp)
169{
170 int i, rc;
171 struct backlight_device *new_bd;
172
173 pr_debug("backlight_device_alloc: name=%s\n", name);
174
175 new_bd = kmalloc(sizeof(struct backlight_device), GFP_KERNEL);
176 if (unlikely(!new_bd))
177 return ERR_PTR(ENOMEM);
178
179 init_MUTEX(&new_bd->sem);
180 new_bd->props = bp;
181 memset(&new_bd->class_dev, 0, sizeof(new_bd->class_dev));
182 new_bd->class_dev.class = &backlight_class;
183 strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN);
184 class_set_devdata(&new_bd->class_dev, devdata);
185
186 rc = class_device_register(&new_bd->class_dev);
187 if (unlikely(rc)) {
188error: kfree(new_bd);
189 return ERR_PTR(rc);
190 }
191
192 memset(&new_bd->fb_notif, 0, sizeof(new_bd->fb_notif));
193 new_bd->fb_notif.notifier_call = fb_notifier_callback;
194
195 rc = fb_register_client(&new_bd->fb_notif);
196 if (unlikely(rc))
197 goto error;
198
199 for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) {
200 rc = class_device_create_file(&new_bd->class_dev,
201 &bl_class_device_attributes[i]);
202 if (unlikely(rc)) {
203 while (--i >= 0)
204 class_device_remove_file(&new_bd->class_dev,
205 &bl_class_device_attributes[i]);
206 class_device_unregister(&new_bd->class_dev);
207 /* No need to kfree(new_bd) since release() method was called */
208 return ERR_PTR(rc);
209 }
210 }
211
212 return new_bd;
213}
214EXPORT_SYMBOL(backlight_device_register);
215
216/**
217 * backlight_device_unregister - unregisters a backlight device object.
218 * @bd: the backlight device object to be unregistered and freed.
219 *
220 * Unregisters a previously registered via backlight_device_register object.
221 */
222void backlight_device_unregister(struct backlight_device *bd)
223{
224 int i;
225
226 if (!bd)
227 return;
228
229 pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id);
230
231 for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++)
232 class_device_remove_file(&bd->class_dev,
233 &bl_class_device_attributes[i]);
234
235 down(&bd->sem);
236 bd->props = NULL;
237 up(&bd->sem);
238
239 fb_unregister_client(&bd->fb_notif);
240
241 class_device_unregister(&bd->class_dev);
242}
243EXPORT_SYMBOL(backlight_device_unregister);
244
245static void __exit backlight_class_exit(void)
246{
247 class_unregister(&backlight_class);
248}
249
250static int __init backlight_class_init(void)
251{
252 return class_register(&backlight_class);
253}
254
255/*
256 * if this is compiled into the kernel, we need to ensure that the
257 * class is registered before users of the class try to register lcd's
258 */
259postcore_initcall(backlight_class_init);
260module_exit(backlight_class_exit);
261
262MODULE_LICENSE("GPL");
263MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
264MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c
new file mode 100644
index 000000000000..3c026b036c86
--- /dev/null
+++ b/drivers/video/backlight/corgi_bl.c
@@ -0,0 +1,198 @@
1/*
2 * Backlight Driver for Sharp Corgi
3 *
4 * Copyright (c) 2004-2005 Richard Purdie
5 *
6 * Based on Sharp's 2.4 Backlight Driver
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/device.h>
18#include <linux/spinlock.h>
19#include <linux/fb.h>
20#include <linux/backlight.h>
21
22#include <asm/arch-pxa/corgi.h>
23#include <asm/hardware/scoop.h>
24
25#define CORGI_MAX_INTENSITY 0x3e
26#define CORGI_DEFAULT_INTENSITY 0x1f
27#define CORGI_LIMIT_MASK 0x0b
28
29static int corgibl_powermode = FB_BLANK_UNBLANK;
30static int current_intensity = 0;
31static int corgibl_limit = 0;
32static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED;
33
34static void corgibl_send_intensity(int intensity)
35{
36 unsigned long flags;
37 void (*corgi_kick_batt)(void);
38
39 if (corgibl_powermode != FB_BLANK_UNBLANK) {
40 intensity = 0;
41 } else {
42 if (corgibl_limit)
43 intensity &= CORGI_LIMIT_MASK;
44 }
45
46 /* Skip 0x20 as it will blank the display */
47 if (intensity >= 0x20)
48 intensity++;
49
50 spin_lock_irqsave(&bl_lock, flags);
51 /* Bits 0-4 are accessed via the SSP interface */
52 corgi_ssp_blduty_set(intensity & 0x1f);
53 /* Bit 5 is via SCOOP */
54 if (intensity & 0x0020)
55 set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT);
56 else
57 reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT);
58 spin_unlock_irqrestore(&bl_lock, flags);
59}
60
61static void corgibl_blank(int blank)
62{
63 switch(blank) {
64
65 case FB_BLANK_NORMAL:
66 case FB_BLANK_VSYNC_SUSPEND:
67 case FB_BLANK_HSYNC_SUSPEND:
68 case FB_BLANK_POWERDOWN:
69 if (corgibl_powermode == FB_BLANK_UNBLANK) {
70 corgibl_send_intensity(0);
71 corgibl_powermode = blank;
72 }
73 break;
74 case FB_BLANK_UNBLANK:
75 if (corgibl_powermode != FB_BLANK_UNBLANK) {
76 corgibl_powermode = blank;
77 corgibl_send_intensity(current_intensity);
78 }
79 break;
80 }
81}
82
83#ifdef CONFIG_PM
84static int corgibl_suspend(struct device *dev, u32 state, u32 level)
85{
86 if (level == SUSPEND_POWER_DOWN)
87 corgibl_blank(FB_BLANK_POWERDOWN);
88 return 0;
89}
90
91static int corgibl_resume(struct device *dev, u32 level)
92{
93 if (level == RESUME_POWER_ON)
94 corgibl_blank(FB_BLANK_UNBLANK);
95 return 0;
96}
97#else
98#define corgibl_suspend NULL
99#define corgibl_resume NULL
100#endif
101
102
103static int corgibl_set_power(struct backlight_device *bd, int state)
104{
105 corgibl_blank(state);
106 return 0;
107}
108
109static int corgibl_get_power(struct backlight_device *bd)
110{
111 return corgibl_powermode;
112}
113
114static int corgibl_set_intensity(struct backlight_device *bd, int intensity)
115{
116 if (intensity > CORGI_MAX_INTENSITY)
117 intensity = CORGI_MAX_INTENSITY;
118 corgibl_send_intensity(intensity);
119 current_intensity=intensity;
120 return 0;
121}
122
123static int corgibl_get_intensity(struct backlight_device *bd)
124{
125 return current_intensity;
126}
127
128/*
129 * Called when the battery is low to limit the backlight intensity.
130 * If limit==0 clear any limit, otherwise limit the intensity
131 */
132void corgibl_limit_intensity(int limit)
133{
134 corgibl_limit = (limit ? 1 : 0);
135 corgibl_send_intensity(current_intensity);
136}
137EXPORT_SYMBOL(corgibl_limit_intensity);
138
139
140static struct backlight_properties corgibl_data = {
141 .owner = THIS_MODULE,
142 .get_power = corgibl_get_power,
143 .set_power = corgibl_set_power,
144 .max_brightness = CORGI_MAX_INTENSITY,
145 .get_brightness = corgibl_get_intensity,
146 .set_brightness = corgibl_set_intensity,
147};
148
149static struct backlight_device *corgi_backlight_device;
150
151static int __init corgibl_probe(struct device *dev)
152{
153 corgi_backlight_device = backlight_device_register ("corgi-bl",
154 NULL, &corgibl_data);
155 if (IS_ERR (corgi_backlight_device))
156 return PTR_ERR (corgi_backlight_device);
157
158 corgibl_set_intensity(NULL, CORGI_DEFAULT_INTENSITY);
159
160 printk("Corgi Backlight Driver Initialized.\n");
161 return 0;
162}
163
164static int corgibl_remove(struct device *dev)
165{
166 backlight_device_unregister(corgi_backlight_device);
167
168 corgibl_set_intensity(NULL, 0);
169
170 printk("Corgi Backlight Driver Unloaded\n");
171 return 0;
172}
173
174static struct device_driver corgibl_driver = {
175 .name = "corgi-bl",
176 .bus = &platform_bus_type,
177 .probe = corgibl_probe,
178 .remove = corgibl_remove,
179 .suspend = corgibl_suspend,
180 .resume = corgibl_resume,
181};
182
183static int __init corgibl_init(void)
184{
185 return driver_register(&corgibl_driver);
186}
187
188static void __exit corgibl_exit(void)
189{
190 driver_unregister(&corgibl_driver);
191}
192
193module_init(corgibl_init);
194module_exit(corgibl_exit);
195
196MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
197MODULE_DESCRIPTION("Corgi Backlight Driver");
198MODULE_LICENSE("GPLv2");
diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c
new file mode 100644
index 000000000000..470e6f0ee4dd
--- /dev/null
+++ b/drivers/video/backlight/lcd.c
@@ -0,0 +1,263 @@
1/*
2 * LCD Lowlevel Control Abstraction
3 *
4 * Copyright (C) 2003,2004 Hewlett-Packard Company
5 *
6 */
7
8#include <linux/version.h>
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/device.h>
12#include <linux/lcd.h>
13#include <linux/notifier.h>
14#include <linux/ctype.h>
15#include <linux/err.h>
16#include <linux/fb.h>
17#include <asm/bug.h>
18
19static ssize_t lcd_show_power(struct class_device *cdev, char *buf)
20{
21 int rc;
22 struct lcd_device *ld = to_lcd_device(cdev);
23
24 down(&ld->sem);
25 if (likely(ld->props && ld->props->get_power))
26 rc = sprintf(buf, "%d\n", ld->props->get_power(ld));
27 else
28 rc = -ENXIO;
29 up(&ld->sem);
30
31 return rc;
32}
33
34static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_t count)
35{
36 int rc, power;
37 char *endp;
38 struct lcd_device *ld = to_lcd_device(cdev);
39
40 power = simple_strtoul(buf, &endp, 0);
41 if (*endp && !isspace(*endp))
42 return -EINVAL;
43
44 down(&ld->sem);
45 if (likely(ld->props && ld->props->set_power)) {
46 pr_debug("lcd: set power to %d\n", power);
47 ld->props->set_power(ld, power);
48 rc = count;
49 } else
50 rc = -ENXIO;
51 up(&ld->sem);
52
53 return rc;
54}
55
56static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf)
57{
58 int rc;
59 struct lcd_device *ld = to_lcd_device(cdev);
60
61 down(&ld->sem);
62 if (likely(ld->props && ld->props->get_contrast))
63 rc = sprintf(buf, "%d\n", ld->props->get_contrast(ld));
64 else
65 rc = -ENXIO;
66 up(&ld->sem);
67
68 return rc;
69}
70
71static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, size_t count)
72{
73 int rc, contrast;
74 char *endp;
75 struct lcd_device *ld = to_lcd_device(cdev);
76
77 contrast = simple_strtoul(buf, &endp, 0);
78 if (*endp && !isspace(*endp))
79 return -EINVAL;
80
81 down(&ld->sem);
82 if (likely(ld->props && ld->props->set_contrast)) {
83 pr_debug("lcd: set contrast to %d\n", contrast);
84 ld->props->set_contrast(ld, contrast);
85 rc = count;
86 } else
87 rc = -ENXIO;
88 up(&ld->sem);
89
90 return rc;
91}
92
93static ssize_t lcd_show_max_contrast(struct class_device *cdev, char *buf)
94{
95 int rc;
96 struct lcd_device *ld = to_lcd_device(cdev);
97
98 down(&ld->sem);
99 if (likely(ld->props))
100 rc = sprintf(buf, "%d\n", ld->props->max_contrast);
101 else
102 rc = -ENXIO;
103 up(&ld->sem);
104
105 return rc;
106}
107
108static void lcd_class_release(struct class_device *dev)
109{
110 struct lcd_device *ld = to_lcd_device(dev);
111 kfree(ld);
112}
113
114static struct class lcd_class = {
115 .name = "lcd",
116 .release = lcd_class_release,
117};
118
119#define DECLARE_ATTR(_name,_mode,_show,_store) \
120{ \
121 .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \
122 .show = _show, \
123 .store = _store, \
124}
125
126static struct class_device_attribute lcd_class_device_attributes[] = {
127 DECLARE_ATTR(power, 0644, lcd_show_power, lcd_store_power),
128 DECLARE_ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast),
129 DECLARE_ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL),
130};
131
132/* This callback gets called when something important happens inside a
133 * framebuffer driver. We're looking if that important event is blanking,
134 * and if it is, we're switching lcd power as well ...
135 */
136static int fb_notifier_callback(struct notifier_block *self,
137 unsigned long event, void *data)
138{
139 struct lcd_device *ld;
140 struct fb_event *evdata =(struct fb_event *)data;
141
142 /* If we aren't interested in this event, skip it immediately ... */
143 if (event != FB_EVENT_BLANK)
144 return 0;
145
146 ld = container_of(self, struct lcd_device, fb_notif);
147 down(&ld->sem);
148 if (ld->props)
149 if (!ld->props->check_fb || ld->props->check_fb(evdata->info))
150 ld->props->set_power(ld, *(int *)evdata->data);
151 up(&ld->sem);
152 return 0;
153}
154
155/**
156 * lcd_device_register - register a new object of lcd_device class.
157 * @name: the name of the new object(must be the same as the name of the
158 * respective framebuffer device).
159 * @devdata: an optional pointer to be stored in the class_device. The
160 * methods may retrieve it by using class_get_devdata(ld->class_dev).
161 * @lp: the lcd properties structure.
162 *
163 * Creates and registers a new lcd class_device. Returns either an ERR_PTR()
164 * or a pointer to the newly allocated device.
165 */
166struct lcd_device *lcd_device_register(const char *name, void *devdata,
167 struct lcd_properties *lp)
168{
169 int i, rc;
170 struct lcd_device *new_ld;
171
172 pr_debug("lcd_device_register: name=%s\n", name);
173
174 new_ld = kmalloc(sizeof(struct lcd_device), GFP_KERNEL);
175 if (unlikely(!new_ld))
176 return ERR_PTR(ENOMEM);
177
178 init_MUTEX(&new_ld->sem);
179 new_ld->props = lp;
180 memset(&new_ld->class_dev, 0, sizeof(new_ld->class_dev));
181 new_ld->class_dev.class = &lcd_class;
182 strlcpy(new_ld->class_dev.class_id, name, KOBJ_NAME_LEN);
183 class_set_devdata(&new_ld->class_dev, devdata);
184
185 rc = class_device_register(&new_ld->class_dev);
186 if (unlikely(rc)) {
187error: kfree(new_ld);
188 return ERR_PTR(rc);
189 }
190
191 memset(&new_ld->fb_notif, 0, sizeof(new_ld->fb_notif));
192 new_ld->fb_notif.notifier_call = fb_notifier_callback;
193
194 rc = fb_register_client(&new_ld->fb_notif);
195 if (unlikely(rc))
196 goto error;
197
198 for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) {
199 rc = class_device_create_file(&new_ld->class_dev,
200 &lcd_class_device_attributes[i]);
201 if (unlikely(rc)) {
202 while (--i >= 0)
203 class_device_remove_file(&new_ld->class_dev,
204 &lcd_class_device_attributes[i]);
205 class_device_unregister(&new_ld->class_dev);
206 /* No need to kfree(new_ld) since release() method was called */
207 return ERR_PTR(rc);
208 }
209 }
210
211 return new_ld;
212}
213EXPORT_SYMBOL(lcd_device_register);
214
215/**
216 * lcd_device_unregister - unregisters a object of lcd_device class.
217 * @ld: the lcd device object to be unregistered and freed.
218 *
219 * Unregisters a previously registered via lcd_device_register object.
220 */
221void lcd_device_unregister(struct lcd_device *ld)
222{
223 int i;
224
225 if (!ld)
226 return;
227
228 pr_debug("lcd_device_unregister: name=%s\n", ld->class_dev.class_id);
229
230 for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++)
231 class_device_remove_file(&ld->class_dev,
232 &lcd_class_device_attributes[i]);
233
234 down(&ld->sem);
235 ld->props = NULL;
236 up(&ld->sem);
237
238 fb_unregister_client(&ld->fb_notif);
239
240 class_device_unregister(&ld->class_dev);
241}
242EXPORT_SYMBOL(lcd_device_unregister);
243
244static void __exit lcd_class_exit(void)
245{
246 class_unregister(&lcd_class);
247}
248
249static int __init lcd_class_init(void)
250{
251 return class_register(&lcd_class);
252}
253
254/*
255 * if this is compiled into the kernel, we need to ensure that the
256 * class is registered before users of the class try to register lcd's
257 */
258postcore_initcall(lcd_class_init);
259module_exit(lcd_class_exit);
260
261MODULE_LICENSE("GPL");
262MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
263MODULE_DESCRIPTION("LCD Lowlevel Control Abstraction");