mirror of https://github.com/torvalds/linux.git
Backlight for v6.19
* Add support for Awinic AW99706 backlight driver.
* Add GPIOLIB dependency to backlight ktd2801 driver.
* Add devlink to LED Backlight's supplier LEDs to enforce correct removal
order and prevent NULL pointer dereferences.
* Fix kernel-doc warnings in lp855x.h
* Do not include <linux/fb.h> in backlight.h.
* Fix unused function warnings from suspend/resume ops in aw99706.c by
switching to DEFINE_SIMPLE_DEV_PM_OPS and using pm_ptr().
* Add Awinic AW99706 backlight binding to MAINTAINERS.
* Add Awinic AW99706 backlight binding documentation.
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmkxtz4ACgkQUa+KL4f8
d2FYoQ//T/M/Vd+6HSuGSnICrPY7CGXwheE1xfg8hhVB9Sa5+LL8Tt4RTJ1BRNzT
B7VromDCPkPI7dAfYL7G0dFTu1kU3nN2Xl+86CjvsXsc7gy+8ftpePvUYJbUdhTl
w4K8sslvcCyMhGHm7U9TwFNDE6+ousrDLADJCo0Tf+tJhmy2IFcl5Sjgt5Y0RnMt
TmSOAXirbCvVJ8eIQSVPm5geCrSCJ7nxh1WRTKI6BMZsazvxAG1KzShKF0xE0xN3
SUurIJydxyw6qiNIwYlqph0h0151shILtELsN2/guegqN9RZTHTAFKa8kcrXMJS+
oGooIZh6B+IkLotv0T3v8LugX3kIi/jvevWffIak3AjHy4zSMhpDXZ0QhVDnMfzT
ueeNBpyqMeGwbkCirn2iZmlwMUIMS7AYhluBkq6HAmdO++GwSkqficypTPbyxCh7
M/DPZaFF0aJ4JOIyWDY5Pk2Fc+owsKWf2DurXhSZ7mHgzHgrY5NTEzG1q4esrPXj
r1mNxSP2CegE6S1XIp3fa96SM2VWnZYvW2ZGIfLHkwqnwOT4dqp8BvEVj7+3jt9x
5awFLVBjxEf4kUOKfF6ztRQfsJaeN+ujobHcP9wsGmEMWvA2ZH9+Z24SlrbsyfRT
kunwxRK9CDVw+l1e7JLWZG0wGbEck25n3Qt4opY+jnSX2SdvnXI=
=n4gn
-----END PGP SIGNATURE-----
Merge tag 'backlight-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight
Pull backlight updates from Lee Jones:
"Additions:
- Add support for Awinic AW99706 backlight driver
Fixes:
- Add GPIOLIB dependency to backlight ktd2801 driver
- Add devlink to LED Backlight's supplier LEDs to enforce correct
removal order and prevent NULL pointer dereferences
- Fix kernel-doc warnings in lp855x.h
Removals:
- Do not include <linux/fb.h> in backlight.h
- Fix unused function warnings from suspend/resume ops in aw99706.c
by switching to DEFINE_SIMPLE_DEV_PM_OPS and using pm_ptr()
Bindings:
- Add Awinic AW99706 backlight binding to MAINTAINERS
- Add Awinic AW99706 backlight binding documentation"
* tag 'backlight-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight:
backlight: aw99706: Fix unused function warnings from suspend/resume ops
backlight: lp855x: Fix lp855x.h kernel-doc warnings
dt-bindings: leds: backlight: Add Awinic AW99706 backlight
backlight: aw99706: Add support for Awinic AW99706 backlight
backlight: led-bl: Add devlink to supplier LEDs
backlight: ktd2801: Depend on GPIOLIB
backlight: Do not include <linux/fb.h> in header file
This commit is contained in:
commit
d1b46f53a5
|
|
@ -0,0 +1,101 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/backlight/awinic,aw99706.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Awinic AW99706 6-channel WLED Backlight Driver
|
||||
|
||||
maintainers:
|
||||
- Junjie Cao <caojunjie650@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: awinic,aw99706
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
enable-gpios:
|
||||
description: GPIO to use to enable/disable the backlight (HWEN pin).
|
||||
maxItems: 1
|
||||
|
||||
awinic,dim-mode:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: >
|
||||
Select dimming mode of the device.
|
||||
0 = Bypass mode.
|
||||
1 = DC mode.
|
||||
2 = MIX mode(PWM at low brightness and DC at high brightness).
|
||||
3 = MIX-26k mode(MIX mode with different PWM frequency).
|
||||
enum: [ 0, 1, 2, 3 ]
|
||||
default: 1
|
||||
|
||||
awinic,sw-freq-hz:
|
||||
description: Boost switching frequency in Hz.
|
||||
enum: [ 300000, 400000, 500000, 600000, 660000, 750000, 850000, 1000000,
|
||||
1200000, 1330000, 1500000, 1700000 ]
|
||||
default: 750000
|
||||
|
||||
awinic,sw-ilmt-microamp:
|
||||
description: Switching current limitation in uA.
|
||||
enum: [ 1500000, 2000000, 2500000, 3000000 ]
|
||||
default: 3000000
|
||||
|
||||
awinic,iled-max-microamp:
|
||||
description: Maximum LED current setting in uA.
|
||||
minimum: 5000
|
||||
maximum: 50000
|
||||
multipleOf: 500
|
||||
default: 20000
|
||||
|
||||
awinic,uvlo-thres-microvolt:
|
||||
description: UVLO(Under Voltage Lock Out) in uV.
|
||||
enum: [ 2200000, 5000000 ]
|
||||
default: 2200000
|
||||
|
||||
awinic,ramp-ctl:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: >
|
||||
Select ramp control and filter of the device.
|
||||
0 = Fade in/fade out.
|
||||
1 = Light filter.
|
||||
2 = Medium filter.
|
||||
3 = Heavy filter.
|
||||
enum: [ 0, 1, 2, 3 ]
|
||||
default: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- enable-gpios
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
backlight@76 {
|
||||
compatible = "awinic,aw99706";
|
||||
reg = <0x76>;
|
||||
enable-gpios = <&tlmm 88 GPIO_ACTIVE_HIGH>;
|
||||
default-brightness = <2047>;
|
||||
max-brightness = <4095>;
|
||||
awinic,dim-mode = <1>;
|
||||
awinic,sw-freq-hz = <750000>;
|
||||
awinic,sw-ilmt-microamp = <3000000>;
|
||||
awinic,uvlo-thres-microvolt = <2200000>;
|
||||
awinic,iled-max-microamp = <20000>;
|
||||
awinic,ramp-ctl = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -4156,6 +4156,12 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml
|
||||
F: drivers/iio/adc/hx711.c
|
||||
|
||||
AWINIC AW99706 WLED BACKLIGHT DRIVER
|
||||
M: Junjie Cao <caojunjie650@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
|
||||
F: drivers/video/backlight/aw99706.c
|
||||
|
||||
AX.25 NETWORK LAYER
|
||||
L: linux-hams@vger.kernel.org
|
||||
S: Orphan
|
||||
|
|
|
|||
|
|
@ -156,6 +156,14 @@ config BACKLIGHT_ATMEL_LCDC
|
|||
If in doubt, it's safe to enable this option; it doesn't kick
|
||||
in unless the board's description says it's wired that way.
|
||||
|
||||
config BACKLIGHT_AW99706
|
||||
tristate "Backlight Driver for Awinic AW99706"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you have a LCD backlight connected to the WLED output of AW99706
|
||||
WLED output, say Y here to enable this driver.
|
||||
|
||||
config BACKLIGHT_EP93XX
|
||||
tristate "Cirrus EP93xx Backlight Driver"
|
||||
depends on FB_EP93XX
|
||||
|
|
@ -185,6 +193,7 @@ config BACKLIGHT_KTD253
|
|||
|
||||
config BACKLIGHT_KTD2801
|
||||
tristate "Backlight Driver for Kinetic KTD2801"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select LEDS_EXPRESSWIRE
|
||||
help
|
||||
Say Y to enable the backlight driver for the Kinetic KTD2801 1-wire
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
|
|||
obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
|
||||
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_CLASS_DEVICE) += backlight.o
|
||||
obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,471 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* aw99706 - Backlight driver for the AWINIC AW99706
|
||||
*
|
||||
* Copyright (C) 2025 Junjie Cao <caojunjie650@gmail.com>
|
||||
* Copyright (C) 2025 Pengyu Luo <mitltlatltl@gmail.com>
|
||||
*
|
||||
* Based on vendor driver:
|
||||
* Copyright (c) 2023 AWINIC Technology CO., LTD
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define AW99706_MAX_BRT_LVL 4095
|
||||
#define AW99706_REG_MAX 0x1F
|
||||
#define AW99706_ID 0x07
|
||||
|
||||
/* registers list */
|
||||
#define AW99706_CFG0_REG 0x00
|
||||
#define AW99706_DIM_MODE_MASK GENMASK(1, 0)
|
||||
|
||||
#define AW99706_CFG1_REG 0x01
|
||||
#define AW99706_SW_FREQ_MASK GENMASK(3, 0)
|
||||
#define AW99706_SW_ILMT_MASK GENMASK(5, 4)
|
||||
|
||||
#define AW99706_CFG2_REG 0x02
|
||||
#define AW99706_ILED_MAX_MASK GENMASK(6, 0)
|
||||
#define AW99706_UVLOSEL_MASK BIT(7)
|
||||
|
||||
#define AW99706_CFG3_REG 0x03
|
||||
#define AW99706_CFG4_REG 0x04
|
||||
#define AW99706_BRT_MSB_MASK GENMASK(3, 0)
|
||||
|
||||
#define AW99706_CFG5_REG 0x05
|
||||
#define AW99706_BRT_LSB_MASK GENMASK(7, 0)
|
||||
|
||||
#define AW99706_CFG6_REG 0x06
|
||||
#define AW99706_RAMP_CTL_MASK GENMASK(7, 6)
|
||||
|
||||
#define AW99706_CFG7_REG 0x07
|
||||
#define AW99706_CFG8_REG 0x08
|
||||
#define AW99706_CFG9_REG 0x09
|
||||
#define AW99706_CFGA_REG 0x0A
|
||||
#define AW99706_CFGB_REG 0x0B
|
||||
#define AW99706_CFGC_REG 0x0C
|
||||
#define AW99706_CFGD_REG 0x0D
|
||||
#define AW99706_FLAG_REG 0x10
|
||||
#define AW99706_BACKLIGHT_EN_MASK BIT(7)
|
||||
|
||||
#define AW99706_CHIPID_REG 0x11
|
||||
#define AW99706_LED_OPEN_FLAG_REG 0x12
|
||||
#define AW99706_LED_SHORT_FLAG_REG 0x13
|
||||
#define AW99706_MTPLDOSEL_REG 0x1E
|
||||
#define AW99706_MTPRUN_REG 0x1F
|
||||
|
||||
#define RESV 0
|
||||
|
||||
/* Boost switching frequency table, in Hz */
|
||||
static const u32 aw99706_sw_freq_tbl[] = {
|
||||
RESV, RESV, RESV, RESV, 300000, 400000, 500000, 600000,
|
||||
660000, 750000, 850000, 1000000, 1200000, 1330000, 1500000, 1700000
|
||||
};
|
||||
|
||||
/* Switching current limitation table, in uA */
|
||||
static const u32 aw99706_sw_ilmt_tbl[] = {
|
||||
1500000, 2000000, 2500000, 3000000
|
||||
};
|
||||
|
||||
/* ULVO threshold table, in uV */
|
||||
static const u32 aw99706_ulvo_thres_tbl[] = {
|
||||
2200000, 5000000
|
||||
};
|
||||
|
||||
struct aw99706_dt_prop {
|
||||
const char * const name;
|
||||
int (*lookup)(const struct aw99706_dt_prop *prop, u32 dt_val, u8 *val);
|
||||
const u32 * const lookup_tbl;
|
||||
u8 tbl_size;
|
||||
u8 reg;
|
||||
u8 mask;
|
||||
u32 def_val;
|
||||
};
|
||||
|
||||
static int aw99706_dt_property_lookup(const struct aw99706_dt_prop *prop,
|
||||
u32 dt_val, u8 *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!prop->lookup_tbl) {
|
||||
*val = dt_val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < prop->tbl_size; i++)
|
||||
if (prop->lookup_tbl[i] == dt_val)
|
||||
break;
|
||||
|
||||
*val = i;
|
||||
|
||||
return i == prop->tbl_size ? -1 : 0;
|
||||
}
|
||||
|
||||
#define MIN_ILED_MAX 5000
|
||||
#define MAX_ILED_MAX 50000
|
||||
#define STEP_ILED_MAX 500
|
||||
|
||||
static int
|
||||
aw99706_dt_property_iled_max_convert(const struct aw99706_dt_prop *prop,
|
||||
u32 dt_val, u8 *val)
|
||||
{
|
||||
if (dt_val > MAX_ILED_MAX || dt_val < MIN_ILED_MAX)
|
||||
return -1;
|
||||
|
||||
*val = (dt_val - MIN_ILED_MAX) / STEP_ILED_MAX;
|
||||
|
||||
return (dt_val - MIN_ILED_MAX) % STEP_ILED_MAX;
|
||||
}
|
||||
|
||||
static const struct aw99706_dt_prop aw99706_dt_props[] = {
|
||||
{
|
||||
"awinic,dim-mode", aw99706_dt_property_lookup,
|
||||
NULL, 0,
|
||||
AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1,
|
||||
},
|
||||
{
|
||||
"awinic,sw-freq", aw99706_dt_property_lookup,
|
||||
aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl),
|
||||
AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000,
|
||||
},
|
||||
{
|
||||
"awinic,sw-ilmt", aw99706_dt_property_lookup,
|
||||
aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl),
|
||||
AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000,
|
||||
},
|
||||
{
|
||||
"awinic,iled-max", aw99706_dt_property_iled_max_convert,
|
||||
NULL, 0,
|
||||
AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000,
|
||||
|
||||
},
|
||||
{
|
||||
"awinic,uvlo-thres", aw99706_dt_property_lookup,
|
||||
aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl),
|
||||
AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000,
|
||||
},
|
||||
{
|
||||
"awinic,ramp-ctl", aw99706_dt_property_lookup,
|
||||
NULL, 0,
|
||||
AW99706_CFG6_REG, AW99706_RAMP_CTL_MASK, 2,
|
||||
}
|
||||
};
|
||||
|
||||
struct reg_init_data {
|
||||
u8 reg;
|
||||
u8 mask;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
struct aw99706_device {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct backlight_device *bl_dev;
|
||||
struct gpio_desc *hwen_gpio;
|
||||
struct reg_init_data init_tbl[ARRAY_SIZE(aw99706_dt_props)];
|
||||
bool bl_enable;
|
||||
};
|
||||
|
||||
enum reg_access {
|
||||
REG_NONE_ACCESS = 0,
|
||||
REG_RD_ACCESS = 1,
|
||||
REG_WR_ACCESS = 2,
|
||||
};
|
||||
|
||||
static const u8 aw99706_regs[AW99706_REG_MAX + 1] = {
|
||||
[AW99706_CFG0_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG1_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG2_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG3_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG4_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG5_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG6_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG7_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG8_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFG9_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFGA_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFGB_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFGC_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_CFGD_REG] = REG_RD_ACCESS | REG_WR_ACCESS,
|
||||
[AW99706_FLAG_REG] = REG_RD_ACCESS,
|
||||
[AW99706_CHIPID_REG] = REG_RD_ACCESS,
|
||||
[AW99706_LED_OPEN_FLAG_REG] = REG_RD_ACCESS,
|
||||
[AW99706_LED_SHORT_FLAG_REG] = REG_RD_ACCESS,
|
||||
|
||||
/*
|
||||
* Write bit is dropped here, writing BIT(0) to MTPLDOSEL will unlock
|
||||
* Multi-time Programmable (MTP).
|
||||
*/
|
||||
[AW99706_MTPLDOSEL_REG] = REG_RD_ACCESS,
|
||||
[AW99706_MTPRUN_REG] = REG_NONE_ACCESS,
|
||||
};
|
||||
|
||||
static bool aw99706_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return aw99706_regs[reg] & REG_RD_ACCESS;
|
||||
}
|
||||
|
||||
static bool aw99706_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return aw99706_regs[reg] & REG_WR_ACCESS;
|
||||
}
|
||||
|
||||
static inline int aw99706_i2c_read(struct aw99706_device *aw, u8 reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
return regmap_read(aw->regmap, reg, val);
|
||||
}
|
||||
|
||||
static inline int aw99706_i2c_write(struct aw99706_device *aw, u8 reg, u8 val)
|
||||
{
|
||||
return regmap_write(aw->regmap, reg, val);
|
||||
}
|
||||
|
||||
static inline int aw99706_i2c_update_bits(struct aw99706_device *aw, u8 reg,
|
||||
u8 mask, u8 val)
|
||||
{
|
||||
return regmap_update_bits(aw->regmap, reg, mask, val);
|
||||
}
|
||||
|
||||
static void aw99706_dt_parse(struct aw99706_device *aw,
|
||||
struct backlight_properties *bl_props)
|
||||
{
|
||||
const struct aw99706_dt_prop *prop;
|
||||
u32 dt_val;
|
||||
int ret, i;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aw99706_dt_props); i++) {
|
||||
prop = &aw99706_dt_props[i];
|
||||
ret = device_property_read_u32(aw->dev, prop->name, &dt_val);
|
||||
if (ret < 0)
|
||||
dt_val = prop->def_val;
|
||||
|
||||
if (prop->lookup(prop, dt_val, &val)) {
|
||||
dev_warn(aw->dev, "invalid value %d for property %s, using default value %d\n",
|
||||
dt_val, prop->name, prop->def_val);
|
||||
|
||||
prop->lookup(prop, prop->def_val, &val);
|
||||
}
|
||||
|
||||
aw->init_tbl[i].reg = prop->reg;
|
||||
aw->init_tbl[i].mask = prop->mask;
|
||||
aw->init_tbl[i].val = val << __ffs(prop->mask);
|
||||
}
|
||||
|
||||
bl_props->brightness = AW99706_MAX_BRT_LVL >> 1;
|
||||
bl_props->max_brightness = AW99706_MAX_BRT_LVL;
|
||||
device_property_read_u32(aw->dev, "default-brightness",
|
||||
&bl_props->brightness);
|
||||
device_property_read_u32(aw->dev, "max-brightness",
|
||||
&bl_props->max_brightness);
|
||||
|
||||
if (bl_props->max_brightness > AW99706_MAX_BRT_LVL)
|
||||
bl_props->max_brightness = AW99706_MAX_BRT_LVL;
|
||||
|
||||
if (bl_props->brightness > bl_props->max_brightness)
|
||||
bl_props->brightness = bl_props->max_brightness;
|
||||
}
|
||||
|
||||
static int aw99706_hw_init(struct aw99706_device *aw)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
gpiod_set_value_cansleep(aw->hwen_gpio, 1);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aw->init_tbl); i++) {
|
||||
ret = aw99706_i2c_update_bits(aw, aw->init_tbl[i].reg,
|
||||
aw->init_tbl[i].mask,
|
||||
aw->init_tbl[i].val);
|
||||
if (ret < 0) {
|
||||
dev_err(aw->dev, "Failed to write init data %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aw99706_bl_enable(struct aw99706_device *aw, bool en)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
val = FIELD_PREP(AW99706_BACKLIGHT_EN_MASK, en);
|
||||
ret = aw99706_i2c_update_bits(aw, AW99706_CFGD_REG,
|
||||
AW99706_BACKLIGHT_EN_MASK, val);
|
||||
if (ret)
|
||||
dev_err(aw->dev, "Failed to enable backlight!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw99706_update_brightness(struct aw99706_device *aw, u32 brt_lvl)
|
||||
{
|
||||
bool bl_enable_now = !!brt_lvl;
|
||||
int ret;
|
||||
|
||||
ret = aw99706_i2c_write(aw, AW99706_CFG4_REG,
|
||||
(brt_lvl >> 8) & AW99706_BRT_MSB_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = aw99706_i2c_write(aw, AW99706_CFG5_REG,
|
||||
brt_lvl & AW99706_BRT_LSB_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (aw->bl_enable != bl_enable_now) {
|
||||
ret = aw99706_bl_enable(aw, bl_enable_now);
|
||||
if (!ret)
|
||||
aw->bl_enable = bl_enable_now;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw99706_bl_update_status(struct backlight_device *bl)
|
||||
{
|
||||
struct aw99706_device *aw = bl_get_data(bl);
|
||||
|
||||
return aw99706_update_brightness(aw, bl->props.brightness);
|
||||
}
|
||||
|
||||
static const struct backlight_ops aw99706_bl_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.update_status = aw99706_bl_update_status,
|
||||
};
|
||||
|
||||
static const struct regmap_config aw99706_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = AW99706_REG_MAX,
|
||||
.writeable_reg = aw99706_writeable_reg,
|
||||
.readable_reg = aw99706_readable_reg,
|
||||
};
|
||||
|
||||
static int aw99706_chip_id_read(struct aw99706_device *aw)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = aw99706_i2c_read(aw, AW99706_CHIPID_REG, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int aw99706_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct aw99706_device *aw;
|
||||
struct backlight_device *bl_dev;
|
||||
struct backlight_properties props = {};
|
||||
int ret = 0;
|
||||
|
||||
aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL);
|
||||
if (!aw)
|
||||
return -ENOMEM;
|
||||
|
||||
aw->client = client;
|
||||
aw->dev = dev;
|
||||
i2c_set_clientdata(client, aw);
|
||||
|
||||
aw->regmap = devm_regmap_init_i2c(client, &aw99706_regmap_config);
|
||||
if (IS_ERR(aw->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(aw->regmap),
|
||||
"Failed to init regmap\n");
|
||||
|
||||
ret = aw99706_chip_id_read(aw);
|
||||
if (ret != AW99706_ID)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"Unknown chip id 0x%02x\n", ret);
|
||||
|
||||
aw99706_dt_parse(aw, &props);
|
||||
|
||||
aw->hwen_gpio = devm_gpiod_get(aw->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(aw->hwen_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(aw->hwen_gpio),
|
||||
"Failed to get enable gpio\n");
|
||||
|
||||
ret = aw99706_hw_init(aw);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to initialize the chip\n");
|
||||
|
||||
props.type = BACKLIGHT_RAW;
|
||||
props.scale = BACKLIGHT_SCALE_LINEAR;
|
||||
|
||||
bl_dev = devm_backlight_device_register(dev, "aw99706-backlight", dev,
|
||||
aw, &aw99706_bl_ops, &props);
|
||||
if (IS_ERR(bl_dev))
|
||||
return dev_err_probe(dev, PTR_ERR(bl_dev),
|
||||
"Failed to register backlight!\n");
|
||||
|
||||
aw->bl_dev = bl_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aw99706_remove(struct i2c_client *client)
|
||||
{
|
||||
struct aw99706_device *aw = i2c_get_clientdata(client);
|
||||
|
||||
aw99706_update_brightness(aw, 0);
|
||||
|
||||
msleep(50);
|
||||
|
||||
gpiod_set_value_cansleep(aw->hwen_gpio, 0);
|
||||
}
|
||||
|
||||
static int aw99706_suspend(struct device *dev)
|
||||
{
|
||||
struct aw99706_device *aw = dev_get_drvdata(dev);
|
||||
|
||||
return aw99706_update_brightness(aw, 0);
|
||||
}
|
||||
|
||||
static int aw99706_resume(struct device *dev)
|
||||
{
|
||||
struct aw99706_device *aw = dev_get_drvdata(dev);
|
||||
|
||||
return aw99706_hw_init(aw);
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(aw99706_pm_ops, aw99706_suspend, aw99706_resume);
|
||||
|
||||
static const struct i2c_device_id aw99706_ids[] = {
|
||||
{ "aw99706" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, aw99706_ids);
|
||||
|
||||
static const struct of_device_id aw99706_match_table[] = {
|
||||
{ .compatible = "awinic,aw99706", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aw99706_match_table);
|
||||
|
||||
static struct i2c_driver aw99706_i2c_driver = {
|
||||
.probe = aw99706_probe,
|
||||
.remove = aw99706_remove,
|
||||
.id_table = aw99706_ids,
|
||||
.driver = {
|
||||
.name = "aw99706",
|
||||
.of_match_table = aw99706_match_table,
|
||||
.pm = pm_ptr(&aw99706_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(aw99706_i2c_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("BackLight driver for aw99706");
|
||||
|
|
@ -210,6 +210,19 @@ static int led_bl_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(priv->bl_dev);
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->nb_leds; i++) {
|
||||
struct device_link *link;
|
||||
|
||||
link = device_link_add(&pdev->dev, priv->leds[i]->dev->parent,
|
||||
DL_FLAG_AUTOREMOVE_CONSUMER);
|
||||
if (!link) {
|
||||
dev_err(&pdev->dev, "Failed to add devlink (consumer %s, supplier %s)\n",
|
||||
dev_name(&pdev->dev), dev_name(priv->leds[i]->dev->parent));
|
||||
backlight_device_unregister(priv->bl_dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->nb_leds; i++) {
|
||||
mutex_lock(&priv->leds[i]->led_access);
|
||||
led_sysfs_disable(priv->leds[i]);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#define _LINUX_BACKLIGHT_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -124,12 +124,12 @@ struct lp855x_rom_data {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct lp855x_platform_data
|
||||
* struct lp855x_platform_data - lp855 platform-specific data
|
||||
* @name : Backlight driver name. If it is not defined, default name is set.
|
||||
* @device_control : value of DEVICE CONTROL register
|
||||
* @initial_brightness : initial value of backlight brightness
|
||||
* @period_ns : platform specific pwm period value. unit is nano.
|
||||
Only valid when mode is PWM_BASED.
|
||||
* Only valid when mode is PWM_BASED.
|
||||
* @size_program : total size of lp855x_rom_data
|
||||
* @rom_data : list of new eeprom/eprom registers
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue