2019-06-04 16:11:33 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2012-03-24 06:02:01 +08:00
|
|
|
/*
|
|
|
|
* TI LP855x Backlight Driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 Texas Instruments
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/backlight.h>
|
2016-06-11 03:39:57 +08:00
|
|
|
#include <linux/delay.h>
|
2012-03-24 06:02:01 +08:00
|
|
|
#include <linux/err.h>
|
2013-04-30 07:18:06 +08:00
|
|
|
#include <linux/of.h>
|
2012-07-31 05:40:53 +08:00
|
|
|
#include <linux/platform_data/lp855x.h>
|
2012-12-18 08:00:43 +08:00
|
|
|
#include <linux/pwm.h>
|
2014-12-03 09:39:12 +08:00
|
|
|
#include <linux/regulator/consumer.h>
|
2012-03-24 06:02:01 +08:00
|
|
|
|
2013-02-22 08:44:06 +08:00
|
|
|
/* LP8550/1/2/3/6 Registers */
|
|
|
|
#define LP855X_BRIGHTNESS_CTRL 0x00
|
|
|
|
#define LP855X_DEVICE_CTRL 0x01
|
|
|
|
#define LP855X_EEPROM_START 0xA0
|
|
|
|
#define LP855X_EEPROM_END 0xA7
|
|
|
|
#define LP8556_EPROM_START 0xA0
|
|
|
|
#define LP8556_EPROM_END 0xAF
|
|
|
|
|
2013-11-13 07:08:57 +08:00
|
|
|
/* LP8555/7 Registers */
|
2013-02-22 08:44:06 +08:00
|
|
|
#define LP8557_BL_CMD 0x00
|
|
|
|
#define LP8557_BL_MASK 0x01
|
|
|
|
#define LP8557_BL_ON 0x01
|
|
|
|
#define LP8557_BL_OFF 0x00
|
|
|
|
#define LP8557_BRIGHTNESS_CTRL 0x04
|
|
|
|
#define LP8557_CONFIG 0x10
|
2013-11-13 07:08:57 +08:00
|
|
|
#define LP8555_EPROM_START 0x10
|
|
|
|
#define LP8555_EPROM_END 0x7A
|
2013-02-22 08:44:06 +08:00
|
|
|
#define LP8557_EPROM_START 0x10
|
|
|
|
#define LP8557_EPROM_END 0x1E
|
2012-03-24 06:02:01 +08:00
|
|
|
|
|
|
|
#define DEFAULT_BL_NAME "lcd-backlight"
|
|
|
|
#define MAX_BRIGHTNESS 255
|
|
|
|
|
2013-04-30 07:18:03 +08:00
|
|
|
enum lp855x_brightness_ctrl_mode {
|
|
|
|
PWM_BASED = 1,
|
|
|
|
REGISTER_BASED,
|
|
|
|
};
|
|
|
|
|
2013-02-22 08:44:05 +08:00
|
|
|
struct lp855x;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* struct lp855x_device_config
|
|
|
|
* @pre_init_device: init device function call before updating the brightness
|
|
|
|
* @reg_brightness: register address for brigthenss control
|
|
|
|
* @reg_devicectrl: register address for device control
|
|
|
|
* @post_init_device: late init device function call
|
|
|
|
*/
|
|
|
|
struct lp855x_device_config {
|
|
|
|
int (*pre_init_device)(struct lp855x *);
|
|
|
|
u8 reg_brightness;
|
|
|
|
u8 reg_devicectrl;
|
|
|
|
int (*post_init_device)(struct lp855x *);
|
|
|
|
};
|
|
|
|
|
2012-03-24 06:02:01 +08:00
|
|
|
struct lp855x {
|
|
|
|
const char *chipname;
|
|
|
|
enum lp855x_chip_id chip_id;
|
2013-04-30 07:18:03 +08:00
|
|
|
enum lp855x_brightness_ctrl_mode mode;
|
2013-02-22 08:44:05 +08:00
|
|
|
struct lp855x_device_config *cfg;
|
2012-03-24 06:02:01 +08:00
|
|
|
struct i2c_client *client;
|
|
|
|
struct backlight_device *bl;
|
|
|
|
struct device *dev;
|
|
|
|
struct lp855x_platform_data *pdata;
|
2012-12-18 08:00:43 +08:00
|
|
|
struct pwm_device *pwm;
|
2015-07-20 14:45:38 +08:00
|
|
|
struct regulator *supply; /* regulator for VDD input */
|
2016-06-11 03:39:57 +08:00
|
|
|
struct regulator *enable; /* regulator for EN/VDDIO input */
|
2012-03-24 06:02:01 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
|
|
|
|
{
|
2012-12-18 08:00:45 +08:00
|
|
|
return i2c_smbus_write_byte_data(lp->client, reg, data);
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
|
2013-02-22 08:44:06 +08:00
|
|
|
static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
u8 tmp;
|
|
|
|
|
|
|
|
ret = i2c_smbus_read_byte_data(lp->client, reg);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = (u8)ret;
|
|
|
|
tmp &= ~mask;
|
|
|
|
tmp |= data & mask;
|
|
|
|
|
|
|
|
return lp855x_write_byte(lp, reg, tmp);
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:02:01 +08:00
|
|
|
static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
|
|
|
|
{
|
|
|
|
u8 start, end;
|
|
|
|
|
|
|
|
switch (lp->chip_id) {
|
|
|
|
case LP8550:
|
|
|
|
case LP8551:
|
|
|
|
case LP8552:
|
|
|
|
case LP8553:
|
2013-02-22 08:44:06 +08:00
|
|
|
start = LP855X_EEPROM_START;
|
|
|
|
end = LP855X_EEPROM_END;
|
2012-03-24 06:02:01 +08:00
|
|
|
break;
|
|
|
|
case LP8556:
|
2013-02-22 08:44:06 +08:00
|
|
|
start = LP8556_EPROM_START;
|
|
|
|
end = LP8556_EPROM_END;
|
|
|
|
break;
|
2013-11-13 07:08:57 +08:00
|
|
|
case LP8555:
|
|
|
|
start = LP8555_EPROM_START;
|
|
|
|
end = LP8555_EPROM_END;
|
|
|
|
break;
|
2013-02-22 08:44:06 +08:00
|
|
|
case LP8557:
|
|
|
|
start = LP8557_EPROM_START;
|
|
|
|
end = LP8557_EPROM_END;
|
2012-03-24 06:02:01 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-01-24 07:54:32 +08:00
|
|
|
return addr >= start && addr <= end;
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
|
2013-02-22 08:44:06 +08:00
|
|
|
static int lp8557_bl_off(struct lp855x *lp)
|
|
|
|
{
|
|
|
|
/* BL_ON = 0 before updating EPROM settings */
|
|
|
|
return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
|
|
|
|
LP8557_BL_OFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lp8557_bl_on(struct lp855x *lp)
|
|
|
|
{
|
|
|
|
/* BL_ON = 1 after updating EPROM settings */
|
|
|
|
return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
|
|
|
|
LP8557_BL_ON);
|
|
|
|
}
|
|
|
|
|
2013-02-22 08:44:05 +08:00
|
|
|
static struct lp855x_device_config lp855x_dev_cfg = {
|
2013-02-22 08:44:06 +08:00
|
|
|
.reg_brightness = LP855X_BRIGHTNESS_CTRL,
|
|
|
|
.reg_devicectrl = LP855X_DEVICE_CTRL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct lp855x_device_config lp8557_dev_cfg = {
|
|
|
|
.reg_brightness = LP8557_BRIGHTNESS_CTRL,
|
|
|
|
.reg_devicectrl = LP8557_CONFIG,
|
|
|
|
.pre_init_device = lp8557_bl_off,
|
|
|
|
.post_init_device = lp8557_bl_on,
|
2013-02-22 08:44:05 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Device specific configuration flow
|
|
|
|
*
|
|
|
|
* a) pre_init_device(optional)
|
|
|
|
* b) update the brightness register
|
|
|
|
* c) update device control register
|
|
|
|
* d) update ROM area(optional)
|
|
|
|
* e) post_init_device(optional)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int lp855x_configure(struct lp855x *lp)
|
2012-03-24 06:02:01 +08:00
|
|
|
{
|
|
|
|
u8 val, addr;
|
|
|
|
int i, ret;
|
|
|
|
struct lp855x_platform_data *pd = lp->pdata;
|
|
|
|
|
2013-02-22 08:44:05 +08:00
|
|
|
switch (lp->chip_id) {
|
2013-11-13 07:08:57 +08:00
|
|
|
case LP8550:
|
|
|
|
case LP8551:
|
|
|
|
case LP8552:
|
|
|
|
case LP8553:
|
|
|
|
case LP8556:
|
2013-02-22 08:44:05 +08:00
|
|
|
lp->cfg = &lp855x_dev_cfg;
|
|
|
|
break;
|
2013-11-13 07:08:57 +08:00
|
|
|
case LP8555:
|
2013-02-22 08:44:06 +08:00
|
|
|
case LP8557:
|
|
|
|
lp->cfg = &lp8557_dev_cfg;
|
|
|
|
break;
|
2013-02-22 08:44:05 +08:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lp->cfg->pre_init_device) {
|
|
|
|
ret = lp->cfg->pre_init_device(lp);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(lp->dev, "pre init device err: %d\n", ret);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:02:01 +08:00
|
|
|
val = pd->initial_brightness;
|
2013-02-22 08:44:05 +08:00
|
|
|
ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
|
2012-03-24 06:02:01 +08:00
|
|
|
if (ret)
|
2013-02-22 08:44:05 +08:00
|
|
|
goto err;
|
2012-03-24 06:02:01 +08:00
|
|
|
|
|
|
|
val = pd->device_control;
|
2013-02-22 08:44:05 +08:00
|
|
|
ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val);
|
2012-03-24 06:02:01 +08:00
|
|
|
if (ret)
|
2013-02-22 08:44:05 +08:00
|
|
|
goto err;
|
2012-03-24 06:02:01 +08:00
|
|
|
|
2013-04-30 07:18:05 +08:00
|
|
|
if (pd->size_program > 0) {
|
2012-03-24 06:02:01 +08:00
|
|
|
for (i = 0; i < pd->size_program; i++) {
|
|
|
|
addr = pd->rom_data[i].addr;
|
|
|
|
val = pd->rom_data[i].val;
|
|
|
|
if (!lp855x_is_valid_rom_area(lp, addr))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = lp855x_write_byte(lp, addr, val);
|
|
|
|
if (ret)
|
2013-02-22 08:44:05 +08:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lp->cfg->post_init_device) {
|
|
|
|
ret = lp->cfg->post_init_device(lp);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(lp->dev, "post init device err: %d\n", ret);
|
|
|
|
goto err;
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-22 08:44:05 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
2012-03-24 06:02:01 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-12-18 08:00:43 +08:00
|
|
|
static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
|
|
|
|
{
|
|
|
|
unsigned int period = lp->pdata->period_ns;
|
|
|
|
unsigned int duty = br * period / max_br;
|
|
|
|
struct pwm_device *pwm;
|
|
|
|
|
|
|
|
/* request pwm device with the consumer name */
|
|
|
|
if (!lp->pwm) {
|
|
|
|
pwm = devm_pwm_get(lp->dev, lp->chipname);
|
|
|
|
if (IS_ERR(pwm))
|
|
|
|
return;
|
|
|
|
|
|
|
|
lp->pwm = pwm;
|
2016-04-15 03:17:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME: pwm_apply_args() should be removed when switching to
|
|
|
|
* the atomic PWM API.
|
|
|
|
*/
|
|
|
|
pwm_apply_args(pwm);
|
2012-12-18 08:00:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pwm_config(lp->pwm, duty, period);
|
|
|
|
if (duty)
|
|
|
|
pwm_enable(lp->pwm);
|
|
|
|
else
|
|
|
|
pwm_disable(lp->pwm);
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:02:01 +08:00
|
|
|
static int lp855x_bl_update_status(struct backlight_device *bl)
|
|
|
|
{
|
|
|
|
struct lp855x *lp = bl_get_data(bl);
|
2015-05-12 04:32:05 +08:00
|
|
|
int brightness = bl->props.brightness;
|
2012-03-24 06:02:01 +08:00
|
|
|
|
2013-07-02 20:15:54 +08:00
|
|
|
if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
|
2015-05-12 04:32:05 +08:00
|
|
|
brightness = 0;
|
2012-03-24 06:02:01 +08:00
|
|
|
|
2015-05-12 04:32:05 +08:00
|
|
|
if (lp->mode == PWM_BASED)
|
|
|
|
lp855x_pwm_ctrl(lp, brightness, bl->props.max_brightness);
|
|
|
|
else if (lp->mode == REGISTER_BASED)
|
|
|
|
lp855x_write_byte(lp, lp->cfg->reg_brightness, (u8)brightness);
|
2012-03-24 06:02:01 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct backlight_ops lp855x_bl_ops = {
|
|
|
|
.options = BL_CORE_SUSPENDRESUME,
|
|
|
|
.update_status = lp855x_bl_update_status,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int lp855x_backlight_register(struct lp855x *lp)
|
|
|
|
{
|
|
|
|
struct backlight_device *bl;
|
|
|
|
struct backlight_properties props;
|
|
|
|
struct lp855x_platform_data *pdata = lp->pdata;
|
2013-04-30 07:18:02 +08:00
|
|
|
const char *name = pdata->name ? : DEFAULT_BL_NAME;
|
2012-03-24 06:02:01 +08:00
|
|
|
|
2015-08-28 01:41:15 +08:00
|
|
|
memset(&props, 0, sizeof(props));
|
2012-03-24 06:02:01 +08:00
|
|
|
props.type = BACKLIGHT_PLATFORM;
|
|
|
|
props.max_brightness = MAX_BRIGHTNESS;
|
|
|
|
|
|
|
|
if (pdata->initial_brightness > props.max_brightness)
|
|
|
|
pdata->initial_brightness = props.max_brightness;
|
|
|
|
|
|
|
|
props.brightness = pdata->initial_brightness;
|
|
|
|
|
2013-11-13 07:09:19 +08:00
|
|
|
bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp,
|
2012-03-24 06:02:01 +08:00
|
|
|
&lp855x_bl_ops, &props);
|
|
|
|
if (IS_ERR(bl))
|
|
|
|
return PTR_ERR(bl);
|
|
|
|
|
|
|
|
lp->bl = bl;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t lp855x_get_chip_id(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct lp855x *lp = dev_get_drvdata(dev);
|
2014-08-27 09:12:53 +08:00
|
|
|
|
2013-04-30 07:17:52 +08:00
|
|
|
return scnprintf(buf, PAGE_SIZE, "%s\n", lp->chipname);
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct lp855x *lp = dev_get_drvdata(dev);
|
|
|
|
char *strmode = NULL;
|
|
|
|
|
2013-04-30 07:18:03 +08:00
|
|
|
if (lp->mode == PWM_BASED)
|
2012-03-24 06:02:01 +08:00
|
|
|
strmode = "pwm based";
|
2013-04-30 07:18:03 +08:00
|
|
|
else if (lp->mode == REGISTER_BASED)
|
2012-03-24 06:02:01 +08:00
|
|
|
strmode = "register based";
|
|
|
|
|
2013-04-30 07:17:52 +08:00
|
|
|
return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
|
|
|
|
static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
|
|
|
|
|
|
|
|
static struct attribute *lp855x_attributes[] = {
|
|
|
|
&dev_attr_chip_id.attr,
|
|
|
|
&dev_attr_bl_ctl_mode.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group lp855x_attr_group = {
|
|
|
|
.attrs = lp855x_attributes,
|
|
|
|
};
|
|
|
|
|
2013-04-30 07:18:06 +08:00
|
|
|
#ifdef CONFIG_OF
|
2014-12-03 09:39:11 +08:00
|
|
|
static int lp855x_parse_dt(struct lp855x *lp)
|
2013-04-30 07:18:06 +08:00
|
|
|
{
|
2014-12-03 09:39:11 +08:00
|
|
|
struct device *dev = lp->dev;
|
|
|
|
struct device_node *node = dev->of_node;
|
2013-04-30 07:18:06 +08:00
|
|
|
struct lp855x_platform_data *pdata;
|
|
|
|
int rom_length;
|
|
|
|
|
|
|
|
if (!node) {
|
|
|
|
dev_err(dev, "no platform data\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
|
|
|
if (!pdata)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
of_property_read_string(node, "bl-name", &pdata->name);
|
|
|
|
of_property_read_u8(node, "dev-ctrl", &pdata->device_control);
|
|
|
|
of_property_read_u8(node, "init-brt", &pdata->initial_brightness);
|
|
|
|
of_property_read_u32(node, "pwm-period", &pdata->period_ns);
|
|
|
|
|
|
|
|
/* Fill ROM platform data if defined */
|
|
|
|
rom_length = of_get_child_count(node);
|
|
|
|
if (rom_length > 0) {
|
|
|
|
struct lp855x_rom_data *rom;
|
|
|
|
struct device_node *child;
|
|
|
|
int i = 0;
|
|
|
|
|
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 05:07:58 +08:00
|
|
|
rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL);
|
2013-04-30 07:18:06 +08:00
|
|
|
if (!rom)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for_each_child_of_node(node, child) {
|
|
|
|
of_property_read_u8(child, "rom-addr", &rom[i].addr);
|
|
|
|
of_property_read_u8(child, "rom-val", &rom[i].val);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
pdata->size_program = rom_length;
|
|
|
|
pdata->rom_data = &rom[0];
|
|
|
|
}
|
|
|
|
|
2014-12-03 09:39:11 +08:00
|
|
|
lp->pdata = pdata;
|
2013-04-30 07:18:06 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
2014-12-03 09:39:11 +08:00
|
|
|
static int lp855x_parse_dt(struct lp855x *lp)
|
2013-04-30 07:18:06 +08:00
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-03-24 06:02:01 +08:00
|
|
|
static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
|
|
|
|
{
|
|
|
|
struct lp855x *lp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
|
|
|
|
if (!lp)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
lp->client = cl;
|
|
|
|
lp->dev = &cl->dev;
|
|
|
|
lp->chipname = id->name;
|
|
|
|
lp->chip_id = id->driver_data;
|
2014-12-03 09:39:11 +08:00
|
|
|
lp->pdata = dev_get_platdata(&cl->dev);
|
|
|
|
|
|
|
|
if (!lp->pdata) {
|
|
|
|
ret = lp855x_parse_dt(lp);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lp->pdata->period_ns > 0)
|
|
|
|
lp->mode = PWM_BASED;
|
|
|
|
else
|
|
|
|
lp->mode = REGISTER_BASED;
|
|
|
|
|
2015-07-20 14:45:38 +08:00
|
|
|
lp->supply = devm_regulator_get(lp->dev, "power");
|
|
|
|
if (IS_ERR(lp->supply)) {
|
|
|
|
if (PTR_ERR(lp->supply) == -EPROBE_DEFER)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
lp->supply = NULL;
|
|
|
|
}
|
|
|
|
|
2016-06-11 03:39:57 +08:00
|
|
|
lp->enable = devm_regulator_get_optional(lp->dev, "enable");
|
|
|
|
if (IS_ERR(lp->enable)) {
|
|
|
|
ret = PTR_ERR(lp->enable);
|
|
|
|
if (ret == -ENODEV) {
|
|
|
|
lp->enable = NULL;
|
|
|
|
} else {
|
|
|
|
if (ret != -EPROBE_DEFER)
|
|
|
|
dev_err(lp->dev, "error getting enable regulator: %d\n",
|
|
|
|
ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-20 14:45:38 +08:00
|
|
|
if (lp->supply) {
|
|
|
|
ret = regulator_enable(lp->supply);
|
2014-12-03 09:39:12 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&cl->dev, "failed to enable supply: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-11 03:39:57 +08:00
|
|
|
if (lp->enable) {
|
|
|
|
ret = regulator_enable(lp->enable);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(lp->dev, "failed to enable vddio: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LP8555 datasheet says t_RESPONSE (time between VDDIO and
|
|
|
|
* I2C) is 1ms.
|
|
|
|
*/
|
|
|
|
usleep_range(1000, 2000);
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:02:01 +08:00
|
|
|
i2c_set_clientdata(cl, lp);
|
|
|
|
|
2013-02-22 08:44:05 +08:00
|
|
|
ret = lp855x_configure(lp);
|
2012-03-24 06:02:01 +08:00
|
|
|
if (ret) {
|
2013-02-22 08:44:05 +08:00
|
|
|
dev_err(lp->dev, "device config err: %d", ret);
|
2013-11-13 07:09:19 +08:00
|
|
|
return ret;
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = lp855x_backlight_register(lp);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(lp->dev,
|
|
|
|
"failed to register backlight. err: %d\n", ret);
|
2013-11-13 07:09:19 +08:00
|
|
|
return ret;
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
|
2013-11-13 07:09:19 +08:00
|
|
|
return ret;
|
2012-03-24 06:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
backlight_update_status(lp->bl);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-20 02:26:34 +08:00
|
|
|
static int lp855x_remove(struct i2c_client *cl)
|
2012-03-24 06:02:01 +08:00
|
|
|
{
|
|
|
|
struct lp855x *lp = i2c_get_clientdata(cl);
|
|
|
|
|
|
|
|
lp->bl->props.brightness = 0;
|
|
|
|
backlight_update_status(lp->bl);
|
2015-07-20 14:45:38 +08:00
|
|
|
if (lp->supply)
|
|
|
|
regulator_disable(lp->supply);
|
2012-03-24 06:02:01 +08:00
|
|
|
sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-30 07:18:06 +08:00
|
|
|
static const struct of_device_id lp855x_dt_ids[] = {
|
|
|
|
{ .compatible = "ti,lp8550", },
|
|
|
|
{ .compatible = "ti,lp8551", },
|
|
|
|
{ .compatible = "ti,lp8552", },
|
|
|
|
{ .compatible = "ti,lp8553", },
|
2013-11-13 07:08:57 +08:00
|
|
|
{ .compatible = "ti,lp8555", },
|
2013-04-30 07:18:06 +08:00
|
|
|
{ .compatible = "ti,lp8556", },
|
|
|
|
{ .compatible = "ti,lp8557", },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, lp855x_dt_ids);
|
|
|
|
|
2012-03-24 06:02:01 +08:00
|
|
|
static const struct i2c_device_id lp855x_ids[] = {
|
|
|
|
{"lp8550", LP8550},
|
|
|
|
{"lp8551", LP8551},
|
|
|
|
{"lp8552", LP8552},
|
|
|
|
{"lp8553", LP8553},
|
2013-11-13 07:08:57 +08:00
|
|
|
{"lp8555", LP8555},
|
2012-03-24 06:02:01 +08:00
|
|
|
{"lp8556", LP8556},
|
2013-02-22 08:44:06 +08:00
|
|
|
{"lp8557", LP8557},
|
2012-03-24 06:02:01 +08:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, lp855x_ids);
|
|
|
|
|
|
|
|
static struct i2c_driver lp855x_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "lp855x",
|
2013-04-30 07:18:06 +08:00
|
|
|
.of_match_table = of_match_ptr(lp855x_dt_ids),
|
2012-03-24 06:02:01 +08:00
|
|
|
},
|
|
|
|
.probe = lp855x_probe,
|
2012-11-20 02:21:09 +08:00
|
|
|
.remove = lp855x_remove,
|
2012-03-24 06:02:01 +08:00
|
|
|
.id_table = lp855x_ids,
|
|
|
|
};
|
|
|
|
|
|
|
|
module_i2c_driver(lp855x_driver);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
|
|
|
|
MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
|
|
|
|
MODULE_LICENSE("GPL");
|