lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <f40abf01-46f2-41e6-a21f-c58c21d653c4@bootlin.com>
Date: Thu, 20 Nov 2025 10:09:03 +0100
From: Thomas Richard <thomas.richard@...tlin.com>
To: petri.karhula@...atron.fi, Lee Jones <lee@...nel.org>,
 Daniel Thompson <danielt@...nel.org>, Jingoo Han <jingoohan1@...il.com>,
 Helge Deller <deller@....de>
Cc: linux-kernel@...r.kernel.org, dri-devel@...ts.freedesktop.org,
 linux-fbdev@...r.kernel.org
Subject: Re: [PATCH v2 1/2] backlight: Add Congatec Board Controller (CGBC)
 backlight support

Hello Petri,

Thanks for your patch.

On 11/19/25 9:25 AM, Petri Karhula via B4 Relay wrote:
> From: Petri Karhula <petri.karhula@...atron.fi>
> 
> This driver provides backlight brightness control through the Linux
> backlight subsystem. It communicates with the board controller to
> adjust LCD backlight using PWM signals. Communication is done
> through Congatec Board Controller core driver.
> 
> Signed-off-by: Petri Karhula <petri.karhula@...atron.fi>
> ---
>  drivers/video/backlight/Kconfig   |  11 ++
>  drivers/video/backlight/Makefile  |   1 +
>  drivers/video/backlight/cgbc_bl.c | 281 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 293 insertions(+)
> 
> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
> index d9374d208cee..702f3b8ed036 100644
> --- a/drivers/video/backlight/Kconfig
> +++ b/drivers/video/backlight/Kconfig
> @@ -249,6 +249,17 @@ config BACKLIGHT_PWM
>  	  If you have a LCD backlight adjustable by PWM, say Y to enable
>  	  this driver.
>  
> +config BACKLIGHT_CGBC
> +	tristate "Congatec Board Controller (CGBC) backlight support"
> +	depends on MFD_CGBC && X86
> +	help
> +	  Say Y here to enable support for LCD backlight control on Congatec
> +	  x86-based boards via the CGBC (Congatec Board Controller).
> +
> +	  This driver provides backlight brightness control through the Linux
> +	  backlight subsystem. It communicates with the board controller to
> +	  adjust LCD backlight using PWM signals.
> +
>  config BACKLIGHT_DA903X
>  	tristate "Backlight Driver for DA9030/DA9034 using WLED"
>  	depends on PMIC_DA903X
> diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
> index dfbb169bf6ea..0169fd8873ed 100644
> --- a/drivers/video/backlight/Makefile
> +++ b/drivers/video/backlight/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_BACKLIGHT_APPLE_DWI)	+= apple_dwi_bl.o
>  obj-$(CONFIG_BACKLIGHT_AS3711)		+= as3711_bl.o
>  obj-$(CONFIG_BACKLIGHT_BD6107)		+= bd6107.o
>  obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE)	+= backlight.o
> +obj-$(CONFIG_BACKLIGHT_CGBC)		+= cgbc_bl.o
>  obj-$(CONFIG_BACKLIGHT_DA903X)		+= da903x_bl.o
>  obj-$(CONFIG_BACKLIGHT_DA9052)		+= da9052_bl.o
>  obj-$(CONFIG_BACKLIGHT_EP93XX)		+= ep93xx_bl.o
> diff --git a/drivers/video/backlight/cgbc_bl.c b/drivers/video/backlight/cgbc_bl.c
> new file mode 100644
> index 000000000000..4382321f4cac
> --- /dev/null
> +++ b/drivers/video/backlight/cgbc_bl.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Congatec Board Controller (CGBC) Backlight Driver
> + *
> + * This driver provides backlight control for LCD displays connected to
> + * Congatec boards via the CGBC (Congatec Board Controller). It integrates
> + * with the Linux backlight subsystem and communicates with hardware through
> + * the cgbc-core module.
> + *
> + * Copyright (C) 2025 Novatron Oy
> + *
> + * Author: Petri Karhula <petri.karhula@...atron.fi>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/backlight.h>
> +
> +#include <linux/mfd/cgbc.h>

headers shall be sorted in alphabetical order

> +
> +#define CGBC_BL_DRIVER_VERSION     "0.0.1"

not needed

> +
> +#define BLT_PWM_DUTY_MASK          0x7F
> +#define BLT_PWM_INVERTED_MASK      0x80

Use GENMASK

> +
> +/* CGBC command for PWM brightness control*/
> +#define CGBC_CMD_BLT0_PWM          0x75
> +
> +#define CGBC_BL_MAX_BRIGHTNESS     100
> +
> +/**
> + * CGBC backlight driver data
> + * @dev: Pointer to the platform device
> + * @bl_dev: Pointer to the backlight device
> + * @cgbc: Pointer to the parent CGBC device data
> + * @current_brightness: Current brightness level (0-100)
> + */
> +struct cgbc_bl_data {
> +	struct device *dev;
> +	struct backlight_device *bl_dev;

not used

> +	struct cgbc_device_data *cgbc;
> +	unsigned int current_brightness;
> +};
> +
> +/**
> + * Read current PWM settings from hardware
> + * @bl_data: Backlight driver data
> + *
> + * Reads the current PWM duty cycle percentage (= brightness level)
> + * from the board controller.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int cgbc_bl_read_pwm_settings(struct cgbc_bl_data *bl_data)
> +{
> +	u8 cmd_buf[4] = { CGBC_CMD_BLT0_PWM, 0, 0, 0 };
> +	u8 reply_buf[3];
> +	int ret;
> +
> +	ret = cgbc_command(bl_data->cgbc, cmd_buf, sizeof(cmd_buf), reply_buf,
> +			   sizeof(reply_buf), NULL);
> +
> +	if (ret < 0) {
> +		dev_err(bl_data->dev, "Failed to read PWM settings: %d\n", ret);
> +		return ret;
> +	}

error message not needed from my point of view.

> +
> +	/*
> +	 * Only return PWM duty factor percentage,
> +	 * ignore polarity inversion bit (bit 7)
> +	 */
> +	bl_data->current_brightness = reply_buf[0] & BLT_PWM_DUTY_MASK;

I would prefer to use FIELD_GET

> +
> +	dev_dbg(bl_data->dev, "Current PWM duty=%u\n", bl_data->current_brightness);

Not needed from my point of view.

> +
> +	return 0;
> +}
> +
> +/**
> + * Set backlight brightness
> + * @bl_data: Backlight driver data
> + * @brightness: Brightness level (0-100)
> + *
> + * Sets the backlight brightness by configuring the PWM duty cycle.
> + * Preserves the current polarity and frequency settings.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int cgbc_bl_set_brightness(struct cgbc_bl_data *bl_data, u8 brightness)
> +{
> +	u8 cmd_buf[4] = { CGBC_CMD_BLT0_PWM, 0, 0, 0 };

u8 cmd_buf[4] = { CGBC_CMD_BLT0_PWM };

> +	u8 reply_buf[3];
> +	int ret;
> +
> +	/* Read the current values */
> +	ret = cgbc_command(bl_data->cgbc, cmd_buf, sizeof(cmd_buf), reply_buf,
> +			   sizeof(reply_buf), NULL);
> +
> +	if (ret < 0) {
> +		dev_err(bl_data->dev, "Failed to read PWM settings: %d\n", ret);
> +		return ret;
> +	}

error message not needed from my point of view.

> +
> +	/*
> +	 * Prepare command buffer for writing new settings. Only 2nd byte is changed
> +	 * to set new brightness (PWM duty cycle %). Other balues (polarity, frequency)

values

> +	 * are preserved from the read values.
> +	 */
> +	cmd_buf[1] = (reply_buf[0] & BLT_PWM_INVERTED_MASK) |
> +		     (BLT_PWM_DUTY_MASK & brightness);

use FIELD_PREP

> +	cmd_buf[2] = reply_buf[1];
> +	cmd_buf[3] = reply_buf[2];
> +
> +	ret = cgbc_command(bl_data->cgbc, cmd_buf, sizeof(cmd_buf), reply_buf,
> +			   sizeof(reply_buf), NULL);
> +
> +	if (ret < 0) {
> +		dev_err(bl_data->dev, "Failed to set brightness: %d\n", ret);

error messages not needed from my point of view.

> +		return ret;
> +	}
> +
> +	bl_data->current_brightness = reply_buf[0] & BLT_PWM_DUTY_MASK;
> +
> +	/* Verify the setting was applied correctly */
> +	if (bl_data->current_brightness != brightness) {
> +		dev_err(bl_data->dev,
> +			"Brightness setting verification failed\n");
> +		return -EIO;
> +	}

Do we really need to check the brightness returned by the board
controller? Have you ever run into a situation where cbgc_command
completed without errors, but the brightness level didn’t match what you
expected? Maybe we could assume that if the cbgc_command returned
successfully the brightness value is correct?

I'm not against checking the backlight value. I looked at Congatec's
implementation and they also check it.

> +
> +	dev_dbg(bl_data->dev, "Set brightness to %u\n", brightness);

Not needed, the core already has this message

> +
> +	return 0;
> +}
> +
> +/**
> + * Backlight update callback
> + * @bl: Backlight device
> + *
> + * Called by the backlight subsystem when brightness needs to be updated.
> + * Changes the brightness level on the hardware
> + * if requested value differs from the current setting.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int cgbc_bl_update_status(struct backlight_device *bl)
> +{
> +	struct cgbc_bl_data *bl_data = bl_get_data(bl);
> +	u8 brightness;
> +	int ret;
> +
> +	brightness = bl->props.brightness;

use backlight_get_brightness()

> +
> +	if (brightness != bl_data->current_brightness) {
> +		ret = cgbc_bl_set_brightness(bl_data, brightness);
> +
> +		if (ret < 0) {
> +			dev_err(bl_data->dev, "Failed to set brightness: %d\n",
> +			       ret);
> +			return ret;
> +		}

error message not needed from my point of view.

> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * Get current backlight brightness
> + * @bl: Backlight device
> + *
> + * Returns the current brightness level by reading from hardware.
> + *
> + * Return: Current brightness level (0-100), or negative error code
> + */
> +static int cgbc_bl_get_brightness(struct backlight_device *bl)
> +{
> +	struct cgbc_bl_data *bl_data = bl_get_data(bl);
> +	int ret;
> +
> +	/* Read current PWM brightness settings */
> +	ret = cgbc_bl_read_pwm_settings(bl_data);
> +
> +	if (ret < 0) {
> +		dev_err(bl_data->dev, "Failed to read PWM settings: %d\n", ret);
> +		return ret;
> +	}

error message not needed from my point of view.
If you remove all these error messages, you can also remove the struct
device in the struct cgbc_bl_data.

> +
> +	return bl_data->current_brightness;
> +}

Maybe you can remove cgbc_bl_read_pwm_settings() and move all the code
in cgbc_bl_get_brightness(). It makes the code easier to read.

> +
> +/* Backlight device operations */
> +static const struct backlight_ops cgbc_bl_ops = {
> +	.options = BL_CORE_SUSPENDRESUME,
> +	.update_status = cgbc_bl_update_status,
> +	.get_brightness = cgbc_bl_get_brightness,
> +};
> +
> +/**
> + * Probe function for CGBC backlight driver
> + * @pdev: Platform device
> + *
> + * Initializes the CGBC backlight driver and registers it with the
> + * Linux backlight subsystem.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int cgbc_bl_probe(struct platform_device *pdev)
> +{
> +	struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);
> +	struct cgbc_bl_data *bl_data;
> +	struct backlight_properties props;
> +	struct backlight_device *bl_dev;
> +	int ret;

nitpick: reverse xmas tree

> +
> +	bl_data = devm_kzalloc(&pdev->dev, sizeof(*bl_data), GFP_KERNEL);
> +

nitpick: drop empty line.

> +	if (!bl_data)
> +		return -ENOMEM;
> +
> +	bl_data->dev = &pdev->dev;
> +	bl_data->cgbc = cgbc;
> +
> +	ret = cgbc_bl_read_pwm_settings(bl_data);
> +

nitpick: drop empty line.

> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to read initial PWM settings: %d\n",
> +			ret);
> +		return ret;
> +	}

Use dev_err_probe().

> +
> +	memset(&props, 0, sizeof(props));

Use struct backlight_properties props = { };

> +	props.type = BACKLIGHT_PLATFORM;
> +	props.max_brightness = CGBC_BL_MAX_BRIGHTNESS;
> +	props.brightness = bl_data->current_brightness;
> +
> +	bl_dev = devm_backlight_device_register(&pdev->dev, "cgbc-backlight",
> +						&pdev->dev, bl_data,
> +						&cgbc_bl_ops, &props);
> +
> +	if (IS_ERR(bl_dev)) {
> +		dev_err(&pdev->dev, "Failed to register backlight device\n");
> +		return PTR_ERR(bl_dev);
> +	}

Use dev_err_probe()

> +
> +	bl_data->bl_dev = bl_dev;
> +	platform_set_drvdata(pdev, bl_data);
> +
> +	dev_info(&pdev->dev,
> +		 "CGBC backlight driver registered (brightness=%u)\n",
> +		 bl_data->current_brightness);

No logs if device probes successfully.

> +
> +	return 0;
> +}
> +
> +/**
> + * Remove function for CGBC backlight driver
> + * @pdev: Platform device
> + *
> + * The Linux device-managed resource framework (devres) does the cleanup.
> + * No explicit cleanup is needed here.
> + */
> +static void cgbc_bl_remove(struct platform_device *pdev)
> +{
> +	dev_info(&pdev->dev, "CGBC backlight driver removed\n");
> +}

Remove operation does nothing so drop it.

> +
> +static struct platform_driver cgbc_bl_driver = {
> +	.driver = {
> +		.name = "cgbc-backlight",
> +	},
> +	.probe = cgbc_bl_probe,
> +	.remove = cgbc_bl_remove,
> +};
> +
> +module_platform_driver(cgbc_bl_driver);
> +
> +MODULE_AUTHOR("Petri Karhula <petri.karhula@...atron.fi>");
> +MODULE_DESCRIPTION("Congatec Board Controller (CGBC) Backlight Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(CGBC_BL_DRIVER_VERSION);

Not needed

> +MODULE_ALIAS("platform:cgbc-backlight");
> 

Best Regards,
Thomas

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ