From nobody Sun Feb 8 01:33:35 2026 Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4242934FF63 for ; Tue, 11 Nov 2025 10:43:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762857809; cv=none; b=P7+Whad4MaeeT5ofKhajQWABTR8HtA7+zz8AsM2bAkgM8iX1sc+gq9pp+vSsE6S+wVDHTGvikr2tqDPMOCod3JWrg0ebC5BzsHnD+6LsNW1WOPsxKhYdFnBMNwtuj34tEbyx1FPUL5FKI0u6SbJmLIZHZ+ftqoYNxhn88Q6YPok= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762857809; c=relaxed/simple; bh=e4v3peRSb0qo0R3SeZtIfYEd5VscID9SOVUwAse2DI0=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=JITtMfemGeCPH+GjL/LQ/WxST1nd2oDL2ndAThcTJ30TbhoshLhxp2908VH2CD9Eo53AE8jJVOSic2o0WzC+r3OoFbGnkTuvCsDXy57GBjSe4/heJV6dNA7+ihJ87KqJ4RtEJ7/NGBuuQPi8d9lDu//jsVNkSM2vWUvepGC1v7k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mCTgTdB4; arc=none smtp.client-ip=209.85.210.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mCTgTdB4" Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-782e93932ffso3440766b3a.3 for ; Tue, 11 Nov 2025 02:43:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762857806; x=1763462606; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=YbNVSPGz2xSPSC8zslWi+rCTz44wy3ZvVMilLVzdp24=; b=mCTgTdB4pinfOB2s5Dgz9mp5WeNAg1uovgI6THZdFGjryG2GMzLGYqOKq8D3vAHJ3t QJQ8UnP+xlR68pC8VVcwoLpW7uvVeoKUPP2hfeFr0qhCmvvX9/K5IbI/TOmcxy6rlp+C sEFSFts9x4XsKzPbfBJWMOpw6HYUcA8aK1cuHAShZCt6mW5+QmOHqN005Tf7Ta4Y5QBM /7i2V+s1x2SMFiKm+zxvSedo/th5cyL4rMM9r7IiVjxttxaE3K1tFW01HB2H6W7C5jol j0syOy7lKQP7TAzDfoRddZU1QOzfi1QqeIc+Wa+63ND1a6YYR8inOuknTi+0rTgpVU1Y ikVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762857806; x=1763462606; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=YbNVSPGz2xSPSC8zslWi+rCTz44wy3ZvVMilLVzdp24=; b=A0nEmjo9p3TEaaM4uA+qychPDH/VezhALqDsWub6yotdUYYXfRLQn+clb5hmrlRaTr pyNBhxPNjyEYmT1LlVT9Y1Oi4BTB8wUSUHnK6V8yP544KBdH8QfvgdXRFke0iAYLeWNt MWHId4D7PnU6okC6nHm/43sMMMI6ct6vRVsMGjeOL/92QaYoMtNkDDIXiWuotjuKneXA HMfmDl0pye6NVPqrxS+gQv/qbddj+vE4Kvya2+d4ztzxz7NEQ6Z5llHfgkvXeJpO2+MU 7AXp960VowwZ+xra1RWcD+KprHZJwTgzEI+VTNB2KUN82asAxm8JIu4DLmm32XihBHJM aQ1A== X-Gm-Message-State: AOJu0YwtBWK/UvjBhII1QaV6qqNygGBpR6/i4PkRNJo3HaP3viofuP93 udWMrzj7QIKTdC7xaM7GC/HYs8Ka7rv6qK2Pesfkje0Z6Q9Ci6UwBLOV X-Gm-Gg: ASbGnctAPPt45Yw33lUJ72BASgGzW0oXOaVSMQq6FRzr4Dma6vhrm9azsOiB/0TOugV GjbJfFvlbHNcymfKkA3rlwpXTMhymu64Og4vH7QPx3E7omv/4+V7PmYqJ3N+HaDGo0EbC2vuWlz qsmtb043usCBCwYJ1Nwq3kAczc8zG4vOMvcz+owpUGKxICfS7iOJaHRhTBVNv9Ue6VfbWfjXZB7 XUmrAL0rDVF7zDNab0IV7itBfYoyrCbLiVTcT+j9bv5LQii7CBkdczflAfBeeIg6LB5HZwRnATZ xRORr9kOdZt86WL5cwVR30xihaYamiXlipRKuCBy2Mb12PeEPAvsM6BFWFhPealfjOUPlpPJAyr RiLg5PWLVWfCoTjQ8PBw+dxh41Vscw3oJQm6YUPmPTGxoWKQMoTJCXQ7AMmKJlCfTzRmzFlklRs Ahtt5iZHaXJTIsdf7Zlttf0zEBwQ== X-Google-Smtp-Source: AGHT+IHnPiD53lN5UhZ+49YiZch2Esxl9CUq6/PlNxvl2xTQ5c+xNgQBU3/w5FnAP8tNj0bBvFCRFg== X-Received: by 2002:a17:90a:b385:b0:343:c3d1:8b9b with SMTP id 98e67ed59e1d1-343c3d18f39mr2179959a91.19.1762857806400; Tue, 11 Nov 2025 02:43:26 -0800 (PST) Received: from test-HP-Desktop-Pro-G3.. ([103.218.174.23]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-343c02112a5sm976781a91.1.2025.11.11.02.43.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 Nov 2025 02:43:26 -0800 (PST) From: Sudarshan Shetty To: lgirdwood@gmail.com, broonie@kernel.org Cc: linux-kernel@vger.kernel.org, Sudarshan Shetty Subject: [PATCH v1] regulator: Add Waveshare panel regulator driver Date: Tue, 11 Nov 2025 16:13:20 +0530 Message-Id: <20251111104320.3425143-1-tessolveupstream@gmail.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds a regulator driver for Waveshare panels. The regulator provides controlled power sequencing and is also used to enable or disable the display backlight. Features: - I2C interface to control panel-specific regulator registers - GPIO-based enable/disable support - Integration with the Linux regulator framework This driver is required for proper initialization of Waveshare MIPI-DSI panels supported by the companion DRM panel driver. Signed-off-by: Sudarshan Shetty --- arch/arm64/configs/defconfig | 1 + drivers/regulator/Kconfig | 11 + drivers/regulator/Makefile | 1 + drivers/regulator/waveshare-panel-regulator.c | 294 ++++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 drivers/regulator/waveshare-panel-regulator.c diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index e3a2d37bd104..a1e564024be2 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1823,3 +1823,4 @@ CONFIG_CORESIGHT_STM=3Dm CONFIG_CORESIGHT_CPU_DEBUG=3Dm CONFIG_CORESIGHT_CTI=3Dm CONFIG_MEMTEST=3Dy +CONFIG_REGULATOR_WAVESHARE_TOUCHSCREEN=3Dy diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e6ea2d6f46a4..287ec02220f2 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1184,6 +1184,17 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 unit. The regulator is used to enable power to the display and to control backlight PWM. =20 +config REGULATOR_WAVESHARE_TOUCHSCREEN + tristate "Waveshare touchscreen panel regulator" + depends on BACKLIGHT_CLASS_DEVICE + depends on I2C + select REGMAP_I2C + help + Enable support for Waveshare DSI touchscreen panels, + This driver supports regulator on the waveshare + touchscreen unit. The regulator is used to enable power to the + display and to control backlight. + config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" depends on MFD_RC5T583 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index b5befee45379..4c4011be74cd 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -138,6 +138,7 @@ obj-$(CONFIG_REGULATOR_PBIAS) +=3D pbias-regulator.o obj-$(CONFIG_REGULATOR_PCAP) +=3D pcap-regulator.o obj-$(CONFIG_REGULATOR_RAA215300) +=3D raa215300.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) +=3D rpi-panel-att= iny-regulator.o +obj-$(CONFIG_REGULATOR_WAVESHARE_TOUCHSCREEN) +=3D waveshare-panel-regula= tor.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2) +=3D rpi-panel-v2-regu= lator.o obj-$(CONFIG_REGULATOR_RC5T583) +=3D rc5t583-regulator.o obj-$(CONFIG_REGULATOR_RK808) +=3D rk808-regulator.o diff --git a/drivers/regulator/waveshare-panel-regulator.c b/drivers/regula= tor/waveshare-panel-regulator.c new file mode 100644 index 000000000000..eba068e9592a --- /dev/null +++ b/drivers/regulator/waveshare-panel-regulator.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Waveshare International Limited + * + * Based on rpi-panel-v2-regulator.c by Dave Stevenson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 + +#define NUM_GPIO 16 /* Treat BL_ENABLE, LCD_RESET, TP_RESET as GPIOs */ + +struct waveshare_panel_lcd { + struct mutex dir_lock; + struct mutex pwr_lock; + struct regmap *regmap; + u16 poweron_state; + u16 direction_state; + + struct gpio_chip gc; + struct gpio_desc *enable; +}; + +static const struct regmap_config waveshare_panel_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 8, + .max_register =3D REG_PWM, +}; + +static int waveshare_panel_gpio_direction_in(struct gpio_chip *gc, + unsigned int offset) +{ + struct waveshare_panel_lcd *state =3D gpiochip_get_data(gc); + + mutex_lock(&state->dir_lock); + state->direction_state |=3D BIT(offset); + mutex_unlock(&state->dir_lock); + + return 0; +} + +static int waveshare_panel_gpio_direction_out(struct gpio_chip *gc, + unsigned int offset, int val) +{ + struct waveshare_panel_lcd *state =3D gpiochip_get_data(gc); + u16 last_val; + + mutex_lock(&state->dir_lock); + state->direction_state &=3D ~BIT(offset); + mutex_unlock(&state->dir_lock); + + mutex_lock(&state->pwr_lock); + last_val =3D state->poweron_state; + if (val) + last_val |=3D BIT(offset); + else + last_val &=3D ~BIT(offset); + + state->poweron_state =3D last_val; + mutex_unlock(&state->pwr_lock); + + regmap_write(state->regmap, REG_TP, last_val >> 8); + regmap_write(state->regmap, REG_LCD, last_val & 0xff); + + return 0; +} + +static int waveshare_panel_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct waveshare_panel_lcd *state =3D gpiochip_get_data(gc); + u16 last_val; + + mutex_lock(&state->dir_lock); + last_val =3D state->direction_state; + mutex_unlock(&state->dir_lock); + + if (last_val & BIT(offset)) + return GPIO_LINE_DIRECTION_IN; + else + return GPIO_LINE_DIRECTION_OUT; +} + +static int waveshare_panel_gpio_get(struct gpio_chip *gc, unsigned int off= set) +{ + struct waveshare_panel_lcd *state =3D gpiochip_get_data(gc); + u16 pwr_state; + + mutex_lock(&state->pwr_lock); + pwr_state =3D state->poweron_state & BIT(offset); + mutex_unlock(&state->pwr_lock); + + return !!pwr_state; +} + +static int waveshare_panel_gpio_set(struct gpio_chip *gc, unsigned int off= set, + int value) +{ + struct waveshare_panel_lcd *state =3D gpiochip_get_data(gc); + u16 last_val; + + if (offset >=3D NUM_GPIO) + return 0; + + mutex_lock(&state->pwr_lock); + + last_val =3D state->poweron_state; + if (value) + last_val |=3D BIT(offset); + else + last_val &=3D ~BIT(offset); + + state->poweron_state =3D last_val; + + regmap_write(state->regmap, REG_TP, last_val >> 8); + regmap_write(state->regmap, REG_LCD, last_val & 0xff); + + mutex_unlock(&state->pwr_lock); + + return 0; +} + +static int waveshare_panel_update_status(struct backlight_device *bl) +{ + struct waveshare_panel_lcd *state =3D bl_get_data(bl); + int brightness =3D bl->props.brightness; + + if (bl->props.power !=3D FB_BLANK_UNBLANK || + bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness =3D 0; + + if (state->enable) + gpiod_set_value_cansleep(state->enable, !!brightness); + + return regmap_write(state->regmap, REG_PWM, brightness); +} + +static const struct backlight_ops waveshare_panel_bl =3D { + .update_status =3D waveshare_panel_update_status, +}; + +static int waveshare_panel_i2c_read(struct i2c_client *client, u8 reg, + unsigned int *buf) +{ + int val; + + val =3D i2c_smbus_read_byte_data(client, reg); + if (val < 0) + return val; + + *buf =3D val; + + return 0; +} + +static int waveshare_panel_i2c_probe(struct i2c_client *i2c) +{ + struct backlight_properties props =3D {}; + struct backlight_device *bl; + struct waveshare_panel_lcd *state; + struct regmap *regmap; + unsigned int data; + int ret; + + state =3D devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + mutex_init(&state->dir_lock); + mutex_init(&state->pwr_lock); + + i2c_set_clientdata(i2c, state); + + regmap =3D devm_regmap_init_i2c(i2c, &waveshare_panel_regmap_config); + if (IS_ERR(regmap)) { + ret =3D PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto error; + } + + ret =3D waveshare_panel_i2c_read(i2c, REG_ID, &data); + if (ret =3D=3D 0) + dev_info(&i2c->dev, "waveshare panel hw id =3D 0x%x\n", data); + + ret =3D waveshare_panel_i2c_read(i2c, REG_SIZE, &data); + if (ret =3D=3D 0) + dev_info(&i2c->dev, "waveshare panel size =3D %d\n", data); + + ret =3D waveshare_panel_i2c_read(i2c, REG_VERSION, &data); + if (ret =3D=3D 0) + dev_info(&i2c->dev, "waveshare panel mcu version =3D 0x%x\n", + data); + + state->direction_state =3D 0; + state->poweron_state =3D BIT(9) | BIT(8); // Enable VCC + regmap_write(regmap, REG_TP, state->poweron_state >> 8); + regmap_write(regmap, REG_LCD, state->poweron_state & 0xff); + msleep(20); + + state->regmap =3D regmap; + state->gc.parent =3D &i2c->dev; + state->gc.label =3D i2c->name; + state->gc.owner =3D THIS_MODULE; + state->gc.base =3D -1; + state->gc.ngpio =3D NUM_GPIO; + + state->gc.get =3D waveshare_panel_gpio_get; + state->gc.set =3D waveshare_panel_gpio_set; + state->gc.direction_input =3D waveshare_panel_gpio_direction_in; + state->gc.direction_output =3D waveshare_panel_gpio_direction_out; + state->gc.get_direction =3D waveshare_panel_gpio_get_direction; + state->gc.can_sleep =3D true; + + ret =3D devm_gpiochip_add_data(&i2c->dev, &state->gc, state); + if (ret) { + dev_err(&i2c->dev, "Failed to create gpiochip: %d\n", ret); + goto error; + } + + state->enable =3D + devm_gpiod_get_optional(&i2c->dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(state->enable)) + return dev_err_probe(&i2c->dev, PTR_ERR(state->enable), + "Couldn't get enable GPIO\n"); + + props.type =3D BACKLIGHT_RAW; + props.max_brightness =3D 255; + bl =3D devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev), + &i2c->dev, state, + &waveshare_panel_bl, &props); + if (IS_ERR(bl)) + return PTR_ERR(bl); + + bl->props.brightness =3D 255; + + return 0; + +error: + mutex_destroy(&state->dir_lock); + mutex_destroy(&state->pwr_lock); + return ret; +} + +static void waveshare_panel_i2c_remove(struct i2c_client *client) +{ + struct waveshare_panel_lcd *state =3D i2c_get_clientdata(client); + + mutex_destroy(&state->dir_lock); + mutex_destroy(&state->pwr_lock); +} + +static void waveshare_panel_i2c_shutdown(struct i2c_client *client) +{ + struct waveshare_panel_lcd *state =3D i2c_get_clientdata(client); + + regmap_write(state->regmap, REG_PWM, 0); +} + +static const struct of_device_id waveshare_panel_dt_ids[] =3D { + { .compatible =3D "waveshare,touchscreen-panel-regulator" }, + {}, +}; +MODULE_DEVICE_TABLE(of, waveshare_panel_dt_ids); + +static struct i2c_driver waveshare_panel_regulator_driver =3D { + .driver =3D { + .name =3D "waveshare_touchscreen", + .of_match_table =3D of_match_ptr(waveshare_panel_dt_ids), + }, + .probe =3D waveshare_panel_i2c_probe, + .remove =3D waveshare_panel_i2c_remove, + .shutdown =3D waveshare_panel_i2c_shutdown, +}; + +module_i2c_driver(waveshare_panel_regulator_driver); + +MODULE_AUTHOR("Waveshare Team "); +MODULE_DESCRIPTION("Regulator device driver for Waveshare touchscreen"); +MODULE_LICENSE("GPL"); --=20 2.34.1