]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
gpio: add GPIO controller found on Waveshare DSI TOUCH panels
authorDmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Thu, 7 May 2026 09:01:33 +0000 (12:01 +0300)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Mon, 11 May 2026 07:56:28 +0000 (09:56 +0200)
The Waveshare DSI TOUCH family of panels has separate on-board GPIO
controller, which controls power supplies to the panel and the touch
screen and provides reset pins for both the panel and the touchscreen.
Also it provides a simple PWM controller for panel backlight. Add
support for this GPIO controller.

Tested-by: Riccardo Mereu <r.mereu@arduino.cc>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://patch.msgid.link/20260507-waveshare-dsi-touch-v5-2-d2ac7ccc22d4@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-waveshare-dsi.c [new file with mode: 0644]

index ce95a25298a8ce07f488c050e0e0a0feef84b4cd..8ae6a423da6d52a64f0ce2894df5908842776c20 100644 (file)
@@ -806,6 +806,16 @@ config GPIO_VISCONTI
        help
          Say yes here to support GPIO on Tohisba Visconti.
 
+config GPIO_WAVESHARE_DSI_TOUCH
+       tristate "Waveshare GPIO controller for DSI panels"
+       depends on BACKLIGHT_CLASS_DEVICE
+       depends on I2C
+       select REGMAP_I2C
+       help
+         Enable support for the GPIO and PWM controller found on Waveshare DSI
+         TOUCH panel kits. It provides GPIOs (used for regulator control and
+          resets) and backlight support.
+
 config GPIO_WCD934X
        tristate "Qualcomm WCD9340/WCD9341 GPIO controller driver"
        depends on MFD_WCD934X
index b267598b517de089cf6339d837264f1d09e275c0..2ea47d9d3dca948e1cdc46965e83b0e1b6de5f70 100644 (file)
@@ -205,6 +205,7 @@ obj-$(CONFIG_GPIO_VIRTUSER)         += gpio-virtuser.o
 obj-$(CONFIG_GPIO_VIRTIO)              += gpio-virtio.o
 obj-$(CONFIG_GPIO_VISCONTI)            += gpio-visconti.o
 obj-$(CONFIG_GPIO_VX855)               += gpio-vx855.o
+obj-$(CONFIG_GPIO_WAVESHARE_DSI_TOUCH) += gpio-waveshare-dsi.o
 obj-$(CONFIG_GPIO_WCD934X)             += gpio-wcd934x.o
 obj-$(CONFIG_GPIO_WHISKEY_COVE)                += gpio-wcove.o
 obj-$(CONFIG_GPIO_WINBOND)             += gpio-winbond.o
diff --git a/drivers/gpio/gpio-waveshare-dsi.c b/drivers/gpio/gpio-waveshare-dsi.c
new file mode 100644 (file)
index 0000000..38f5235
--- /dev/null
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Waveshare International Limited
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+/* I2C registers of the microcontroller. */
+#define REG_TP         0x94
+#define REG_LCD                0x95
+#define REG_PWM                0x96
+#define REG_SIZE       0x97
+#define REG_ID         0x98
+#define REG_VERSION    0x99
+
+enum {
+       GPIO_AVDD = 0,
+       GPIO_PANEL_RESET = 1,
+       GPIO_BL_ENABLE = 2,
+       GPIO_IOVCC = 4,
+       GPIO_VCC = 8,
+       GPIO_TS_RESET = 9,
+};
+
+#define NUM_GPIO 16
+
+struct waveshare_gpio {
+       struct mutex dir_lock;
+       struct mutex pwr_lock;
+       struct regmap *regmap;
+       u16 poweron_state;
+
+       struct gpio_chip gc;
+};
+
+static const struct regmap_config waveshare_gpio_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = REG_VERSION,
+};
+
+static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset)
+{
+       u16 pwr_state;
+
+       guard(mutex)(&state->pwr_lock);
+       pwr_state = state->poweron_state & BIT(offset);
+
+       return !!pwr_state;
+}
+
+static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value)
+{
+       u16 last_val;
+       int err;
+
+       guard(mutex)(&state->pwr_lock);
+
+       last_val = state->poweron_state;
+       if (value)
+               last_val |= BIT(offset);
+       else
+               last_val &= ~BIT(offset);
+
+       state->poweron_state = last_val;
+
+       err = regmap_write(state->regmap, REG_TP, last_val >> 8);
+       if (!err)
+               err = regmap_write(state->regmap, REG_LCD, last_val & 0xff);
+
+       return err;
+}
+
+static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+       return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+       struct waveshare_gpio *state = gpiochip_get_data(gc);
+
+       return waveshare_gpio_get(state, offset);
+}
+
+static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+       struct waveshare_gpio *state = gpiochip_get_data(gc);
+
+       return waveshare_gpio_set(state, offset, value);
+}
+
+static int waveshare_gpio_update_status(struct backlight_device *bl)
+{
+       struct waveshare_gpio *state = bl_get_data(bl);
+       int brightness = backlight_get_brightness(bl);
+
+       waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness);
+
+       return regmap_write(state->regmap, REG_PWM, brightness);
+}
+
+static const struct backlight_ops waveshare_gpio_bl = {
+       .update_status = waveshare_gpio_update_status,
+};
+
+static int waveshare_gpio_probe(struct i2c_client *i2c)
+{
+       struct backlight_properties props = {};
+       struct waveshare_gpio *state;
+       struct device *dev = &i2c->dev;
+       struct backlight_device *bl;
+       struct regmap *regmap;
+       unsigned int data;
+       int ret;
+
+       state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       ret = devm_mutex_init(dev, &state->dir_lock);
+       if (ret)
+               return ret;
+
+       ret = devm_mutex_init(dev, &state->pwr_lock);
+       if (ret)
+               return ret;
+
+       regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config);
+       if (IS_ERR(regmap))
+               return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
+
+       state->regmap = regmap;
+       i2c_set_clientdata(i2c, state);
+
+       ret = regmap_read(regmap, REG_ID, &data);
+       if (ret < 0)
+               return dev_err_probe(dev, ret, "Failed to read register\n");
+
+       dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data);
+
+       ret = regmap_read(regmap, REG_SIZE, &data);
+       if (ret < 0)
+               return dev_err_probe(dev, ret, "Failed to read register\n");
+
+       dev_dbg(dev, "waveshare panel size = %d\n", data);
+
+       ret = regmap_read(regmap, REG_VERSION, &data);
+       if (ret < 0)
+               return dev_err_probe(dev, ret, "Failed to read register\n");
+
+       dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data);
+
+       ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to program GPIOs\n");
+
+       msleep(20);
+
+       state->gc.parent = dev;
+       state->gc.label = i2c->name;
+       state->gc.owner = THIS_MODULE;
+       state->gc.base = -1;
+       state->gc.ngpio = NUM_GPIO;
+
+       /* it is output only */
+       state->gc.get = waveshare_gpio_gpio_get;
+       state->gc.set = waveshare_gpio_gpio_set;
+       state->gc.get_direction = waveshare_gpio_gpio_get_direction;
+       state->gc.can_sleep = true;
+
+       ret = devm_gpiochip_add_data(dev, &state->gc, state);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to create gpiochip\n");
+
+       props.type = BACKLIGHT_RAW;
+       props.max_brightness = 255;
+       props.brightness = 255;
+       bl = devm_backlight_device_register(dev, dev_name(dev), dev, state,
+                                           &waveshare_gpio_bl, &props);
+       return PTR_ERR_OR_ZERO(bl);
+}
+
+static const struct of_device_id waveshare_gpio_dt_ids[] = {
+       { .compatible = "waveshare,dsi-touch-gpio" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids);
+
+static struct i2c_driver waveshare_gpio_regulator_driver = {
+       .driver = {
+               .name = "waveshare-regulator",
+               .of_match_table = of_match_ptr(waveshare_gpio_dt_ids),
+       },
+       .probe = waveshare_gpio_probe,
+};
+
+module_i2c_driver(waveshare_gpio_regulator_driver);
+
+MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels");
+MODULE_LICENSE("GPL");