linux/drivers/video/backlight/sky81452-backlight.c
Uwe Kleine-König 541ec23c60 backlight: sky81452-backlight: Convert to platform remove callback returning void
The .remove() callback for a platform driver returns an int which makes
many driver authors wrongly assume it's possible to do error handling by
returning an error code. However the value returned is (mostly) ignored
and this typically results in resource leaks. To improve here there is a
quest to make the remove callback return void. In the first step of this
quest all drivers are converted to .remove_new() which already returns
void.

Trivially convert this driver from always returning zero in the remove
callback to the void returning variant.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Lee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/20230308073945.2336302-14-u.kleine-koenig@pengutronix.de
2023-03-16 15:08:37 +00:00

352 lines
8.9 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* sky81452-backlight.c SKY81452 backlight driver
*
* Copyright 2014 Skyworks Solutions Inc.
* Author : Gyungoh Yoo <jack.yoo@skyworksinc.com>
*/
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* registers */
#define SKY81452_REG0 0x00
#define SKY81452_REG1 0x01
#define SKY81452_REG2 0x02
#define SKY81452_REG4 0x04
#define SKY81452_REG5 0x05
/* bit mask */
#define SKY81452_CS 0xFF
#define SKY81452_EN 0x3F
#define SKY81452_IGPW 0x20
#define SKY81452_PWMMD 0x10
#define SKY81452_PHASE 0x08
#define SKY81452_ILIM 0x04
#define SKY81452_VSHRT 0x03
#define SKY81452_OCP 0x80
#define SKY81452_OTMP 0x40
#define SKY81452_SHRT 0x3F
#define SKY81452_OPN 0x3F
#define SKY81452_DEFAULT_NAME "lcd-backlight"
#define SKY81452_MAX_BRIGHTNESS (SKY81452_CS + 1)
/**
* struct sky81452_bl_platform_data - backlight platform data
* @name: backlight driver name.
* If it is not defined, default name is lcd-backlight.
* @gpiod_enable:GPIO descriptor which control EN pin
* @enable: Enable mask for current sink channel 1, 2, 3, 4, 5 and 6.
* @ignore_pwm: true if DPWMI should be ignored.
* @dpwm_mode: true is DPWM dimming mode, otherwise Analog dimming mode.
* @phase_shift:true is phase shift mode.
* @short_detection_threshold: It should be one of 4, 5, 6 and 7V.
* @boost_current_limit: It should be one of 2300, 2750mA.
*/
struct sky81452_bl_platform_data {
const char *name;
struct gpio_desc *gpiod_enable;
unsigned int enable;
bool ignore_pwm;
bool dpwm_mode;
bool phase_shift;
unsigned int short_detection_threshold;
unsigned int boost_current_limit;
};
#define CTZ(b) __builtin_ctz(b)
static int sky81452_bl_update_status(struct backlight_device *bd)
{
const struct sky81452_bl_platform_data *pdata =
dev_get_platdata(bd->dev.parent);
const unsigned int brightness = (unsigned int)bd->props.brightness;
struct regmap *regmap = bl_get_data(bd);
int ret;
if (brightness > 0) {
ret = regmap_write(regmap, SKY81452_REG0, brightness - 1);
if (ret < 0)
return ret;
return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN,
pdata->enable << CTZ(SKY81452_EN));
}
return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, 0);
}
static const struct backlight_ops sky81452_bl_ops = {
.update_status = sky81452_bl_update_status,
};
static ssize_t sky81452_bl_store_enable(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct regmap *regmap = bl_get_data(to_backlight_device(dev));
unsigned long value;
int ret;
ret = kstrtoul(buf, 16, &value);
if (ret < 0)
return ret;
ret = regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN,
value << CTZ(SKY81452_EN));
if (ret < 0)
return ret;
return count;
}
static ssize_t sky81452_bl_show_open_short(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct regmap *regmap = bl_get_data(to_backlight_device(dev));
unsigned int reg, value = 0;
char tmp[3];
int i, ret;
reg = !strcmp(attr->attr.name, "open") ? SKY81452_REG5 : SKY81452_REG4;
ret = regmap_read(regmap, reg, &value);
if (ret < 0)
return ret;
if (value & SKY81452_SHRT) {
*buf = 0;
for (i = 0; i < 6; i++) {
if (value & 0x01) {
sprintf(tmp, "%d ", i + 1);
strcat(buf, tmp);
}
value >>= 1;
}
strcat(buf, "\n");
} else {
strcpy(buf, "none\n");
}
return strlen(buf);
}
static ssize_t sky81452_bl_show_fault(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct regmap *regmap = bl_get_data(to_backlight_device(dev));
unsigned int value = 0;
int ret;
ret = regmap_read(regmap, SKY81452_REG4, &value);
if (ret < 0)
return ret;
*buf = 0;
if (value & SKY81452_OCP)
strcat(buf, "over-current ");
if (value & SKY81452_OTMP)
strcat(buf, "over-temperature");
strcat(buf, "\n");
return strlen(buf);
}
static DEVICE_ATTR(enable, S_IWGRP | S_IWUSR, NULL, sky81452_bl_store_enable);
static DEVICE_ATTR(open, S_IRUGO, sky81452_bl_show_open_short, NULL);
static DEVICE_ATTR(short, S_IRUGO, sky81452_bl_show_open_short, NULL);
static DEVICE_ATTR(fault, S_IRUGO, sky81452_bl_show_fault, NULL);
static struct attribute *sky81452_bl_attribute[] = {
&dev_attr_enable.attr,
&dev_attr_open.attr,
&dev_attr_short.attr,
&dev_attr_fault.attr,
NULL
};
static const struct attribute_group sky81452_bl_attr_group = {
.attrs = sky81452_bl_attribute,
};
#ifdef CONFIG_OF
static struct sky81452_bl_platform_data *sky81452_bl_parse_dt(
struct device *dev)
{
struct device_node *np = of_node_get(dev->of_node);
struct sky81452_bl_platform_data *pdata;
int num_entry;
unsigned int sources[6];
int ret;
if (!np) {
dev_err(dev, "backlight node not found.\n");
return ERR_PTR(-ENODATA);
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
of_node_put(np);
return ERR_PTR(-ENOMEM);
}
of_property_read_string(np, "name", &pdata->name);
pdata->ignore_pwm = of_property_read_bool(np, "skyworks,ignore-pwm");
pdata->dpwm_mode = of_property_read_bool(np, "skyworks,dpwm-mode");
pdata->phase_shift = of_property_read_bool(np, "skyworks,phase-shift");
pdata->gpiod_enable = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
ret = of_property_count_u32_elems(np, "led-sources");
if (ret < 0) {
pdata->enable = SKY81452_EN >> CTZ(SKY81452_EN);
} else {
num_entry = ret;
if (num_entry > 6)
num_entry = 6;
ret = of_property_read_u32_array(np, "led-sources", sources,
num_entry);
if (ret < 0) {
dev_err(dev, "led-sources node is invalid.\n");
of_node_put(np);
return ERR_PTR(-EINVAL);
}
pdata->enable = 0;
while (--num_entry)
pdata->enable |= (1 << sources[num_entry]);
}
ret = of_property_read_u32(np,
"skyworks,short-detection-threshold-volt",
&pdata->short_detection_threshold);
if (ret < 0)
pdata->short_detection_threshold = 7;
ret = of_property_read_u32(np, "skyworks,current-limit-mA",
&pdata->boost_current_limit);
if (ret < 0)
pdata->boost_current_limit = 2750;
of_node_put(np);
return pdata;
}
#else
static struct sky81452_bl_platform_data *sky81452_bl_parse_dt(
struct device *dev)
{
return ERR_PTR(-EINVAL);
}
#endif
static int sky81452_bl_init_device(struct regmap *regmap,
struct sky81452_bl_platform_data *pdata)
{
unsigned int value;
value = pdata->ignore_pwm ? SKY81452_IGPW : 0;
value |= pdata->dpwm_mode ? SKY81452_PWMMD : 0;
value |= pdata->phase_shift ? 0 : SKY81452_PHASE;
if (pdata->boost_current_limit == 2300)
value |= SKY81452_ILIM;
else if (pdata->boost_current_limit != 2750)
return -EINVAL;
if (pdata->short_detection_threshold < 4 ||
pdata->short_detection_threshold > 7)
return -EINVAL;
value |= (7 - pdata->short_detection_threshold) << CTZ(SKY81452_VSHRT);
return regmap_write(regmap, SKY81452_REG2, value);
}
static int sky81452_bl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct regmap *regmap = dev_get_drvdata(dev->parent);
struct sky81452_bl_platform_data *pdata;
struct backlight_device *bd;
struct backlight_properties props;
const char *name;
int ret;
pdata = sky81452_bl_parse_dt(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
ret = sky81452_bl_init_device(regmap, pdata);
if (ret < 0) {
dev_err(dev, "failed to initialize. err=%d\n", ret);
return ret;
}
memset(&props, 0, sizeof(props));
props.max_brightness = SKY81452_MAX_BRIGHTNESS;
name = pdata->name ? pdata->name : SKY81452_DEFAULT_NAME;
bd = devm_backlight_device_register(dev, name, dev, regmap,
&sky81452_bl_ops, &props);
if (IS_ERR(bd)) {
dev_err(dev, "failed to register. err=%ld\n", PTR_ERR(bd));
return PTR_ERR(bd);
}
platform_set_drvdata(pdev, bd);
ret = sysfs_create_group(&bd->dev.kobj, &sky81452_bl_attr_group);
if (ret < 0) {
dev_err(dev, "failed to create attribute. err=%d\n", ret);
return ret;
}
return ret;
}
static void sky81452_bl_remove(struct platform_device *pdev)
{
const struct sky81452_bl_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct backlight_device *bd = platform_get_drvdata(pdev);
sysfs_remove_group(&bd->dev.kobj, &sky81452_bl_attr_group);
bd->props.power = FB_BLANK_UNBLANK;
bd->props.brightness = 0;
backlight_update_status(bd);
if (pdata->gpiod_enable)
gpiod_set_value_cansleep(pdata->gpiod_enable, 0);
}
#ifdef CONFIG_OF
static const struct of_device_id sky81452_bl_of_match[] = {
{ .compatible = "skyworks,sky81452-backlight", },
{ }
};
MODULE_DEVICE_TABLE(of, sky81452_bl_of_match);
#endif
static struct platform_driver sky81452_bl_driver = {
.driver = {
.name = "sky81452-backlight",
.of_match_table = of_match_ptr(sky81452_bl_of_match),
},
.probe = sky81452_bl_probe,
.remove_new = sky81452_bl_remove,
};
module_platform_driver(sky81452_bl_driver);
MODULE_DESCRIPTION("Skyworks SKY81452 backlight driver");
MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");
MODULE_LICENSE("GPL v2");