All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: chitranshmandhaniya@gmail.com
Cc: Hans de Goede <hansg@kernel.org>,
	platform-driver-x86@vger.kernel.org,
	 LKML <linux-kernel@vger.kernel.org>,
	Chitransh_Mandhaniya@dell.com
Subject: Re: [PATCH v2] platform/x86: dell-uart-backlight: fall back to PCH PWM on UART timeout
Date: Fri, 9 Oct 2026 13:48:36 +0300 (EEST)	[thread overview]
Message-ID: <853d1552-0ec4-3fbc-a4a5-e6538a3fab6f@linux.intel.com> (raw)
In-Reply-To: <20261009072401.52509-1-chitranshmandhaniya@gmail.com>

On Fri, 9 Oct 2026, chitranshmandhaniya@gmail.com wrote:

> From: Chitransh Mandhaniya <Chitransh_Mandhaniya@dell.com>
> 
> Some Dell OptiPlex AIO 7410 (35W, board 01KP8D) ship with DELL0501 in
> the ACPI DSDT (status = 0x0F) even though no UART scalar board is
> physically present.  This causes acpi_video_get_backlight_type() to
> return acpi_backlight_dell_uart, which makes i915 skip registering
> intel_backlight.  The dell-uart-backlight driver then times out talking
> to the absent board and the probe fails, leaving the machine with zero
> backlight control.
> 
> A BIOS update fixes this on some models (e.g. OptiPlex 7420, board
> 0914RT) by setting DELL0501 _STA to 0, but Dell has not shipped a
> corrected BIOS for the 7410 even in the latest release (1.37.0,
> 2026-05-28).
> 
> When the initial get_version command returns -ETIMEDOUT, fall back to
> driving the backlight through the Intel PCH PWM registers (BLC_PWM_CTL /
> BLC_PWM_FREQ / BLC_PWM_DUTY at MMIO BAR0 + 0xC8250).  The fallback
> reads the BIOS-programmed PWM frequency as max_brightness and registers
> a BACKLIGHT_PLATFORM device so that compositors (Weston, GNOME, KDE)
> pick it up normally.
> 
> Tested on:
>   - OptiPlex AIO 7410 35W (01KP8D) -- timeout, fallback activates, works
>   - OptiPlex AIO 7420 35W (0914RT, old BIOS) -- timeout, fallback works
>   - OptiPlex AIO 7420 35W (0914RT, new BIOS) -- DELL0501 absent, driver
>     not loaded, intel_backlight works natively (no regression)
> 
> Signed-off-by: Chitransh Mandhaniya <Chitransh_Mandhaniya@dell.com>
> ---
> v1 -> v2:
>   - Use __free(pci_dev_put) for automatic PCI device cleanup (Ilpo)
>   - Replace 0x10 with SZ_16, add linux/sizes.h (Ilpo)
>   - Name BIT(31) as PCH_BLC_PWM_ENABLE (Ilpo)
>   - Fix double-space in block comment (Ilpo)
>   - Reduce devm_backlight_device_register() to fewer lines (Ilpo)

>   - Extract dell_uart_bl_init() for the normal UART backlight path
>     so the probe function reads more clearly (Ilpo)

I suggest you do dell_uart_bl_init() refactor in a preparetory patch and 
do a two patch series. I believe it would make the patches easier to 
review.

-- 
 i.


>  .../platform/x86/dell/dell-uart-backlight.c   | 157 +++++++++++++++---
>  1 file changed, 134 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c
> index f323a667dc2d..edc7cfe38069 100644
> --- a/drivers/platform/x86/dell/dell-uart-backlight.c
> +++ b/drivers/platform/x86/dell/dell-uart-backlight.c
> @@ -13,10 +13,13 @@
>  #include <linux/delay.h>
>  #include <linux/device.h>
>  #include <linux/err.h>
> +#include <linux/io.h>
>  #include <linux/module.h>
>  #include <linux/mutex.h>
> +#include <linux/pci.h>
>  #include <linux/platform_device.h>
>  #include <linux/serdev.h>
> +#include <linux/sizes.h>
>  #include <linux/string.h>
>  #include <linux/types.h>
>  #include <linux/wait.h>
> @@ -50,6 +53,12 @@
>  #define MIN_RESP_LEN			3
>  #define MAX_RESP_LEN			80
>  
> +/* PCH PWM backlight registers relative to Intel GPU MMIO BAR0 */
> +#define PCH_BLC_PWM_CTL		0xC8250
> +#define PCH_BLC_PWM_FREQ	0xC8254
> +#define PCH_BLC_PWM_DUTY	0xC8258
> +#define PCH_BLC_PWM_ENABLE	BIT(31)
> +
>  struct dell_uart_backlight {
>  	struct mutex mutex;
>  	wait_queue_head_t wait_queue;
> @@ -64,6 +73,11 @@ struct dell_uart_backlight {
>  	int power;
>  };
>  
> +struct dell_pwm_backlight {
> +	void __iomem *mmio;
> +	u32 max_brightness;
> +};
> +
>  /* Checksum: SUM(Length and Cmd and Data) xor 0xFF */
>  static u8 dell_uart_checksum(u8 *buf, int len)
>  {
> @@ -267,32 +281,97 @@ static const struct serdev_device_ops dell_uart_bl_serdev_ops = {
>  	.write_wakeup = serdev_device_write_wakeup,
>  };
>  
> -static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)
> +/*
> + * Some Dell AIO models (e.g. OptiPlex 7410) declare DELL0501 in ACPI but
> + * have no physical UART scalar board. When the UART times out fall back
> + * to driving the Intel PCH PWM backlight registers directly.
> + */
> +static int dell_pwm_update_status(struct backlight_device *bd)
>  {
> -	u8 get_version[GET_CMD_LEN], resp[MAX_RESP_LEN];
> +	struct dell_pwm_backlight *pwm = bl_get_data(bd);
> +	u32 duty;
> +
> +	if (bd->props.power != BACKLIGHT_POWER_ON ||
> +	    bd->props.state & BL_CORE_SUSPENDED)
> +		duty = 0;
> +	else
> +		duty = bd->props.brightness;
> +
> +	writel(duty, pwm->mmio + (PCH_BLC_PWM_DUTY - PCH_BLC_PWM_CTL));
> +	return 0;
> +}
> +
> +static int dell_pwm_get_brightness(struct backlight_device *bd)
> +{
> +	struct dell_pwm_backlight *pwm = bl_get_data(bd);
> +
> +	return readl(pwm->mmio + (PCH_BLC_PWM_DUTY - PCH_BLC_PWM_CTL));
> +}
> +
> +static const struct backlight_ops dell_pwm_backlight_ops = {
> +	.update_status  = dell_pwm_update_status,
> +	.get_brightness = dell_pwm_get_brightness,
> +};
> +
> +static int dell_pwm_fallback_register(struct device *dev)
> +{
> +	struct pci_dev *gpu __free(pci_dev_put) = NULL;
>  	struct backlight_properties props = {};
> -	struct dell_uart_backlight *dell_bl;
> -	struct device *dev = &serdev->dev;
> -	int ret;
> +	struct dell_pwm_backlight *pwm;
> +	struct backlight_device *bl;
> +	resource_size_t bar0;
> +	u32 freq, duty, ctl;
>  
> -	dell_bl = devm_kzalloc(dev, sizeof(*dell_bl), GFP_KERNEL);
> -	if (!dell_bl)
> +	gpu = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL);
> +	if (!gpu || gpu->vendor != PCI_VENDOR_ID_INTEL) {
> +		dev_warn(dev, "No Intel GPU found, cannot fall back to PCH PWM\n");
> +		return -ENODEV;
> +	}
> +
> +	bar0 = pci_resource_start(gpu, 0);
> +	if (!bar0) {
> +		dev_warn(dev, "Intel GPU BAR0 not assigned\n");
> +		return -ENODEV;
> +	}
> +
> +	pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
> +	if (!pwm)
>  		return -ENOMEM;
>  
> -	mutex_init(&dell_bl->mutex);
> -	init_waitqueue_head(&dell_bl->wait_queue);
> -	dell_bl->dev = dev;
> +	pwm->mmio = devm_ioremap(dev, bar0 + PCH_BLC_PWM_CTL, SZ_16);
> +	if (!pwm->mmio) {
> +		dev_warn(dev, "Failed to ioremap PCH PWM registers\n");
> +		return -ENOMEM;
> +	}
>  
> -	serdev_device_set_drvdata(serdev, dell_bl);
> -	serdev_device_set_client_ops(serdev, &dell_uart_bl_serdev_ops);
> +	ctl  = readl(pwm->mmio);
> +	freq = readl(pwm->mmio + (PCH_BLC_PWM_FREQ - PCH_BLC_PWM_CTL));
> +	duty = readl(pwm->mmio + (PCH_BLC_PWM_DUTY - PCH_BLC_PWM_CTL));
>  
> -	ret = devm_serdev_device_open(dev, serdev);
> -	if (ret)
> -		return dev_err_probe(dev, ret, "opening UART device\n");
> +	if (!(ctl & PCH_BLC_PWM_ENABLE) || freq == 0) {
> +		dev_warn(dev, "PCH PWM not enabled by BIOS (ctl=0x%08x freq=%u)\n", ctl, freq);
> +		return -ENODEV;
> +	}
>  
> -	/* 9600 bps, no flow control, these are the default but set them to be sure */
> -	serdev_device_set_baudrate(serdev, 9600);
> -	serdev_device_set_flow_control(serdev, false);
> +	pwm->max_brightness = freq;
> +
> +	dev_info(dev, "Falling back to PCH PWM backlight (max=%u current=%u)\n", freq, duty);
> +
> +	props.type = BACKLIGHT_PLATFORM;
> +	props.brightness = duty;
> +	props.max_brightness = freq;
> +
> +	bl = devm_backlight_device_register(dev, "dell_uart_backlight", dev, pwm,
> +					    &dell_pwm_backlight_ops, &props);
> +	return PTR_ERR_OR_ZERO(bl);
> +}
> +
> +static int dell_uart_bl_init(struct device *dev, struct serdev_device *serdev,
> +			     struct dell_uart_backlight *dell_bl)
> +{
> +	u8 get_version[GET_CMD_LEN], resp[MAX_RESP_LEN];
> +	struct backlight_properties props = {};
> +	int ret;
>  
>  	get_version[0] = DELL_SOF(GET_CMD_LEN);
>  	get_version[1] = CMD_GET_VERSION;
> @@ -300,7 +379,7 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)
>  
>  	ret = dell_uart_bl_command(dell_bl, get_version, GET_CMD_LEN, resp, MAX_RESP_LEN);
>  	if (ret)
> -		return dev_err_probe(dev, ret, "getting firmware version\n");
> +		return ret;
>  
>  	dev_dbg(dev, "Firmware version: %.*s\n", resp[RESP_LEN] - 3, resp + RESP_DATA);
>  
> @@ -318,13 +397,45 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)
>  	props.max_brightness = DELL_BL_MAX_BRIGHTNESS;
>  	props.power = dell_bl->power;
>  
> -	dell_bl->bl = devm_backlight_device_register(dev, "dell_uart_backlight",
> -						     dev, dell_bl,
> -						     &dell_uart_backlight_ops,
> -						     &props);
> +	dell_bl->bl = devm_backlight_device_register(dev, "dell_uart_backlight", dev, dell_bl,
> +						     &dell_uart_backlight_ops, &props);
>  	return PTR_ERR_OR_ZERO(dell_bl->bl);
>  }
>  
> +static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)
> +{
> +	struct dell_uart_backlight *dell_bl;
> +	struct device *dev = &serdev->dev;
> +	int ret;
> +
> +	dell_bl = devm_kzalloc(dev, sizeof(*dell_bl), GFP_KERNEL);
> +	if (!dell_bl)
> +		return -ENOMEM;
> +
> +	mutex_init(&dell_bl->mutex);
> +	init_waitqueue_head(&dell_bl->wait_queue);
> +	dell_bl->dev = dev;
> +
> +	serdev_device_set_drvdata(serdev, dell_bl);
> +	serdev_device_set_client_ops(serdev, &dell_uart_bl_serdev_ops);
> +
> +	ret = devm_serdev_device_open(dev, serdev);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "opening UART device\n");
> +
> +	/* 9600 bps, no flow control, these are the default but set them to be sure */
> +	serdev_device_set_baudrate(serdev, 9600);
> +	serdev_device_set_flow_control(serdev, false);
> +
> +	ret = dell_uart_bl_init(dev, serdev, dell_bl);
> +	if (ret == -ETIMEDOUT) {
> +		dev_info(dev, "UART backlight controller not responding, trying PCH PWM fallback\n");
> +		return dell_pwm_fallback_register(dev);
> +	}
> +
> +	return ret;
> +}
> +
>  static struct serdev_device_driver dell_uart_bl_serdev_driver = {
>  	.probe = dell_uart_bl_serdev_probe,
>  	.driver = {
> 

  reply	other threads:[~2026-10-09 10:48 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-08 14:16 [PATCH] platform/x86: dell-uart-backlight: fall back to PCH PWM on UART timeout chitranshmandhaniya
2026-10-08 14:30 ` Ilpo Järvinen
2026-10-09  7:24 ` [PATCH v2] " chitranshmandhaniya
2026-10-09 10:48   ` Ilpo Järvinen [this message]
2026-10-09 12:26   ` [PATCH v3 0/2] " chitranshmandhaniya
2026-10-09 12:26     ` [PATCH v3 1/2] platform/x86: dell-uart-backlight: extract dell_uart_bl_init() from probe chitranshmandhaniya
2026-10-09 12:26     ` [PATCH v3 2/2] platform/x86: dell-uart-backlight: fall back to PCH PWM on UART timeout chitranshmandhaniya
2026-10-09 23:46   ` [PATCH v2] " kernel test robot
2026-10-10 10:20   ` [PATCH v4 0/2] " chitranshmandhaniya
2026-10-10 10:20     ` [PATCH v4 1/2] platform/x86: dell-uart-backlight: extract dell_uart_bl_init() from probe chitranshmandhaniya
2026-10-10 10:20     ` [PATCH v4 2/2] platform/x86: dell-uart-backlight: fall back to PCH PWM on UART timeout chitranshmandhaniya
2026-10-10 11:41     ` [PATCH v4 0/2] " Hans de Goede
2026-10-10 12:48   ` [PATCH v5] ACPI: video: force native backlight on Dell OptiPlex AIO 7410 35W chitranshmandhaniya
2026-10-12 14:54     ` Hans de Goede
2026-10-12 18:11       ` Rafael J. Wysocki (Intel)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=853d1552-0ec4-3fbc-a4a5-e6538a3fab6f@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=Chitransh_Mandhaniya@dell.com \
    --cc=chitranshmandhaniya@gmail.com \
    --cc=hansg@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.