mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 04:37:32 -04:00
gpio: add GPIO controller found on Waveshare DSI TOUCH panels
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>
This commit is contained in:
committed by
Bartosz Golaszewski
parent
8cd19f5560
commit
79f44c8acf
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
208
drivers/gpio/gpio-waveshare-dsi.c
Normal file
208
drivers/gpio/gpio-waveshare-dsi.c
Normal file
@@ -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");
|
||||
Reference in New Issue
Block a user