Commit 4bfa4a54 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull backlight updates from Lee Jones:
 "New Support & Features:

   - Add a new driver for the Congatec Board Controller (CGBC)
     backlight, providing brightness control via the board controller's
     PWM interface

  Improvements & Fixes:

   - Resolve build failures in the Awinic AW99706 driver by switching to
     the correct GPIO consumer header

   - Extend the Qualcomm WLED driver to support the specific
     over-voltage protection (OVP) values required for the PMI8994 and
     PMI8950 variants

  Device Tree Bindings Updates:

   - Document the device-specific over-voltage protection (OVP)
     millivolt ranges and default values for Qualcomm PMI8994 and
     PMI8950 WLED controllers"

* tag 'backlight-next-6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight:
  backlight: qcom-wled: Change PM8950 WLED configurations
  dt-bindings: backlight: qcom-wled: Document ovp values for PMI8950
  backlight: qcom-wled: Support ovp values for PMI8994
  dt-bindings: backlight: qcom-wled: Document ovp values for PMI8994
  backlight: aw99706: Fix build errors caused by wrong gpio header
  backlight: Add Congatec Board Controller (CGBC) backlight support
parents 4668c483 83333aa9
Loading
Loading
Loading
Loading
+22 −2
Original line number Diff line number Diff line
@@ -98,8 +98,8 @@ properties:
    description: |
      Over-voltage protection limit. This property is for WLED4 only.
    $ref: /schemas/types.yaml#/definitions/uint32
    enum: [ 18100, 19600, 29600, 31100 ]
    default: 29600
    minimum: 17800
    maximum: 31100

  qcom,num-strings:
    description: |
@@ -239,6 +239,26 @@ allOf:
          minimum: 0
          maximum: 4095

  - if:
      properties:
        compatible:
          contains:
            enum:
              - qcom,pmi8950-wled
              - qcom,pmi8994-wled

    then:
      properties:
        qcom,ovp-millivolt:
          enum: [ 17800, 19400, 29500, 31000 ]
          default: 29500

    else:
      properties:
        qcom,ovp-millivolt:
          enum: [ 18100, 19600, 29600, 31100 ]
          default: 29600

required:
  - compatible
  - reg
+11 −0
Original line number Diff line number Diff line
@@ -258,6 +258,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
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ obj-$(CONFIG_BACKLIGHT_APPLE_DWI) += apple_dwi_bl.o
obj-$(CONFIG_BACKLIGHT_AS3711)		+= as3711_bl.o
obj-$(CONFIG_BACKLIGHT_AW99706)		+= aw99706.o
obj-$(CONFIG_BACKLIGHT_BD6107)		+= bd6107.o
obj-$(CONFIG_BACKLIGHT_CGBC)		+= cgbc_bl.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE)	+= backlight.o
obj-$(CONFIG_BACKLIGHT_DA903X)		+= da903x_bl.o
obj-$(CONFIG_BACKLIGHT_DA9052)		+= da9052_bl.o
+1 −1
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@
#include <linux/backlight.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
+180 −0
Original line number Diff line number Diff line
// 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@novatron.fi>
 */

#include <linux/backlight.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/mfd/cgbc.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#define BLT_PWM_DUTY_MASK          GENMASK(6, 0)

/* 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
 * @cgbc: Pointer to the parent CGBC device data
 * @current_brightness: Current brightness level (0-100)
 */
struct cgbc_bl_data {
	struct device *dev;
	struct cgbc_device_data *cgbc;
	unsigned int current_brightness;
};

static int cgbc_bl_read_brightness(struct cgbc_bl_data *bl_data)
{
	u8 cmd_buf[4] = { CGBC_CMD_BLT0_PWM };
	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)
		return ret;

	/*
	 * Get only PWM duty factor percentage,
	 * ignore polarity inversion bit (bit 7)
	 */
	bl_data->current_brightness = FIELD_GET(BLT_PWM_DUTY_MASK, reply_buf[0]);

	return 0;
}

static int cgbc_bl_update_status(struct backlight_device *bl)
{
	struct cgbc_bl_data *bl_data = bl_get_data(bl);
	u8 cmd_buf[4] = { CGBC_CMD_BLT0_PWM };
	u8 reply_buf[3];
	u8 brightness;
	int ret;

	brightness = backlight_get_brightness(bl);

	if (brightness != bl_data->current_brightness) {
		/* 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;
		}

		/*
		 * Prepare command buffer for writing new settings. Only 2nd byte is changed
		 * to set new brightness (PWM duty cycle %). Other values (polarity, frequency)
		 * are preserved from the read values.
		 */
		cmd_buf[1] = (reply_buf[0] & ~BLT_PWM_DUTY_MASK) |
			FIELD_PREP(BLT_PWM_DUTY_MASK, brightness);
		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);
			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 (got %u, expected %u)\n",
				bl_data->current_brightness, (unsigned int)brightness);
			return -EIO;
		}
	}

	return 0;
}

static int cgbc_bl_get_brightness(struct backlight_device *bl)
{
	struct cgbc_bl_data *bl_data = bl_get_data(bl);
	int ret;

	ret = cgbc_bl_read_brightness(bl_data);
	if (ret < 0) {
		dev_err(bl_data->dev, "Failed to read brightness: %d\n", ret);
		return ret;
	}

	return bl_data->current_brightness;
}

static const struct backlight_ops cgbc_bl_ops = {
	.options = BL_CORE_SUSPENDRESUME,
	.update_status = cgbc_bl_update_status,
	.get_brightness = cgbc_bl_get_brightness,
};

static int cgbc_bl_probe(struct platform_device *pdev)
{
	struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);
	struct backlight_properties props = { };
	struct backlight_device *bl_dev;
	struct cgbc_bl_data *bl_data;
	int ret;

	bl_data = devm_kzalloc(&pdev->dev, sizeof(*bl_data), GFP_KERNEL);
	if (!bl_data)
		return -ENOMEM;

	bl_data->dev = &pdev->dev;
	bl_data->cgbc = cgbc;

	ret = cgbc_bl_read_brightness(bl_data);
	if (ret < 0)
		return dev_err_probe(&pdev->dev, ret,
				     "Failed to read initial brightness\n");

	props.type = BACKLIGHT_PLATFORM;
	props.max_brightness = CGBC_BL_MAX_BRIGHTNESS;
	props.brightness = bl_data->current_brightness;
	props.scale = BACKLIGHT_SCALE_LINEAR;

	bl_dev = devm_backlight_device_register(&pdev->dev, "cgbc-backlight",
						&pdev->dev, bl_data,
						&cgbc_bl_ops, &props);
	if (IS_ERR(bl_dev))
		return dev_err_probe(&pdev->dev, PTR_ERR(bl_dev),
			     "Failed to register backlight device\n");

	platform_set_drvdata(pdev, bl_data);

	return 0;
}

static struct platform_driver cgbc_bl_driver = {
	.driver = {
		.name = "cgbc-backlight",
	},
	.probe = cgbc_bl_probe,
};

module_platform_driver(cgbc_bl_driver);

MODULE_AUTHOR("Petri Karhula <petri.karhula@novatron.fi>");
MODULE_DESCRIPTION("Congatec Board Controller (CGBC) Backlight Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:cgbc-backlight");
Loading