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

lp8788_bl.c (7723B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * TI LP8788 MFD - backlight driver
      4 *
      5 * Copyright 2012 Texas Instruments
      6 *
      7 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
      8 */
      9
     10#include <linux/backlight.h>
     11#include <linux/err.h>
     12#include <linux/mfd/lp8788.h>
     13#include <linux/module.h>
     14#include <linux/platform_device.h>
     15#include <linux/pwm.h>
     16#include <linux/slab.h>
     17
     18/* Register address */
     19#define LP8788_BL_CONFIG		0x96
     20#define LP8788_BL_EN			BIT(0)
     21#define LP8788_BL_PWM_INPUT_EN		BIT(5)
     22#define LP8788_BL_FULLSCALE_SHIFT	2
     23#define LP8788_BL_DIM_MODE_SHIFT	1
     24#define LP8788_BL_PWM_POLARITY_SHIFT	6
     25
     26#define LP8788_BL_BRIGHTNESS		0x97
     27
     28#define LP8788_BL_RAMP			0x98
     29#define LP8788_BL_RAMP_RISE_SHIFT	4
     30
     31#define MAX_BRIGHTNESS			127
     32#define DEFAULT_BL_NAME			"lcd-backlight"
     33
     34struct lp8788_bl_config {
     35	enum lp8788_bl_ctrl_mode bl_mode;
     36	enum lp8788_bl_dim_mode dim_mode;
     37	enum lp8788_bl_full_scale_current full_scale;
     38	enum lp8788_bl_ramp_step rise_time;
     39	enum lp8788_bl_ramp_step fall_time;
     40	enum pwm_polarity pwm_pol;
     41};
     42
     43struct lp8788_bl {
     44	struct lp8788 *lp;
     45	struct backlight_device *bl_dev;
     46	struct lp8788_backlight_platform_data *pdata;
     47	enum lp8788_bl_ctrl_mode mode;
     48	struct pwm_device *pwm;
     49};
     50
     51static struct lp8788_bl_config default_bl_config = {
     52	.bl_mode    = LP8788_BL_REGISTER_ONLY,
     53	.dim_mode   = LP8788_DIM_EXPONENTIAL,
     54	.full_scale = LP8788_FULLSCALE_1900uA,
     55	.rise_time  = LP8788_RAMP_8192us,
     56	.fall_time  = LP8788_RAMP_8192us,
     57	.pwm_pol    = PWM_POLARITY_NORMAL,
     58};
     59
     60static inline bool is_brightness_ctrl_by_pwm(enum lp8788_bl_ctrl_mode mode)
     61{
     62	return mode == LP8788_BL_COMB_PWM_BASED;
     63}
     64
     65static inline bool is_brightness_ctrl_by_register(enum lp8788_bl_ctrl_mode mode)
     66{
     67	return mode == LP8788_BL_REGISTER_ONLY ||
     68		mode == LP8788_BL_COMB_REGISTER_BASED;
     69}
     70
     71static int lp8788_backlight_configure(struct lp8788_bl *bl)
     72{
     73	struct lp8788_backlight_platform_data *pdata = bl->pdata;
     74	struct lp8788_bl_config *cfg = &default_bl_config;
     75	int ret;
     76	u8 val;
     77
     78	/*
     79	 * Update chip configuration if platform data exists,
     80	 * otherwise use the default settings.
     81	 */
     82	if (pdata) {
     83		cfg->bl_mode    = pdata->bl_mode;
     84		cfg->dim_mode   = pdata->dim_mode;
     85		cfg->full_scale = pdata->full_scale;
     86		cfg->rise_time  = pdata->rise_time;
     87		cfg->fall_time  = pdata->fall_time;
     88		cfg->pwm_pol    = pdata->pwm_pol;
     89	}
     90
     91	/* Brightness ramp up/down */
     92	val = (cfg->rise_time << LP8788_BL_RAMP_RISE_SHIFT) | cfg->fall_time;
     93	ret = lp8788_write_byte(bl->lp, LP8788_BL_RAMP, val);
     94	if (ret)
     95		return ret;
     96
     97	/* Fullscale current setting */
     98	val = (cfg->full_scale << LP8788_BL_FULLSCALE_SHIFT) |
     99		(cfg->dim_mode << LP8788_BL_DIM_MODE_SHIFT);
    100
    101	/* Brightness control mode */
    102	switch (cfg->bl_mode) {
    103	case LP8788_BL_REGISTER_ONLY:
    104		val |= LP8788_BL_EN;
    105		break;
    106	case LP8788_BL_COMB_PWM_BASED:
    107	case LP8788_BL_COMB_REGISTER_BASED:
    108		val |= LP8788_BL_EN | LP8788_BL_PWM_INPUT_EN |
    109			(cfg->pwm_pol << LP8788_BL_PWM_POLARITY_SHIFT);
    110		break;
    111	default:
    112		dev_err(bl->lp->dev, "invalid mode: %d\n", cfg->bl_mode);
    113		return -EINVAL;
    114	}
    115
    116	bl->mode = cfg->bl_mode;
    117
    118	return lp8788_write_byte(bl->lp, LP8788_BL_CONFIG, val);
    119}
    120
    121static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
    122{
    123	unsigned int period;
    124	unsigned int duty;
    125	struct device *dev;
    126	struct pwm_device *pwm;
    127
    128	if (!bl->pdata)
    129		return;
    130
    131	period = bl->pdata->period_ns;
    132	duty = br * period / max_br;
    133	dev = bl->lp->dev;
    134
    135	/* request PWM device with the consumer name */
    136	if (!bl->pwm) {
    137		pwm = devm_pwm_get(dev, LP8788_DEV_BACKLIGHT);
    138		if (IS_ERR(pwm)) {
    139			dev_err(dev, "can not get PWM device\n");
    140			return;
    141		}
    142
    143		bl->pwm = pwm;
    144
    145		/*
    146		 * FIXME: pwm_apply_args() should be removed when switching to
    147		 * the atomic PWM API.
    148		 */
    149		pwm_apply_args(pwm);
    150	}
    151
    152	pwm_config(bl->pwm, duty, period);
    153	if (duty)
    154		pwm_enable(bl->pwm);
    155	else
    156		pwm_disable(bl->pwm);
    157}
    158
    159static int lp8788_bl_update_status(struct backlight_device *bl_dev)
    160{
    161	struct lp8788_bl *bl = bl_get_data(bl_dev);
    162	enum lp8788_bl_ctrl_mode mode = bl->mode;
    163
    164	if (bl_dev->props.state & BL_CORE_SUSPENDED)
    165		bl_dev->props.brightness = 0;
    166
    167	if (is_brightness_ctrl_by_pwm(mode)) {
    168		int brt = bl_dev->props.brightness;
    169		int max = bl_dev->props.max_brightness;
    170
    171		lp8788_pwm_ctrl(bl, brt, max);
    172	} else if (is_brightness_ctrl_by_register(mode)) {
    173		u8 brt = bl_dev->props.brightness;
    174
    175		lp8788_write_byte(bl->lp, LP8788_BL_BRIGHTNESS, brt);
    176	}
    177
    178	return 0;
    179}
    180
    181static const struct backlight_ops lp8788_bl_ops = {
    182	.options = BL_CORE_SUSPENDRESUME,
    183	.update_status = lp8788_bl_update_status,
    184};
    185
    186static int lp8788_backlight_register(struct lp8788_bl *bl)
    187{
    188	struct backlight_device *bl_dev;
    189	struct backlight_properties props;
    190	struct lp8788_backlight_platform_data *pdata = bl->pdata;
    191	int init_brt;
    192	char *name;
    193
    194	props.type = BACKLIGHT_PLATFORM;
    195	props.max_brightness = MAX_BRIGHTNESS;
    196
    197	/* Initial brightness */
    198	if (pdata)
    199		init_brt = min_t(int, pdata->initial_brightness,
    200				props.max_brightness);
    201	else
    202		init_brt = 0;
    203
    204	props.brightness = init_brt;
    205
    206	/* Backlight device name */
    207	if (!pdata || !pdata->name)
    208		name = DEFAULT_BL_NAME;
    209	else
    210		name = pdata->name;
    211
    212	bl_dev = backlight_device_register(name, bl->lp->dev, bl,
    213				       &lp8788_bl_ops, &props);
    214	if (IS_ERR(bl_dev))
    215		return PTR_ERR(bl_dev);
    216
    217	bl->bl_dev = bl_dev;
    218
    219	return 0;
    220}
    221
    222static void lp8788_backlight_unregister(struct lp8788_bl *bl)
    223{
    224	struct backlight_device *bl_dev = bl->bl_dev;
    225
    226	backlight_device_unregister(bl_dev);
    227}
    228
    229static ssize_t lp8788_get_bl_ctl_mode(struct device *dev,
    230				     struct device_attribute *attr, char *buf)
    231{
    232	struct lp8788_bl *bl = dev_get_drvdata(dev);
    233	enum lp8788_bl_ctrl_mode mode = bl->mode;
    234	char *strmode;
    235
    236	if (is_brightness_ctrl_by_pwm(mode))
    237		strmode = "PWM based";
    238	else if (is_brightness_ctrl_by_register(mode))
    239		strmode = "Register based";
    240	else
    241		strmode = "Invalid mode";
    242
    243	return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
    244}
    245
    246static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp8788_get_bl_ctl_mode, NULL);
    247
    248static struct attribute *lp8788_attributes[] = {
    249	&dev_attr_bl_ctl_mode.attr,
    250	NULL,
    251};
    252
    253static const struct attribute_group lp8788_attr_group = {
    254	.attrs = lp8788_attributes,
    255};
    256
    257static int lp8788_backlight_probe(struct platform_device *pdev)
    258{
    259	struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
    260	struct lp8788_bl *bl;
    261	int ret;
    262
    263	bl = devm_kzalloc(lp->dev, sizeof(struct lp8788_bl), GFP_KERNEL);
    264	if (!bl)
    265		return -ENOMEM;
    266
    267	bl->lp = lp;
    268	if (lp->pdata)
    269		bl->pdata = lp->pdata->bl_pdata;
    270
    271	platform_set_drvdata(pdev, bl);
    272
    273	ret = lp8788_backlight_configure(bl);
    274	if (ret) {
    275		dev_err(lp->dev, "backlight config err: %d\n", ret);
    276		goto err_dev;
    277	}
    278
    279	ret = lp8788_backlight_register(bl);
    280	if (ret) {
    281		dev_err(lp->dev, "register backlight err: %d\n", ret);
    282		goto err_dev;
    283	}
    284
    285	ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group);
    286	if (ret) {
    287		dev_err(lp->dev, "register sysfs err: %d\n", ret);
    288		goto err_sysfs;
    289	}
    290
    291	backlight_update_status(bl->bl_dev);
    292
    293	return 0;
    294
    295err_sysfs:
    296	lp8788_backlight_unregister(bl);
    297err_dev:
    298	return ret;
    299}
    300
    301static int lp8788_backlight_remove(struct platform_device *pdev)
    302{
    303	struct lp8788_bl *bl = platform_get_drvdata(pdev);
    304	struct backlight_device *bl_dev = bl->bl_dev;
    305
    306	bl_dev->props.brightness = 0;
    307	backlight_update_status(bl_dev);
    308	sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group);
    309	lp8788_backlight_unregister(bl);
    310
    311	return 0;
    312}
    313
    314static struct platform_driver lp8788_bl_driver = {
    315	.probe = lp8788_backlight_probe,
    316	.remove = lp8788_backlight_remove,
    317	.driver = {
    318		.name = LP8788_DEV_BACKLIGHT,
    319	},
    320};
    321module_platform_driver(lp8788_bl_driver);
    322
    323MODULE_DESCRIPTION("Texas Instruments LP8788 Backlight Driver");
    324MODULE_AUTHOR("Milo Kim");
    325MODULE_LICENSE("GPL");
    326MODULE_ALIAS("platform:lp8788-backlight");