xref: /openbmc/u-boot/drivers/video/pwm_backlight.c (revision a4f737a9c39abb45a5bde47f313df48e645331f7)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
265fba592SSimon Glass /*
365fba592SSimon Glass  * Copyright (c) 2016 Google, Inc
465fba592SSimon Glass  * Written by Simon Glass <sjg@chromium.org>
565fba592SSimon Glass  */
665fba592SSimon Glass 
7*a4f737a9SSimon Glass #define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
8*a4f737a9SSimon Glass 
965fba592SSimon Glass #include <common.h>
1065fba592SSimon Glass #include <dm.h>
1165fba592SSimon Glass #include <backlight.h>
1265fba592SSimon Glass #include <pwm.h>
1365fba592SSimon Glass #include <asm/gpio.h>
1465fba592SSimon Glass #include <power/regulator.h>
1565fba592SSimon Glass 
16*a4f737a9SSimon Glass /**
17*a4f737a9SSimon Glass  * Private information for the PWM backlight
18*a4f737a9SSimon Glass  *
19*a4f737a9SSimon Glass  * If @num_levels is 0 then the levels are simple values with the backlight
20*a4f737a9SSimon Glass  * value going between the minimum (default 0) and the maximum (default 255).
21*a4f737a9SSimon Glass  * Otherwise the levels are an index into @levels (0..n-1).
22*a4f737a9SSimon Glass  *
23*a4f737a9SSimon Glass  * @reg: Regulator to enable to turn the backlight on (NULL if none)
24*a4f737a9SSimon Glass  * @enable, GPIO to set to enable the backlight (can be missing)
25*a4f737a9SSimon Glass  * @pwm: PWM to use to change the backlight brightness
26*a4f737a9SSimon Glass  * @channel: PWM channel to use
27*a4f737a9SSimon Glass  * @period_ns: Period of the backlight in nanoseconds
28*a4f737a9SSimon Glass  * @levels: Levels for the backlight, or NULL if not using indexed levels
29*a4f737a9SSimon Glass  * @num_levels: Number of levels
30*a4f737a9SSimon Glass  * @cur_level: Current level for the backlight (index or value)
31*a4f737a9SSimon Glass  * @default_level: Default level for the backlight (index or value)
32*a4f737a9SSimon Glass  * @min_level: Minimum level of the backlight (full off)
33*a4f737a9SSimon Glass  * @min_level: Maximum level of the backlight (full on)
34*a4f737a9SSimon Glass  * @enabled: true if backlight is enabled
35*a4f737a9SSimon Glass  */
3665fba592SSimon Glass struct pwm_backlight_priv {
3765fba592SSimon Glass 	struct udevice *reg;
3865fba592SSimon Glass 	struct gpio_desc enable;
3965fba592SSimon Glass 	struct udevice *pwm;
4065fba592SSimon Glass 	uint channel;
4165fba592SSimon Glass 	uint period_ns;
42*a4f737a9SSimon Glass 	u32 *levels;
43*a4f737a9SSimon Glass 	int num_levels;
4465fba592SSimon Glass 	uint default_level;
45*a4f737a9SSimon Glass 	int cur_level;
4665fba592SSimon Glass 	uint min_level;
4765fba592SSimon Glass 	uint max_level;
48*a4f737a9SSimon Glass 	bool enabled;
4965fba592SSimon Glass };
5065fba592SSimon Glass 
51*a4f737a9SSimon Glass static int set_pwm(struct pwm_backlight_priv *priv)
52*a4f737a9SSimon Glass {
53*a4f737a9SSimon Glass 	uint duty_cycle;
54*a4f737a9SSimon Glass 	int ret;
55*a4f737a9SSimon Glass 
56*a4f737a9SSimon Glass 	duty_cycle = priv->period_ns * (priv->cur_level - priv->min_level) /
57*a4f737a9SSimon Glass 		(priv->max_level - priv->min_level + 1);
58*a4f737a9SSimon Glass 	ret = pwm_set_config(priv->pwm, priv->channel, priv->period_ns,
59*a4f737a9SSimon Glass 			     duty_cycle);
60*a4f737a9SSimon Glass 
61*a4f737a9SSimon Glass 	return log_ret(ret);
62*a4f737a9SSimon Glass }
63*a4f737a9SSimon Glass 
64*a4f737a9SSimon Glass static int enable_sequence(struct udevice *dev, int seq)
65*a4f737a9SSimon Glass {
66*a4f737a9SSimon Glass 	struct pwm_backlight_priv *priv = dev_get_priv(dev);
67*a4f737a9SSimon Glass 	int ret;
68*a4f737a9SSimon Glass 
69*a4f737a9SSimon Glass 	switch (seq) {
70*a4f737a9SSimon Glass 	case 0:
71*a4f737a9SSimon Glass 		if (priv->reg) {
72*a4f737a9SSimon Glass 			__maybe_unused struct dm_regulator_uclass_platdata
73*a4f737a9SSimon Glass 				*plat;
74*a4f737a9SSimon Glass 
75*a4f737a9SSimon Glass 			plat = dev_get_uclass_platdata(priv->reg);
76*a4f737a9SSimon Glass 			log_debug("Enable '%s', regulator '%s'/'%s'\n",
77*a4f737a9SSimon Glass 				  dev->name, priv->reg->name, plat->name);
78*a4f737a9SSimon Glass 			ret = regulator_set_enable(priv->reg, true);
79*a4f737a9SSimon Glass 			if (ret) {
80*a4f737a9SSimon Glass 				log_debug("Cannot enable regulator for PWM '%s'\n",
81*a4f737a9SSimon Glass 					  __func__, dev->name);
82*a4f737a9SSimon Glass 				return log_ret(ret);
83*a4f737a9SSimon Glass 			}
84*a4f737a9SSimon Glass 			mdelay(120);
85*a4f737a9SSimon Glass 		}
86*a4f737a9SSimon Glass 		break;
87*a4f737a9SSimon Glass 	case 1:
88*a4f737a9SSimon Glass 		mdelay(10);
89*a4f737a9SSimon Glass 		dm_gpio_set_value(&priv->enable, 1);
90*a4f737a9SSimon Glass 		break;
91*a4f737a9SSimon Glass 	}
92*a4f737a9SSimon Glass 
93*a4f737a9SSimon Glass 	return 0;
94*a4f737a9SSimon Glass }
95*a4f737a9SSimon Glass 
9665fba592SSimon Glass static int pwm_backlight_enable(struct udevice *dev)
9765fba592SSimon Glass {
9865fba592SSimon Glass 	struct pwm_backlight_priv *priv = dev_get_priv(dev);
9965fba592SSimon Glass 	int ret;
10065fba592SSimon Glass 
101*a4f737a9SSimon Glass 	ret = enable_sequence(dev, 0);
10265fba592SSimon Glass 	if (ret)
103*a4f737a9SSimon Glass 		return log_ret(ret);
104*a4f737a9SSimon Glass 	ret = set_pwm(priv);
105*a4f737a9SSimon Glass 	if (ret)
106*a4f737a9SSimon Glass 		return log_ret(ret);
10765fba592SSimon Glass 	ret = pwm_set_enable(priv->pwm, priv->channel, true);
10865fba592SSimon Glass 	if (ret)
109*a4f737a9SSimon Glass 		return log_ret(ret);
110*a4f737a9SSimon Glass 	ret = enable_sequence(dev, 1);
111*a4f737a9SSimon Glass 	if (ret)
112*a4f737a9SSimon Glass 		return log_ret(ret);
113*a4f737a9SSimon Glass 	priv->enabled = true;
114*a4f737a9SSimon Glass 
115*a4f737a9SSimon Glass 	return 0;
116*a4f737a9SSimon Glass }
117*a4f737a9SSimon Glass 
118*a4f737a9SSimon Glass static int pwm_backlight_set_brightness(struct udevice *dev, int percent)
119*a4f737a9SSimon Glass {
120*a4f737a9SSimon Glass 	struct pwm_backlight_priv *priv = dev_get_priv(dev);
121*a4f737a9SSimon Glass 	bool disable = false;
122*a4f737a9SSimon Glass 	int level;
123*a4f737a9SSimon Glass 	int ret;
124*a4f737a9SSimon Glass 
125*a4f737a9SSimon Glass 	if (!priv->enabled) {
126*a4f737a9SSimon Glass 		ret = enable_sequence(dev, 0);
127*a4f737a9SSimon Glass 		if (ret)
128*a4f737a9SSimon Glass 			return log_ret(ret);
129*a4f737a9SSimon Glass 	}
130*a4f737a9SSimon Glass 	if (percent == BACKLIGHT_OFF) {
131*a4f737a9SSimon Glass 		disable = true;
132*a4f737a9SSimon Glass 		percent = 0;
133*a4f737a9SSimon Glass 	}
134*a4f737a9SSimon Glass 	if (percent == BACKLIGHT_DEFAULT) {
135*a4f737a9SSimon Glass 		level = priv->default_level;
136*a4f737a9SSimon Glass 	} else {
137*a4f737a9SSimon Glass 		if (priv->levels) {
138*a4f737a9SSimon Glass 			level = priv->levels[percent * (priv->num_levels - 1)
139*a4f737a9SSimon Glass 				/ 100];
140*a4f737a9SSimon Glass 		} else {
141*a4f737a9SSimon Glass 			level = priv->min_level +
142*a4f737a9SSimon Glass 				(priv->max_level - priv->min_level) *
143*a4f737a9SSimon Glass 				percent / 100;
144*a4f737a9SSimon Glass 		}
145*a4f737a9SSimon Glass 	}
146*a4f737a9SSimon Glass 	priv->cur_level = level;
147*a4f737a9SSimon Glass 
148*a4f737a9SSimon Glass 	ret = set_pwm(priv);
149*a4f737a9SSimon Glass 	if (ret)
150*a4f737a9SSimon Glass 		return log_ret(ret);
151*a4f737a9SSimon Glass 	if (!priv->enabled) {
152*a4f737a9SSimon Glass 		ret = enable_sequence(dev, 1);
153*a4f737a9SSimon Glass 		if (ret)
154*a4f737a9SSimon Glass 			return log_ret(ret);
155*a4f737a9SSimon Glass 		priv->enabled = true;
156*a4f737a9SSimon Glass 	}
157*a4f737a9SSimon Glass 	if (disable) {
158*a4f737a9SSimon Glass 		dm_gpio_set_value(&priv->enable, 0);
159*a4f737a9SSimon Glass 		if (priv->reg) {
160*a4f737a9SSimon Glass 			ret = regulator_set_enable(priv->reg, false);
161*a4f737a9SSimon Glass 			if (ret)
162*a4f737a9SSimon Glass 				return log_ret(ret);
163*a4f737a9SSimon Glass 		}
164*a4f737a9SSimon Glass 		priv->enabled = false;
165*a4f737a9SSimon Glass 	}
16665fba592SSimon Glass 
16765fba592SSimon Glass 	return 0;
16865fba592SSimon Glass }
16965fba592SSimon Glass 
17065fba592SSimon Glass static int pwm_backlight_ofdata_to_platdata(struct udevice *dev)
17165fba592SSimon Glass {
17265fba592SSimon Glass 	struct pwm_backlight_priv *priv = dev_get_priv(dev);
1737cf208d7SSimon Glass 	struct ofnode_phandle_args args;
17465fba592SSimon Glass 	int index, ret, count, len;
17565fba592SSimon Glass 	const u32 *cell;
17665fba592SSimon Glass 
177*a4f737a9SSimon Glass 	log_debug("start\n");
17865fba592SSimon Glass 	ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
17965fba592SSimon Glass 					   "power-supply", &priv->reg);
18019f124d8SKever Yang 	if (ret)
181*a4f737a9SSimon Glass 		log_debug("Cannot get power supply: ret=%d\n", ret);
18265fba592SSimon Glass 	ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
18365fba592SSimon Glass 				   GPIOD_IS_OUT);
18465fba592SSimon Glass 	if (ret) {
185*a4f737a9SSimon Glass 		log_debug("Warning: cannot get enable GPIO: ret=%d\n", ret);
18665fba592SSimon Glass 		if (ret != -ENOENT)
187*a4f737a9SSimon Glass 			return log_ret(ret);
18865fba592SSimon Glass 	}
1897cf208d7SSimon Glass 	ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0,
1907cf208d7SSimon Glass 					 &args);
19165fba592SSimon Glass 	if (ret) {
192*a4f737a9SSimon Glass 		log_debug("Cannot get PWM phandle: ret=%d\n", ret);
193*a4f737a9SSimon Glass 		return log_ret(ret);
19465fba592SSimon Glass 	}
19565fba592SSimon Glass 
1967cf208d7SSimon Glass 	ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm);
19765fba592SSimon Glass 	if (ret) {
198*a4f737a9SSimon Glass 		log_debug("Cannot get PWM: ret=%d\n", ret);
199*a4f737a9SSimon Glass 		return log_ret(ret);
20065fba592SSimon Glass 	}
201*a4f737a9SSimon Glass 	if (args.args_count < 2)
202*a4f737a9SSimon Glass 		return log_msg_ret("Not enough arguments to pwm\n", -EINVAL);
20365fba592SSimon Glass 	priv->channel = args.args[0];
20465fba592SSimon Glass 	priv->period_ns = args.args[1];
20565fba592SSimon Glass 
2067cf208d7SSimon Glass 	index = dev_read_u32_default(dev, "default-brightness-level", 255);
2077cf208d7SSimon Glass 	cell = dev_read_prop(dev, "brightness-levels", &len);
20865fba592SSimon Glass 	count = len / sizeof(u32);
20965fba592SSimon Glass 	if (cell && count > index) {
210*a4f737a9SSimon Glass 		priv->levels = malloc(len);
211*a4f737a9SSimon Glass 		if (!priv->levels)
212*a4f737a9SSimon Glass 			return log_ret(-ENOMEM);
213*a4f737a9SSimon Glass 		dev_read_u32_array(dev, "brightness-levels", priv->levels,
214*a4f737a9SSimon Glass 				   count);
215*a4f737a9SSimon Glass 		priv->num_levels = count;
216*a4f737a9SSimon Glass 		priv->default_level = priv->levels[index];
217*a4f737a9SSimon Glass 		priv->max_level = priv->levels[count - 1];
21865fba592SSimon Glass 	} else {
21965fba592SSimon Glass 		priv->default_level = index;
22065fba592SSimon Glass 		priv->max_level = 255;
22165fba592SSimon Glass 	}
222*a4f737a9SSimon Glass 	priv->cur_level = priv->default_level;
223*a4f737a9SSimon Glass 	log_debug("done\n");
22465fba592SSimon Glass 
22565fba592SSimon Glass 
22665fba592SSimon Glass 	return 0;
22765fba592SSimon Glass }
22865fba592SSimon Glass 
22965fba592SSimon Glass static int pwm_backlight_probe(struct udevice *dev)
23065fba592SSimon Glass {
23165fba592SSimon Glass 	return 0;
23265fba592SSimon Glass }
23365fba592SSimon Glass 
23465fba592SSimon Glass static const struct backlight_ops pwm_backlight_ops = {
23565fba592SSimon Glass 	.enable		= pwm_backlight_enable,
236*a4f737a9SSimon Glass 	.set_brightness	= pwm_backlight_set_brightness,
23765fba592SSimon Glass };
23865fba592SSimon Glass 
23965fba592SSimon Glass static const struct udevice_id pwm_backlight_ids[] = {
24065fba592SSimon Glass 	{ .compatible = "pwm-backlight" },
24165fba592SSimon Glass 	{ }
24265fba592SSimon Glass };
24365fba592SSimon Glass 
24465fba592SSimon Glass U_BOOT_DRIVER(pwm_backlight) = {
24565fba592SSimon Glass 	.name	= "pwm_backlight",
24665fba592SSimon Glass 	.id	= UCLASS_PANEL_BACKLIGHT,
24765fba592SSimon Glass 	.of_match = pwm_backlight_ids,
24865fba592SSimon Glass 	.ops	= &pwm_backlight_ops,
24965fba592SSimon Glass 	.ofdata_to_platdata	= pwm_backlight_ofdata_to_platdata,
25065fba592SSimon Glass 	.probe		= pwm_backlight_probe,
25165fba592SSimon Glass 	.priv_auto_alloc_size	= sizeof(struct pwm_backlight_priv),
25265fba592SSimon Glass };
253