1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for MPS MP3309C White LED driver with I2C interface
5 * This driver support both analog (by I2C commands) and PWM dimming control
8 * Copyright (C) 2023 ASEM Srl
9 * Author: Flavio Suligoi <f.suligoi@asem.it>
14 #include <linux/backlight.h>
15 #include <linux/delay.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/i2c.h>
18 #include <linux/mod_devicetable.h>
19 #include <linux/property.h>
20 #include <linux/pwm.h>
21 #include <linux/regmap.h>
23 #define REG_I2C_0 0x00
24 #define REG_I2C_1 0x01
26 #define REG_I2C_0_EN 0x80
27 #define REG_I2C_0_D0 0x40
28 #define REG_I2C_0_D1 0x20
29 #define REG_I2C_0_D2 0x10
30 #define REG_I2C_0_D3 0x08
31 #define REG_I2C_0_D4 0x04
32 #define REG_I2C_0_RSRV1 0x02
33 #define REG_I2C_0_RSRV2 0x01
35 #define REG_I2C_1_RSRV1 0x80
36 #define REG_I2C_1_DIMS 0x40
37 #define REG_I2C_1_SYNC 0x20
38 #define REG_I2C_1_OVP0 0x10
39 #define REG_I2C_1_OVP1 0x08
40 #define REG_I2C_1_VOS 0x04
41 #define REG_I2C_1_LEDO 0x02
42 #define REG_I2C_1_OTP 0x01
44 #define ANALOG_I2C_NUM_LEVELS 32 /* 0..31 */
45 #define ANALOG_I2C_REG_MASK 0x7c
47 #define MP3309C_PWM_DEFAULT_NUM_LEVELS 256 /* 0..255 */
49 enum mp3309c_status_value
{
55 enum mp3309c_dimming_mode_value
{
60 struct mp3309c_platform_data
{
61 unsigned int max_brightness
;
62 unsigned int default_brightness
;
65 u8 over_voltage_protection
;
72 struct mp3309c_platform_data
*pdata
;
73 struct backlight_device
*bl
;
74 struct gpio_desc
*enable_gpio
;
75 struct regmap
*regmap
;
76 struct pwm_device
*pwmd
;
79 static const struct regmap_config mp3309c_regmap
= {
80 .name
= "mp3309c_regmap",
84 .max_register
= REG_I2C_1
,
87 static int mp3309c_enable_device(struct mp3309c_chip
*chip
)
92 /* I2C register #0 - Device enable */
93 ret
= regmap_update_bits(chip
->regmap
, REG_I2C_0
, REG_I2C_0_EN
,
99 * I2C register #1 - Set working mode:
100 * - enable/disable synchronous mode
101 * - set overvoltage protection (OVP)
104 if (chip
->pdata
->sync_mode
)
105 reg_val
|= REG_I2C_1_SYNC
;
106 reg_val
|= chip
->pdata
->over_voltage_protection
;
107 ret
= regmap_write(chip
->regmap
, REG_I2C_1
, reg_val
);
114 static int mp3309c_bl_update_status(struct backlight_device
*bl
)
116 struct mp3309c_chip
*chip
= bl_get_data(bl
);
117 int brightness
= backlight_get_brightness(bl
);
118 struct pwm_state pwmstate
;
119 unsigned int analog_val
, bits_val
;
122 if (chip
->pdata
->dimming_mode
== DIMMING_PWM
) {
126 pwm_get_state(chip
->pwmd
, &pwmstate
);
127 pwm_set_relative_duty_cycle(&pwmstate
,
128 chip
->pdata
->levels
[brightness
],
129 chip
->pdata
->levels
[chip
->pdata
->max_brightness
]);
130 pwmstate
.enabled
= true;
131 ret
= pwm_apply_might_sleep(chip
->pwmd
, &pwmstate
);
135 switch (chip
->pdata
->status
) {
139 * After 20ms of low pwm signal level, the chip turns
140 * off automatically. In this case, before enabling the
141 * chip again, we must wait about 10ms for pwm signal to
144 if (brightness
> 0) {
146 mp3309c_enable_device(chip
);
147 chip
->pdata
->status
= BACKLIGHT_ON
;
149 chip
->pdata
->status
= BACKLIGHT_OFF
;
154 chip
->pdata
->status
= BACKLIGHT_OFF
;
159 * Analog (by I2C command) control mode
161 * The first time, before setting brightness, we must enable the
164 if (chip
->pdata
->status
== FIRST_POWER_ON
)
165 mp3309c_enable_device(chip
);
168 * Dimming mode I2C command (fixed dimming range 0..31)
170 * The 5 bits of the dimming analog value D4..D0 is allocated
171 * in the I2C register #0, in the following way:
173 * +--+--+--+--+--+--+--+--+
174 * |EN|D0|D1|D2|D3|D4|XX|XX|
175 * +--+--+--+--+--+--+--+--+
177 analog_val
= brightness
;
179 for (i
= 0; i
<= 5; i
++)
180 bits_val
+= ((analog_val
>> i
) & 0x01) << (6 - i
);
181 ret
= regmap_update_bits(chip
->regmap
, REG_I2C_0
,
182 ANALOG_I2C_REG_MASK
, bits_val
);
187 chip
->pdata
->status
= BACKLIGHT_ON
;
189 chip
->pdata
->status
= BACKLIGHT_OFF
;
195 static const struct backlight_ops mp3309c_bl_ops
= {
196 .update_status
= mp3309c_bl_update_status
,
199 static int mp3309c_parse_fwnode(struct mp3309c_chip
*chip
,
200 struct mp3309c_platform_data
*pdata
)
203 unsigned int tmp_value
;
204 struct device
*dev
= chip
->dev
;
207 if (!dev_fwnode(dev
))
208 return dev_err_probe(dev
, -ENODEV
, "failed to get firmware node\n");
211 * Dimming mode: the MP3309C provides two dimming control mode:
214 * - Analog by I2C control mode (default)
216 * I2C control mode is assumed as default but, if the pwms property is
217 * found in the backlight node, the mode switches to PWM mode.
219 pdata
->dimming_mode
= DIMMING_ANALOG_I2C
;
220 if (device_property_present(dev
, "pwms")) {
221 chip
->pwmd
= devm_pwm_get(dev
, NULL
);
222 if (IS_ERR(chip
->pwmd
))
223 return dev_err_probe(dev
, PTR_ERR(chip
->pwmd
), "error getting pwm data\n");
224 pdata
->dimming_mode
= DIMMING_PWM
;
225 pwm_apply_args(chip
->pwmd
);
229 * In I2C control mode the dimming levels (0..31) are fixed by the
230 * hardware, while in PWM control mode they can be chosen by the user,
231 * to allow nonlinear mappings.
233 if (pdata
->dimming_mode
== DIMMING_ANALOG_I2C
) {
235 * Analog (by I2C commands) control mode: fixed 0..31 brightness
238 num_levels
= ANALOG_I2C_NUM_LEVELS
;
240 /* Enable GPIO used in I2C dimming mode only */
241 chip
->enable_gpio
= devm_gpiod_get(dev
, "enable", GPIOD_OUT_HIGH
);
242 if (IS_ERR(chip
->enable_gpio
))
243 return dev_err_probe(dev
, PTR_ERR(chip
->enable_gpio
),
244 "error getting enable gpio\n");
247 * PWM control mode: check for brightness level in DT
249 if (device_property_present(dev
, "brightness-levels")) {
250 /* Read brightness levels from DT */
251 num_levels
= device_property_count_u32(dev
, "brightness-levels");
255 /* Use default brightness levels */
256 num_levels
= MP3309C_PWM_DEFAULT_NUM_LEVELS
;
260 /* Fill brightness levels array */
261 pdata
->levels
= devm_kcalloc(dev
, num_levels
, sizeof(*pdata
->levels
), GFP_KERNEL
);
264 if (device_property_present(dev
, "brightness-levels")) {
265 ret
= device_property_read_u32_array(dev
, "brightness-levels",
266 pdata
->levels
, num_levels
);
270 for (i
= 0; i
< num_levels
; i
++)
271 pdata
->levels
[i
] = i
;
274 pdata
->max_brightness
= num_levels
- 1;
276 ret
= device_property_read_u32(dev
, "default-brightness", &pdata
->default_brightness
);
278 pdata
->default_brightness
= pdata
->max_brightness
;
279 if (pdata
->default_brightness
> pdata
->max_brightness
) {
280 dev_err_probe(dev
, -ERANGE
, "default brightness exceeds max brightness\n");
281 pdata
->default_brightness
= pdata
->max_brightness
;
285 * Over-voltage protection (OVP)
287 * This (optional) property values are:
291 * - 35.5V (hardware default setting)
293 * If missing, the default value for OVP is 35.5V
295 pdata
->over_voltage_protection
= REG_I2C_1_OVP1
;
296 ret
= device_property_read_u32(dev
, "mps,overvoltage-protection-microvolt", &tmp_value
);
300 pdata
->over_voltage_protection
= 0x00;
303 pdata
->over_voltage_protection
= REG_I2C_1_OVP0
;
306 pdata
->over_voltage_protection
= REG_I2C_1_OVP1
;
313 /* Synchronous (default) and non-synchronous mode */
314 pdata
->sync_mode
= !device_property_read_bool(dev
, "mps,no-sync-mode");
319 static int mp3309c_probe(struct i2c_client
*client
)
321 struct device
*dev
= &client
->dev
;
322 struct mp3309c_platform_data
*pdata
= dev_get_platdata(dev
);
323 struct mp3309c_chip
*chip
;
324 struct backlight_properties props
;
325 struct pwm_state pwmstate
;
328 if (!i2c_check_functionality(client
->adapter
, I2C_FUNC_I2C
))
329 return dev_err_probe(dev
, -EOPNOTSUPP
, "failed to check i2c functionality\n");
331 chip
= devm_kzalloc(dev
, sizeof(*chip
), GFP_KERNEL
);
337 chip
->regmap
= devm_regmap_init_i2c(client
, &mp3309c_regmap
);
338 if (IS_ERR(chip
->regmap
))
339 return dev_err_probe(dev
, PTR_ERR(chip
->regmap
),
340 "failed to allocate register map\n");
342 i2c_set_clientdata(client
, chip
);
345 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
349 ret
= mp3309c_parse_fwnode(chip
, pdata
);
355 /* Backlight properties */
356 memset(&props
, 0, sizeof(struct backlight_properties
));
357 props
.brightness
= pdata
->default_brightness
;
358 props
.max_brightness
= pdata
->max_brightness
;
359 props
.scale
= BACKLIGHT_SCALE_LINEAR
;
360 props
.type
= BACKLIGHT_RAW
;
361 props
.power
= BACKLIGHT_POWER_ON
;
362 chip
->bl
= devm_backlight_device_register(dev
, "mp3309c", dev
, chip
,
363 &mp3309c_bl_ops
, &props
);
364 if (IS_ERR(chip
->bl
))
365 return dev_err_probe(dev
, PTR_ERR(chip
->bl
),
366 "error registering backlight device\n");
368 /* In PWM dimming mode, enable pwm device */
369 if (chip
->pdata
->dimming_mode
== DIMMING_PWM
) {
370 pwm_init_state(chip
->pwmd
, &pwmstate
);
371 pwm_set_relative_duty_cycle(&pwmstate
,
372 chip
->pdata
->default_brightness
,
373 chip
->pdata
->max_brightness
);
374 pwmstate
.enabled
= true;
375 ret
= pwm_apply_might_sleep(chip
->pwmd
, &pwmstate
);
377 return dev_err_probe(dev
, ret
, "error setting pwm device\n");
380 chip
->pdata
->status
= FIRST_POWER_ON
;
381 backlight_update_status(chip
->bl
);
386 static void mp3309c_remove(struct i2c_client
*client
)
388 struct mp3309c_chip
*chip
= i2c_get_clientdata(client
);
389 struct backlight_device
*bl
= chip
->bl
;
391 bl
->props
.power
= BACKLIGHT_POWER_OFF
;
392 bl
->props
.brightness
= 0;
393 backlight_update_status(chip
->bl
);
396 static const struct of_device_id mp3309c_match_table
[] = {
397 { .compatible
= "mps,mp3309c", },
400 MODULE_DEVICE_TABLE(of
, mp3309c_match_table
);
402 static const struct i2c_device_id mp3309c_id
[] = {
406 MODULE_DEVICE_TABLE(i2c
, mp3309c_id
);
408 static struct i2c_driver mp3309c_i2c_driver
= {
410 .name
= KBUILD_MODNAME
,
411 .of_match_table
= mp3309c_match_table
,
413 .probe
= mp3309c_probe
,
414 .remove
= mp3309c_remove
,
415 .id_table
= mp3309c_id
,
418 module_i2c_driver(mp3309c_i2c_driver
);
420 MODULE_DESCRIPTION("Backlight Driver for MPS MP3309C");
421 MODULE_AUTHOR("Flavio Suligoi <f.suligoi@asem.it>");
422 MODULE_LICENSE("GPL");