1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* TI LP8788 MFD - backlight driver
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
*/
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/mfd/lp8788.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* Register address */
#define LP8788_BL_CONFIG 0x96
#define LP8788_BL_EN BIT(0)
#define LP8788_BL_PWM_INPUT_EN BIT(5)
#define LP8788_BL_FULLSCALE_SHIFT 2
#define LP8788_BL_DIM_MODE_SHIFT 1
#define LP8788_BL_PWM_POLARITY_SHIFT 6
#define LP8788_BL_BRIGHTNESS 0x97
#define LP8788_BL_RAMP 0x98
#define LP8788_BL_RAMP_RISE_SHIFT 4
#define MAX_BRIGHTNESS 127
#define DEFAULT_BL_NAME "lcd-backlight"
struct lp8788_bl {
struct lp8788 *lp;
struct backlight_device *bl_dev;
};
static int lp8788_backlight_configure(struct lp8788_bl *bl)
{
int ret;
u8 val;
/* Brightness ramp up/down */
val = (LP8788_RAMP_8192us << LP8788_BL_RAMP_RISE_SHIFT) | LP8788_RAMP_8192us;
ret = lp8788_write_byte(bl->lp, LP8788_BL_RAMP, val);
if (ret)
return ret;
/* Fullscale current setting */
val = (LP8788_FULLSCALE_1900uA << LP8788_BL_FULLSCALE_SHIFT) |
(LP8788_DIM_EXPONENTIAL << LP8788_BL_DIM_MODE_SHIFT);
/* Brightness control mode */
val |= LP8788_BL_EN;
return lp8788_write_byte(bl->lp, LP8788_BL_CONFIG, val);
}
static int lp8788_bl_update_status(struct backlight_device *bl_dev)
{
struct lp8788_bl *bl = bl_get_data(bl_dev);
if (bl_dev->props.state & BL_CORE_SUSPENDED)
bl_dev->props.brightness = 0;
lp8788_write_byte(bl->lp, LP8788_BL_BRIGHTNESS, bl_dev->props.brightness);
return 0;
}
static const struct backlight_ops lp8788_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = lp8788_bl_update_status,
};
static int lp8788_backlight_register(struct lp8788_bl *bl)
{
struct backlight_device *bl_dev;
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = MAX_BRIGHTNESS;
/* Initial brightness */
props.brightness = 0;
/* Backlight device name */
bl_dev = backlight_device_register(DEFAULT_BL_NAME, bl->lp->dev, bl,
&lp8788_bl_ops, &props);
if (IS_ERR(bl_dev))
return PTR_ERR(bl_dev);
bl->bl_dev = bl_dev;
return 0;
}
static void lp8788_backlight_unregister(struct lp8788_bl *bl)
{
struct backlight_device *bl_dev = bl->bl_dev;
backlight_device_unregister(bl_dev);
}
static ssize_t lp8788_get_bl_ctl_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
const char *strmode = "Register based";
return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
}
static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp8788_get_bl_ctl_mode, NULL);
static struct attribute *lp8788_attributes[] = {
&dev_attr_bl_ctl_mode.attr,
NULL,
};
static const struct attribute_group lp8788_attr_group = {
.attrs = lp8788_attributes,
};
static int lp8788_backlight_probe(struct platform_device *pdev)
{
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
struct lp8788_bl *bl;
int ret;
bl = devm_kzalloc(lp->dev, sizeof(struct lp8788_bl), GFP_KERNEL);
if (!bl)
return -ENOMEM;
bl->lp = lp;
platform_set_drvdata(pdev, bl);
ret = lp8788_backlight_configure(bl);
if (ret) {
dev_err(lp->dev, "backlight config err: %d\n", ret);
goto err_dev;
}
ret = lp8788_backlight_register(bl);
if (ret) {
dev_err(lp->dev, "register backlight err: %d\n", ret);
goto err_dev;
}
ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group);
if (ret) {
dev_err(lp->dev, "register sysfs err: %d\n", ret);
goto err_sysfs;
}
backlight_update_status(bl->bl_dev);
return 0;
err_sysfs:
lp8788_backlight_unregister(bl);
err_dev:
return ret;
}
static void lp8788_backlight_remove(struct platform_device *pdev)
{
struct lp8788_bl *bl = platform_get_drvdata(pdev);
struct backlight_device *bl_dev = bl->bl_dev;
bl_dev->props.brightness = 0;
backlight_update_status(bl_dev);
sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group);
lp8788_backlight_unregister(bl);
}
static struct platform_driver lp8788_bl_driver = {
.probe = lp8788_backlight_probe,
.remove_new = lp8788_backlight_remove,
.driver = {
.name = LP8788_DEV_BACKLIGHT,
},
};
module_platform_driver(lp8788_bl_driver);
MODULE_DESCRIPTION("Texas Instruments LP8788 Backlight Driver");
MODULE_AUTHOR("Milo Kim");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:lp8788-backlight");
|