cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

leds-lm3530.c (12910B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2011 ST-Ericsson SA.
      4 * Copyright (C) 2009 Motorola, Inc.
      5 *
      6 * Simple driver for National Semiconductor LM3530 Backlight driver chip
      7 *
      8 * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
      9 * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>
     10 */
     11
     12#include <linux/i2c.h>
     13#include <linux/leds.h>
     14#include <linux/slab.h>
     15#include <linux/platform_device.h>
     16#include <linux/input.h>
     17#include <linux/led-lm3530.h>
     18#include <linux/types.h>
     19#include <linux/regulator/consumer.h>
     20#include <linux/module.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_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			14
     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_ALS_ZB_MAX		(4)
     71#define LM3530_ALS_WINDOW_mV		(1000)
     72#define LM3530_ALS_OFFSET_mV		(4)
     73
     74/* Zone Target Register defaults */
     75#define LM3530_DEF_ZT_0			(0x7F)
     76#define LM3530_DEF_ZT_1			(0x66)
     77#define LM3530_DEF_ZT_2			(0x4C)
     78#define LM3530_DEF_ZT_3			(0x33)
     79#define LM3530_DEF_ZT_4			(0x19)
     80
     81/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */
     82#define MAX_BRIGHTNESS			(127)
     83
     84struct lm3530_mode_map {
     85	const char *mode;
     86	enum lm3530_mode mode_val;
     87};
     88
     89static struct lm3530_mode_map mode_map[] = {
     90	{ "man", LM3530_BL_MODE_MANUAL },
     91	{ "als", LM3530_BL_MODE_ALS },
     92	{ "pwm", LM3530_BL_MODE_PWM },
     93};
     94
     95/**
     96 * struct lm3530_data
     97 * @led_dev: led class device
     98 * @client: i2c client
     99 * @pdata: LM3530 platform data
    100 * @mode: mode of operation - manual, ALS, PWM
    101 * @regulator: regulator
    102 * @brightness: previous brightness value
    103 * @enable: regulator is enabled
    104 */
    105struct lm3530_data {
    106	struct led_classdev led_dev;
    107	struct i2c_client *client;
    108	struct lm3530_platform_data *pdata;
    109	enum lm3530_mode mode;
    110	struct regulator *regulator;
    111	enum led_brightness brightness;
    112	bool enable;
    113};
    114
    115/*
    116 * struct lm3530_als_data
    117 * @config  : value of ALS configuration register
    118 * @imp_sel : value of ALS resistor select register
    119 * @zone    : values of ALS ZB(Zone Boundary) registers
    120 */
    121struct lm3530_als_data {
    122	u8 config;
    123	u8 imp_sel;
    124	u8 zones[LM3530_ALS_ZB_MAX];
    125};
    126
    127static const u8 lm3530_reg[LM3530_REG_MAX] = {
    128	LM3530_GEN_CONFIG,
    129	LM3530_ALS_CONFIG,
    130	LM3530_BRT_RAMP_RATE,
    131	LM3530_ALS_IMP_SELECT,
    132	LM3530_BRT_CTRL_REG,
    133	LM3530_ALS_ZB0_REG,
    134	LM3530_ALS_ZB1_REG,
    135	LM3530_ALS_ZB2_REG,
    136	LM3530_ALS_ZB3_REG,
    137	LM3530_ALS_Z0T_REG,
    138	LM3530_ALS_Z1T_REG,
    139	LM3530_ALS_Z2T_REG,
    140	LM3530_ALS_Z3T_REG,
    141	LM3530_ALS_Z4T_REG,
    142};
    143
    144static int lm3530_get_mode_from_str(const char *str)
    145{
    146	int i;
    147
    148	for (i = 0; i < ARRAY_SIZE(mode_map); i++)
    149		if (sysfs_streq(str, mode_map[i].mode))
    150			return mode_map[i].mode_val;
    151
    152	return -EINVAL;
    153}
    154
    155static void lm3530_als_configure(struct lm3530_platform_data *pdata,
    156				struct lm3530_als_data *als)
    157{
    158	int i;
    159	u32 als_vmin, als_vmax, als_vstep;
    160
    161	if (pdata->als_vmax == 0) {
    162		pdata->als_vmin = 0;
    163		pdata->als_vmax = LM3530_ALS_WINDOW_mV;
    164	}
    165
    166	als_vmin = pdata->als_vmin;
    167	als_vmax = pdata->als_vmax;
    168
    169	if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
    170		pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV;
    171
    172	/* n zone boundary makes n+1 zones */
    173	als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
    174
    175	for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
    176		als->zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
    177			als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
    178
    179	als->config =
    180		(pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
    181		(LM3530_ENABLE_ALS) |
    182		(pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);
    183
    184	als->imp_sel =
    185		(pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
    186		(pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
    187}
    188
    189static int lm3530_led_enable(struct lm3530_data *drvdata)
    190{
    191	int ret;
    192
    193	if (drvdata->enable)
    194		return 0;
    195
    196	ret = regulator_enable(drvdata->regulator);
    197	if (ret) {
    198		dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret);
    199		return ret;
    200	}
    201
    202	drvdata->enable = true;
    203	return 0;
    204}
    205
    206static void lm3530_led_disable(struct lm3530_data *drvdata)
    207{
    208	int ret;
    209
    210	if (!drvdata->enable)
    211		return;
    212
    213	ret = regulator_disable(drvdata->regulator);
    214	if (ret) {
    215		dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n",
    216			ret);
    217		return;
    218	}
    219
    220	drvdata->enable = false;
    221}
    222
    223static int lm3530_init_registers(struct lm3530_data *drvdata)
    224{
    225	int ret = 0;
    226	int i;
    227	u8 gen_config;
    228	u8 brt_ramp;
    229	u8 brightness;
    230	u8 reg_val[LM3530_REG_MAX];
    231	struct lm3530_platform_data *pdata = drvdata->pdata;
    232	struct i2c_client *client = drvdata->client;
    233	struct lm3530_pwm_data *pwm = &pdata->pwm_data;
    234	struct lm3530_als_data als;
    235
    236	memset(&als, 0, sizeof(struct lm3530_als_data));
    237
    238	gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
    239			((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT);
    240
    241	switch (drvdata->mode) {
    242	case LM3530_BL_MODE_MANUAL:
    243		gen_config |= LM3530_ENABLE_I2C;
    244		break;
    245	case LM3530_BL_MODE_ALS:
    246		gen_config |= LM3530_ENABLE_I2C;
    247		lm3530_als_configure(pdata, &als);
    248		break;
    249	case LM3530_BL_MODE_PWM:
    250		gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |
    251			      (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT);
    252		break;
    253	}
    254
    255	brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
    256			(pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
    257
    258	if (drvdata->brightness)
    259		brightness = drvdata->brightness;
    260	else
    261		brightness = drvdata->brightness = pdata->brt_val;
    262
    263	if (brightness > drvdata->led_dev.max_brightness)
    264		brightness = drvdata->led_dev.max_brightness;
    265
    266	reg_val[0] = gen_config;	/* LM3530_GEN_CONFIG */
    267	reg_val[1] = als.config;	/* LM3530_ALS_CONFIG */
    268	reg_val[2] = brt_ramp;		/* LM3530_BRT_RAMP_RATE */
    269	reg_val[3] = als.imp_sel;	/* LM3530_ALS_IMP_SELECT */
    270	reg_val[4] = brightness;	/* LM3530_BRT_CTRL_REG */
    271	reg_val[5] = als.zones[0];	/* LM3530_ALS_ZB0_REG */
    272	reg_val[6] = als.zones[1];	/* LM3530_ALS_ZB1_REG */
    273	reg_val[7] = als.zones[2];	/* LM3530_ALS_ZB2_REG */
    274	reg_val[8] = als.zones[3];	/* LM3530_ALS_ZB3_REG */
    275	reg_val[9] = LM3530_DEF_ZT_0;	/* LM3530_ALS_Z0T_REG */
    276	reg_val[10] = LM3530_DEF_ZT_1;	/* LM3530_ALS_Z1T_REG */
    277	reg_val[11] = LM3530_DEF_ZT_2;	/* LM3530_ALS_Z2T_REG */
    278	reg_val[12] = LM3530_DEF_ZT_3;	/* LM3530_ALS_Z3T_REG */
    279	reg_val[13] = LM3530_DEF_ZT_4;	/* LM3530_ALS_Z4T_REG */
    280
    281	ret = lm3530_led_enable(drvdata);
    282	if (ret)
    283		return ret;
    284
    285	for (i = 0; i < LM3530_REG_MAX; i++) {
    286		/* do not update brightness register when pwm mode */
    287		if (lm3530_reg[i] == LM3530_BRT_CTRL_REG &&
    288		    drvdata->mode == LM3530_BL_MODE_PWM) {
    289			if (pwm->pwm_set_intensity)
    290				pwm->pwm_set_intensity(reg_val[i],
    291					drvdata->led_dev.max_brightness);
    292			continue;
    293		}
    294
    295		ret = i2c_smbus_write_byte_data(client,
    296				lm3530_reg[i], reg_val[i]);
    297		if (ret)
    298			break;
    299	}
    300
    301	return ret;
    302}
    303
    304static void lm3530_brightness_set(struct led_classdev *led_cdev,
    305				     enum led_brightness brt_val)
    306{
    307	int err;
    308	struct lm3530_data *drvdata =
    309	    container_of(led_cdev, struct lm3530_data, led_dev);
    310	struct lm3530_platform_data *pdata = drvdata->pdata;
    311	struct lm3530_pwm_data *pwm = &pdata->pwm_data;
    312	u8 max_brightness = led_cdev->max_brightness;
    313
    314	switch (drvdata->mode) {
    315	case LM3530_BL_MODE_MANUAL:
    316
    317		if (!drvdata->enable) {
    318			err = lm3530_init_registers(drvdata);
    319			if (err) {
    320				dev_err(&drvdata->client->dev,
    321					"Register Init failed: %d\n", err);
    322				break;
    323			}
    324		}
    325
    326		/* set the brightness in brightness control register*/
    327		err = i2c_smbus_write_byte_data(drvdata->client,
    328				LM3530_BRT_CTRL_REG, brt_val);
    329		if (err)
    330			dev_err(&drvdata->client->dev,
    331				"Unable to set brightness: %d\n", err);
    332		else
    333			drvdata->brightness = brt_val;
    334
    335		if (brt_val == 0)
    336			lm3530_led_disable(drvdata);
    337		break;
    338	case LM3530_BL_MODE_ALS:
    339		break;
    340	case LM3530_BL_MODE_PWM:
    341		if (pwm->pwm_set_intensity)
    342			pwm->pwm_set_intensity(brt_val, max_brightness);
    343		break;
    344	default:
    345		break;
    346	}
    347}
    348
    349static ssize_t mode_show(struct device *dev,
    350			 struct device_attribute *attr, char *buf)
    351{
    352	struct led_classdev *led_cdev = dev_get_drvdata(dev);
    353	struct lm3530_data *drvdata;
    354	int i, len = 0;
    355
    356	drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
    357	for (i = 0; i < ARRAY_SIZE(mode_map); i++)
    358		if (drvdata->mode == mode_map[i].mode_val)
    359			len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
    360		else
    361			len += sprintf(buf + len, "%s ", mode_map[i].mode);
    362
    363	len += sprintf(buf + len, "\n");
    364
    365	return len;
    366}
    367
    368static ssize_t mode_store(struct device *dev, struct device_attribute
    369			  *attr, const char *buf, size_t size)
    370{
    371	struct led_classdev *led_cdev = dev_get_drvdata(dev);
    372	struct lm3530_data *drvdata;
    373	struct lm3530_pwm_data *pwm;
    374	u8 max_brightness;
    375	int mode, err;
    376
    377	drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
    378	pwm = &drvdata->pdata->pwm_data;
    379	max_brightness = led_cdev->max_brightness;
    380	mode = lm3530_get_mode_from_str(buf);
    381	if (mode < 0) {
    382		dev_err(dev, "Invalid mode\n");
    383		return mode;
    384	}
    385
    386	drvdata->mode = mode;
    387
    388	/* set pwm to low if unnecessary */
    389	if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity)
    390		pwm->pwm_set_intensity(0, max_brightness);
    391
    392	err = lm3530_init_registers(drvdata);
    393	if (err) {
    394		dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);
    395		return err;
    396	}
    397
    398	return sizeof(drvdata->mode);
    399}
    400static DEVICE_ATTR_RW(mode);
    401
    402static struct attribute *lm3530_attrs[] = {
    403	&dev_attr_mode.attr,
    404	NULL
    405};
    406ATTRIBUTE_GROUPS(lm3530);
    407
    408static int lm3530_probe(struct i2c_client *client,
    409			   const struct i2c_device_id *id)
    410{
    411	struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev);
    412	struct lm3530_data *drvdata;
    413	int err = 0;
    414
    415	if (pdata == NULL) {
    416		dev_err(&client->dev, "platform data required\n");
    417		return -ENODEV;
    418	}
    419
    420	/* BL mode */
    421	if (pdata->mode > LM3530_BL_MODE_PWM) {
    422		dev_err(&client->dev, "Illegal Mode request\n");
    423		return -EINVAL;
    424	}
    425
    426	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    427		dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
    428		return -EIO;
    429	}
    430
    431	drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data),
    432				GFP_KERNEL);
    433	if (drvdata == NULL)
    434		return -ENOMEM;
    435
    436	drvdata->mode = pdata->mode;
    437	drvdata->client = client;
    438	drvdata->pdata = pdata;
    439	drvdata->brightness = LED_OFF;
    440	drvdata->enable = false;
    441	drvdata->led_dev.name = LM3530_LED_DEV;
    442	drvdata->led_dev.brightness_set = lm3530_brightness_set;
    443	drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;
    444	drvdata->led_dev.groups = lm3530_groups;
    445
    446	i2c_set_clientdata(client, drvdata);
    447
    448	drvdata->regulator = devm_regulator_get(&client->dev, "vin");
    449	if (IS_ERR(drvdata->regulator)) {
    450		dev_err(&client->dev, "regulator get failed\n");
    451		err = PTR_ERR(drvdata->regulator);
    452		drvdata->regulator = NULL;
    453		return err;
    454	}
    455
    456	if (drvdata->pdata->brt_val) {
    457		err = lm3530_init_registers(drvdata);
    458		if (err < 0) {
    459			dev_err(&client->dev,
    460				"Register Init failed: %d\n", err);
    461			return err;
    462		}
    463	}
    464	err = led_classdev_register(&client->dev, &drvdata->led_dev);
    465	if (err < 0) {
    466		dev_err(&client->dev, "Register led class failed: %d\n", err);
    467		return err;
    468	}
    469
    470	return 0;
    471}
    472
    473static int lm3530_remove(struct i2c_client *client)
    474{
    475	struct lm3530_data *drvdata = i2c_get_clientdata(client);
    476
    477	lm3530_led_disable(drvdata);
    478	led_classdev_unregister(&drvdata->led_dev);
    479	return 0;
    480}
    481
    482static const struct i2c_device_id lm3530_id[] = {
    483	{LM3530_NAME, 0},
    484	{}
    485};
    486MODULE_DEVICE_TABLE(i2c, lm3530_id);
    487
    488static struct i2c_driver lm3530_i2c_driver = {
    489	.probe = lm3530_probe,
    490	.remove = lm3530_remove,
    491	.id_table = lm3530_id,
    492	.driver = {
    493		.name = LM3530_NAME,
    494	},
    495};
    496
    497module_i2c_driver(lm3530_i2c_driver);
    498
    499MODULE_DESCRIPTION("Back Light driver for LM3530");
    500MODULE_LICENSE("GPL v2");
    501MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");