Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2011 ST-Ericsson SA.
0004  * Copyright (C) 2009 Motorola, Inc.
0005  *
0006  * Simple driver for National Semiconductor LM3530 Backlight driver chip
0007  *
0008  * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
0009  * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>
0010  */
0011 
0012 #include <linux/i2c.h>
0013 #include <linux/leds.h>
0014 #include <linux/slab.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/input.h>
0017 #include <linux/led-lm3530.h>
0018 #include <linux/types.h>
0019 #include <linux/regulator/consumer.h>
0020 #include <linux/module.h>
0021 
0022 #define LM3530_LED_DEV "lcd-backlight"
0023 #define LM3530_NAME "lm3530-led"
0024 
0025 #define LM3530_GEN_CONFIG       0x10
0026 #define LM3530_ALS_CONFIG       0x20
0027 #define LM3530_BRT_RAMP_RATE        0x30
0028 #define LM3530_ALS_IMP_SELECT       0x41
0029 #define LM3530_BRT_CTRL_REG     0xA0
0030 #define LM3530_ALS_ZB0_REG      0x60
0031 #define LM3530_ALS_ZB1_REG      0x61
0032 #define LM3530_ALS_ZB2_REG      0x62
0033 #define LM3530_ALS_ZB3_REG      0x63
0034 #define LM3530_ALS_Z0T_REG      0x70
0035 #define LM3530_ALS_Z1T_REG      0x71
0036 #define LM3530_ALS_Z2T_REG      0x72
0037 #define LM3530_ALS_Z3T_REG      0x73
0038 #define LM3530_ALS_Z4T_REG      0x74
0039 #define LM3530_REG_MAX          14
0040 
0041 /* General Control Register */
0042 #define LM3530_EN_I2C_SHIFT     (0)
0043 #define LM3530_RAMP_LAW_SHIFT       (1)
0044 #define LM3530_MAX_CURR_SHIFT       (2)
0045 #define LM3530_EN_PWM_SHIFT     (5)
0046 #define LM3530_PWM_POL_SHIFT        (6)
0047 #define LM3530_EN_PWM_SIMPLE_SHIFT  (7)
0048 
0049 #define LM3530_ENABLE_I2C       (1 << LM3530_EN_I2C_SHIFT)
0050 #define LM3530_ENABLE_PWM       (1 << LM3530_EN_PWM_SHIFT)
0051 #define LM3530_POL_LOW          (1 << LM3530_PWM_POL_SHIFT)
0052 #define LM3530_ENABLE_PWM_SIMPLE    (1 << LM3530_EN_PWM_SIMPLE_SHIFT)
0053 
0054 /* ALS Config Register Options */
0055 #define LM3530_ALS_AVG_TIME_SHIFT   (0)
0056 #define LM3530_EN_ALS_SHIFT     (3)
0057 #define LM3530_ALS_SEL_SHIFT        (5)
0058 
0059 #define LM3530_ENABLE_ALS       (3 << LM3530_EN_ALS_SHIFT)
0060 
0061 /* Brightness Ramp Rate Register */
0062 #define LM3530_BRT_RAMP_FALL_SHIFT  (0)
0063 #define LM3530_BRT_RAMP_RISE_SHIFT  (3)
0064 
0065 /* ALS Resistor Select */
0066 #define LM3530_ALS1_IMP_SHIFT       (0)
0067 #define LM3530_ALS2_IMP_SHIFT       (4)
0068 
0069 /* Zone Boundary Register defaults */
0070 #define LM3530_ALS_ZB_MAX       (4)
0071 #define LM3530_ALS_WINDOW_mV        (1000)
0072 #define LM3530_ALS_OFFSET_mV        (4)
0073 
0074 /* Zone Target Register defaults */
0075 #define LM3530_DEF_ZT_0         (0x7F)
0076 #define LM3530_DEF_ZT_1         (0x66)
0077 #define LM3530_DEF_ZT_2         (0x4C)
0078 #define LM3530_DEF_ZT_3         (0x33)
0079 #define LM3530_DEF_ZT_4         (0x19)
0080 
0081 /* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */
0082 #define MAX_BRIGHTNESS          (127)
0083 
0084 struct lm3530_mode_map {
0085     const char *mode;
0086     enum lm3530_mode mode_val;
0087 };
0088 
0089 static struct lm3530_mode_map mode_map[] = {
0090     { "man", LM3530_BL_MODE_MANUAL },
0091     { "als", LM3530_BL_MODE_ALS },
0092     { "pwm", LM3530_BL_MODE_PWM },
0093 };
0094 
0095 /**
0096  * struct lm3530_data
0097  * @led_dev: led class device
0098  * @client: i2c client
0099  * @pdata: LM3530 platform data
0100  * @mode: mode of operation - manual, ALS, PWM
0101  * @regulator: regulator
0102  * @brightness: previous brightness value
0103  * @enable: regulator is enabled
0104  */
0105 struct lm3530_data {
0106     struct led_classdev led_dev;
0107     struct i2c_client *client;
0108     struct lm3530_platform_data *pdata;
0109     enum lm3530_mode mode;
0110     struct regulator *regulator;
0111     enum led_brightness brightness;
0112     bool enable;
0113 };
0114 
0115 /*
0116  * struct lm3530_als_data
0117  * @config  : value of ALS configuration register
0118  * @imp_sel : value of ALS resistor select register
0119  * @zone    : values of ALS ZB(Zone Boundary) registers
0120  */
0121 struct lm3530_als_data {
0122     u8 config;
0123     u8 imp_sel;
0124     u8 zones[LM3530_ALS_ZB_MAX];
0125 };
0126 
0127 static const u8 lm3530_reg[LM3530_REG_MAX] = {
0128     LM3530_GEN_CONFIG,
0129     LM3530_ALS_CONFIG,
0130     LM3530_BRT_RAMP_RATE,
0131     LM3530_ALS_IMP_SELECT,
0132     LM3530_BRT_CTRL_REG,
0133     LM3530_ALS_ZB0_REG,
0134     LM3530_ALS_ZB1_REG,
0135     LM3530_ALS_ZB2_REG,
0136     LM3530_ALS_ZB3_REG,
0137     LM3530_ALS_Z0T_REG,
0138     LM3530_ALS_Z1T_REG,
0139     LM3530_ALS_Z2T_REG,
0140     LM3530_ALS_Z3T_REG,
0141     LM3530_ALS_Z4T_REG,
0142 };
0143 
0144 static int lm3530_get_mode_from_str(const char *str)
0145 {
0146     int i;
0147 
0148     for (i = 0; i < ARRAY_SIZE(mode_map); i++)
0149         if (sysfs_streq(str, mode_map[i].mode))
0150             return mode_map[i].mode_val;
0151 
0152     return -EINVAL;
0153 }
0154 
0155 static void lm3530_als_configure(struct lm3530_platform_data *pdata,
0156                 struct lm3530_als_data *als)
0157 {
0158     int i;
0159     u32 als_vmin, als_vmax, als_vstep;
0160 
0161     if (pdata->als_vmax == 0) {
0162         pdata->als_vmin = 0;
0163         pdata->als_vmax = LM3530_ALS_WINDOW_mV;
0164     }
0165 
0166     als_vmin = pdata->als_vmin;
0167     als_vmax = pdata->als_vmax;
0168 
0169     if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
0170         pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV;
0171 
0172     /* n zone boundary makes n+1 zones */
0173     als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
0174 
0175     for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
0176         als->zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
0177             als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
0178 
0179     als->config =
0180         (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
0181         (LM3530_ENABLE_ALS) |
0182         (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);
0183 
0184     als->imp_sel =
0185         (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
0186         (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
0187 }
0188 
0189 static int lm3530_led_enable(struct lm3530_data *drvdata)
0190 {
0191     int ret;
0192 
0193     if (drvdata->enable)
0194         return 0;
0195 
0196     ret = regulator_enable(drvdata->regulator);
0197     if (ret) {
0198         dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret);
0199         return ret;
0200     }
0201 
0202     drvdata->enable = true;
0203     return 0;
0204 }
0205 
0206 static void lm3530_led_disable(struct lm3530_data *drvdata)
0207 {
0208     int ret;
0209 
0210     if (!drvdata->enable)
0211         return;
0212 
0213     ret = regulator_disable(drvdata->regulator);
0214     if (ret) {
0215         dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n",
0216             ret);
0217         return;
0218     }
0219 
0220     drvdata->enable = false;
0221 }
0222 
0223 static int lm3530_init_registers(struct lm3530_data *drvdata)
0224 {
0225     int ret = 0;
0226     int i;
0227     u8 gen_config;
0228     u8 brt_ramp;
0229     u8 brightness;
0230     u8 reg_val[LM3530_REG_MAX];
0231     struct lm3530_platform_data *pdata = drvdata->pdata;
0232     struct i2c_client *client = drvdata->client;
0233     struct lm3530_pwm_data *pwm = &pdata->pwm_data;
0234     struct lm3530_als_data als;
0235 
0236     memset(&als, 0, sizeof(struct lm3530_als_data));
0237 
0238     gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
0239             ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT);
0240 
0241     switch (drvdata->mode) {
0242     case LM3530_BL_MODE_MANUAL:
0243         gen_config |= LM3530_ENABLE_I2C;
0244         break;
0245     case LM3530_BL_MODE_ALS:
0246         gen_config |= LM3530_ENABLE_I2C;
0247         lm3530_als_configure(pdata, &als);
0248         break;
0249     case LM3530_BL_MODE_PWM:
0250         gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |
0251                   (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT);
0252         break;
0253     }
0254 
0255     brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
0256             (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
0257 
0258     if (drvdata->brightness)
0259         brightness = drvdata->brightness;
0260     else
0261         brightness = drvdata->brightness = pdata->brt_val;
0262 
0263     if (brightness > drvdata->led_dev.max_brightness)
0264         brightness = drvdata->led_dev.max_brightness;
0265 
0266     reg_val[0] = gen_config;    /* LM3530_GEN_CONFIG */
0267     reg_val[1] = als.config;    /* LM3530_ALS_CONFIG */
0268     reg_val[2] = brt_ramp;      /* LM3530_BRT_RAMP_RATE */
0269     reg_val[3] = als.imp_sel;   /* LM3530_ALS_IMP_SELECT */
0270     reg_val[4] = brightness;    /* LM3530_BRT_CTRL_REG */
0271     reg_val[5] = als.zones[0];  /* LM3530_ALS_ZB0_REG */
0272     reg_val[6] = als.zones[1];  /* LM3530_ALS_ZB1_REG */
0273     reg_val[7] = als.zones[2];  /* LM3530_ALS_ZB2_REG */
0274     reg_val[8] = als.zones[3];  /* LM3530_ALS_ZB3_REG */
0275     reg_val[9] = LM3530_DEF_ZT_0;   /* LM3530_ALS_Z0T_REG */
0276     reg_val[10] = LM3530_DEF_ZT_1;  /* LM3530_ALS_Z1T_REG */
0277     reg_val[11] = LM3530_DEF_ZT_2;  /* LM3530_ALS_Z2T_REG */
0278     reg_val[12] = LM3530_DEF_ZT_3;  /* LM3530_ALS_Z3T_REG */
0279     reg_val[13] = LM3530_DEF_ZT_4;  /* LM3530_ALS_Z4T_REG */
0280 
0281     ret = lm3530_led_enable(drvdata);
0282     if (ret)
0283         return ret;
0284 
0285     for (i = 0; i < LM3530_REG_MAX; i++) {
0286         /* do not update brightness register when pwm mode */
0287         if (lm3530_reg[i] == LM3530_BRT_CTRL_REG &&
0288             drvdata->mode == LM3530_BL_MODE_PWM) {
0289             if (pwm->pwm_set_intensity)
0290                 pwm->pwm_set_intensity(reg_val[i],
0291                     drvdata->led_dev.max_brightness);
0292             continue;
0293         }
0294 
0295         ret = i2c_smbus_write_byte_data(client,
0296                 lm3530_reg[i], reg_val[i]);
0297         if (ret)
0298             break;
0299     }
0300 
0301     return ret;
0302 }
0303 
0304 static void lm3530_brightness_set(struct led_classdev *led_cdev,
0305                      enum led_brightness brt_val)
0306 {
0307     int err;
0308     struct lm3530_data *drvdata =
0309         container_of(led_cdev, struct lm3530_data, led_dev);
0310     struct lm3530_platform_data *pdata = drvdata->pdata;
0311     struct lm3530_pwm_data *pwm = &pdata->pwm_data;
0312     u8 max_brightness = led_cdev->max_brightness;
0313 
0314     switch (drvdata->mode) {
0315     case LM3530_BL_MODE_MANUAL:
0316 
0317         if (!drvdata->enable) {
0318             err = lm3530_init_registers(drvdata);
0319             if (err) {
0320                 dev_err(&drvdata->client->dev,
0321                     "Register Init failed: %d\n", err);
0322                 break;
0323             }
0324         }
0325 
0326         /* set the brightness in brightness control register*/
0327         err = i2c_smbus_write_byte_data(drvdata->client,
0328                 LM3530_BRT_CTRL_REG, brt_val);
0329         if (err)
0330             dev_err(&drvdata->client->dev,
0331                 "Unable to set brightness: %d\n", err);
0332         else
0333             drvdata->brightness = brt_val;
0334 
0335         if (brt_val == 0)
0336             lm3530_led_disable(drvdata);
0337         break;
0338     case LM3530_BL_MODE_ALS:
0339         break;
0340     case LM3530_BL_MODE_PWM:
0341         if (pwm->pwm_set_intensity)
0342             pwm->pwm_set_intensity(brt_val, max_brightness);
0343         break;
0344     default:
0345         break;
0346     }
0347 }
0348 
0349 static ssize_t mode_show(struct device *dev,
0350              struct device_attribute *attr, char *buf)
0351 {
0352     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0353     struct lm3530_data *drvdata;
0354     int i, len = 0;
0355 
0356     drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
0357     for (i = 0; i < ARRAY_SIZE(mode_map); i++)
0358         if (drvdata->mode == mode_map[i].mode_val)
0359             len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
0360         else
0361             len += sprintf(buf + len, "%s ", mode_map[i].mode);
0362 
0363     len += sprintf(buf + len, "\n");
0364 
0365     return len;
0366 }
0367 
0368 static ssize_t mode_store(struct device *dev, struct device_attribute
0369               *attr, const char *buf, size_t size)
0370 {
0371     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0372     struct lm3530_data *drvdata;
0373     struct lm3530_pwm_data *pwm;
0374     u8 max_brightness;
0375     int mode, err;
0376 
0377     drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
0378     pwm = &drvdata->pdata->pwm_data;
0379     max_brightness = led_cdev->max_brightness;
0380     mode = lm3530_get_mode_from_str(buf);
0381     if (mode < 0) {
0382         dev_err(dev, "Invalid mode\n");
0383         return mode;
0384     }
0385 
0386     drvdata->mode = mode;
0387 
0388     /* set pwm to low if unnecessary */
0389     if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity)
0390         pwm->pwm_set_intensity(0, max_brightness);
0391 
0392     err = lm3530_init_registers(drvdata);
0393     if (err) {
0394         dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);
0395         return err;
0396     }
0397 
0398     return sizeof(drvdata->mode);
0399 }
0400 static DEVICE_ATTR_RW(mode);
0401 
0402 static struct attribute *lm3530_attrs[] = {
0403     &dev_attr_mode.attr,
0404     NULL
0405 };
0406 ATTRIBUTE_GROUPS(lm3530);
0407 
0408 static int lm3530_probe(struct i2c_client *client,
0409                const struct i2c_device_id *id)
0410 {
0411     struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev);
0412     struct lm3530_data *drvdata;
0413     int err = 0;
0414 
0415     if (pdata == NULL) {
0416         dev_err(&client->dev, "platform data required\n");
0417         return -ENODEV;
0418     }
0419 
0420     /* BL mode */
0421     if (pdata->mode > LM3530_BL_MODE_PWM) {
0422         dev_err(&client->dev, "Illegal Mode request\n");
0423         return -EINVAL;
0424     }
0425 
0426     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
0427         dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
0428         return -EIO;
0429     }
0430 
0431     drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data),
0432                 GFP_KERNEL);
0433     if (drvdata == NULL)
0434         return -ENOMEM;
0435 
0436     drvdata->mode = pdata->mode;
0437     drvdata->client = client;
0438     drvdata->pdata = pdata;
0439     drvdata->brightness = LED_OFF;
0440     drvdata->enable = false;
0441     drvdata->led_dev.name = LM3530_LED_DEV;
0442     drvdata->led_dev.brightness_set = lm3530_brightness_set;
0443     drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;
0444     drvdata->led_dev.groups = lm3530_groups;
0445 
0446     i2c_set_clientdata(client, drvdata);
0447 
0448     drvdata->regulator = devm_regulator_get(&client->dev, "vin");
0449     if (IS_ERR(drvdata->regulator)) {
0450         dev_err(&client->dev, "regulator get failed\n");
0451         err = PTR_ERR(drvdata->regulator);
0452         drvdata->regulator = NULL;
0453         return err;
0454     }
0455 
0456     if (drvdata->pdata->brt_val) {
0457         err = lm3530_init_registers(drvdata);
0458         if (err < 0) {
0459             dev_err(&client->dev,
0460                 "Register Init failed: %d\n", err);
0461             return err;
0462         }
0463     }
0464     err = led_classdev_register(&client->dev, &drvdata->led_dev);
0465     if (err < 0) {
0466         dev_err(&client->dev, "Register led class failed: %d\n", err);
0467         return err;
0468     }
0469 
0470     return 0;
0471 }
0472 
0473 static int lm3530_remove(struct i2c_client *client)
0474 {
0475     struct lm3530_data *drvdata = i2c_get_clientdata(client);
0476 
0477     lm3530_led_disable(drvdata);
0478     led_classdev_unregister(&drvdata->led_dev);
0479     return 0;
0480 }
0481 
0482 static const struct i2c_device_id lm3530_id[] = {
0483     {LM3530_NAME, 0},
0484     {}
0485 };
0486 MODULE_DEVICE_TABLE(i2c, lm3530_id);
0487 
0488 static struct i2c_driver lm3530_i2c_driver = {
0489     .probe = lm3530_probe,
0490     .remove = lm3530_remove,
0491     .id_table = lm3530_id,
0492     .driver = {
0493         .name = LM3530_NAME,
0494     },
0495 };
0496 
0497 module_i2c_driver(lm3530_i2c_driver);
0498 
0499 MODULE_DESCRIPTION("Back Light driver for LM3530");
0500 MODULE_LICENSE("GPL v2");
0501 MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");