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