Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/leds/leds-lm3530.c
15109 views
1
/*
2
* Copyright (C) 2011 ST-Ericsson SA.
3
* Copyright (C) 2009 Motorola, Inc.
4
*
5
* License Terms: GNU General Public License v2
6
*
7
* Simple driver for National Semiconductor LM3530 Backlight driver chip
8
*
9
* Author: Shreshtha Kumar SAHU <[email protected]>
10
* based on leds-lm3530.c by Dan Murphy <[email protected]>
11
*/
12
13
#include <linux/i2c.h>
14
#include <linux/leds.h>
15
#include <linux/slab.h>
16
#include <linux/platform_device.h>
17
#include <linux/input.h>
18
#include <linux/led-lm3530.h>
19
#include <linux/types.h>
20
#include <linux/regulator/consumer.h>
21
22
#define LM3530_LED_DEV "lcd-backlight"
23
#define LM3530_NAME "lm3530-led"
24
25
#define LM3530_GEN_CONFIG 0x10
26
#define LM3530_ALS_CONFIG 0x20
27
#define LM3530_BRT_RAMP_RATE 0x30
28
#define LM3530_ALS_ZONE_REG 0x40
29
#define LM3530_ALS_IMP_SELECT 0x41
30
#define LM3530_BRT_CTRL_REG 0xA0
31
#define LM3530_ALS_ZB0_REG 0x60
32
#define LM3530_ALS_ZB1_REG 0x61
33
#define LM3530_ALS_ZB2_REG 0x62
34
#define LM3530_ALS_ZB3_REG 0x63
35
#define LM3530_ALS_Z0T_REG 0x70
36
#define LM3530_ALS_Z1T_REG 0x71
37
#define LM3530_ALS_Z2T_REG 0x72
38
#define LM3530_ALS_Z3T_REG 0x73
39
#define LM3530_ALS_Z4T_REG 0x74
40
#define LM3530_REG_MAX 15
41
42
/* General Control Register */
43
#define LM3530_EN_I2C_SHIFT (0)
44
#define LM3530_RAMP_LAW_SHIFT (1)
45
#define LM3530_MAX_CURR_SHIFT (2)
46
#define LM3530_EN_PWM_SHIFT (5)
47
#define LM3530_PWM_POL_SHIFT (6)
48
#define LM3530_EN_PWM_SIMPLE_SHIFT (7)
49
50
#define LM3530_ENABLE_I2C (1 << LM3530_EN_I2C_SHIFT)
51
#define LM3530_ENABLE_PWM (1 << LM3530_EN_PWM_SHIFT)
52
#define LM3530_POL_LOW (1 << LM3530_PWM_POL_SHIFT)
53
#define LM3530_ENABLE_PWM_SIMPLE (1 << LM3530_EN_PWM_SIMPLE_SHIFT)
54
55
/* ALS Config Register Options */
56
#define LM3530_ALS_AVG_TIME_SHIFT (0)
57
#define LM3530_EN_ALS_SHIFT (3)
58
#define LM3530_ALS_SEL_SHIFT (5)
59
60
#define LM3530_ENABLE_ALS (3 << LM3530_EN_ALS_SHIFT)
61
62
/* Brightness Ramp Rate Register */
63
#define LM3530_BRT_RAMP_FALL_SHIFT (0)
64
#define LM3530_BRT_RAMP_RISE_SHIFT (3)
65
66
/* ALS Resistor Select */
67
#define LM3530_ALS1_IMP_SHIFT (0)
68
#define LM3530_ALS2_IMP_SHIFT (4)
69
70
/* Zone Boundary Register defaults */
71
#define LM3530_DEF_ZB_0 (0x33)
72
#define LM3530_DEF_ZB_1 (0x66)
73
#define LM3530_DEF_ZB_2 (0x99)
74
#define LM3530_DEF_ZB_3 (0xCC)
75
76
/* Zone Target Register defaults */
77
#define LM3530_DEF_ZT_0 (0x19)
78
#define LM3530_DEF_ZT_1 (0x33)
79
#define LM3530_DEF_ZT_2 (0x4C)
80
#define LM3530_DEF_ZT_3 (0x66)
81
#define LM3530_DEF_ZT_4 (0x7F)
82
83
struct lm3530_mode_map {
84
const char *mode;
85
enum lm3530_mode mode_val;
86
};
87
88
static struct lm3530_mode_map mode_map[] = {
89
{ "man", LM3530_BL_MODE_MANUAL },
90
{ "als", LM3530_BL_MODE_ALS },
91
{ "pwm", LM3530_BL_MODE_PWM },
92
};
93
94
/**
95
* struct lm3530_data
96
* @led_dev: led class device
97
* @client: i2c client
98
* @pdata: LM3530 platform data
99
* @mode: mode of operation - manual, ALS, PWM
100
* @regulator: regulator
101
* @brighness: previous brightness value
102
* @enable: regulator is enabled
103
*/
104
struct lm3530_data {
105
struct led_classdev led_dev;
106
struct i2c_client *client;
107
struct lm3530_platform_data *pdata;
108
enum lm3530_mode mode;
109
struct regulator *regulator;
110
enum led_brightness brightness;
111
bool enable;
112
};
113
114
static const u8 lm3530_reg[LM3530_REG_MAX] = {
115
LM3530_GEN_CONFIG,
116
LM3530_ALS_CONFIG,
117
LM3530_BRT_RAMP_RATE,
118
LM3530_ALS_ZONE_REG,
119
LM3530_ALS_IMP_SELECT,
120
LM3530_BRT_CTRL_REG,
121
LM3530_ALS_ZB0_REG,
122
LM3530_ALS_ZB1_REG,
123
LM3530_ALS_ZB2_REG,
124
LM3530_ALS_ZB3_REG,
125
LM3530_ALS_Z0T_REG,
126
LM3530_ALS_Z1T_REG,
127
LM3530_ALS_Z2T_REG,
128
LM3530_ALS_Z3T_REG,
129
LM3530_ALS_Z4T_REG,
130
};
131
132
static int lm3530_get_mode_from_str(const char *str)
133
{
134
int i;
135
136
for (i = 0; i < ARRAY_SIZE(mode_map); i++)
137
if (sysfs_streq(str, mode_map[i].mode))
138
return mode_map[i].mode_val;
139
140
return -1;
141
}
142
143
static int lm3530_init_registers(struct lm3530_data *drvdata)
144
{
145
int ret = 0;
146
int i;
147
u8 gen_config;
148
u8 als_config = 0;
149
u8 brt_ramp;
150
u8 als_imp_sel = 0;
151
u8 brightness;
152
u8 reg_val[LM3530_REG_MAX];
153
struct lm3530_platform_data *pltfm = drvdata->pdata;
154
struct i2c_client *client = drvdata->client;
155
156
gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
157
((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT);
158
159
if (drvdata->mode == LM3530_BL_MODE_MANUAL ||
160
drvdata->mode == LM3530_BL_MODE_ALS)
161
gen_config |= (LM3530_ENABLE_I2C);
162
163
if (drvdata->mode == LM3530_BL_MODE_ALS) {
164
als_config =
165
(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
166
(LM3530_ENABLE_ALS) |
167
(pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT);
168
169
als_imp_sel =
170
(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
171
(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
172
}
173
174
if (drvdata->mode == LM3530_BL_MODE_PWM)
175
gen_config |= (LM3530_ENABLE_PWM) |
176
(pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) |
177
(LM3530_ENABLE_PWM_SIMPLE);
178
179
brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
180
(pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
181
182
if (drvdata->brightness)
183
brightness = drvdata->brightness;
184
else
185
brightness = drvdata->brightness = pltfm->brt_val;
186
187
reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */
188
reg_val[1] = als_config; /* LM3530_ALS_CONFIG */
189
reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */
190
reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
191
reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
192
reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
193
reg_val[6] = LM3530_DEF_ZB_0; /* LM3530_ALS_ZB0_REG */
194
reg_val[7] = LM3530_DEF_ZB_1; /* LM3530_ALS_ZB1_REG */
195
reg_val[8] = LM3530_DEF_ZB_2; /* LM3530_ALS_ZB2_REG */
196
reg_val[9] = LM3530_DEF_ZB_3; /* LM3530_ALS_ZB3_REG */
197
reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
198
reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
199
reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
200
reg_val[13] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
201
reg_val[14] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
202
203
if (!drvdata->enable) {
204
ret = regulator_enable(drvdata->regulator);
205
if (ret) {
206
dev_err(&drvdata->client->dev,
207
"Enable regulator failed\n");
208
return ret;
209
}
210
drvdata->enable = true;
211
}
212
213
for (i = 0; i < LM3530_REG_MAX; i++) {
214
ret = i2c_smbus_write_byte_data(client,
215
lm3530_reg[i], reg_val[i]);
216
if (ret)
217
break;
218
}
219
220
return ret;
221
}
222
223
static void lm3530_brightness_set(struct led_classdev *led_cdev,
224
enum led_brightness brt_val)
225
{
226
int err;
227
struct lm3530_data *drvdata =
228
container_of(led_cdev, struct lm3530_data, led_dev);
229
230
switch (drvdata->mode) {
231
case LM3530_BL_MODE_MANUAL:
232
233
if (!drvdata->enable) {
234
err = lm3530_init_registers(drvdata);
235
if (err) {
236
dev_err(&drvdata->client->dev,
237
"Register Init failed: %d\n", err);
238
break;
239
}
240
}
241
242
/* set the brightness in brightness control register*/
243
err = i2c_smbus_write_byte_data(drvdata->client,
244
LM3530_BRT_CTRL_REG, brt_val / 2);
245
if (err)
246
dev_err(&drvdata->client->dev,
247
"Unable to set brightness: %d\n", err);
248
else
249
drvdata->brightness = brt_val / 2;
250
251
if (brt_val == 0) {
252
err = regulator_disable(drvdata->regulator);
253
if (err)
254
dev_err(&drvdata->client->dev,
255
"Disable regulator failed\n");
256
drvdata->enable = false;
257
}
258
break;
259
case LM3530_BL_MODE_ALS:
260
break;
261
case LM3530_BL_MODE_PWM:
262
break;
263
default:
264
break;
265
}
266
}
267
268
269
static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
270
*attr, const char *buf, size_t size)
271
{
272
int err;
273
struct i2c_client *client = container_of(
274
dev->parent, struct i2c_client, dev);
275
struct lm3530_data *drvdata = i2c_get_clientdata(client);
276
int mode;
277
278
mode = lm3530_get_mode_from_str(buf);
279
if (mode < 0) {
280
dev_err(dev, "Invalid mode\n");
281
return -EINVAL;
282
}
283
284
if (mode == LM3530_BL_MODE_MANUAL)
285
drvdata->mode = LM3530_BL_MODE_MANUAL;
286
else if (mode == LM3530_BL_MODE_ALS)
287
drvdata->mode = LM3530_BL_MODE_ALS;
288
else if (mode == LM3530_BL_MODE_PWM) {
289
dev_err(dev, "PWM mode not supported\n");
290
return -EINVAL;
291
}
292
293
err = lm3530_init_registers(drvdata);
294
if (err) {
295
dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);
296
return err;
297
}
298
299
return sizeof(drvdata->mode);
300
}
301
302
static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
303
304
static int __devinit lm3530_probe(struct i2c_client *client,
305
const struct i2c_device_id *id)
306
{
307
struct lm3530_platform_data *pdata = client->dev.platform_data;
308
struct lm3530_data *drvdata;
309
int err = 0;
310
311
if (pdata == NULL) {
312
dev_err(&client->dev, "platform data required\n");
313
err = -ENODEV;
314
goto err_out;
315
}
316
317
/* BL mode */
318
if (pdata->mode > LM3530_BL_MODE_PWM) {
319
dev_err(&client->dev, "Illegal Mode request\n");
320
err = -EINVAL;
321
goto err_out;
322
}
323
324
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
325
dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
326
err = -EIO;
327
goto err_out;
328
}
329
330
drvdata = kzalloc(sizeof(struct lm3530_data), GFP_KERNEL);
331
if (drvdata == NULL) {
332
err = -ENOMEM;
333
goto err_out;
334
}
335
336
drvdata->mode = pdata->mode;
337
drvdata->client = client;
338
drvdata->pdata = pdata;
339
drvdata->brightness = LED_OFF;
340
drvdata->enable = false;
341
drvdata->led_dev.name = LM3530_LED_DEV;
342
drvdata->led_dev.brightness_set = lm3530_brightness_set;
343
344
i2c_set_clientdata(client, drvdata);
345
346
drvdata->regulator = regulator_get(&client->dev, "vin");
347
if (IS_ERR(drvdata->regulator)) {
348
dev_err(&client->dev, "regulator get failed\n");
349
err = PTR_ERR(drvdata->regulator);
350
drvdata->regulator = NULL;
351
goto err_regulator_get;
352
}
353
354
if (drvdata->pdata->brt_val) {
355
err = lm3530_init_registers(drvdata);
356
if (err < 0) {
357
dev_err(&client->dev,
358
"Register Init failed: %d\n", err);
359
err = -ENODEV;
360
goto err_reg_init;
361
}
362
}
363
err = led_classdev_register(&client->dev, &drvdata->led_dev);
364
if (err < 0) {
365
dev_err(&client->dev, "Register led class failed: %d\n", err);
366
err = -ENODEV;
367
goto err_class_register;
368
}
369
370
err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode);
371
if (err < 0) {
372
dev_err(&client->dev, "File device creation failed: %d\n", err);
373
err = -ENODEV;
374
goto err_create_file;
375
}
376
377
return 0;
378
379
err_create_file:
380
led_classdev_unregister(&drvdata->led_dev);
381
err_class_register:
382
err_reg_init:
383
regulator_put(drvdata->regulator);
384
err_regulator_get:
385
i2c_set_clientdata(client, NULL);
386
kfree(drvdata);
387
err_out:
388
return err;
389
}
390
391
static int __devexit lm3530_remove(struct i2c_client *client)
392
{
393
struct lm3530_data *drvdata = i2c_get_clientdata(client);
394
395
device_remove_file(drvdata->led_dev.dev, &dev_attr_mode);
396
397
if (drvdata->enable)
398
regulator_disable(drvdata->regulator);
399
regulator_put(drvdata->regulator);
400
led_classdev_unregister(&drvdata->led_dev);
401
kfree(drvdata);
402
return 0;
403
}
404
405
static const struct i2c_device_id lm3530_id[] = {
406
{LM3530_NAME, 0},
407
{}
408
};
409
MODULE_DEVICE_TABLE(i2c, lm3530_id);
410
411
static struct i2c_driver lm3530_i2c_driver = {
412
.probe = lm3530_probe,
413
.remove = lm3530_remove,
414
.id_table = lm3530_id,
415
.driver = {
416
.name = LM3530_NAME,
417
.owner = THIS_MODULE,
418
},
419
};
420
421
static int __init lm3530_init(void)
422
{
423
return i2c_add_driver(&lm3530_i2c_driver);
424
}
425
426
static void __exit lm3530_exit(void)
427
{
428
i2c_del_driver(&lm3530_i2c_driver);
429
}
430
431
module_init(lm3530_init);
432
module_exit(lm3530_exit);
433
434
MODULE_DESCRIPTION("Back Light driver for LM3530");
435
MODULE_LICENSE("GPL v2");
436
MODULE_AUTHOR("Shreshtha Kumar SAHU <[email protected]>");
437
438