summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorMaxime Ripard <mripard@kernel.org>2024-01-29 14:20:23 +0100
committerMaxime Ripard <mripard@kernel.org>2024-01-29 14:20:23 +0100
commit4db102dcb0396a4ccf89b1eac0f4eb3fd167a080 (patch)
treeea47469abffb236c5ba305c8a406e1f8209c6f34 /drivers/video
parentaeb262c353354eab81ab0d3242afa70984b7dc34 (diff)
parent6613476e225e090cc9aad49be7fa504e290dd33d (diff)
downloadlinux-4db102dcb0396a4ccf89b1eac0f4eb3fd167a080.tar.gz
linux-4db102dcb0396a4ccf89b1eac0f4eb3fd167a080.tar.bz2
linux-4db102dcb0396a4ccf89b1eac0f4eb3fd167a080.zip
Merge drm/drm-next into drm-misc-next
Kickstart 6.9 development cycle. Signed-off-by: Maxime Ripard <mripard@kernel.org>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/backlight/Kconfig18
-rw-r--r--drivers/video/backlight/Makefile2
-rw-r--r--drivers/video/backlight/cr_bllcd.c264
-rw-r--r--drivers/video/backlight/hx8357.c74
-rw-r--r--drivers/video/backlight/ili922x.c8
-rw-r--r--drivers/video/backlight/lm3630a_bl.c2
-rw-r--r--drivers/video/backlight/lp855x_bl.c2
-rw-r--r--drivers/video/backlight/mp3309c.c444
-rw-r--r--drivers/video/backlight/pwm_bl.c46
-rw-r--r--drivers/video/console/Kconfig2
-rw-r--r--drivers/video/fbdev/Kconfig70
-rw-r--r--drivers/video/fbdev/Makefile2
-rw-r--r--drivers/video/fbdev/amba-clcd.c986
-rw-r--r--drivers/video/fbdev/core/fb_ddc.c1
-rw-r--r--drivers/video/fbdev/core/fb_defio.c8
-rw-r--r--drivers/video/fbdev/cyber2000fb.c1
-rw-r--r--drivers/video/fbdev/fsl-diu-fb.c2
-rw-r--r--drivers/video/fbdev/hgafb.c13
-rw-r--r--drivers/video/fbdev/hyperv_fb.c26
-rw-r--r--drivers/video/fbdev/i740fb.c1
-rw-r--r--drivers/video/fbdev/imxfb.c179
-rw-r--r--drivers/video/fbdev/intelfb/Makefile8
-rw-r--r--drivers/video/fbdev/intelfb/intelfb.h382
-rw-r--r--drivers/video/fbdev/intelfb/intelfb_i2c.c209
-rw-r--r--drivers/video/fbdev/intelfb/intelfbdrv.c1680
-rw-r--r--drivers/video/fbdev/intelfb/intelfbhw.c2115
-rw-r--r--drivers/video/fbdev/intelfb/intelfbhw.h609
-rw-r--r--drivers/video/fbdev/matrox/i2c-matroxfb.c15
-rw-r--r--drivers/video/fbdev/mmp/hw/mmp_spi.c2
-rw-r--r--drivers/video/fbdev/s3fb.c1
-rw-r--r--drivers/video/fbdev/sis/sis_main.c37
-rw-r--r--drivers/video/fbdev/ssd1307fb.c2
-rw-r--r--drivers/video/fbdev/stifb.c109
-rw-r--r--drivers/video/fbdev/tdfxfb.c1
-rw-r--r--drivers/video/fbdev/tridentfb.c1
-rw-r--r--drivers/video/fbdev/vermilion/Makefile6
-rw-r--r--drivers/video/fbdev/vermilion/cr_pll.c195
-rw-r--r--drivers/video/fbdev/vermilion/vermilion.c1175
-rw-r--r--drivers/video/fbdev/vermilion/vermilion.h245
-rw-r--r--drivers/video/fbdev/via/via_i2c.c1
-rw-r--r--drivers/video/logo/pnmtologo.c6
-rw-r--r--drivers/video/sticore.c5
42 files changed, 708 insertions, 8247 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 51387b1ef012..ea2d0d69bd8c 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -235,13 +235,6 @@ config BACKLIGHT_HP700
If you have an HP Jornada 700 series,
say Y to include backlight control driver.
-config BACKLIGHT_CARILLO_RANCH
- tristate "Intel Carillo Ranch Backlight Driver"
- depends on LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578
- help
- If you have a Intel LE80578 (Carillo Ranch) say Y to enable the
- backlight driver.
-
config BACKLIGHT_PWM
tristate "Generic PWM based Backlight Driver"
depends on PWM
@@ -402,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
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index f72e1c3c59e9..06966cb20459 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -25,7 +25,6 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o
obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o
-obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o
obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o
@@ -44,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
diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c
deleted file mode 100644
index 781aeecc451d..000000000000
--- a/drivers/video/backlight/cr_bllcd.c
+++ /dev/null
@@ -1,264 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (c) Intel Corp. 2007.
- * All Rights Reserved.
- *
- * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
- * develop this driver.
- *
- * This file is part of the Carillo Ranch video subsystem driver.
- *
- * Authors:
- * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
- * Alan Hourihane <alanh-at-tungstengraphics-dot-com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/fb.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-
-/* The LVDS- and panel power controls sits on the
- * GPIO port of the ISA bridge.
- */
-
-#define CRVML_DEVICE_LPC 0x27B8
-#define CRVML_REG_GPIOBAR 0x48
-#define CRVML_REG_GPIOEN 0x4C
-#define CRVML_GPIOEN_BIT (1 << 4)
-#define CRVML_PANEL_PORT 0x38
-#define CRVML_LVDS_ON 0x00000001
-#define CRVML_PANEL_ON 0x00000002
-#define CRVML_BACKLIGHT_OFF 0x00000004
-
-/* The PLL Clock register sits on Host bridge */
-#define CRVML_DEVICE_MCH 0x5001
-#define CRVML_REG_MCHBAR 0x44
-#define CRVML_REG_MCHEN 0x54
-#define CRVML_MCHEN_BIT (1 << 28)
-#define CRVML_MCHMAP_SIZE 4096
-#define CRVML_REG_CLOCK 0xc3c
-#define CRVML_CLOCK_SHIFT 8
-#define CRVML_CLOCK_MASK 0x00000f00
-
-static struct pci_dev *lpc_dev;
-static u32 gpio_bar;
-
-struct cr_panel {
- struct backlight_device *cr_backlight_device;
- struct lcd_device *cr_lcd_device;
-};
-
-static int cr_backlight_set_intensity(struct backlight_device *bd)
-{
- u32 addr = gpio_bar + CRVML_PANEL_PORT;
- u32 cur = inl(addr);
-
- if (backlight_get_brightness(bd) == 0) {
- /* OFF */
- cur |= CRVML_BACKLIGHT_OFF;
- outl(cur, addr);
- } else {
- /* FULL ON */
- cur &= ~CRVML_BACKLIGHT_OFF;
- outl(cur, addr);
- }
-
- return 0;
-}
-
-static int cr_backlight_get_intensity(struct backlight_device *bd)
-{
- u32 addr = gpio_bar + CRVML_PANEL_PORT;
- u32 cur = inl(addr);
- u8 intensity;
-
- if (cur & CRVML_BACKLIGHT_OFF)
- intensity = 0;
- else
- intensity = 1;
-
- return intensity;
-}
-
-static const struct backlight_ops cr_backlight_ops = {
- .get_brightness = cr_backlight_get_intensity,
- .update_status = cr_backlight_set_intensity,
-};
-
-static void cr_panel_on(void)
-{
- u32 addr = gpio_bar + CRVML_PANEL_PORT;
- u32 cur = inl(addr);
-
- if (!(cur & CRVML_PANEL_ON)) {
- /* Make sure LVDS controller is down. */
- if (cur & 0x00000001) {
- cur &= ~CRVML_LVDS_ON;
- outl(cur, addr);
- }
- /* Power up Panel */
- schedule_timeout(HZ / 10);
- cur |= CRVML_PANEL_ON;
- outl(cur, addr);
- }
-
- /* Power up LVDS controller */
-
- if (!(cur & CRVML_LVDS_ON)) {
- schedule_timeout(HZ / 10);
- outl(cur | CRVML_LVDS_ON, addr);
- }
-}
-
-static void cr_panel_off(void)
-{
- u32 addr = gpio_bar + CRVML_PANEL_PORT;
- u32 cur = inl(addr);
-
- /* Power down LVDS controller first to avoid high currents */
- if (cur & CRVML_LVDS_ON) {
- cur &= ~CRVML_LVDS_ON;
- outl(cur, addr);
- }
- if (cur & CRVML_PANEL_ON) {
- schedule_timeout(HZ / 10);
- outl(cur & ~CRVML_PANEL_ON, addr);
- }
-}
-
-static int cr_lcd_set_power(struct lcd_device *ld, int power)
-{
- if (power == FB_BLANK_UNBLANK)
- cr_panel_on();
- if (power == FB_BLANK_POWERDOWN)
- cr_panel_off();
-
- return 0;
-}
-
-static struct lcd_ops cr_lcd_ops = {
- .set_power = cr_lcd_set_power,
-};
-
-static int cr_backlight_probe(struct platform_device *pdev)
-{
- struct backlight_properties props;
- struct backlight_device *bdp;
- struct lcd_device *ldp;
- struct cr_panel *crp;
- u8 dev_en;
-
- lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
- CRVML_DEVICE_LPC, NULL);
- if (!lpc_dev) {
- pr_err("INTEL CARILLO RANCH LPC not found.\n");
- return -ENODEV;
- }
-
- pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en);
- if (!(dev_en & CRVML_GPIOEN_BIT)) {
- pr_err("Carillo Ranch GPIO device was not enabled.\n");
- pci_dev_put(lpc_dev);
- return -ENODEV;
- }
-
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_RAW;
- bdp = devm_backlight_device_register(&pdev->dev, "cr-backlight",
- &pdev->dev, NULL, &cr_backlight_ops,
- &props);
- if (IS_ERR(bdp)) {
- pci_dev_put(lpc_dev);
- return PTR_ERR(bdp);
- }
-
- ldp = devm_lcd_device_register(&pdev->dev, "cr-lcd", &pdev->dev, NULL,
- &cr_lcd_ops);
- if (IS_ERR(ldp)) {
- pci_dev_put(lpc_dev);
- return PTR_ERR(ldp);
- }
-
- pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR,
- &gpio_bar);
- gpio_bar &= ~0x3F;
-
- crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL);
- if (!crp) {
- pci_dev_put(lpc_dev);
- return -ENOMEM;
- }
-
- crp->cr_backlight_device = bdp;
- crp->cr_lcd_device = ldp;
- crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK;
- crp->cr_backlight_device->props.brightness = 0;
- cr_backlight_set_intensity(crp->cr_backlight_device);
- cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK);
-
- platform_set_drvdata(pdev, crp);
-
- return 0;
-}
-
-static void cr_backlight_remove(struct platform_device *pdev)
-{
- struct cr_panel *crp = platform_get_drvdata(pdev);
-
- crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN;
- crp->cr_backlight_device->props.brightness = 0;
- crp->cr_backlight_device->props.max_brightness = 0;
- cr_backlight_set_intensity(crp->cr_backlight_device);
- cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN);
- pci_dev_put(lpc_dev);
-}
-
-static struct platform_driver cr_backlight_driver = {
- .probe = cr_backlight_probe,
- .remove_new = cr_backlight_remove,
- .driver = {
- .name = "cr_backlight",
- },
-};
-
-static struct platform_device *crp;
-
-static int __init cr_backlight_init(void)
-{
- int ret = platform_driver_register(&cr_backlight_driver);
-
- if (ret)
- return ret;
-
- crp = platform_device_register_simple("cr_backlight", -1, NULL, 0);
- if (IS_ERR(crp)) {
- platform_driver_unregister(&cr_backlight_driver);
- return PTR_ERR(crp);
- }
-
- pr_info("Carillo Ranch Backlight Driver Initialized.\n");
-
- return 0;
-}
-
-static void __exit cr_backlight_exit(void)
-{
- platform_device_unregister(crp);
- platform_driver_unregister(&cr_backlight_driver);
-}
-
-module_init(cr_backlight_init);
-module_exit(cr_backlight_exit);
-
-MODULE_AUTHOR("Tungsten Graphics Inc.");
-MODULE_DESCRIPTION("Carillo Ranch Backlight Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c
index f76d2469d490..d7298376cf74 100644
--- a/drivers/video/backlight/hx8357.c
+++ b/drivers/video/backlight/hx8357.c
@@ -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
@@ -83,11 +83,10 @@
#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);
diff --git a/drivers/video/backlight/ili922x.c b/drivers/video/backlight/ili922x.c
index e7b6bd827986..c8e0e655dc86 100644
--- a/drivers/video/backlight/ili922x.c
+++ b/drivers/video/backlight/ili922x.c
@@ -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.
@@ -100,7 +100,7 @@
#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/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c
index 8fcb62be597b..a3412c936ca2 100644
--- a/drivers/video/backlight/lm3630a_bl.c
+++ b/drivers/video/backlight/lm3630a_bl.c
@@ -180,7 +180,7 @@ static int lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
pchip->pwmd_state.enabled = pchip->pwmd_state.duty_cycle ? true : false;
- return pwm_apply_state(pchip->pwmd, &pchip->pwmd_state);
+ return pwm_apply_might_sleep(pchip->pwmd, &pchip->pwmd_state);
}
/* update and get brightness */
diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c
index da1f124db69c..7075bfab59c4 100644
--- a/drivers/video/backlight/lp855x_bl.c
+++ b/drivers/video/backlight/lp855x_bl.c
@@ -234,7 +234,7 @@ static int lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
state.duty_cycle = div_u64(br * state.period, max_br);
state.enabled = state.duty_cycle;
- return pwm_apply_state(lp->pwm, &state);
+ return pwm_apply_might_sleep(lp->pwm, &state);
}
static int lp855x_bl_update_status(struct backlight_device *bl)
diff --git a/drivers/video/backlight/mp3309c.c b/drivers/video/backlight/mp3309c.c
new file mode 100644
index 000000000000..34d71259fac1
--- /dev/null
+++ b/drivers/video/backlight/mp3309c.c
@@ -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: