xref: /linux/drivers/video/backlight/mp3309c.c (revision 021bc4b9)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Driver for MPS MP3309C White LED driver with I2C interface
4  *
5  * This driver support both analog (by I2C commands) and PWM dimming control
6  * modes.
7  *
8  * Copyright (C) 2023 ASEM Srl
9  * Author: Flavio Suligoi <f.suligoi@asem.it>
10  *
11  * Based on pwm_bl.c
12  */
13 
14 #include <linux/backlight.h>
15 #include <linux/delay.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/i2c.h>
18 #include <linux/pwm.h>
19 #include <linux/regmap.h>
20 
21 #define REG_I2C_0	0x00
22 #define REG_I2C_1	0x01
23 
24 #define REG_I2C_0_EN	0x80
25 #define REG_I2C_0_D0	0x40
26 #define REG_I2C_0_D1	0x20
27 #define REG_I2C_0_D2	0x10
28 #define REG_I2C_0_D3	0x08
29 #define REG_I2C_0_D4	0x04
30 #define REG_I2C_0_RSRV1	0x02
31 #define REG_I2C_0_RSRV2	0x01
32 
33 #define REG_I2C_1_RSRV1	0x80
34 #define REG_I2C_1_DIMS	0x40
35 #define REG_I2C_1_SYNC	0x20
36 #define REG_I2C_1_OVP0	0x10
37 #define REG_I2C_1_OVP1	0x08
38 #define REG_I2C_1_VOS	0x04
39 #define REG_I2C_1_LEDO	0x02
40 #define REG_I2C_1_OTP	0x01
41 
42 #define ANALOG_I2C_NUM_LEVELS	32		/* 0..31 */
43 #define ANALOG_I2C_REG_MASK	0x7c
44 
45 #define MP3309C_PWM_DEFAULT_NUM_LEVELS	256	/* 0..255 */
46 
47 enum mp3309c_status_value {
48 	FIRST_POWER_ON,
49 	BACKLIGHT_OFF,
50 	BACKLIGHT_ON,
51 };
52 
53 enum mp3309c_dimming_mode_value {
54 	DIMMING_PWM,
55 	DIMMING_ANALOG_I2C,
56 };
57 
58 struct mp3309c_platform_data {
59 	unsigned int max_brightness;
60 	unsigned int default_brightness;
61 	unsigned int *levels;
62 	u8  dimming_mode;
63 	u8  over_voltage_protection;
64 	bool sync_mode;
65 	u8 status;
66 };
67 
68 struct mp3309c_chip {
69 	struct device *dev;
70 	struct mp3309c_platform_data *pdata;
71 	struct backlight_device *bl;
72 	struct gpio_desc *enable_gpio;
73 	struct regmap *regmap;
74 	struct pwm_device *pwmd;
75 };
76 
77 static const struct regmap_config mp3309c_regmap = {
78 	.name = "mp3309c_regmap",
79 	.reg_bits = 8,
80 	.reg_stride = 1,
81 	.val_bits = 8,
82 	.max_register = REG_I2C_1,
83 };
84 
85 static int mp3309c_enable_device(struct mp3309c_chip *chip)
86 {
87 	u8 reg_val;
88 	int ret;
89 
90 	/* I2C register #0 - Device enable */
91 	ret = regmap_update_bits(chip->regmap, REG_I2C_0, REG_I2C_0_EN,
92 				 REG_I2C_0_EN);
93 	if (ret)
94 		return ret;
95 
96 	/*
97 	 * I2C register #1 - Set working mode:
98 	 *  - set one of the two dimming mode:
99 	 *    - PWM dimming using an external PWM dimming signal
100 	 *    - analog dimming using I2C commands
101 	 *  - enable/disable synchronous mode
102 	 *  - set overvoltage protection (OVP)
103 	 */
104 	reg_val = 0x00;
105 	if (chip->pdata->dimming_mode == DIMMING_PWM)
106 		reg_val |= REG_I2C_1_DIMS;
107 	if (chip->pdata->sync_mode)
108 		reg_val |= REG_I2C_1_SYNC;
109 	reg_val |= chip->pdata->over_voltage_protection;
110 	ret = regmap_write(chip->regmap, REG_I2C_1, reg_val);
111 	if (ret)
112 		return ret;
113 
114 	return 0;
115 }
116 
117 static int mp3309c_bl_update_status(struct backlight_device *bl)
118 {
119 	struct mp3309c_chip *chip = bl_get_data(bl);
120 	int brightness = backlight_get_brightness(bl);
121 	struct pwm_state pwmstate;
122 	unsigned int analog_val, bits_val;
123 	int i, ret;
124 
125 	if (chip->pdata->dimming_mode == DIMMING_PWM) {
126 		/*
127 		 * PWM control mode
128 		 */
129 		pwm_get_state(chip->pwmd, &pwmstate);
130 		pwm_set_relative_duty_cycle(&pwmstate,
131 					    chip->pdata->levels[brightness],
132 					    chip->pdata->levels[chip->pdata->max_brightness]);
133 		pwmstate.enabled = true;
134 		ret = pwm_apply_state(chip->pwmd, &pwmstate);
135 		if (ret)
136 			return ret;
137 
138 		switch (chip->pdata->status) {
139 		case FIRST_POWER_ON:
140 		case BACKLIGHT_OFF:
141 			/*
142 			 * After 20ms of low pwm signal level, the chip turns
143 			 * off automatically. In this case, before enabling the
144 			 * chip again, we must wait about 10ms for pwm signal to
145 			 * stabilize.
146 			 */
147 			if (brightness > 0) {
148 				msleep(10);
149 				mp3309c_enable_device(chip);
150 				chip->pdata->status = BACKLIGHT_ON;
151 			} else {
152 				chip->pdata->status = BACKLIGHT_OFF;
153 			}
154 			break;
155 		case BACKLIGHT_ON:
156 			if (brightness == 0)
157 				chip->pdata->status = BACKLIGHT_OFF;
158 			break;
159 		}
160 	} else {
161 		/*
162 		 * Analog (by I2C command) control mode
163 		 *
164 		 * The first time, before setting brightness, we must enable the
165 		 * device
166 		 */
167 		if (chip->pdata->status == FIRST_POWER_ON)
168 			mp3309c_enable_device(chip);
169 
170 		/*
171 		 * Dimming mode I2C command (fixed dimming range 0..31)
172 		 *
173 		 * The 5 bits of the dimming analog value D4..D0 is allocated
174 		 * in the I2C register #0, in the following way:
175 		 *
176 		 *     +--+--+--+--+--+--+--+--+
177 		 *     |EN|D0|D1|D2|D3|D4|XX|XX|
178 		 *     +--+--+--+--+--+--+--+--+
179 		 */
180 		analog_val = brightness;
181 		bits_val = 0;
182 		for (i = 0; i <= 5; i++)
183 			bits_val += ((analog_val >> i) & 0x01) << (6 - i);
184 		ret = regmap_update_bits(chip->regmap, REG_I2C_0,
185 					 ANALOG_I2C_REG_MASK, bits_val);
186 		if (ret)
187 			return ret;
188 
189 		if (brightness > 0)
190 			chip->pdata->status = BACKLIGHT_ON;
191 		else
192 			chip->pdata->status = BACKLIGHT_OFF;
193 	}
194 
195 	return 0;
196 }
197 
198 static const struct backlight_ops mp3309c_bl_ops = {
199 	.update_status = mp3309c_bl_update_status,
200 };
201 
202 static int pm3309c_parse_dt_node(struct mp3309c_chip *chip,
203 				 struct mp3309c_platform_data *pdata)
204 {
205 	struct device_node *node = chip->dev->of_node;
206 	struct property *prop_pwms;
207 	struct property *prop_levels = NULL;
208 	int length = 0;
209 	int ret, i;
210 	unsigned int num_levels, tmp_value;
211 
212 	if (!node) {
213 		dev_err(chip->dev, "failed to get DT node\n");
214 		return -ENODEV;
215 	}
216 
217 	/*
218 	 * Dimming mode: the MP3309C provides two dimming control mode:
219 	 *
220 	 * - PWM mode
221 	 * - Analog by I2C control mode (default)
222 	 *
223 	 * I2C control mode is assumed as default but, if the pwms property is
224 	 * found in the backlight node, the mode switches to PWM mode.
225 	 */
226 	pdata->dimming_mode = DIMMING_ANALOG_I2C;
227 	prop_pwms = of_find_property(node, "pwms", &length);
228 	if (prop_pwms) {
229 		chip->pwmd = devm_pwm_get(chip->dev, NULL);
230 		if (IS_ERR(chip->pwmd))
231 			return dev_err_probe(chip->dev, PTR_ERR(chip->pwmd),
232 					     "error getting pwm data\n");
233 		pdata->dimming_mode = DIMMING_PWM;
234 		pwm_apply_args(chip->pwmd);
235 	}
236 
237 	/*
238 	 * In I2C control mode the dimming levels (0..31) are fixed by the
239 	 * hardware, while in PWM control mode they can be chosen by the user,
240 	 * to allow nonlinear mappings.
241 	 */
242 	if  (pdata->dimming_mode == DIMMING_ANALOG_I2C) {
243 		/*
244 		 * Analog (by I2C commands) control mode: fixed 0..31 brightness
245 		 * levels
246 		 */
247 		num_levels = ANALOG_I2C_NUM_LEVELS;
248 
249 		/* Enable GPIO used in I2C dimming mode only */
250 		chip->enable_gpio = devm_gpiod_get(chip->dev, "enable",
251 						   GPIOD_OUT_HIGH);
252 		if (IS_ERR(chip->enable_gpio))
253 			return dev_err_probe(chip->dev,
254 					     PTR_ERR(chip->enable_gpio),
255 					     "error getting enable gpio\n");
256 	} else {
257 		/*
258 		 * PWM control mode: check for brightness level in DT
259 		 */
260 		prop_levels = of_find_property(node, "brightness-levels",
261 					       &length);
262 		if (prop_levels) {
263 			/* Read brightness levels from DT */
264 			num_levels = length / sizeof(u32);
265 			if (num_levels < 2)
266 				return -EINVAL;
267 		} else {
268 			/* Use default brightness levels */
269 			num_levels = MP3309C_PWM_DEFAULT_NUM_LEVELS;
270 		}
271 	}
272 
273 	/* Fill brightness levels array */
274 	pdata->levels = devm_kcalloc(chip->dev, num_levels,
275 				     sizeof(*pdata->levels), GFP_KERNEL);
276 	if (!pdata->levels)
277 		return -ENOMEM;
278 	if (prop_levels) {
279 		ret = of_property_read_u32_array(node, "brightness-levels",
280 						 pdata->levels,
281 						 num_levels);
282 		if (ret < 0)
283 			return ret;
284 	} else {
285 		for (i = 0; i < num_levels; i++)
286 			pdata->levels[i] = i;
287 	}
288 
289 	pdata->max_brightness = num_levels - 1;
290 
291 	ret = of_property_read_u32(node, "default-brightness",
292 				   &pdata->default_brightness);
293 	if (ret)
294 		pdata->default_brightness = pdata->max_brightness;
295 	if (pdata->default_brightness > pdata->max_brightness) {
296 		dev_err(chip->dev,
297 			"default brightness exceeds max brightness\n");
298 		pdata->default_brightness = pdata->max_brightness;
299 	}
300 
301 	/*
302 	 * Over-voltage protection (OVP)
303 	 *
304 	 * This (optional) property values are:
305 	 *
306 	 *  - 13.5V
307 	 *  - 24V
308 	 *  - 35.5V (hardware default setting)
309 	 *
310 	 * If missing, the default value for OVP is 35.5V
311 	 */
312 	pdata->over_voltage_protection = REG_I2C_1_OVP1;
313 	if (!of_property_read_u32(node, "mps,overvoltage-protection-microvolt",
314 				  &tmp_value)) {
315 		switch (tmp_value) {
316 		case 13500000:
317 			pdata->over_voltage_protection = 0x00;
318 			break;
319 		case 24000000:
320 			pdata->over_voltage_protection = REG_I2C_1_OVP0;
321 			break;
322 		case 35500000:
323 			pdata->over_voltage_protection = REG_I2C_1_OVP1;
324 			break;
325 		default:
326 			return -EINVAL;
327 		}
328 	}
329 
330 	/* Synchronous (default) and non-synchronous mode */
331 	pdata->sync_mode = true;
332 	if (of_property_read_bool(node, "mps,no-sync-mode"))
333 		pdata->sync_mode = false;
334 
335 	return 0;
336 }
337 
338 static int mp3309c_probe(struct i2c_client *client)
339 {
340 	struct mp3309c_platform_data *pdata = dev_get_platdata(&client->dev);
341 	struct mp3309c_chip *chip;
342 	struct backlight_properties props;
343 	struct pwm_state pwmstate;
344 	int ret;
345 
346 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
347 		dev_err(&client->dev, "failed to check i2c functionality\n");
348 		return -EOPNOTSUPP;
349 	}
350 
351 	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
352 	if (!chip)
353 		return -ENOMEM;
354 
355 	chip->dev = &client->dev;
356 
357 	chip->regmap = devm_regmap_init_i2c(client, &mp3309c_regmap);
358 	if (IS_ERR(chip->regmap))
359 		return dev_err_probe(&client->dev, PTR_ERR(chip->regmap),
360 				     "failed to allocate register map\n");
361 
362 	i2c_set_clientdata(client, chip);
363 
364 	if (!pdata) {
365 		pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL);
366 		if (!pdata)
367 			return -ENOMEM;
368 
369 		ret = pm3309c_parse_dt_node(chip, pdata);
370 		if (ret)
371 			return ret;
372 	}
373 	chip->pdata = pdata;
374 
375 	/* Backlight properties */
376 	props.brightness = pdata->default_brightness;
377 	props.max_brightness = pdata->max_brightness;
378 	props.scale = BACKLIGHT_SCALE_LINEAR;
379 	props.type = BACKLIGHT_RAW;
380 	props.power = FB_BLANK_UNBLANK;
381 	props.fb_blank = FB_BLANK_UNBLANK;
382 	chip->bl = devm_backlight_device_register(chip->dev, "mp3309c",
383 						  chip->dev, chip,
384 						  &mp3309c_bl_ops, &props);
385 	if (IS_ERR(chip->bl))
386 		return dev_err_probe(chip->dev, PTR_ERR(chip->bl),
387 				     "error registering backlight device\n");
388 
389 	/* In PWM dimming mode, enable pwm device */
390 	if (chip->pdata->dimming_mode == DIMMING_PWM) {
391 		pwm_init_state(chip->pwmd, &pwmstate);
392 		pwm_set_relative_duty_cycle(&pwmstate,
393 					    chip->pdata->default_brightness,
394 					    chip->pdata->max_brightness);
395 		pwmstate.enabled = true;
396 		ret = pwm_apply_state(chip->pwmd, &pwmstate);
397 		if (ret)
398 			return dev_err_probe(chip->dev, ret,
399 					     "error setting pwm device\n");
400 	}
401 
402 	chip->pdata->status = FIRST_POWER_ON;
403 	backlight_update_status(chip->bl);
404 
405 	return 0;
406 }
407 
408 static void mp3309c_remove(struct i2c_client *client)
409 {
410 	struct mp3309c_chip *chip = i2c_get_clientdata(client);
411 	struct backlight_device *bl = chip->bl;
412 
413 	bl->props.power = FB_BLANK_POWERDOWN;
414 	bl->props.brightness = 0;
415 	backlight_update_status(chip->bl);
416 }
417 
418 static const struct of_device_id mp3309c_match_table[] = {
419 	{ .compatible = "mps,mp3309c", },
420 	{ },
421 };
422 MODULE_DEVICE_TABLE(of, mp3309c_match_table);
423 
424 static const struct i2c_device_id mp3309c_id[] = {
425 	{ "mp3309c", 0 },
426 	{ }
427 };
428 MODULE_DEVICE_TABLE(i2c, mp3309c_id);
429 
430 static struct i2c_driver mp3309c_i2c_driver = {
431 	.driver	= {
432 			.name		= KBUILD_MODNAME,
433 			.of_match_table	= mp3309c_match_table,
434 	},
435 	.probe		= mp3309c_probe,
436 	.remove		= mp3309c_remove,
437 	.id_table	= mp3309c_id,
438 };
439 
440 module_i2c_driver(mp3309c_i2c_driver);
441 
442 MODULE_DESCRIPTION("Backlight Driver for MPS MP3309C");
443 MODULE_AUTHOR("Flavio Suligoi <f.suligoi@asem.it>");
444 MODULE_LICENSE("GPL");
445