Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b98d13c7 SJ |
2 | /* |
3 | * Simple driver for Texas Instruments LM355x LED Flash driver chip | |
4 | * Copyright (C) 2012 Texas Instruments | |
b98d13c7 SJ |
5 | */ |
6 | ||
7 | #include <linux/module.h> | |
8 | #include <linux/delay.h> | |
9 | #include <linux/i2c.h> | |
b98d13c7 SJ |
10 | #include <linux/leds.h> |
11 | #include <linux/slab.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/fs.h> | |
14 | #include <linux/regmap.h> | |
b98d13c7 SJ |
15 | #include <linux/platform_data/leds-lm355x.h> |
16 | ||
17 | enum lm355x_type { | |
18 | CHIP_LM3554 = 0, | |
19 | CHIP_LM3556, | |
20 | }; | |
21 | ||
22 | enum lm355x_regs { | |
23 | REG_FLAG = 0, | |
24 | REG_TORCH_CFG, | |
25 | REG_TORCH_CTRL, | |
26 | REG_STROBE_CFG, | |
27 | REG_FLASH_CTRL, | |
28 | REG_INDI_CFG, | |
29 | REG_INDI_CTRL, | |
30 | REG_OPMODE, | |
31 | REG_MAX, | |
32 | }; | |
33 | ||
34 | /* operation mode */ | |
35 | enum lm355x_mode { | |
36 | MODE_SHDN = 0, | |
37 | MODE_INDIC, | |
38 | MODE_TORCH, | |
39 | MODE_FLASH | |
40 | }; | |
41 | ||
42 | /* register map info. */ | |
43 | struct lm355x_reg_data { | |
44 | u8 regno; | |
45 | u8 mask; | |
46 | u8 shift; | |
47 | }; | |
48 | ||
49 | struct lm355x_chip_data { | |
50 | struct device *dev; | |
51 | enum lm355x_type type; | |
52 | ||
53 | struct led_classdev cdev_flash; | |
54 | struct led_classdev cdev_torch; | |
55 | struct led_classdev cdev_indicator; | |
56 | ||
b98d13c7 SJ |
57 | struct lm355x_platform_data *pdata; |
58 | struct regmap *regmap; | |
59 | struct mutex lock; | |
60 | ||
61 | unsigned int last_flag; | |
62 | struct lm355x_reg_data *regs; | |
63 | }; | |
64 | ||
65 | /* specific indicator function for lm3556 */ | |
66 | enum lm3556_indic_pulse_time { | |
67 | PULSE_TIME_0_MS = 0, | |
68 | PULSE_TIME_32_MS, | |
69 | PULSE_TIME_64_MS, | |
70 | PULSE_TIME_92_MS, | |
71 | PULSE_TIME_128_MS, | |
72 | PULSE_TIME_160_MS, | |
73 | PULSE_TIME_196_MS, | |
74 | PULSE_TIME_224_MS, | |
75 | PULSE_TIME_256_MS, | |
76 | PULSE_TIME_288_MS, | |
77 | PULSE_TIME_320_MS, | |
78 | PULSE_TIME_352_MS, | |
79 | PULSE_TIME_384_MS, | |
80 | PULSE_TIME_416_MS, | |
81 | PULSE_TIME_448_MS, | |
82 | PULSE_TIME_480_MS, | |
83 | }; | |
84 | ||
85 | enum lm3556_indic_n_blank { | |
86 | INDIC_N_BLANK_0 = 0, | |
87 | INDIC_N_BLANK_1, | |
88 | INDIC_N_BLANK_2, | |
89 | INDIC_N_BLANK_3, | |
90 | INDIC_N_BLANK_4, | |
91 | INDIC_N_BLANK_5, | |
92 | INDIC_N_BLANK_6, | |
93 | INDIC_N_BLANK_7, | |
94 | INDIC_N_BLANK_8, | |
95 | INDIC_N_BLANK_9, | |
96 | INDIC_N_BLANK_10, | |
97 | INDIC_N_BLANK_11, | |
98 | INDIC_N_BLANK_12, | |
99 | INDIC_N_BLANK_13, | |
100 | INDIC_N_BLANK_14, | |
101 | INDIC_N_BLANK_15, | |
102 | }; | |
103 | ||
104 | enum lm3556_indic_period { | |
105 | INDIC_PERIOD_0 = 0, | |
106 | INDIC_PERIOD_1, | |
107 | INDIC_PERIOD_2, | |
108 | INDIC_PERIOD_3, | |
109 | INDIC_PERIOD_4, | |
110 | INDIC_PERIOD_5, | |
111 | INDIC_PERIOD_6, | |
112 | INDIC_PERIOD_7, | |
113 | }; | |
114 | ||
115 | #define INDIC_PATTERN_SIZE 4 | |
116 | ||
117 | struct indicator { | |
118 | u8 blinking; | |
119 | u8 period_cnt; | |
120 | }; | |
121 | ||
122 | /* indicator pattern data only for lm3556 */ | |
123 | static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { | |
124 | [0] = {(INDIC_N_BLANK_1 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_1}, | |
125 | [1] = {(INDIC_N_BLANK_15 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_2}, | |
126 | [2] = {(INDIC_N_BLANK_10 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_4}, | |
127 | [3] = {(INDIC_N_BLANK_5 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_7}, | |
128 | }; | |
129 | ||
130 | static struct lm355x_reg_data lm3554_regs[REG_MAX] = { | |
131 | [REG_FLAG] = {0xD0, 0xBF, 0}, | |
132 | [REG_TORCH_CFG] = {0xE0, 0x80, 7}, | |
133 | [REG_TORCH_CTRL] = {0xA0, 0x38, 3}, | |
134 | [REG_STROBE_CFG] = {0xE0, 0x04, 2}, | |
135 | [REG_FLASH_CTRL] = {0xB0, 0x78, 3}, | |
136 | [REG_INDI_CFG] = {0xE0, 0x08, 3}, | |
137 | [REG_INDI_CTRL] = {0xA0, 0xC0, 6}, | |
138 | [REG_OPMODE] = {0xA0, 0x03, 0}, | |
139 | }; | |
140 | ||
141 | static struct lm355x_reg_data lm3556_regs[REG_MAX] = { | |
142 | [REG_FLAG] = {0x0B, 0xFF, 0}, | |
143 | [REG_TORCH_CFG] = {0x0A, 0x10, 4}, | |
144 | [REG_TORCH_CTRL] = {0x09, 0x70, 4}, | |
145 | [REG_STROBE_CFG] = {0x0A, 0x20, 5}, | |
146 | [REG_FLASH_CTRL] = {0x09, 0x0F, 0}, | |
147 | [REG_INDI_CFG] = {0xFF, 0xFF, 0}, | |
148 | [REG_INDI_CTRL] = {0x09, 0x70, 4}, | |
149 | [REG_OPMODE] = {0x0A, 0x03, 0}, | |
150 | }; | |
151 | ||
152 | static char lm355x_name[][I2C_NAME_SIZE] = { | |
153 | [CHIP_LM3554] = LM3554_NAME, | |
154 | [CHIP_LM3556] = LM3556_NAME, | |
155 | }; | |
156 | ||
157 | /* chip initialize */ | |
98ea1ea2 | 158 | static int lm355x_chip_init(struct lm355x_chip_data *chip) |
b98d13c7 SJ |
159 | { |
160 | int ret; | |
161 | unsigned int reg_val; | |
162 | struct lm355x_platform_data *pdata = chip->pdata; | |
163 | ||
164 | /* input and output pins configuration */ | |
165 | switch (chip->type) { | |
166 | case CHIP_LM3554: | |
985b1f59 | 167 | reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin; |
b98d13c7 SJ |
168 | ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val); |
169 | if (ret < 0) | |
170 | goto out; | |
985b1f59 | 171 | reg_val = (u32)pdata->pass_mode; |
b98d13c7 SJ |
172 | ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val); |
173 | if (ret < 0) | |
174 | goto out; | |
175 | break; | |
176 | ||
177 | case CHIP_LM3556: | |
985b1f59 AB |
178 | reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin | |
179 | (u32)pdata->pass_mode; | |
b98d13c7 SJ |
180 | ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val); |
181 | if (ret < 0) | |
182 | goto out; | |
183 | break; | |
184 | default: | |
185 | return -ENODATA; | |
186 | } | |
187 | ||
188 | return ret; | |
189 | out: | |
190 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | |
191 | return ret; | |
192 | } | |
193 | ||
194 | /* chip control */ | |
df8ff5d2 | 195 | static int lm355x_control(struct lm355x_chip_data *chip, |
b98d13c7 SJ |
196 | u8 brightness, enum lm355x_mode opmode) |
197 | { | |
198 | int ret; | |
199 | unsigned int reg_val; | |
200 | struct lm355x_platform_data *pdata = chip->pdata; | |
201 | struct lm355x_reg_data *preg = chip->regs; | |
202 | ||
203 | ret = regmap_read(chip->regmap, preg[REG_FLAG].regno, &chip->last_flag); | |
204 | if (ret < 0) | |
205 | goto out; | |
206 | if (chip->last_flag & preg[REG_FLAG].mask) | |
207 | dev_info(chip->dev, "%s Last FLAG is 0x%x\n", | |
208 | lm355x_name[chip->type], | |
209 | chip->last_flag & preg[REG_FLAG].mask); | |
210 | /* brightness 0 means shutdown */ | |
211 | if (!brightness) | |
212 | opmode = MODE_SHDN; | |
213 | ||
214 | switch (opmode) { | |
215 | case MODE_TORCH: | |
216 | ret = | |
217 | regmap_update_bits(chip->regmap, preg[REG_TORCH_CTRL].regno, | |
218 | preg[REG_TORCH_CTRL].mask, | |
219 | (brightness - 1) | |
220 | << preg[REG_TORCH_CTRL].shift); | |
221 | if (ret < 0) | |
222 | goto out; | |
223 | ||
224 | if (pdata->pin_tx1 != LM355x_PIN_TORCH_DISABLE) { | |
225 | ret = | |
226 | regmap_update_bits(chip->regmap, | |
227 | preg[REG_TORCH_CFG].regno, | |
228 | preg[REG_TORCH_CFG].mask, | |
229 | 0x01 << | |
230 | preg[REG_TORCH_CFG].shift); | |
231 | if (ret < 0) | |
232 | goto out; | |
233 | opmode = MODE_SHDN; | |
234 | dev_info(chip->dev, | |
235 | "torch brt is set - ext. torch pin mode\n"); | |
236 | } | |
237 | break; | |
238 | ||
239 | case MODE_FLASH: | |
240 | ||
241 | ret = | |
242 | regmap_update_bits(chip->regmap, preg[REG_FLASH_CTRL].regno, | |
243 | preg[REG_FLASH_CTRL].mask, | |
244 | (brightness - 1) | |
245 | << preg[REG_FLASH_CTRL].shift); | |
246 | if (ret < 0) | |
247 | goto out; | |
248 | ||
249 | if (pdata->pin_strobe != LM355x_PIN_STROBE_DISABLE) { | |
250 | if (chip->type == CHIP_LM3554) | |
251 | reg_val = 0x00; | |
252 | else | |
253 | reg_val = 0x01; | |
254 | ret = | |
255 | regmap_update_bits(chip->regmap, | |
256 | preg[REG_STROBE_CFG].regno, | |
257 | preg[REG_STROBE_CFG].mask, | |
258 | reg_val << | |
259 | preg[REG_STROBE_CFG].shift); | |
260 | if (ret < 0) | |
261 | goto out; | |
262 | opmode = MODE_SHDN; | |
263 | dev_info(chip->dev, | |
264 | "flash brt is set - ext. strobe pin mode\n"); | |
265 | } | |
266 | break; | |
267 | ||
268 | case MODE_INDIC: | |
269 | ret = | |
270 | regmap_update_bits(chip->regmap, preg[REG_INDI_CTRL].regno, | |
271 | preg[REG_INDI_CTRL].mask, | |
272 | (brightness - 1) | |
273 | << preg[REG_INDI_CTRL].shift); | |
274 | if (ret < 0) | |
275 | goto out; | |
276 | ||
277 | if (pdata->pin_tx2 != LM355x_PIN_TX_DISABLE) { | |
278 | ret = | |
279 | regmap_update_bits(chip->regmap, | |
280 | preg[REG_INDI_CFG].regno, | |
281 | preg[REG_INDI_CFG].mask, | |
282 | 0x01 << | |
283 | preg[REG_INDI_CFG].shift); | |
284 | if (ret < 0) | |
285 | goto out; | |
286 | opmode = MODE_SHDN; | |
287 | } | |
288 | break; | |
289 | case MODE_SHDN: | |
290 | break; | |
291 | default: | |
df8ff5d2 | 292 | return -EINVAL; |
b98d13c7 SJ |
293 | } |
294 | /* operation mode control */ | |
295 | ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno, | |
296 | preg[REG_OPMODE].mask, | |
297 | opmode << preg[REG_OPMODE].shift); | |
298 | if (ret < 0) | |
299 | goto out; | |
df8ff5d2 | 300 | return ret; |
b98d13c7 SJ |
301 | out: |
302 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | |
df8ff5d2 | 303 | return ret; |
b98d13c7 SJ |
304 | } |
305 | ||
306 | /* torch */ | |
b98d13c7 | 307 | |
df8ff5d2 | 308 | static int lm355x_torch_brightness_set(struct led_classdev *cdev, |
b98d13c7 SJ |
309 | enum led_brightness brightness) |
310 | { | |
311 | struct lm355x_chip_data *chip = | |
312 | container_of(cdev, struct lm355x_chip_data, cdev_torch); | |
df8ff5d2 | 313 | int ret; |
b98d13c7 SJ |
314 | |
315 | mutex_lock(&chip->lock); | |
df8ff5d2 | 316 | ret = lm355x_control(chip, brightness, MODE_TORCH); |
b98d13c7 | 317 | mutex_unlock(&chip->lock); |
df8ff5d2 | 318 | return ret; |
b98d13c7 SJ |
319 | } |
320 | ||
df8ff5d2 JA |
321 | /* flash */ |
322 | ||
323 | static int lm355x_strobe_brightness_set(struct led_classdev *cdev, | |
b98d13c7 SJ |
324 | enum led_brightness brightness) |
325 | { | |
326 | struct lm355x_chip_data *chip = | |
327 | container_of(cdev, struct lm355x_chip_data, cdev_flash); | |
df8ff5d2 | 328 | int ret; |
b98d13c7 SJ |
329 | |
330 | mutex_lock(&chip->lock); | |
df8ff5d2 | 331 | ret = lm355x_control(chip, brightness, MODE_FLASH); |
b98d13c7 | 332 | mutex_unlock(&chip->lock); |
df8ff5d2 | 333 | return ret; |
b98d13c7 SJ |
334 | } |
335 | ||
df8ff5d2 JA |
336 | /* indicator */ |
337 | ||
338 | static int lm355x_indicator_brightness_set(struct led_classdev *cdev, | |
b98d13c7 SJ |
339 | enum led_brightness brightness) |
340 | { | |
341 | struct lm355x_chip_data *chip = | |
342 | container_of(cdev, struct lm355x_chip_data, cdev_indicator); | |
df8ff5d2 | 343 | int ret; |
b98d13c7 | 344 | |
df8ff5d2 JA |
345 | mutex_lock(&chip->lock); |
346 | ret = lm355x_control(chip, brightness, MODE_INDIC); | |
347 | mutex_unlock(&chip->lock); | |
348 | return ret; | |
b98d13c7 SJ |
349 | } |
350 | ||
351 | /* indicator pattern only for lm3556*/ | |
5ccfa39d DR |
352 | static ssize_t pattern_store(struct device *dev, |
353 | struct device_attribute *attr, | |
354 | const char *buf, size_t size) | |
b98d13c7 SJ |
355 | { |
356 | ssize_t ret; | |
357 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | |
358 | struct lm355x_chip_data *chip = | |
359 | container_of(led_cdev, struct lm355x_chip_data, cdev_indicator); | |
360 | unsigned int state; | |
361 | ||
362 | ret = kstrtouint(buf, 10, &state); | |
363 | if (ret) | |
364 | goto out; | |
365 | if (state > INDIC_PATTERN_SIZE - 1) | |
366 | state = INDIC_PATTERN_SIZE - 1; | |
367 | ||
368 | ret = regmap_write(chip->regmap, 0x04, | |
369 | indicator_pattern[state].blinking); | |
370 | if (ret < 0) | |
371 | goto out; | |
372 | ||
373 | ret = regmap_write(chip->regmap, 0x05, | |
374 | indicator_pattern[state].period_cnt); | |
375 | if (ret < 0) | |
376 | goto out; | |
377 | ||
378 | return size; | |
379 | out: | |
380 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | |
14ce82e5 | 381 | return ret; |
b98d13c7 SJ |
382 | } |
383 | ||
5ccfa39d | 384 | static DEVICE_ATTR_WO(pattern); |
b98d13c7 | 385 | |
0d25376c JH |
386 | static struct attribute *lm355x_indicator_attrs[] = { |
387 | &dev_attr_pattern.attr, | |
388 | NULL | |
389 | }; | |
390 | ATTRIBUTE_GROUPS(lm355x_indicator); | |
391 | ||
b98d13c7 SJ |
392 | static const struct regmap_config lm355x_regmap = { |
393 | .reg_bits = 8, | |
394 | .val_bits = 8, | |
395 | .max_register = 0xFF, | |
396 | }; | |
397 | ||
398 | /* module initialize */ | |
0712cb93 | 399 | static int lm355x_probe(struct i2c_client *client) |
b98d13c7 | 400 | { |
0712cb93 | 401 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
87aae1ea | 402 | struct lm355x_platform_data *pdata = dev_get_platdata(&client->dev); |
b98d13c7 SJ |
403 | struct lm355x_chip_data *chip; |
404 | ||
405 | int err; | |
406 | ||
407 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | |
408 | dev_err(&client->dev, "i2c functionality check fail.\n"); | |
409 | return -EOPNOTSUPP; | |
410 | } | |
411 | ||
412 | if (pdata == NULL) { | |
413 | dev_err(&client->dev, "needs Platform Data.\n"); | |
414 | return -ENODATA; | |
415 | } | |
416 | ||
417 | chip = devm_kzalloc(&client->dev, | |
418 | sizeof(struct lm355x_chip_data), GFP_KERNEL); | |
419 | if (!chip) | |
420 | return -ENOMEM; | |
421 | ||
422 | chip->dev = &client->dev; | |
423 | chip->type = id->driver_data; | |
424 | switch (id->driver_data) { | |
425 | case CHIP_LM3554: | |
426 | chip->regs = lm3554_regs; | |
427 | break; | |
428 | case CHIP_LM3556: | |
429 | chip->regs = lm3556_regs; | |
430 | break; | |
431 | default: | |
432 | return -ENOSYS; | |
433 | } | |
434 | chip->pdata = pdata; | |
435 | ||
436 | chip->regmap = devm_regmap_init_i2c(client, &lm355x_regmap); | |
437 | if (IS_ERR(chip->regmap)) { | |
438 | err = PTR_ERR(chip->regmap); | |
439 | dev_err(&client->dev, | |
440 | "Failed to allocate register map: %d\n", err); | |
441 | return err; | |
442 | } | |
443 | ||
444 | mutex_init(&chip->lock); | |
445 | i2c_set_clientdata(client, chip); | |
446 | ||
447 | err = lm355x_chip_init(chip); | |
448 | if (err < 0) | |
449 | goto err_out; | |
450 | ||
451 | /* flash */ | |
b98d13c7 SJ |
452 | chip->cdev_flash.name = "flash"; |
453 | chip->cdev_flash.max_brightness = 16; | |
df8ff5d2 | 454 | chip->cdev_flash.brightness_set_blocking = lm355x_strobe_brightness_set; |
313bf0b1 | 455 | chip->cdev_flash.default_trigger = "flash"; |
b5684a73 | 456 | err = led_classdev_register(&client->dev, &chip->cdev_flash); |
b98d13c7 SJ |
457 | if (err < 0) |
458 | goto err_out; | |
459 | /* torch */ | |
b98d13c7 SJ |
460 | chip->cdev_torch.name = "torch"; |
461 | chip->cdev_torch.max_brightness = 8; | |
df8ff5d2 | 462 | chip->cdev_torch.brightness_set_blocking = lm355x_torch_brightness_set; |
313bf0b1 | 463 | chip->cdev_torch.default_trigger = "torch"; |
b5684a73 | 464 | err = led_classdev_register(&client->dev, &chip->cdev_torch); |
b98d13c7 SJ |
465 | if (err < 0) |
466 | goto err_create_torch_file; | |
467 | /* indicator */ | |
b98d13c7 SJ |
468 | chip->cdev_indicator.name = "indicator"; |
469 | if (id->driver_data == CHIP_LM3554) | |
470 | chip->cdev_indicator.max_brightness = 4; | |
471 | else | |
472 | chip->cdev_indicator.max_brightness = 8; | |
df8ff5d2 JA |
473 | chip->cdev_indicator.brightness_set_blocking = |
474 | lm355x_indicator_brightness_set; | |
0d25376c JH |
475 | /* indicator pattern control only for LM3556 */ |
476 | if (id->driver_data == CHIP_LM3556) | |
477 | chip->cdev_indicator.groups = lm355x_indicator_groups; | |
b5684a73 | 478 | err = led_classdev_register(&client->dev, &chip->cdev_indicator); |
b98d13c7 SJ |
479 | if (err < 0) |
480 | goto err_create_indicator_file; | |
b98d13c7 SJ |
481 | |
482 | dev_info(&client->dev, "%s is initialized\n", | |
483 | lm355x_name[id->driver_data]); | |
484 | return 0; | |
485 | ||
b98d13c7 SJ |
486 | err_create_indicator_file: |
487 | led_classdev_unregister(&chip->cdev_torch); | |
488 | err_create_torch_file: | |
489 | led_classdev_unregister(&chip->cdev_flash); | |
490 | err_out: | |
491 | return err; | |
492 | } | |
493 | ||
ed5c2f5f | 494 | static void lm355x_remove(struct i2c_client *client) |
b98d13c7 SJ |
495 | { |
496 | struct lm355x_chip_data *chip = i2c_get_clientdata(client); | |
497 | struct lm355x_reg_data *preg = chip->regs; | |
498 | ||
499 | regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); | |
b98d13c7 | 500 | led_classdev_unregister(&chip->cdev_indicator); |
b98d13c7 | 501 | led_classdev_unregister(&chip->cdev_torch); |
b98d13c7 | 502 | led_classdev_unregister(&chip->cdev_flash); |
b98d13c7 | 503 | dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]); |
b98d13c7 SJ |
504 | } |
505 | ||
506 | static const struct i2c_device_id lm355x_id[] = { | |
507 | {LM3554_NAME, CHIP_LM3554}, | |
508 | {LM3556_NAME, CHIP_LM3556}, | |
509 | {} | |
510 | }; | |
511 | ||
512 | MODULE_DEVICE_TABLE(i2c, lm355x_id); | |
513 | ||
514 | static struct i2c_driver lm355x_i2c_driver = { | |
515 | .driver = { | |
516 | .name = LM355x_NAME, | |
b98d13c7 SJ |
517 | .pm = NULL, |
518 | }, | |
0712cb93 | 519 | .probe_new = lm355x_probe, |
df07cf81 | 520 | .remove = lm355x_remove, |
b98d13c7 SJ |
521 | .id_table = lm355x_id, |
522 | }; | |
523 | ||
524 | module_i2c_driver(lm355x_i2c_driver); | |
525 | ||
526 | MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM355x"); | |
527 | MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); | |
528 | MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); | |
529 | MODULE_LICENSE("GPL v2"); |