Merge tag 'backlight-next-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/lee...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 19 Jan 2024 00:53:35 +0000 (16:53 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 19 Jan 2024 00:53:35 +0000 (16:53 -0800)
Pull backlight updates from Lee Jones:
 "New Drivers:
   - Add support for Monolithic Power Systems MP3309C WLED Step-up Converter

  Fix-ups:
   - Use/convert to new/better APIs/helpers/MACROs instead of
     hand-rolling implementations
   - Device Tree Binding updates
   - Demote non-kerneldoc header comments
   - Improve error handling; return proper error values, simplify, avoid
     duplicates, etc
   - Convert over to the new (kinda) GPIOD API

  Bug Fixes:
   - Fix uninitialised local variable"

* tag 'backlight-next-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight:
  backlight: hx8357: Convert to agnostic GPIO API
  backlight: ili922x: Add an error code check in ili922x_write()
  backlight: ili922x: Drop kernel-doc for local macros
  backlight: mp3309c: Fix uninitialized local variable
  backlight: pwm_bl: Use dev_err_probe
  backlight: mp3309c: Add support for MPS MP3309C
  dt-bindings: backlight: mp3309c: Remove two required properties

Documentation/devicetree/bindings/leds/backlight/mps,mp3309c.yaml
MAINTAINERS
drivers/gpio/gpiolib-of.c
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/hx8357.c
drivers/video/backlight/ili922x.c
drivers/video/backlight/mp3309c.c [new file with mode: 0644]
drivers/video/backlight/pwm_bl.c

index 4191e33626f51ad3ccb5843e61caeb83eafea193..527a37368ed7422ae605d693e95b2765fa15246d 100644 (file)
@@ -14,8 +14,8 @@ description: |
   programmable switching frequency to optimize efficiency.
   It supports two different dimming modes:
 
-  - analog mode, via I2C commands (default)
-  - PWM controlled mode.
+  - analog mode, via I2C commands, as default mode (32 dimming levels)
+  - PWM controlled mode (optional)
 
   The datasheet is available at:
   https://www.monolithicpower.com/en/mp3309c.html
@@ -50,8 +50,6 @@ properties:
 required:
   - compatible
   - reg
-  - max-brightness
-  - default-brightness
 
 unevaluatedProperties: false
 
@@ -66,8 +64,8 @@ examples:
             compatible = "mps,mp3309c";
             reg = <0x17>;
             pwms = <&pwm1 0 3333333 0>; /* 300 Hz --> (1/f) * 1*10^9 */
-            max-brightness = <100>;
-            default-brightness = <80>;
+            brightness-levels = <0 4 8 16 32 64 128 255>;
+            default-brightness = <6>;
             mps,overvoltage-protection-microvolt = <24000000>;
         };
     };
index ca8baa391ac17a71c3a6cacbc8e7e13c708ac749..c755a8f7b93e7f1bf4dccb7f3372c6e92476cc35 100644 (file)
@@ -14804,6 +14804,13 @@ S:     Maintained
 F:     Documentation/driver-api/tty/moxa-smartio.rst
 F:     drivers/tty/mxser.*
 
+MP3309C BACKLIGHT DRIVER
+M:     Flavio Suligoi <f.suligoi@asem.it>
+L:     dri-devel@lists.freedesktop.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/leds/backlight/mps,mp3309c.yaml
+F:     drivers/video/backlight/mp3309c.c
+
 MR800 AVERMEDIA USB FM RADIO DRIVER
 M:     Alexey Klimov <klimov.linux@gmail.com>
 L:     linux-media@vger.kernel.org
index 402f7d99b0c1e4a36a136e7dd1e3b6f7863cf6f6..e7770eedd14693ac827a0c4ce875aa5484e84a80 100644 (file)
@@ -184,7 +184,7 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np,
                const char *propname;
                bool active_high;
        } gpios[] = {
-#if !IS_ENABLED(CONFIG_LCD_HX8357)
+#if IS_ENABLED(CONFIG_LCD_HX8357)
                /*
                 * Himax LCD controllers used incorrectly named
                 * "gpios-reset" property and also specified wrong
@@ -478,7 +478,7 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np,
                 */
                const char *compatible;
        } gpios[] = {
-#if !IS_ENABLED(CONFIG_LCD_HX8357)
+#if IS_ENABLED(CONFIG_LCD_HX8357)
                /* Himax LCD controllers used "gpios-reset" */
                { "reset",      "gpios-reset",  "himax,hx8357" },
                { "reset",      "gpios-reset",  "himax,hx8369" },
index 94d092091b5e76cdfe2d7696d1edf0a2de2c4b3f..ea2d0d69bd8cc11f6393bea249b9744d57fb86a7 100644 (file)
@@ -395,6 +395,17 @@ config BACKLIGHT_LP8788
        help
          This supports TI LP8788 backlight driver.
 
+config BACKLIGHT_MP3309C
+       tristate "Backlight Driver for MPS MP3309C"
+       depends on I2C && PWM
+       select REGMAP_I2C
+       help
+         This supports MPS MP3309C backlight WLED driver in both PWM and
+         analog/I2C dimming modes.
+
+         To compile this driver as a module, choose M here: the module will
+         be called mp3309c.
+
 config BACKLIGHT_PANDORA
        tristate "Backlight driver for Pandora console"
        depends on TWL4030_CORE
index 67d3ff39be3c1e4d4969c2ea5eff199504c585a0..06966cb204597b74afffe16ba4814357843b2d86 100644 (file)
@@ -43,6 +43,7 @@ obj-$(CONFIG_BACKLIGHT_LP855X)                += lp855x_bl.o
 obj-$(CONFIG_BACKLIGHT_LP8788)         += lp8788_bl.o
 obj-$(CONFIG_BACKLIGHT_LV5207LP)       += lv5207lp.o
 obj-$(CONFIG_BACKLIGHT_MAX8925)                += max8925_bl.o
+obj-$(CONFIG_BACKLIGHT_MP3309C)                += mp3309c.o
 obj-$(CONFIG_BACKLIGHT_MT6370)         += mt6370-backlight.o
 obj-$(CONFIG_BACKLIGHT_OMAP1)          += omap1_bl.o
 obj-$(CONFIG_BACKLIGHT_PANDORA)                += pandora_bl.o
index f76d2469d490103dfd7090310c60a73f7736207b..d7298376cf74ddd89c4804ab297b621538ff15e2 100644 (file)
@@ -6,11 +6,11 @@
  */
 
 #include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/lcd.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_gpio.h>
 #include <linux/spi/spi.h>
 
 #define HX8357_NUM_IM_PINS     3
 #define HX8369_SET_GAMMA_CURVE_RELATED         0xe0
 
 struct hx8357_data {
-       unsigned                im_pins[HX8357_NUM_IM_PINS];
-       unsigned                reset;
+       struct gpio_descs       *im_pins;
+       struct gpio_desc        *reset;
        struct spi_device       *spi;
        int                     state;
-       bool                    use_im_pins;
 };
 
 static u8 hx8357_seq_power[] = {
@@ -321,11 +320,11 @@ static void hx8357_lcd_reset(struct lcd_device *lcdev)
        struct hx8357_data *lcd = lcd_get_data(lcdev);
 
        /* Reset the screen */
-       gpio_set_value(lcd->reset, 1);
+       gpiod_set_value(lcd->reset, 0);
        usleep_range(10000, 12000);
-       gpio_set_value(lcd->reset, 0);
+       gpiod_set_value(lcd->reset, 1);
        usleep_range(10000, 12000);
-       gpio_set_value(lcd->reset, 1);
+       gpiod_set_value(lcd->reset, 0);
 
        /* The controller needs 120ms to recover from reset */
        msleep(120);
@@ -340,10 +339,10 @@ static int hx8357_lcd_init(struct lcd_device *lcdev)
         * Set the interface selection pins to SPI mode, with three
         * wires
         */
-       if (lcd->use_im_pins) {
-               gpio_set_value_cansleep(lcd->im_pins[0], 1);
-               gpio_set_value_cansleep(lcd->im_pins[1], 0);
-               gpio_set_value_cansleep(lcd->im_pins[2], 1);
+       if (lcd->im_pins) {
+               gpiod_set_value_cansleep(lcd->im_pins->desc[0], 1);
+               gpiod_set_value_cansleep(lcd->im_pins->desc[1], 0);
+               gpiod_set_value_cansleep(lcd->im_pins->desc[2], 1);
        }
 
        ret = hx8357_spi_write_array(lcdev, hx8357_seq_power,
@@ -580,6 +579,7 @@ MODULE_DEVICE_TABLE(of, hx8357_dt_ids);
 
 static int hx8357_probe(struct spi_device *spi)
 {
+       struct device *dev = &spi->dev;
        struct lcd_device *lcdev;
        struct hx8357_data *lcd;
        const struct of_device_id *match;
@@ -601,49 +601,19 @@ static int hx8357_probe(struct spi_device *spi)
        if (!match || !match->data)
                return -EINVAL;
 
-       lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0);
-       if (!gpio_is_valid(lcd->reset)) {
-               dev_err(&spi->dev, "Missing dt property: gpios-reset\n");
-               return -EINVAL;
-       }
+       lcd->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(lcd->reset))
+               return dev_err_probe(dev, PTR_ERR(lcd->reset), "failed to request reset GPIO\n");
+       gpiod_set_consumer_name(lcd->reset, "hx8357-reset");
 
-       ret = devm_gpio_request_one(&spi->dev, lcd->reset,
-                                   GPIOF_OUT_INIT_HIGH,
-                                   "hx8357-reset");
-       if (ret) {
-               dev_err(&spi->dev,
-                       "failed to request gpio %d: %d\n",
-                       lcd->reset, ret);
-               return -EINVAL;
-       }
+       lcd->im_pins = devm_gpiod_get_array_optional(dev, "im", GPIOD_OUT_LOW);
+       if (IS_ERR(lcd->im_pins))
+               return dev_err_probe(dev, PTR_ERR(lcd->im_pins), "failed to request im GPIOs\n");
+       if (lcd->im_pins->ndescs < HX8357_NUM_IM_PINS)
+               return dev_err_probe(dev, -EINVAL, "not enough im GPIOs\n");
 
-       if (of_property_present(spi->dev.of_node, "im-gpios")) {
-               lcd->use_im_pins = 1;
-
-               for (i = 0; i < HX8357_NUM_IM_PINS; i++) {
-                       lcd->im_pins[i] = of_get_named_gpio(spi->dev.of_node,
-                                                           "im-gpios", i);
-                       if (lcd->im_pins[i] == -EPROBE_DEFER) {
-                               dev_info(&spi->dev, "GPIO requested is not here yet, deferring the probe\n");
-                               return -EPROBE_DEFER;
-                       }
-                       if (!gpio_is_valid(lcd->im_pins[i])) {
-                               dev_err(&spi->dev, "Missing dt property: im-gpios\n");
-                               return -EINVAL;
-                       }
-
-                       ret = devm_gpio_request_one(&spi->dev, lcd->im_pins[i],
-                                                   GPIOF_OUT_INIT_LOW,
-                                                   "im_pins");
-                       if (ret) {
-                               dev_err(&spi->dev, "failed to request gpio %d: %d\n",
-                                       lcd->im_pins[i], ret);
-                               return -EINVAL;
-                       }
-               }
-       } else {
-               lcd->use_im_pins = 0;
-       }
+       for (i = 0; i < HX8357_NUM_IM_PINS; i++)
+               gpiod_set_consumer_name(lcd->im_pins->desc[i], "im_pins");
 
        lcdev = devm_lcd_device_register(&spi->dev, "mxsfb", &spi->dev, lcd,
                                        &hx8357_ops);
index e7b6bd827986fa931de3d3e6c22565e8227afd35..c8e0e655dc867d49ac6729abecea39e9590a8792 100644 (file)
@@ -81,7 +81,7 @@
 #define START_RW_WRITE         0
 #define START_RW_READ          1
 
-/**
+/*
  * START_BYTE(id, rs, rw)
  *
  * Set the start byte according to the required operation.
 #define START_BYTE(id, rs, rw) \
        (0x70 | (((id) & 0x01) << 2) | (((rs) & 0x01) << 1) | ((rw) & 0x01))
 
-/**
+/*
  * CHECK_FREQ_REG(spi_device s, spi_transfer x) - Check the frequency
  *     for the SPI transfer. According to the datasheet, the controller
  *     accept higher frequency for the GRAM transfer, but it requires
@@ -269,6 +269,10 @@ static int ili922x_write(struct spi_device *spi, u8 reg, u16 value)
        spi_message_add_tail(&xfer_regindex, &msg);
 
        ret = spi_sync(spi, &msg);
+       if (ret < 0) {
+               dev_err(&spi->dev, "Error sending SPI message 0x%x", ret);
+               return ret;
+       }
 
        spi_message_init(&msg);
        tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_REG,
diff --git a/drivers/video/backlight/mp3309c.c b/drivers/video/backlight/mp3309c.c
new file mode 100644 (file)
index 0000000..34d7125
--- /dev/null
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for MPS MP3309C White LED driver with I2C interface
+ *
+ * This driver support both analog (by I2C commands) and PWM dimming control
+ * modes.
+ *
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Flavio Suligoi <f.suligoi@asem.it>
+ *
+ * Based on pwm_bl.c
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define REG_I2C_0      0x00
+#define REG_I2C_1      0x01
+
+#define REG_I2C_0_EN   0x80
+#define REG_I2C_0_D0   0x40
+#define REG_I2C_0_D1   0x20
+#define REG_I2C_0_D2   0x10
+#define REG_I2C_0_D3   0x08
+#define REG_I2C_0_D4   0x04
+#define REG_I2C_0_RSRV1        0x02
+#define REG_I2C_0_RSRV2        0x01
+
+#define REG_I2C_1_RSRV1        0x80
+#define REG_I2C_1_DIMS 0x40
+#define REG_I2C_1_SYNC 0x20
+#define REG_I2C_1_OVP0 0x10
+#define REG_I2C_1_OVP1 0x08
+#define REG_I2C_1_VOS  0x04
+#define REG_I2C_1_LEDO 0x02
+#define REG_I2C_1_OTP  0x01
+
+#define ANALOG_I2C_NUM_LEVELS  32              /* 0..31 */
+#define ANALOG_I2C_REG_MASK    0x7c
+
+#define MP3309C_PWM_DEFAULT_NUM_LEVELS 256     /* 0..255 */
+
+enum mp3309c_status_value {
+       FIRST_POWER_ON,
+       BACKLIGHT_OFF,
+       BACKLIGHT_ON,
+};
+
+enum mp3309c_dimming_mode_value {
+       DIMMING_PWM,
+       DIMMING_ANALOG_I2C,
+};
+
+struct mp3309c_platform_data {
+       unsigned int max_brightness;
+       unsigned int default_brightness;
+       unsigned int *levels;
+       u8  dimming_mode;
+       u8  over_voltage_protection;
+       bool sync_mode;
+       u8 status;
+};
+
+struct mp3309c_chip {
+       struct device *dev;
+       struct mp3309c_platform_data *pdata;
+       struct backlight_device *bl;
+       struct gpio_desc *enable_gpio;
+       struct regmap *regmap;
+       struct pwm_device *pwmd;
+};
+
+static const struct regmap_config mp3309c_regmap = {
+       .name = "mp3309c_regmap",
+       .reg_bits = 8,
+       .reg_stride = 1,
+       .val_bits = 8,
+       .max_register = REG_I2C_1,
+};
+
+static int mp3309c_enable_device(struct mp3309c_chip *chip)
+{
+       u8 reg_val;
+       int ret;
+
+       /* I2C register #0 - Device enable */
+       ret = regmap_update_bits(chip->regmap, REG_I2C_0, REG_I2C_0_EN,
+                                REG_I2C_0_EN);
+       if (ret)
+               return ret;
+
+       /*
+        * I2C register #1 - Set working mode:
+        *  - set one of the two dimming mode:
+        *    - PWM dimming using an external PWM dimming signal
+        *    - analog dimming using I2C commands
+        *  - enable/disable synchronous mode
+        *  - set overvoltage protection (OVP)
+        */
+       reg_val = 0x00;
+       if (chip->pdata->dimming_mode == DIMMING_PWM)
+               reg_val |= REG_I2C_1_DIMS;
+       if (chip->pdata->sync_mode)
+               reg_val |= REG_I2C_1_SYNC;
+       reg_val |= chip->pdata->over_voltage_protection;
+       ret = regmap_write(chip->regmap, REG_I2C_1, reg_val);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int mp3309c_bl_update_status(struct backlight_device *bl)
+{
+       struct mp3309c_chip *chip = bl_get_data(bl);
+       int brightness = backlight_get_brightness(bl);
+       struct pwm_state pwmstate;
+       unsigned int analog_val, bits_val;
+       int i, ret;
+
+       if (chip->pdata->dimming_mode == DIMMING_PWM) {
+               /*
+                * PWM control mode
+                */
+               pwm_get_state(chip->pwmd, &pwmstate);
+               pwm_set_relative_duty_cycle(&pwmstate,
+                                           chip->pdata->levels[brightness],
+                                           chip->pdata->levels[chip->pdata->max_brightness]);
+               pwmstate.enabled = true;
+               ret = pwm_apply_state(chip->pwmd, &pwmstate);
+               if (ret)
+                       return ret;
+
+               switch (chip->pdata->status) {
+               case FIRST_POWER_ON:
+               case BACKLIGHT_OFF:
+                       /*
+                        * After 20ms of low pwm signal level, the chip turns
+                        * off automatically. In this case, before enabling the
+                        * chip again, we must wait about 10ms for pwm signal to
+                        * stabilize.
+                        */
+                       if (brightness > 0) {
+                               msleep(10);
+                               mp3309c_enable_device(chip);
+                               chip->pdata->status = BACKLIGHT_ON;
+                       } else {
+                               chip->pdata->status = BACKLIGHT_OFF;
+                       }
+                       break;
+               case BACKLIGHT_ON:
+                       if (brightness == 0)
+                               chip->pdata->status = BACKLIGHT_OFF;
+                       break;
+               }
+       } else {
+               /*
+                * Analog (by I2C command) control mode
+                *
+                * The first time, before setting brightness, we must enable the
+                * device
+                */
+               if (chip->pdata->status == FIRST_POWER_ON)
+                       mp3309c_enable_device(chip);
+
+               /*
+                * Dimming mode I2C command (fixed dimming range 0..31)
+                *
+                * The 5 bits of the dimming analog value D4..D0 is allocated
+                * in the I2C register #0, in the following way:
+                *
+                *     +--+--+--+--+--+--+--+--+
+                *     |EN|D0|D1|D2|D3|D4|XX|XX|
+                *     +--+--+--+--+--+--+--+--+
+                */
+               analog_val = brightness;
+               bits_val = 0;
+               for (i = 0; i <= 5; i++)
+                       bits_val += ((analog_val >> i) & 0x01) << (6 - i);
+               ret = regmap_update_bits(chip->regmap, REG_I2C_0,
+                                        ANALOG_I2C_REG_MASK, bits_val);
+               if (ret)
+                       return ret;
+
+               if (brightness > 0)
+                       chip->pdata->status = BACKLIGHT_ON;
+               else
+                       chip->pdata->status = BACKLIGHT_OFF;
+       }
+
+       return 0;
+}
+
+static const struct backlight_ops mp3309c_bl_ops = {
+       .update_status = mp3309c_bl_update_status,
+};
+
+static int pm3309c_parse_dt_node(struct mp3309c_chip *chip,
+                                struct mp3309c_platform_data *pdata)
+{
+       struct device_node *node = chip->dev->of_node;
+       struct property *prop_pwms;
+       struct property *prop_levels = NULL;
+       int length = 0;
+       int ret, i;
+       unsigned int num_levels, tmp_value;
+
+       if (!node) {
+               dev_err(chip->dev, "failed to get DT node\n");
+               return -ENODEV;
+       }
+
+       /*
+        * Dimming mode: the MP3309C provides two dimming control mode:
+        *
+        * - PWM mode
+        * - Analog by I2C control mode (default)
+        *
+        * I2C control mode is assumed as default but, if the pwms property is
+        * found in the backlight node, the mode switches to PWM mode.
+        */
+       pdata->dimming_mode = DIMMING_ANALOG_I2C;
+       prop_pwms = of_find_property(node, "pwms", &length);
+       if (prop_pwms) {
+               chip->pwmd = devm_pwm_get(chip->dev, NULL);
+               if (IS_ERR(chip->pwmd))
+                       return dev_err_probe(chip->dev, PTR_ERR(chip->pwmd),
+                                            "error getting pwm data\n");
+               pdata->dimming_mode = DIMMING_PWM;
+               pwm_apply_args(chip->pwmd);
+       }
+
+       /*
+        * In I2C control mode the dimming levels (0..31) are fixed by the
+        * hardware, while in PWM control mode they can be chosen by the user,
+        * to allow nonlinear mappings.
+        */
+       if  (pdata->dimming_mode == DIMMING_ANALOG_I2C) {
+               /*
+                * Analog (by I2C commands) control mode: fixed 0..31 brightness
+                * levels
+                */
+               num_levels = ANALOG_I2C_NUM_LEVELS;
+
+               /* Enable GPIO used in I2C dimming mode only */
+               chip->enable_gpio = devm_gpiod_get(chip->dev, "enable",
+                                                  GPIOD_OUT_HIGH);
+               if (IS_ERR(chip->enable_gpio))
+                       return dev_err_probe(chip->dev,
+                                            PTR_ERR(chip->enable_gpio),
+                                            "error getting enable gpio\n");
+       } else {
+               /*
+                * PWM control mode: check for brightness level in DT
+                */
+               prop_levels = of_find_property(node, "brightness-levels",
+                                              &length);
+               if (prop_levels) {
+                       /* Read brightness levels from DT */
+                       num_levels = length / sizeof(u32);
+                       if (num_levels < 2)
+                               return -EINVAL;
+               } else {
+                       /* Use default brightness levels */
+                       num_levels = MP3309C_PWM_DEFAULT_NUM_LEVELS;
+               }
+       }
+
+       /* Fill brightness levels array */
+       pdata->levels = devm_kcalloc(chip->dev, num_levels,
+                                    sizeof(*pdata->levels), GFP_KERNEL);
+       if (!pdata->levels)
+               return -ENOMEM;
+       if (prop_levels) {
+               ret = of_property_read_u32_array(node, "brightness-levels",
+                                                pdata->levels,
+                                                num_levels);
+               if (ret < 0)
+                       return ret;
+       } else {
+               for (i = 0; i < num_levels; i++)
+                       pdata->levels[i] = i;
+       }
+
+       pdata->max_brightness = num_levels - 1;
+
+       ret = of_property_read_u32(node, "default-brightness",
+                                  &pdata->default_brightness);
+       if (ret)
+               pdata->default_brightness = pdata->max_brightness;
+       if (pdata->default_brightness > pdata->max_brightness) {
+               dev_err(chip->dev,
+                       "default brightness exceeds max brightness\n");
+               pdata->default_brightness = pdata->max_brightness;
+       }
+
+       /*
+        * Over-voltage protection (OVP)
+        *
+        * This (optional) property values are:
+        *
+        *  - 13.5V
+        *  - 24V
+        *  - 35.5V (hardware default setting)
+        *
+        * If missing, the default value for OVP is 35.5V
+        */
+       pdata->over_voltage_protection = REG_I2C_1_OVP1;
+       if (!of_property_read_u32(node, "mps,overvoltage-protection-microvolt",
+                                 &tmp_value)) {
+               switch (tmp_value) {
+               case 13500000:
+                       pdata->over_voltage_protection = 0x00;
+                       break;
+               case 24000000:
+                       pdata->over_voltage_protection = REG_I2C_1_OVP0;
+                       break;
+               case 35500000:
+                       pdata->over_voltage_protection = REG_I2C_1_OVP1;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       /* Synchronous (default) and non-synchronous mode */
+       pdata->sync_mode = true;
+       if (of_property_read_bool(node, "mps,no-sync-mode"))
+               pdata->sync_mode = false;
+
+       return 0;
+}
+
+static int mp3309c_probe(struct i2c_client *client)
+{
+       struct mp3309c_platform_data *pdata = dev_get_platdata(&client->dev);
+       struct mp3309c_chip *chip;
+       struct backlight_properties props;
+       struct pwm_state pwmstate;
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "failed to check i2c functionality\n");
+               return -EOPNOTSUPP;
+       }
+
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->dev = &client->dev;
+
+       chip->regmap = devm_regmap_init_i2c(client, &mp3309c_regmap);
+       if (IS_ERR(chip->regmap))
+               return dev_err_probe(&client->dev, PTR_ERR(chip->regmap),
+                                    "failed to allocate register map\n");
+
+       i2c_set_clientdata(client, chip);
+
+       if (!pdata) {
+               pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL);
+               if (!pdata)
+                       return -ENOMEM;
+
+               ret = pm3309c_parse_dt_node(chip, pdata);
+               if (ret)
+                       return ret;
+       }
+       chip->pdata = pdata;
+
+       /* Backlight properties */
+       props.brightness = pdata->default_brightness;
+       props.max_brightness = pdata->max_brightness;
+       props.scale = BACKLIGHT_SCALE_LINEAR;
+       props.type = BACKLIGHT_RAW;
+       props.power = FB_BLANK_UNBLANK;
+       props.fb_blank = FB_BLANK_UNBLANK;
+       chip->bl = devm_backlight_device_register(chip->dev, "mp3309c",
+                                                 chip->dev, chip,
+                                                 &mp3309c_bl_ops, &props);
+       if (IS_ERR(chip->bl))
+               return dev_err_probe(chip->dev, PTR_ERR(chip->bl),
+                                    "error registering backlight device\n");
+
+       /* In PWM dimming mode, enable pwm device */
+       if (chip->pdata->dimming_mode == DIMMING_PWM) {
+               pwm_init_state(chip->pwmd, &pwmstate);
+               pwm_set_relative_duty_cycle(&pwmstate,
+                                           chip->pdata->default_brightness,
+                                           chip->pdata->max_brightness);
+               pwmstate.enabled = true;
+               ret = pwm_apply_state(chip->pwmd, &pwmstate);
+               if (ret)
+                       return dev_err_probe(chip->dev, ret,
+                                            "error setting pwm device\n");
+       }
+
+       chip->pdata->status = FIRST_POWER_ON;
+       backlight_update_status(chip->bl);
+
+       return 0;
+}
+
+static void mp3309c_remove(struct i2c_client *client)
+{
+       struct mp3309c_chip *chip = i2c_get_clientdata(client);
+       struct backlight_device *bl = chip->bl;
+
+       bl->props.power = FB_BLANK_POWERDOWN;
+       bl->props.brightness = 0;
+       backlight_update_status(chip->bl);
+}
+
+static const struct of_device_id mp3309c_match_table[] = {
+       { .compatible = "mps,mp3309c", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, mp3309c_match_table);
+
+static const struct i2c_device_id mp3309c_id[] = {
+       { "mp3309c", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, mp3309c_id);
+
+static struct i2c_driver mp3309c_i2c_driver = {
+       .driver = {
+                       .name           = KBUILD_MODNAME,
+                       .of_match_table = mp3309c_match_table,
+       },
+       .probe          = mp3309c_probe,
+       .remove         = mp3309c_remove,
+       .id_table       = mp3309c_id,
+};
+
+module_i2c_driver(mp3309c_i2c_driver);
+
+MODULE_DESCRIPTION("Backlight Driver for MPS MP3309C");
+MODULE_AUTHOR("Flavio Suligoi <f.suligoi@asem.it>");
+MODULE_LICENSE("GPL");
index 35c716e9043c3d5ee6e1466010b7c82ead0c38ad..ffcebf6aa76a967a05716a1db8f316295a0c2f66 100644 (file)
@@ -461,10 +461,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 
        if (!data) {
                ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to find platform data\n");
-                       return ret;
-               }
+               if (ret < 0)
+                       return dev_err_probe(&pdev->dev, ret,
+                                            "failed to find platform data\n");
 
                data = &defdata;
        }
@@ -493,24 +492,27 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
                                                  GPIOD_ASIS);
        if (IS_ERR(pb->enable_gpio)) {
-               ret = PTR_ERR(pb->enable_gpio);
+               ret = dev_err_probe(&pdev->dev, PTR_ERR(pb->enable_gpio),
+                                   "failed to acquire enable GPIO\n");
                goto err_alloc;
        }
 
        pb->power_supply = devm_regulator_get_optional(&pdev->dev, "power");
        if (IS_ERR(pb->power_supply)) {
                ret = PTR_ERR(pb->power_supply);
-               if (ret == -ENODEV)
+               if (ret == -ENODEV) {
                        pb->power_supply = NULL;
-               else
+               } else {
+                       dev_err_probe(&pdev->dev, ret,
+                                     "failed to acquire power regulator\n");
                        goto err_alloc;
+               }
        }
 
        pb->pwm = devm_pwm_get(&pdev->dev, NULL);
        if (IS_ERR(pb->pwm)) {
-               ret = PTR_ERR(pb->pwm);
-               if (ret != -EPROBE_DEFER)
-                       dev_err(&pdev->dev, "unable to request PWM\n");
+               ret = dev_err_probe(&pdev->dev, PTR_ERR(pb->pwm),
+                                   "unable to request PWM\n");
                goto err_alloc;
        }
 
@@ -530,8 +532,8 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 
        ret = pwm_apply_might_sleep(pb->pwm, &state);
        if (ret) {
-               dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n",
-                       ret);
+               dev_err_probe(&pdev->dev, ret,
+                             "failed to apply initial PWM state");
                goto err_alloc;
        }
 
@@ -568,8 +570,8 @@ static int pwm_backlight_probe(struct platform_device *pdev)
                ret = pwm_backlight_brightness_default(&pdev->dev, data,
                                                       state.period);
                if (ret < 0) {
-                       dev_err(&pdev->dev,
-                               "failed to setup default brightness table\n");
+                       dev_err_probe(&pdev->dev, ret,
+                                     "failed to setup default brightness table\n");
                        goto err_alloc;
                }
 
@@ -597,8 +599,8 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
                                       &pwm_backlight_ops, &props);
        if (IS_ERR(bl)) {
-               dev_err(&pdev->dev, "failed to register backlight\n");
-               ret = PTR_ERR(bl);
+               ret = dev_err_probe(&pdev->dev, PTR_ERR(bl),
+                                   "failed to register backlight\n");
                goto err_alloc;
        }