xref: /linux/drivers/video/backlight/as3711_bl.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
1*6f6d5c3fSKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
2de474b5bSGuennadi Liakhovetski /*
3de474b5bSGuennadi Liakhovetski  * AS3711 PMIC backlight driver, using DCDC Step Up Converters
4de474b5bSGuennadi Liakhovetski  *
5de474b5bSGuennadi Liakhovetski  * Copyright (C) 2012 Renesas Electronics Corporation
6de474b5bSGuennadi Liakhovetski  * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
7de474b5bSGuennadi Liakhovetski  */
8de474b5bSGuennadi Liakhovetski 
9de474b5bSGuennadi Liakhovetski #include <linux/backlight.h>
10de474b5bSGuennadi Liakhovetski #include <linux/delay.h>
11de474b5bSGuennadi Liakhovetski #include <linux/device.h>
12de474b5bSGuennadi Liakhovetski #include <linux/err.h>
13de474b5bSGuennadi Liakhovetski #include <linux/fb.h>
14de474b5bSGuennadi Liakhovetski #include <linux/kernel.h>
15de474b5bSGuennadi Liakhovetski #include <linux/mfd/as3711.h>
16de474b5bSGuennadi Liakhovetski #include <linux/module.h>
17de474b5bSGuennadi Liakhovetski #include <linux/platform_device.h>
18de474b5bSGuennadi Liakhovetski #include <linux/regmap.h>
19de474b5bSGuennadi Liakhovetski #include <linux/slab.h>
20de474b5bSGuennadi Liakhovetski 
21de474b5bSGuennadi Liakhovetski enum as3711_bl_type {
22de474b5bSGuennadi Liakhovetski 	AS3711_BL_SU1,
23de474b5bSGuennadi Liakhovetski 	AS3711_BL_SU2,
24de474b5bSGuennadi Liakhovetski };
25de474b5bSGuennadi Liakhovetski 
26de474b5bSGuennadi Liakhovetski struct as3711_bl_data {
27de474b5bSGuennadi Liakhovetski 	bool powered;
28de474b5bSGuennadi Liakhovetski 	enum as3711_bl_type type;
29de474b5bSGuennadi Liakhovetski 	int brightness;
30de474b5bSGuennadi Liakhovetski 	struct backlight_device *bl;
31de474b5bSGuennadi Liakhovetski };
32de474b5bSGuennadi Liakhovetski 
33de474b5bSGuennadi Liakhovetski struct as3711_bl_supply {
34de474b5bSGuennadi Liakhovetski 	struct as3711_bl_data su1;
35de474b5bSGuennadi Liakhovetski 	struct as3711_bl_data su2;
36de474b5bSGuennadi Liakhovetski 	const struct as3711_bl_pdata *pdata;
37de474b5bSGuennadi Liakhovetski 	struct as3711 *as3711;
38de474b5bSGuennadi Liakhovetski };
39de474b5bSGuennadi Liakhovetski 
40de474b5bSGuennadi Liakhovetski static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su)
41de474b5bSGuennadi Liakhovetski {
42de474b5bSGuennadi Liakhovetski 	switch (su->type) {
43de474b5bSGuennadi Liakhovetski 	case AS3711_BL_SU1:
44de474b5bSGuennadi Liakhovetski 		return container_of(su, struct as3711_bl_supply, su1);
45de474b5bSGuennadi Liakhovetski 	case AS3711_BL_SU2:
46de474b5bSGuennadi Liakhovetski 		return container_of(su, struct as3711_bl_supply, su2);
47de474b5bSGuennadi Liakhovetski 	}
48de474b5bSGuennadi Liakhovetski 	return NULL;
49de474b5bSGuennadi Liakhovetski }
50de474b5bSGuennadi Liakhovetski 
51de474b5bSGuennadi Liakhovetski static int as3711_set_brightness_auto_i(struct as3711_bl_data *data,
52de474b5bSGuennadi Liakhovetski 					unsigned int brightness)
53de474b5bSGuennadi Liakhovetski {
54de474b5bSGuennadi Liakhovetski 	struct as3711_bl_supply *supply = to_supply(data);
55de474b5bSGuennadi Liakhovetski 	struct as3711 *as3711 = supply->as3711;
56de474b5bSGuennadi Liakhovetski 	const struct as3711_bl_pdata *pdata = supply->pdata;
57de474b5bSGuennadi Liakhovetski 	int ret = 0;
58de474b5bSGuennadi Liakhovetski 
59de474b5bSGuennadi Liakhovetski 	/* Only all equal current values are supported */
60de474b5bSGuennadi Liakhovetski 	if (pdata->su2_auto_curr1)
61de474b5bSGuennadi Liakhovetski 		ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
62de474b5bSGuennadi Liakhovetski 				   brightness);
63de474b5bSGuennadi Liakhovetski 	if (!ret && pdata->su2_auto_curr2)
64de474b5bSGuennadi Liakhovetski 		ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
65de474b5bSGuennadi Liakhovetski 				   brightness);
66de474b5bSGuennadi Liakhovetski 	if (!ret && pdata->su2_auto_curr3)
67de474b5bSGuennadi Liakhovetski 		ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
68de474b5bSGuennadi Liakhovetski 				   brightness);
69de474b5bSGuennadi Liakhovetski 
70de474b5bSGuennadi Liakhovetski 	return ret;
71de474b5bSGuennadi Liakhovetski }
72de474b5bSGuennadi Liakhovetski 
73de474b5bSGuennadi Liakhovetski static int as3711_set_brightness_v(struct as3711 *as3711,
74de474b5bSGuennadi Liakhovetski 				   unsigned int brightness,
75de474b5bSGuennadi Liakhovetski 				   unsigned int reg)
76de474b5bSGuennadi Liakhovetski {
77de474b5bSGuennadi Liakhovetski 	if (brightness > 31)
78de474b5bSGuennadi Liakhovetski 		return -EINVAL;
79de474b5bSGuennadi Liakhovetski 
80de474b5bSGuennadi Liakhovetski 	return regmap_update_bits(as3711->regmap, reg, 0xf0,
81de474b5bSGuennadi Liakhovetski 				  brightness << 4);
82de474b5bSGuennadi Liakhovetski }
83de474b5bSGuennadi Liakhovetski 
84de474b5bSGuennadi Liakhovetski static int as3711_bl_su2_reset(struct as3711_bl_supply *supply)
85de474b5bSGuennadi Liakhovetski {
86de474b5bSGuennadi Liakhovetski 	struct as3711 *as3711 = supply->as3711;
87de474b5bSGuennadi Liakhovetski 	int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5,
88de474b5bSGuennadi Liakhovetski 				     3, supply->pdata->su2_fbprot);
89de474b5bSGuennadi Liakhovetski 	if (!ret)
90de474b5bSGuennadi Liakhovetski 		ret = regmap_update_bits(as3711->regmap,
91de474b5bSGuennadi Liakhovetski 					 AS3711_STEPUP_CONTROL_2, 1, 0);
92de474b5bSGuennadi Liakhovetski 	if (!ret)
93de474b5bSGuennadi Liakhovetski 		ret = regmap_update_bits(as3711->regmap,
94de474b5bSGuennadi Liakhovetski 					 AS3711_STEPUP_CONTROL_2, 1, 1);
95de474b5bSGuennadi Liakhovetski 	return ret;
96de474b5bSGuennadi Liakhovetski }
97de474b5bSGuennadi Liakhovetski 
98de474b5bSGuennadi Liakhovetski /*
99de474b5bSGuennadi Liakhovetski  * Someone with less fragile or less expensive hardware could try to simplify
100de474b5bSGuennadi Liakhovetski  * the brightness adjustment procedure.
101de474b5bSGuennadi Liakhovetski  */
102de474b5bSGuennadi Liakhovetski static int as3711_bl_update_status(struct backlight_device *bl)
103de474b5bSGuennadi Liakhovetski {
104de474b5bSGuennadi Liakhovetski 	struct as3711_bl_data *data = bl_get_data(bl);
105de474b5bSGuennadi Liakhovetski 	struct as3711_bl_supply *supply = to_supply(data);
106de474b5bSGuennadi Liakhovetski 	struct as3711 *as3711 = supply->as3711;
107de474b5bSGuennadi Liakhovetski 	int brightness = bl->props.brightness;
108de474b5bSGuennadi Liakhovetski 	int ret = 0;
109de474b5bSGuennadi Liakhovetski 
110de474b5bSGuennadi Liakhovetski 	dev_dbg(&bl->dev, "%s(): brightness %u, pwr %x, blank %x, state %x\n",
111de474b5bSGuennadi Liakhovetski 		__func__, bl->props.brightness, bl->props.power,
112de474b5bSGuennadi Liakhovetski 		bl->props.fb_blank, bl->props.state);
113de474b5bSGuennadi Liakhovetski 
114de474b5bSGuennadi Liakhovetski 	if (bl->props.power != FB_BLANK_UNBLANK ||
115de474b5bSGuennadi Liakhovetski 	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
116de474b5bSGuennadi Liakhovetski 	    bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
117de474b5bSGuennadi Liakhovetski 		brightness = 0;
118de474b5bSGuennadi Liakhovetski 
119de474b5bSGuennadi Liakhovetski 	if (data->type == AS3711_BL_SU1) {
120de474b5bSGuennadi Liakhovetski 		ret = as3711_set_brightness_v(as3711, brightness,
121de474b5bSGuennadi Liakhovetski 					      AS3711_STEPUP_CONTROL_1);
122de474b5bSGuennadi Liakhovetski 	} else {
123de474b5bSGuennadi Liakhovetski 		const struct as3711_bl_pdata *pdata = supply->pdata;
124de474b5bSGuennadi Liakhovetski 
125de474b5bSGuennadi Liakhovetski 		switch (pdata->su2_feedback) {
126de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_VOLTAGE:
127de474b5bSGuennadi Liakhovetski 			ret = as3711_set_brightness_v(as3711, brightness,
128de474b5bSGuennadi Liakhovetski 						      AS3711_STEPUP_CONTROL_2);
129de474b5bSGuennadi Liakhovetski 			break;
130de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR_AUTO:
131de474b5bSGuennadi Liakhovetski 			ret = as3711_set_brightness_auto_i(data, brightness / 4);
132de474b5bSGuennadi Liakhovetski 			if (ret < 0)
133de474b5bSGuennadi Liakhovetski 				return ret;
134de474b5bSGuennadi Liakhovetski 			if (brightness) {
135de474b5bSGuennadi Liakhovetski 				ret = as3711_bl_su2_reset(supply);
136de474b5bSGuennadi Liakhovetski 				if (ret < 0)
137de474b5bSGuennadi Liakhovetski 					return ret;
138de474b5bSGuennadi Liakhovetski 				udelay(500);
139de474b5bSGuennadi Liakhovetski 				ret = as3711_set_brightness_auto_i(data, brightness);
140de474b5bSGuennadi Liakhovetski 			} else {
141de474b5bSGuennadi Liakhovetski 				ret = regmap_update_bits(as3711->regmap,
142de474b5bSGuennadi Liakhovetski 						AS3711_STEPUP_CONTROL_2, 1, 0);
143de474b5bSGuennadi Liakhovetski 			}
144de474b5bSGuennadi Liakhovetski 			break;
145de474b5bSGuennadi Liakhovetski 		/* Manual one current feedback pin below */
146de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR1:
147de474b5bSGuennadi Liakhovetski 			ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
148de474b5bSGuennadi Liakhovetski 					   brightness);
149de474b5bSGuennadi Liakhovetski 			break;
150de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR2:
151de474b5bSGuennadi Liakhovetski 			ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
152de474b5bSGuennadi Liakhovetski 					   brightness);
153de474b5bSGuennadi Liakhovetski 			break;
154de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR3:
155de474b5bSGuennadi Liakhovetski 			ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
156de474b5bSGuennadi Liakhovetski 					   brightness);
157de474b5bSGuennadi Liakhovetski 			break;
158de474b5bSGuennadi Liakhovetski 		default:
159de474b5bSGuennadi Liakhovetski 			ret = -EINVAL;
160de474b5bSGuennadi Liakhovetski 		}
161de474b5bSGuennadi Liakhovetski 	}
162de474b5bSGuennadi Liakhovetski 	if (!ret)
163de474b5bSGuennadi Liakhovetski 		data->brightness = brightness;
164de474b5bSGuennadi Liakhovetski 
165de474b5bSGuennadi Liakhovetski 	return ret;
166de474b5bSGuennadi Liakhovetski }
167de474b5bSGuennadi Liakhovetski 
168de474b5bSGuennadi Liakhovetski static int as3711_bl_get_brightness(struct backlight_device *bl)
169de474b5bSGuennadi Liakhovetski {
170de474b5bSGuennadi Liakhovetski 	struct as3711_bl_data *data = bl_get_data(bl);
171de474b5bSGuennadi Liakhovetski 
172de474b5bSGuennadi Liakhovetski 	return data->brightness;
173de474b5bSGuennadi Liakhovetski }
174de474b5bSGuennadi Liakhovetski 
175de474b5bSGuennadi Liakhovetski static const struct backlight_ops as3711_bl_ops = {
176de474b5bSGuennadi Liakhovetski 	.update_status	= as3711_bl_update_status,
177de474b5bSGuennadi Liakhovetski 	.get_brightness	= as3711_bl_get_brightness,
178de474b5bSGuennadi Liakhovetski };
179de474b5bSGuennadi Liakhovetski 
180de474b5bSGuennadi Liakhovetski static int as3711_bl_init_su2(struct as3711_bl_supply *supply)
181de474b5bSGuennadi Liakhovetski {
182de474b5bSGuennadi Liakhovetski 	struct as3711 *as3711 = supply->as3711;
183de474b5bSGuennadi Liakhovetski 	const struct as3711_bl_pdata *pdata = supply->pdata;
184de474b5bSGuennadi Liakhovetski 	u8 ctl = 0;
185de474b5bSGuennadi Liakhovetski 	int ret;
186de474b5bSGuennadi Liakhovetski 
187de474b5bSGuennadi Liakhovetski 	dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback);
188de474b5bSGuennadi Liakhovetski 
189de474b5bSGuennadi Liakhovetski 	/* Turn SU2 off */
190de474b5bSGuennadi Liakhovetski 	ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0);
191de474b5bSGuennadi Liakhovetski 	if (ret < 0)
192de474b5bSGuennadi Liakhovetski 		return ret;
193de474b5bSGuennadi Liakhovetski 
194de474b5bSGuennadi Liakhovetski 	switch (pdata->su2_feedback) {
195de474b5bSGuennadi Liakhovetski 	case AS3711_SU2_VOLTAGE:
196de474b5bSGuennadi Liakhovetski 		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0);
197de474b5bSGuennadi Liakhovetski 		break;
198de474b5bSGuennadi Liakhovetski 	case AS3711_SU2_CURR1:
199de474b5bSGuennadi Liakhovetski 		ctl = 1;
200de474b5bSGuennadi Liakhovetski 		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1);
201de474b5bSGuennadi Liakhovetski 		break;
202de474b5bSGuennadi Liakhovetski 	case AS3711_SU2_CURR2:
203de474b5bSGuennadi Liakhovetski 		ctl = 4;
204de474b5bSGuennadi Liakhovetski 		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2);
205de474b5bSGuennadi Liakhovetski 		break;
206de474b5bSGuennadi Liakhovetski 	case AS3711_SU2_CURR3:
207de474b5bSGuennadi Liakhovetski 		ctl = 0x10;
208de474b5bSGuennadi Liakhovetski 		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3);
209de474b5bSGuennadi Liakhovetski 		break;
210de474b5bSGuennadi Liakhovetski 	case AS3711_SU2_CURR_AUTO:
211de474b5bSGuennadi Liakhovetski 		if (pdata->su2_auto_curr1)
212de474b5bSGuennadi Liakhovetski 			ctl = 2;
213de474b5bSGuennadi Liakhovetski 		if (pdata->su2_auto_curr2)
214de474b5bSGuennadi Liakhovetski 			ctl |= 8;
215de474b5bSGuennadi Liakhovetski 		if (pdata->su2_auto_curr3)
216de474b5bSGuennadi Liakhovetski 			ctl |= 0x20;
217de474b5bSGuennadi Liakhovetski 		ret = 0;
218de474b5bSGuennadi Liakhovetski 		break;
219de474b5bSGuennadi Liakhovetski 	default:
220de474b5bSGuennadi Liakhovetski 		return -EINVAL;
221de474b5bSGuennadi Liakhovetski 	}
222de474b5bSGuennadi Liakhovetski 
223de474b5bSGuennadi Liakhovetski 	if (!ret)
224de474b5bSGuennadi Liakhovetski 		ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl);
225de474b5bSGuennadi Liakhovetski 
226de474b5bSGuennadi Liakhovetski 	return ret;
227de474b5bSGuennadi Liakhovetski }
228de474b5bSGuennadi Liakhovetski 
229de474b5bSGuennadi Liakhovetski static int as3711_bl_register(struct platform_device *pdev,
230de474b5bSGuennadi Liakhovetski 			      unsigned int max_brightness, struct as3711_bl_data *su)
231de474b5bSGuennadi Liakhovetski {
232de474b5bSGuennadi Liakhovetski 	struct backlight_properties props = {.type = BACKLIGHT_RAW,};
233de474b5bSGuennadi Liakhovetski 	struct backlight_device *bl;
234de474b5bSGuennadi Liakhovetski 
235de474b5bSGuennadi Liakhovetski 	/* max tuning I = 31uA for voltage- and 38250uA for current-feedback */
236de474b5bSGuennadi Liakhovetski 	props.max_brightness = max_brightness;
237de474b5bSGuennadi Liakhovetski 
23883dedc05SJingoo Han 	bl = devm_backlight_device_register(&pdev->dev,
23983dedc05SJingoo Han 				       su->type == AS3711_BL_SU1 ?
240de474b5bSGuennadi Liakhovetski 				       "as3711-su1" : "as3711-su2",
241de474b5bSGuennadi Liakhovetski 				       &pdev->dev, su,
242de474b5bSGuennadi Liakhovetski 				       &as3711_bl_ops, &props);
243de474b5bSGuennadi Liakhovetski 	if (IS_ERR(bl)) {
244de474b5bSGuennadi Liakhovetski 		dev_err(&pdev->dev, "failed to register backlight\n");
245de474b5bSGuennadi Liakhovetski 		return PTR_ERR(bl);
246de474b5bSGuennadi Liakhovetski 	}
247de474b5bSGuennadi Liakhovetski 
248de474b5bSGuennadi Liakhovetski 	bl->props.brightness = props.max_brightness;
249de474b5bSGuennadi Liakhovetski 
250de474b5bSGuennadi Liakhovetski 	backlight_update_status(bl);
251de474b5bSGuennadi Liakhovetski 
252de474b5bSGuennadi Liakhovetski 	su->bl = bl;
253de474b5bSGuennadi Liakhovetski 
254de474b5bSGuennadi Liakhovetski 	return 0;
255de474b5bSGuennadi Liakhovetski }
256de474b5bSGuennadi Liakhovetski 
25759eb2b5eSGuennadi Liakhovetski static int as3711_backlight_parse_dt(struct device *dev)
25859eb2b5eSGuennadi Liakhovetski {
25959eb2b5eSGuennadi Liakhovetski 	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
2604a9c8bb2SJohan Hovold 	struct device_node *bl, *fb;
26159eb2b5eSGuennadi Liakhovetski 	int ret;
26259eb2b5eSGuennadi Liakhovetski 
2634a9c8bb2SJohan Hovold 	bl = of_get_child_by_name(dev->parent->of_node, "backlight");
26459eb2b5eSGuennadi Liakhovetski 	if (!bl) {
26559eb2b5eSGuennadi Liakhovetski 		dev_dbg(dev, "backlight node not found\n");
26659eb2b5eSGuennadi Liakhovetski 		return -ENODEV;
26759eb2b5eSGuennadi Liakhovetski 	}
26859eb2b5eSGuennadi Liakhovetski 
26959eb2b5eSGuennadi Liakhovetski 	fb = of_parse_phandle(bl, "su1-dev", 0);
27059eb2b5eSGuennadi Liakhovetski 	if (fb) {
271d5318d30SJohan Hovold 		of_node_put(fb);
272d5318d30SJohan Hovold 
273d5318d30SJohan Hovold 		pdata->su1_fb = true;
27459eb2b5eSGuennadi Liakhovetski 
27559eb2b5eSGuennadi Liakhovetski 		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
27659eb2b5eSGuennadi Liakhovetski 		if (pdata->su1_max_uA <= 0)
27759eb2b5eSGuennadi Liakhovetski 			ret = -EINVAL;
27859eb2b5eSGuennadi Liakhovetski 		if (ret < 0)
2794a9c8bb2SJohan Hovold 			goto err_put_bl;
28059eb2b5eSGuennadi Liakhovetski 	}
28159eb2b5eSGuennadi Liakhovetski 
28259eb2b5eSGuennadi Liakhovetski 	fb = of_parse_phandle(bl, "su2-dev", 0);
28359eb2b5eSGuennadi Liakhovetski 	if (fb) {
28459eb2b5eSGuennadi Liakhovetski 		int count = 0;
28559eb2b5eSGuennadi Liakhovetski 
286d5318d30SJohan Hovold 		of_node_put(fb);
287d5318d30SJohan Hovold 
288d5318d30SJohan Hovold 		pdata->su2_fb = true;
28959eb2b5eSGuennadi Liakhovetski 
29059eb2b5eSGuennadi Liakhovetski 		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
29159eb2b5eSGuennadi Liakhovetski 		if (pdata->su2_max_uA <= 0)
29259eb2b5eSGuennadi Liakhovetski 			ret = -EINVAL;
29359eb2b5eSGuennadi Liakhovetski 		if (ret < 0)
2944a9c8bb2SJohan Hovold 			goto err_put_bl;
29559eb2b5eSGuennadi Liakhovetski 
29659eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
29759eb2b5eSGuennadi Liakhovetski 			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
29859eb2b5eSGuennadi Liakhovetski 			count++;
29959eb2b5eSGuennadi Liakhovetski 		}
30059eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
30159eb2b5eSGuennadi Liakhovetski 			pdata->su2_feedback = AS3711_SU2_CURR1;
30259eb2b5eSGuennadi Liakhovetski 			count++;
30359eb2b5eSGuennadi Liakhovetski 		}
30459eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
30559eb2b5eSGuennadi Liakhovetski 			pdata->su2_feedback = AS3711_SU2_CURR2;
30659eb2b5eSGuennadi Liakhovetski 			count++;
30759eb2b5eSGuennadi Liakhovetski 		}
30859eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
30959eb2b5eSGuennadi Liakhovetski 			pdata->su2_feedback = AS3711_SU2_CURR3;
31059eb2b5eSGuennadi Liakhovetski 			count++;
31159eb2b5eSGuennadi Liakhovetski 		}
31259eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
31359eb2b5eSGuennadi Liakhovetski 			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
31459eb2b5eSGuennadi Liakhovetski 			count++;
31559eb2b5eSGuennadi Liakhovetski 		}
3164a9c8bb2SJohan Hovold 		if (count != 1) {
3174a9c8bb2SJohan Hovold 			ret = -EINVAL;
3184a9c8bb2SJohan Hovold 			goto err_put_bl;
3194a9c8bb2SJohan Hovold 		}
32059eb2b5eSGuennadi Liakhovetski 
32159eb2b5eSGuennadi Liakhovetski 		count = 0;
32259eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
32359eb2b5eSGuennadi Liakhovetski 			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
32459eb2b5eSGuennadi Liakhovetski 			count++;
32559eb2b5eSGuennadi Liakhovetski 		}
32659eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
32759eb2b5eSGuennadi Liakhovetski 			pdata->su2_fbprot = AS3711_SU2_GPIO2;
32859eb2b5eSGuennadi Liakhovetski 			count++;
32959eb2b5eSGuennadi Liakhovetski 		}
33059eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
33159eb2b5eSGuennadi Liakhovetski 			pdata->su2_fbprot = AS3711_SU2_GPIO3;
33259eb2b5eSGuennadi Liakhovetski 			count++;
33359eb2b5eSGuennadi Liakhovetski 		}
33459eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
33559eb2b5eSGuennadi Liakhovetski 			pdata->su2_fbprot = AS3711_SU2_GPIO4;
33659eb2b5eSGuennadi Liakhovetski 			count++;
33759eb2b5eSGuennadi Liakhovetski 		}
3384a9c8bb2SJohan Hovold 		if (count != 1) {
3394a9c8bb2SJohan Hovold 			ret = -EINVAL;
3404a9c8bb2SJohan Hovold 			goto err_put_bl;
3414a9c8bb2SJohan Hovold 		}
34259eb2b5eSGuennadi Liakhovetski 
34359eb2b5eSGuennadi Liakhovetski 		count = 0;
34459eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
34559eb2b5eSGuennadi Liakhovetski 			pdata->su2_auto_curr1 = true;
34659eb2b5eSGuennadi Liakhovetski 			count++;
34759eb2b5eSGuennadi Liakhovetski 		}
34859eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
34959eb2b5eSGuennadi Liakhovetski 			pdata->su2_auto_curr2 = true;
35059eb2b5eSGuennadi Liakhovetski 			count++;
35159eb2b5eSGuennadi Liakhovetski 		}
35259eb2b5eSGuennadi Liakhovetski 		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
35359eb2b5eSGuennadi Liakhovetski 			pdata->su2_auto_curr3 = true;
35459eb2b5eSGuennadi Liakhovetski 			count++;
35559eb2b5eSGuennadi Liakhovetski 		}
35659eb2b5eSGuennadi Liakhovetski 
35759eb2b5eSGuennadi Liakhovetski 		/*
35859eb2b5eSGuennadi Liakhovetski 		 * At least one su2-auto-curr* must be specified iff
35959eb2b5eSGuennadi Liakhovetski 		 * AS3711_SU2_CURR_AUTO is used
36059eb2b5eSGuennadi Liakhovetski 		 */
3614a9c8bb2SJohan Hovold 		if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO)) {
3624a9c8bb2SJohan Hovold 			ret = -EINVAL;
3634a9c8bb2SJohan Hovold 			goto err_put_bl;
3644a9c8bb2SJohan Hovold 		}
36559eb2b5eSGuennadi Liakhovetski 	}
36659eb2b5eSGuennadi Liakhovetski 
3674a9c8bb2SJohan Hovold 	of_node_put(bl);
3684a9c8bb2SJohan Hovold 
36959eb2b5eSGuennadi Liakhovetski 	return 0;
3704a9c8bb2SJohan Hovold 
3714a9c8bb2SJohan Hovold err_put_bl:
3724a9c8bb2SJohan Hovold 	of_node_put(bl);
3734a9c8bb2SJohan Hovold 
3744a9c8bb2SJohan Hovold 	return ret;
37559eb2b5eSGuennadi Liakhovetski }
37659eb2b5eSGuennadi Liakhovetski 
377de474b5bSGuennadi Liakhovetski static int as3711_backlight_probe(struct platform_device *pdev)
378de474b5bSGuennadi Liakhovetski {
379de474b5bSGuennadi Liakhovetski 	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
380de474b5bSGuennadi Liakhovetski 	struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);
381de474b5bSGuennadi Liakhovetski 	struct as3711_bl_supply *supply;
382de474b5bSGuennadi Liakhovetski 	struct as3711_bl_data *su;
383de474b5bSGuennadi Liakhovetski 	unsigned int max_brightness;
384de474b5bSGuennadi Liakhovetski 	int ret;
385de474b5bSGuennadi Liakhovetski 
38659eb2b5eSGuennadi Liakhovetski 	if (!pdata) {
387de474b5bSGuennadi Liakhovetski 		dev_err(&pdev->dev, "No platform data, exiting...\n");
388de474b5bSGuennadi Liakhovetski 		return -ENODEV;
389de474b5bSGuennadi Liakhovetski 	}
390de474b5bSGuennadi Liakhovetski 
39159eb2b5eSGuennadi Liakhovetski 	if (pdev->dev.parent->of_node) {
39259eb2b5eSGuennadi Liakhovetski 		ret = as3711_backlight_parse_dt(&pdev->dev);
39359eb2b5eSGuennadi Liakhovetski 		if (ret < 0) {
39459eb2b5eSGuennadi Liakhovetski 			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
39559eb2b5eSGuennadi Liakhovetski 			return ret;
39659eb2b5eSGuennadi Liakhovetski 		}
39759eb2b5eSGuennadi Liakhovetski 	}
39859eb2b5eSGuennadi Liakhovetski 
39959eb2b5eSGuennadi Liakhovetski 	if (!pdata->su1_fb && !pdata->su2_fb) {
40059eb2b5eSGuennadi Liakhovetski 		dev_err(&pdev->dev, "No framebuffer specified\n");
40159eb2b5eSGuennadi Liakhovetski 		return -EINVAL;
40259eb2b5eSGuennadi Liakhovetski 	}
40359eb2b5eSGuennadi Liakhovetski 
404de474b5bSGuennadi Liakhovetski 	/*
405de474b5bSGuennadi Liakhovetski 	 * Due to possible hardware damage I chose to block all modes,
406de474b5bSGuennadi Liakhovetski 	 * unsupported on my hardware. Anyone, wishing to use any of those modes
407de474b5bSGuennadi Liakhovetski 	 * will have to first review the code, then activate and test it.
408de474b5bSGuennadi Liakhovetski 	 */
409de474b5bSGuennadi Liakhovetski 	if (pdata->su1_fb ||
410de474b5bSGuennadi Liakhovetski 	    pdata->su2_fbprot != AS3711_SU2_GPIO4 ||
411de474b5bSGuennadi Liakhovetski 	    pdata->su2_feedback != AS3711_SU2_CURR_AUTO) {
412de474b5bSGuennadi Liakhovetski 		dev_warn(&pdev->dev,
413de474b5bSGuennadi Liakhovetski 			 "Attention! An untested mode has been chosen!\n"
414de474b5bSGuennadi Liakhovetski 			 "Please, review the code, enable, test, and report success:-)\n");
415de474b5bSGuennadi Liakhovetski 		return -EINVAL;
416de474b5bSGuennadi Liakhovetski 	}
417de474b5bSGuennadi Liakhovetski 
418de474b5bSGuennadi Liakhovetski 	supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL);
419de474b5bSGuennadi Liakhovetski 	if (!supply)
420de474b5bSGuennadi Liakhovetski 		return -ENOMEM;
421de474b5bSGuennadi Liakhovetski 
422de474b5bSGuennadi Liakhovetski 	supply->as3711 = as3711;
423de474b5bSGuennadi Liakhovetski 	supply->pdata = pdata;
424de474b5bSGuennadi Liakhovetski 
425de474b5bSGuennadi Liakhovetski 	if (pdata->su1_fb) {
426de474b5bSGuennadi Liakhovetski 		su = &supply->su1;
427de474b5bSGuennadi Liakhovetski 		su->type = AS3711_BL_SU1;
428de474b5bSGuennadi Liakhovetski 
429de474b5bSGuennadi Liakhovetski 		max_brightness = min(pdata->su1_max_uA, 31);
430de474b5bSGuennadi Liakhovetski 		ret = as3711_bl_register(pdev, max_brightness, su);
431de474b5bSGuennadi Liakhovetski 		if (ret < 0)
432de474b5bSGuennadi Liakhovetski 			return ret;
433de474b5bSGuennadi Liakhovetski 	}
434de474b5bSGuennadi Liakhovetski 
435de474b5bSGuennadi Liakhovetski 	if (pdata->su2_fb) {
436de474b5bSGuennadi Liakhovetski 		su = &supply->su2;
437de474b5bSGuennadi Liakhovetski 		su->type = AS3711_BL_SU2;
438de474b5bSGuennadi Liakhovetski 
439de474b5bSGuennadi Liakhovetski 		switch (pdata->su2_fbprot) {
440de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_GPIO2:
441de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_GPIO3:
442de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_GPIO4:
443de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_LX_SD4:
444de474b5bSGuennadi Liakhovetski 			break;
445de474b5bSGuennadi Liakhovetski 		default:
44683dedc05SJingoo Han 			return -EINVAL;
447de474b5bSGuennadi Liakhovetski 		}
448de474b5bSGuennadi Liakhovetski 
449de474b5bSGuennadi Liakhovetski 		switch (pdata->su2_feedback) {
450de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_VOLTAGE:
451de474b5bSGuennadi Liakhovetski 			max_brightness = min(pdata->su2_max_uA, 31);
452de474b5bSGuennadi Liakhovetski 			break;
453de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR1:
454de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR2:
455de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR3:
456de474b5bSGuennadi Liakhovetski 		case AS3711_SU2_CURR_AUTO:
457de474b5bSGuennadi Liakhovetski 			max_brightness = min(pdata->su2_max_uA / 150, 255);
458de474b5bSGuennadi Liakhovetski 			break;
459de474b5bSGuennadi Liakhovetski 		default:
46083dedc05SJingoo Han 			return -EINVAL;
461de474b5bSGuennadi Liakhovetski 		}
462de474b5bSGuennadi Liakhovetski 
463de474b5bSGuennadi Liakhovetski 		ret = as3711_bl_init_su2(supply);
464de474b5bSGuennadi Liakhovetski 		if (ret < 0)
465de474b5bSGuennadi Liakhovetski 			return ret;
466de474b5bSGuennadi Liakhovetski 
467de474b5bSGuennadi Liakhovetski 		ret = as3711_bl_register(pdev, max_brightness, su);
468de474b5bSGuennadi Liakhovetski 		if (ret < 0)
469de474b5bSGuennadi Liakhovetski 			return ret;
470de474b5bSGuennadi Liakhovetski 	}
471de474b5bSGuennadi Liakhovetski 
47283dedc05SJingoo Han 	platform_set_drvdata(pdev, supply);
473de474b5bSGuennadi Liakhovetski 
474de474b5bSGuennadi Liakhovetski 	return 0;
475de474b5bSGuennadi Liakhovetski }
476de474b5bSGuennadi Liakhovetski 
477de474b5bSGuennadi Liakhovetski static struct platform_driver as3711_backlight_driver = {
478de474b5bSGuennadi Liakhovetski 	.driver		= {
479de474b5bSGuennadi Liakhovetski 		.name	= "as3711-backlight",
480de474b5bSGuennadi Liakhovetski 	},
481de474b5bSGuennadi Liakhovetski 	.probe		= as3711_backlight_probe,
482de474b5bSGuennadi Liakhovetski };
483de474b5bSGuennadi Liakhovetski 
484de474b5bSGuennadi Liakhovetski module_platform_driver(as3711_backlight_driver);
485de474b5bSGuennadi Liakhovetski 
486de474b5bSGuennadi Liakhovetski MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs");
487de474b5bSGuennadi Liakhovetski MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de");
488*6f6d5c3fSKuninori Morimoto MODULE_LICENSE("GPL v2");
489de474b5bSGuennadi Liakhovetski MODULE_ALIAS("platform:as3711-backlight");
490