brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · 24e1a19 Raw
344 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2#include <linux/backlight.h>3#include <linux/delay.h>4#include <linux/gpio/consumer.h>5#include <linux/i2c.h>6#include <linux/module.h>7#include <linux/regmap.h>8 9#define LM3509_NAME "lm3509_bl"10 11#define LM3509_SINK_MAIN 012#define LM3509_SINK_SUB 113#define LM3509_NUM_SINKS 214 15#define LM3509_DEF_BRIGHTNESS 0x1216#define LM3509_MAX_BRIGHTNESS 0x1F17 18#define REG_GP 0x1019#define REG_BMAIN 0xA020#define REG_BSUB 0xB021#define REG_MAX 0xFF22 23enum {24	REG_GP_ENM_BIT = 0,25	REG_GP_ENS_BIT,26	REG_GP_UNI_BIT,27	REG_GP_RMP0_BIT,28	REG_GP_RMP1_BIT,29	REG_GP_OLED_BIT,30};31 32struct lm3509_bl {33	struct regmap *regmap;34	struct backlight_device *bl_main;35	struct backlight_device *bl_sub;36	struct gpio_desc *reset_gpio;37};38 39struct lm3509_bl_led_data {40	const char *label;41	int led_sources;42	u32 brightness;43	u32 max_brightness;44};45 46static void lm3509_reset(struct lm3509_bl *data)47{48	if (data->reset_gpio) {49		gpiod_set_value(data->reset_gpio, 1);50		udelay(1);51		gpiod_set_value(data->reset_gpio, 0);52		udelay(10);53	}54}55 56static int lm3509_update_status(struct backlight_device *bl,57				unsigned int en_mask, unsigned int br_reg)58{59	struct lm3509_bl *data = bl_get_data(bl);60	int ret;61	bool en;62 63	ret = regmap_write(data->regmap, br_reg, backlight_get_brightness(bl));64	if (ret < 0)65		return ret;66 67	en = !backlight_is_blank(bl);68	return regmap_update_bits(data->regmap, REG_GP, en_mask,69				  en ? en_mask : 0);70}71 72static int lm3509_main_update_status(struct backlight_device *bl)73{74	return lm3509_update_status(bl, BIT(REG_GP_ENM_BIT), REG_BMAIN);75}76 77static const struct backlight_ops lm3509_main_ops = {78	.options = BL_CORE_SUSPENDRESUME,79	.update_status = lm3509_main_update_status,80};81 82static int lm3509_sub_update_status(struct backlight_device *bl)83{84	return lm3509_update_status(bl, BIT(REG_GP_ENS_BIT), REG_BSUB);85}86 87static const struct backlight_ops lm3509_sub_ops = {88	.options = BL_CORE_SUSPENDRESUME,89	.update_status = lm3509_sub_update_status,90};91 92static struct backlight_device *93lm3509_backlight_register(struct device *dev, const char *name_suffix,94			  struct lm3509_bl *data,95			  const struct backlight_ops *ops,96			  const struct lm3509_bl_led_data *led_data)97 98{99	struct backlight_device *bd;100	struct backlight_properties props;101	const char *label = led_data->label;102	char name[64];103 104	memset(&props, 0, sizeof(props));105	props.type = BACKLIGHT_RAW;106	props.brightness = led_data->brightness;107	props.max_brightness = led_data->max_brightness;108	props.scale = BACKLIGHT_SCALE_NON_LINEAR;109 110	if (!label) {111		snprintf(name, sizeof(name), "lm3509-%s-%s", dev_name(dev),112			 name_suffix);113		label = name;114	}115 116	bd = devm_backlight_device_register(dev, label, dev, data, ops, &props);117	if (IS_ERR(bd))118		return bd;119 120	backlight_update_status(bd);121	return bd;122}123 124static const struct regmap_config lm3509_regmap = {125	.reg_bits = 8,126	.val_bits = 8,127	.max_register = REG_MAX,128};129 130static int lm3509_parse_led_sources(struct device_node *node,131				    int default_led_sources)132{133	u32 sources[LM3509_NUM_SINKS];134	int ret, num_sources, i;135 136	num_sources = of_property_count_u32_elems(node, "led-sources");137	if (num_sources < 0)138		return default_led_sources;139	else if (num_sources > ARRAY_SIZE(sources))140		return -EINVAL;141 142	ret = of_property_read_u32_array(node, "led-sources", sources,143					 num_sources);144	if (ret)145		return ret;146 147	for (i = 0; i < num_sources; i++) {148		if (sources[i] >= LM3509_NUM_SINKS)149			return -EINVAL;150 151		ret |= BIT(sources[i]);152	}153 154	return ret;155}156 157static int lm3509_parse_dt_node(struct device *dev,158				struct lm3509_bl_led_data *led_data)159{160	int seen_led_sources = 0;161 162	for_each_child_of_node_scoped(dev->of_node, child) {163		struct lm3509_bl_led_data *ld;164		int ret;165		u32 reg;166		int valid_led_sources;167 168		ret = of_property_read_u32(child, "reg", &reg);169		if (ret < 0)170			return ret;171		if (reg >= LM3509_NUM_SINKS)172			return -EINVAL;173		ld = &led_data[reg];174 175		ld->led_sources = lm3509_parse_led_sources(child, BIT(reg));176		if (ld->led_sources < 0)177			return ld->led_sources;178 179		if (reg == 0)180			valid_led_sources = BIT(LM3509_SINK_MAIN) |181					    BIT(LM3509_SINK_SUB);182		else183			valid_led_sources = BIT(LM3509_SINK_SUB);184 185		if (ld->led_sources != (ld->led_sources & valid_led_sources))186			return -EINVAL;187 188		if (seen_led_sources & ld->led_sources)189			return -EINVAL;190 191		seen_led_sources |= ld->led_sources;192 193		ld->label = NULL;194		of_property_read_string(child, "label", &ld->label);195 196		ld->max_brightness = LM3509_MAX_BRIGHTNESS;197		of_property_read_u32(child, "max-brightness",198				     &ld->max_brightness);199		ld->max_brightness =200			min_t(u32, ld->max_brightness, LM3509_MAX_BRIGHTNESS);201 202		ld->brightness = LM3509_DEF_BRIGHTNESS;203		of_property_read_u32(child, "default-brightness",204				     &ld->brightness);205		ld->brightness = min_t(u32, ld->brightness, ld->max_brightness);206	}207 208	return 0;209}210 211static int lm3509_probe(struct i2c_client *client)212{213	struct lm3509_bl *data;214	struct device *dev = &client->dev;215	int ret;216	bool oled_mode = false;217	unsigned int reg_gp_val = 0;218	struct lm3509_bl_led_data led_data[LM3509_NUM_SINKS];219	u32 rate_of_change = 0;220 221	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {222		dev_err(dev, "i2c functionality check failed\n");223		return -EOPNOTSUPP;224	}225 226	data = devm_kzalloc(dev, sizeof(struct lm3509_bl), GFP_KERNEL);227	if (!data)228		return -ENOMEM;229 230	data->regmap = devm_regmap_init_i2c(client, &lm3509_regmap);231	if (IS_ERR(data->regmap))232		return PTR_ERR(data->regmap);233	i2c_set_clientdata(client, data);234 235	data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);236	if (IS_ERR(data->reset_gpio))237		return dev_err_probe(dev, PTR_ERR(data->reset_gpio),238				     "Failed to get 'reset' gpio\n");239 240	lm3509_reset(data);241 242	memset(led_data, 0, sizeof(led_data));243	ret = lm3509_parse_dt_node(dev, led_data);244	if (ret)245		return ret;246 247	oled_mode = of_property_read_bool(dev->of_node, "ti,oled-mode");248 249	if (!of_property_read_u32(dev->of_node,250				  "ti,brightness-rate-of-change-us",251				  &rate_of_change)) {252		switch (rate_of_change) {253		case 51:254			reg_gp_val = 0;255			break;256		case 13000:257			reg_gp_val = BIT(REG_GP_RMP1_BIT);258			break;259		case 26000:260			reg_gp_val = BIT(REG_GP_RMP0_BIT);261			break;262		case 52000:263			reg_gp_val = BIT(REG_GP_RMP0_BIT) |264				     BIT(REG_GP_RMP1_BIT);265			break;266		default:267			dev_warn(dev, "invalid rate of change %u\n",268				 rate_of_change);269			break;270		}271	}272 273	if (led_data[0].led_sources ==274	    (BIT(LM3509_SINK_MAIN) | BIT(LM3509_SINK_SUB)))275		reg_gp_val |= BIT(REG_GP_UNI_BIT);276	if (oled_mode)277		reg_gp_val |= BIT(REG_GP_OLED_BIT);278 279	ret = regmap_write(data->regmap, REG_GP, reg_gp_val);280	if (ret < 0)281		return dev_err_probe(dev, ret, "failed to write register\n");282 283	if (led_data[0].led_sources) {284		data->bl_main = lm3509_backlight_register(285			dev, "main", data, &lm3509_main_ops, &led_data[0]);286		if (IS_ERR(data->bl_main)) {287			return dev_err_probe(288				dev, PTR_ERR(data->bl_main),289				"failed to register main backlight\n");290		}291	}292 293	if (led_data[1].led_sources) {294		data->bl_sub = lm3509_backlight_register(295			dev, "sub", data, &lm3509_sub_ops, &led_data[1]);296		if (IS_ERR(data->bl_sub)) {297			return dev_err_probe(298				dev, PTR_ERR(data->bl_sub),299				"failed to register secondary backlight\n");300		}301	}302 303	return 0;304}305 306static void lm3509_remove(struct i2c_client *client)307{308	struct lm3509_bl *data = i2c_get_clientdata(client);309 310	regmap_write(data->regmap, REG_GP, 0x00);311}312 313static const struct i2c_device_id lm3509_id[] = {314	{ LM3509_NAME },315	{}316};317 318MODULE_DEVICE_TABLE(i2c, lm3509_id);319 320static const struct of_device_id lm3509_match_table[] = {321	{322		.compatible = "ti,lm3509",323	},324	{},325};326 327MODULE_DEVICE_TABLE(of, lm3509_match_table);328 329static struct i2c_driver lm3509_i2c_driver = {330	.driver = {331		.name = LM3509_NAME,332		.of_match_table = lm3509_match_table,333	},334	.probe = lm3509_probe,335	.remove = lm3509_remove,336	.id_table = lm3509_id,337};338 339module_i2c_driver(lm3509_i2c_driver);340 341MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3509");342MODULE_AUTHOR("Patrick Gansterer <paroga@paroga.com>");343MODULE_LICENSE("GPL");344