diff options
author | Olimpiu Dejeu <olimpiu@arcticsand.com> | 2017-03-21 12:58:48 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2017-04-25 04:22:23 -0400 |
commit | e739c5bb20bebb74028d9262255dc1e470b60fcf (patch) | |
tree | 1714a77f9c0d01d59993b7f04bb7cd6b89e53922 | |
parent | ce9d22573d85341c987f997461ac712e4dbe47b1 (diff) |
backlight: Add support for Arctic Sand LED backlight driver chips
This driver provides support for the Arctic Sand arc2c0608 chip,
and provides a framework to support future devices.
Signed-off-by: Olimpiu Dejeu <olimpiu@arcticsand.com>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r-- | drivers/video/backlight/Kconfig | 7 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/backlight/arcxcnn_bl.c | 419 |
3 files changed, 427 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 5ffa4b4e26c0..4e1d2ad50ba1 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
@@ -460,6 +460,13 @@ config BACKLIGHT_BD6107 | |||
460 | help | 460 | help |
461 | If you have a Rohm BD6107 say Y to enable the backlight driver. | 461 | If you have a Rohm BD6107 say Y to enable the backlight driver. |
462 | 462 | ||
463 | config BACKLIGHT_ARCXCNN | ||
464 | tristate "Backlight driver for the Arctic Sands ARCxCnnnn family" | ||
465 | depends on I2C | ||
466 | help | ||
467 | If you have an ARCxCnnnn family backlight say Y to enable | ||
468 | the backlight driver. | ||
469 | |||
463 | endif # BACKLIGHT_CLASS_DEVICE | 470 | endif # BACKLIGHT_CLASS_DEVICE |
464 | 471 | ||
465 | endif # BACKLIGHT_LCD_SUPPORT | 472 | endif # BACKLIGHT_LCD_SUPPORT |
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 16ec534cff30..8905129691e8 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
@@ -55,3 +55,4 @@ obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o | |||
55 | obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o | 55 | obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o |
56 | obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o | 56 | obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o |
57 | obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o | 57 | obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o |
58 | obj-$(CONFIG_BACKLIGHT_ARCXCNN) += arcxcnn_bl.o | ||
diff --git a/drivers/video/backlight/arcxcnn_bl.c b/drivers/video/backlight/arcxcnn_bl.c new file mode 100644 index 000000000000..dec790de72ff --- /dev/null +++ b/drivers/video/backlight/arcxcnn_bl.c | |||
@@ -0,0 +1,419 @@ | |||
1 | /* | ||
2 | * Backlight driver for ArcticSand ARC_X_C_0N_0N Devices | ||
3 | * | ||
4 | * Copyright 2016 ArcticSand, Inc. | ||
5 | * Author : Brian Dodge <bdodge@arcticsand.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 | ||
9 | * as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <linux/backlight.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/i2c.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/slab.h> | ||
26 | |||
27 | enum arcxcnn_chip_id { | ||
28 | ARC2C0608 | ||
29 | }; | ||
30 | |||
31 | /** | ||
32 | * struct arcxcnn_platform_data | ||
33 | * @name : Backlight driver name (NULL will use default) | ||
34 | * @initial_brightness : initial value of backlight brightness | ||
35 | * @leden : initial LED string enables, upper bit is global on/off | ||
36 | * @led_config_0 : fading speed (period between intensity steps) | ||
37 | * @led_config_1 : misc settings, see datasheet | ||
38 | * @dim_freq : pwm dimming frequency if in pwm mode | ||
39 | * @comp_config : misc config, see datasheet | ||
40 | * @filter_config : RC/PWM filter config, see datasheet | ||
41 | * @trim_config : full scale current trim, see datasheet | ||
42 | */ | ||
43 | struct arcxcnn_platform_data { | ||
44 | const char *name; | ||
45 | u16 initial_brightness; | ||
46 | u8 leden; | ||
47 | u8 led_config_0; | ||
48 | u8 led_config_1; | ||
49 | u8 dim_freq; | ||
50 | u8 comp_config; | ||
51 | u8 filter_config; | ||
52 | u8 trim_config; | ||
53 | }; | ||
54 | |||
55 | #define ARCXCNN_CMD 0x00 /* Command Register */ | ||
56 | #define ARCXCNN_CMD_STDBY 0x80 /* I2C Standby */ | ||
57 | #define ARCXCNN_CMD_RESET 0x40 /* Reset */ | ||
58 | #define ARCXCNN_CMD_BOOST 0x10 /* Boost */ | ||
59 | #define ARCXCNN_CMD_OVP_MASK 0x0C /* --- Over Voltage Threshold */ | ||
60 | #define ARCXCNN_CMD_OVP_XXV 0x0C /* <rsvrd> Over Voltage Threshold */ | ||
61 | #define ARCXCNN_CMD_OVP_20V 0x08 /* 20v Over Voltage Threshold */ | ||
62 | #define ARCXCNN_CMD_OVP_24V 0x04 /* 24v Over Voltage Threshold */ | ||
63 | #define ARCXCNN_CMD_OVP_31V 0x00 /* 31.4v Over Voltage Threshold */ | ||
64 | #define ARCXCNN_CMD_EXT_COMP 0x01 /* part (0) or full (1) ext. comp */ | ||
65 | |||
66 | #define ARCXCNN_CONFIG 0x01 /* Configuration */ | ||
67 | #define ARCXCNN_STATUS1 0x02 /* Status 1 */ | ||
68 | #define ARCXCNN_STATUS2 0x03 /* Status 2 */ | ||
69 | #define ARCXCNN_FADECTRL 0x04 /* Fading Control */ | ||
70 | #define ARCXCNN_ILED_CONFIG 0x05 /* ILED Configuration */ | ||
71 | #define ARCXCNN_ILED_DIM_PWM 0x00 /* config dim mode pwm */ | ||
72 | #define ARCXCNN_ILED_DIM_INT 0x04 /* config dim mode internal */ | ||
73 | #define ARCXCNN_LEDEN 0x06 /* LED Enable Register */ | ||
74 | #define ARCXCNN_LEDEN_ISETEXT 0x80 /* Full-scale current set extern */ | ||
75 | #define ARCXCNN_LEDEN_MASK 0x3F /* LED string enables mask */ | ||
76 | #define ARCXCNN_LEDEN_BITS 0x06 /* Bits of LED string enables */ | ||
77 | #define ARCXCNN_LEDEN_LED1 0x01 | ||
78 | #define ARCXCNN_LEDEN_LED2 0x02 | ||
79 | #define ARCXCNN_LEDEN_LED3 0x04 | ||
80 | #define ARCXCNN_LEDEN_LED4 0x08 | ||
81 | #define ARCXCNN_LEDEN_LED5 0x10 | ||
82 | #define ARCXCNN_LEDEN_LED6 0x20 | ||
83 | |||
84 | #define ARCXCNN_WLED_ISET_LSB 0x07 /* LED ISET LSB (in upper nibble) */ | ||
85 | #define ARCXCNN_WLED_ISET_LSB_SHIFT 0x04 /* ISET LSB Left Shift */ | ||
86 | #define ARCXCNN_WLED_ISET_MSB 0x08 /* LED ISET MSB (8 bits) */ | ||
87 | |||
88 | #define ARCXCNN_DIMFREQ 0x09 | ||
89 | #define ARCXCNN_COMP_CONFIG 0x0A | ||
90 | #define ARCXCNN_FILT_CONFIG 0x0B | ||
91 | #define ARCXCNN_IMAXTUNE 0x0C | ||
92 | #define ARCXCNN_ID_MSB 0x1E | ||
93 | #define ARCXCNN_ID_LSB 0x1F | ||
94 | |||
95 | #define MAX_BRIGHTNESS 4095 | ||
96 | #define INIT_BRIGHT 60 | ||
97 | |||
98 | struct arcxcnn { | ||
99 | struct i2c_client *client; | ||
100 | struct backlight_device *bl; | ||
101 | struct device *dev; | ||
102 | struct arcxcnn_platform_data *pdata; | ||
103 | }; | ||
104 | |||
105 | static int arcxcnn_update_field(struct arcxcnn *lp, u8 reg, u8 mask, u8 data) | ||
106 | { | ||
107 | int ret; | ||
108 | u8 tmp; | ||
109 | |||
110 | ret = i2c_smbus_read_byte_data(lp->client, reg); | ||
111 | if (ret < 0) { | ||
112 | dev_err(lp->dev, "failed to read 0x%.2x\n", reg); | ||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | tmp = (u8)ret; | ||
117 | tmp &= ~mask; | ||
118 | tmp |= data & mask; | ||
119 | |||
120 | return i2c_smbus_write_byte_data(lp->client, reg, tmp); | ||
121 | } | ||
122 | |||
123 | static int arcxcnn_set_brightness(struct arcxcnn *lp, u32 brightness) | ||
124 | { | ||
125 | int ret; | ||
126 | u8 val; | ||
127 | |||
128 | /* lower nibble of brightness goes in upper nibble of LSB register */ | ||
129 | val = (brightness & 0xF) << ARCXCNN_WLED_ISET_LSB_SHIFT; | ||
130 | ret = i2c_smbus_write_byte_data(lp->client, | ||
131 | ARCXCNN_WLED_ISET_LSB, val); | ||
132 | if (ret < 0) | ||
133 | return ret; | ||
134 | |||
135 | /* remaining 8 bits of brightness go in MSB register */ | ||
136 | val = (brightness >> 4); | ||
137 | return i2c_smbus_write_byte_data(lp->client, | ||
138 | ARCXCNN_WLED_ISET_MSB, val); | ||
139 | } | ||
140 | |||
141 | static int arcxcnn_bl_update_status(struct backlight_device *bl) | ||
142 | { | ||
143 | struct arcxcnn *lp = bl_get_data(bl); | ||
144 | u32 brightness = bl->props.brightness; | ||
145 | int ret; | ||
146 | |||
147 | if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) | ||
148 | brightness = 0; | ||
149 | |||
150 | ret = arcxcnn_set_brightness(lp, brightness); | ||
151 | if (ret) | ||
152 | return ret; | ||
153 | |||
154 | /* set power-on/off/save modes */ | ||
155 | return arcxcnn_update_field(lp, ARCXCNN_CMD, ARCXCNN_CMD_STDBY, | ||
156 | (bl->props.power == 0) ? 0 : ARCXCNN_CMD_STDBY); | ||
157 | } | ||
158 | |||
159 | static const struct backlight_ops arcxcnn_bl_ops = { | ||
160 | .options = BL_CORE_SUSPENDRESUME, | ||
161 | .update_status = arcxcnn_bl_update_status, | ||
162 | }; | ||
163 | |||
164 | static int arcxcnn_backlight_register(struct arcxcnn *lp) | ||
165 | { | ||
166 | struct backlight_properties *props; | ||
167 | const char *name = lp->pdata->name ? : "arctic_bl"; | ||
168 | |||
169 | props = devm_kzalloc(lp->dev, sizeof(*props), GFP_KERNEL); | ||
170 | if (!props) | ||
171 | return -ENOMEM; | ||
172 | |||
173 | props->type = BACKLIGHT_PLATFORM; | ||
174 | props->max_brightness = MAX_BRIGHTNESS; | ||
175 | |||
176 | if (lp->pdata->initial_brightness > props->max_brightness) | ||
177 | lp->pdata->initial_brightness = props->max_brightness; | ||
178 | |||
179 | props->brightness = lp->pdata->initial_brightness; | ||
180 | |||
181 | lp->bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp, | ||
182 | &arcxcnn_bl_ops, props); | ||
183 | return PTR_ERR_OR_ZERO(lp->bl); | ||
184 | } | ||
185 | |||
186 | static void arcxcnn_parse_dt(struct arcxcnn *lp) | ||
187 | { | ||
188 | struct device *dev = lp->dev; | ||
189 | struct device_node *node = dev->of_node; | ||
190 | u32 prog_val, num_entry, entry, sources[ARCXCNN_LEDEN_BITS]; | ||
191 | int ret; | ||
192 | |||
193 | /* device tree entry isn't required, defaults are OK */ | ||
194 | if (!node) | ||
195 | return; | ||
196 | |||
197 | ret = of_property_read_string(node, "label", &lp->pdata->name); | ||
198 | if (ret < 0) | ||
199 | lp->pdata->name = NULL; | ||
200 | |||
201 | ret = of_property_read_u32(node, "default-brightness", &prog_val); | ||
202 | if (ret == 0) | ||
203 | lp->pdata->initial_brightness = prog_val; | ||
204 | |||
205 | ret = of_property_read_u32(node, "arc,led-config-0", &prog_val); | ||
206 | if (ret == 0) | ||
207 | lp->pdata->led_config_0 = (u8)prog_val; | ||
208 | |||
209 | ret = of_property_read_u32(node, "arc,led-config-1", &prog_val); | ||
210 | if (ret == 0) | ||
211 | lp->pdata->led_config_1 = (u8)prog_val; | ||
212 | |||
213 | ret = of_property_read_u32(node, "arc,dim-freq", &prog_val); | ||
214 | if (ret == 0) | ||
215 | lp->pdata->dim_freq = (u8)prog_val; | ||
216 | |||
217 | ret = of_property_read_u32(node, "arc,comp-config", &prog_val); | ||
218 | if (ret == 0) | ||
219 | lp->pdata->comp_config = (u8)prog_val; | ||
220 | |||
221 | ret = of_property_read_u32(node, "arc,filter-config", &prog_val); | ||
222 | if (ret == 0) | ||
223 | lp->pdata->filter_config = (u8)prog_val; | ||
224 | |||
225 | ret = of_property_read_u32(node, "arc,trim-config", &prog_val); | ||
226 | if (ret == 0) | ||
227 | lp->pdata->trim_config = (u8)prog_val; | ||
228 | |||
229 | ret = of_property_count_u32_elems(node, "led-sources"); | ||
230 | if (ret < 0) { | ||
231 | lp->pdata->leden = ARCXCNN_LEDEN_MASK; /* all on is default */ | ||
232 | } else { | ||
233 | num_entry = ret; | ||
234 | if (num_entry > ARCXCNN_LEDEN_BITS) | ||
235 | num_entry = ARCXCNN_LEDEN_BITS; | ||
236 | |||
237 | ret = of_property_read_u32_array(node, "led-sources", sources, | ||
238 | num_entry); | ||
239 | if (ret < 0) { | ||
240 | dev_err(dev, "led-sources node is invalid.\n"); | ||
241 | return; | ||
242 | } | ||
243 | |||
244 | lp->pdata->leden = 0; | ||
245 | |||
246 | /* for each enable in source, set bit in led enable */ | ||
247 | for (entry = 0; entry < num_entry; entry++) { | ||
248 | u8 onbit = 1 << sources[entry]; | ||
249 | |||
250 | lp->pdata->leden |= onbit; | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | static int arcxcnn_probe(struct i2c_client *cl, const struct i2c_device_id *id) | ||
256 | { | ||
257 | struct arcxcnn *lp; | ||
258 | int ret; | ||
259 | |||
260 | if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
261 | return -EIO; | ||
262 | |||
263 | lp = devm_kzalloc(&cl->dev, sizeof(*lp), GFP_KERNEL); | ||
264 | if (!lp) | ||
265 | return -ENOMEM; | ||
266 | |||
267 | lp->client = cl; | ||
268 | lp->dev = &cl->dev; | ||
269 | lp->pdata = dev_get_platdata(&cl->dev); | ||
270 | |||
271 | /* reset the device */ | ||
272 | ret = i2c_smbus_write_byte_data(lp->client, | ||
273 | ARCXCNN_CMD, ARCXCNN_CMD_RESET); | ||
274 | if (ret) | ||
275 | goto probe_err; | ||
276 | |||
277 | if (!lp->pdata) { | ||
278 | lp->pdata = devm_kzalloc(lp->dev, | ||
279 | sizeof(*lp->pdata), GFP_KERNEL); | ||
280 | if (!lp->pdata) | ||
281 | return -ENOMEM; | ||
282 | |||
283 | /* Setup defaults based on power-on defaults */ | ||
284 | lp->pdata->name = NULL; | ||
285 | lp->pdata->initial_brightness = INIT_BRIGHT; | ||
286 | lp->pdata->leden = ARCXCNN_LEDEN_MASK; | ||
287 | |||
288 | lp->pdata->led_config_0 = i2c_smbus_read_byte_data( | ||
289 | lp->client, ARCXCNN_FADECTRL); | ||
290 | |||
291 | lp->pdata->led_config_1 = i2c_smbus_read_byte_data( | ||
292 | lp->client, ARCXCNN_ILED_CONFIG); | ||
293 | /* insure dim mode is not default pwm */ | ||
294 | lp->pdata->led_config_1 |= ARCXCNN_ILED_DIM_INT; | ||
295 | |||
296 | lp->pdata->dim_freq = i2c_smbus_read_byte_data( | ||
297 | lp->client, ARCXCNN_DIMFREQ); | ||
298 | |||
299 | lp->pdata->comp_config = i2c_smbus_read_byte_data( | ||
300 | lp->client, ARCXCNN_COMP_CONFIG); | ||
301 | |||
302 | lp->pdata->filter_config = i2c_smbus_read_byte_data( | ||
303 | lp->client, ARCXCNN_FILT_CONFIG); | ||
304 | |||
305 | lp->pdata->trim_config = i2c_smbus_read_byte_data( | ||
306 | lp->client, ARCXCNN_IMAXTUNE); | ||
307 | |||
308 | if (IS_ENABLED(CONFIG_OF)) | ||
309 | arcxcnn_parse_dt(lp); | ||
310 | } | ||
311 | |||
312 | i2c_set_clientdata(cl, lp); | ||
313 | |||
314 | /* constrain settings to what is possible */ | ||
315 | if (lp->pdata->initial_brightness > MAX_BRIGHTNESS) | ||
316 | lp->pdata->initial_brightness = MAX_BRIGHTNESS; | ||
317 | |||
318 | /* set initial brightness */ | ||
319 | ret = arcxcnn_set_brightness(lp, lp->pdata->initial_brightness); | ||
320 | if (ret) | ||
321 | goto probe_err; | ||
322 | |||
323 | /* set other register values directly */ | ||
324 | ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FADECTRL, | ||
325 | lp->pdata->led_config_0); | ||
326 | if (ret) | ||
327 | goto probe_err; | ||
328 | |||
329 | ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_ILED_CONFIG, | ||
330 | lp->pdata->led_config_1); | ||
331 | if (ret) | ||
332 | goto probe_err; | ||
333 | |||
334 | ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_DIMFREQ, | ||
335 | lp->pdata->dim_freq); | ||
336 | if (ret) | ||
337 | goto probe_err; | ||
338 | |||
339 | ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_COMP_CONFIG, | ||
340 | lp->pdata->comp_config); | ||
341 | if (ret) | ||
342 | goto probe_err; | ||
343 | |||
344 | ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FILT_CONFIG, | ||
345 | lp->pdata->filter_config); | ||
346 | if (ret) | ||
347 | goto probe_err; | ||
348 | |||
349 | ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_IMAXTUNE, | ||
350 | lp->pdata->trim_config); | ||
351 | if (ret) | ||
352 | goto probe_err; | ||
353 | |||
354 | /* set initial LED Enables */ | ||
355 | arcxcnn_update_field(lp, ARCXCNN_LEDEN, | ||
356 | ARCXCNN_LEDEN_MASK, lp->pdata->leden); | ||
357 | |||
358 | ret = arcxcnn_backlight_register(lp); | ||
359 | if (ret) | ||
360 | goto probe_register_err; | ||
361 | |||
362 | backlight_update_status(lp->bl); | ||
363 | |||
364 | return 0; | ||
365 | |||
366 | probe_register_err: | ||
367 | dev_err(lp->dev, | ||
368 | "failed to register backlight.\n"); | ||
369 | |||
370 | probe_err: | ||
371 | dev_err(lp->dev, | ||
372 | "failure ret: %d\n", ret); | ||
373 | return ret; | ||
374 | } | ||
375 | |||
376 | static int arcxcnn_remove(struct i2c_client *cl) | ||
377 | { | ||
378 | struct arcxcnn *lp = i2c_get_clientdata(cl); | ||
379 | |||
380 | /* disable all strings (ignore errors) */ | ||
381 | i2c_smbus_write_byte_data(lp->client, | ||
382 | ARCXCNN_LEDEN, 0x00); | ||
383 | /* reset the device (ignore errors) */ | ||
384 | i2c_smbus_write_byte_data(lp->client, | ||
385 | ARCXCNN_CMD, ARCXCNN_CMD_RESET); | ||
386 | |||
387 | lp->bl->props.brightness = 0; | ||
388 | |||
389 | backlight_update_status(lp->bl); | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static const struct of_device_id arcxcnn_dt_ids[] = { | ||
395 | { .compatible = "arc,arc2c0608" }, | ||
396 | { } | ||
397 | }; | ||
398 | MODULE_DEVICE_TABLE(of, arcxcnn_dt_ids); | ||
399 | |||
400 | static const struct i2c_device_id arcxcnn_ids[] = { | ||
401 | {"arc2c0608", ARC2C0608}, | ||
402 | { } | ||
403 | }; | ||
404 | MODULE_DEVICE_TABLE(i2c, arcxcnn_ids); | ||
405 | |||
406 | static struct i2c_driver arcxcnn_driver = { | ||
407 | .driver = { | ||
408 | .name = "arcxcnn_bl", | ||
409 | .of_match_table = of_match_ptr(arcxcnn_dt_ids), | ||
410 | }, | ||
411 | .probe = arcxcnn_probe, | ||
412 | .remove = arcxcnn_remove, | ||
413 | .id_table = arcxcnn_ids, | ||
414 | }; | ||
415 | module_i2c_driver(arcxcnn_driver); | ||
416 | |||
417 | MODULE_LICENSE("GPL v2"); | ||
418 | MODULE_AUTHOR("Brian Dodge <bdodge@arcticsand.com>"); | ||
419 | MODULE_DESCRIPTION("ARCXCNN Backlight driver"); | ||