Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

backlight: rt4831: Adds support for Richtek RT4831 backlight

Adds support for Richtek RT4831 backlight.

Signed-off-by: ChiYuan Huang <cy_huang@richtek.com>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

ChiYuan Huang and committed by
Lee Jones
190ccab3 f3e6c298

+212
+8
drivers/video/backlight/Kconfig
··· 289 289 If you have the Qualcomm PMIC, say Y to enable a driver for the 290 290 WLED block. Currently it supports PM8941 and PMI8998. 291 291 292 + config BACKLIGHT_RT4831 293 + tristate "Richtek RT4831 Backlight Driver" 294 + depends on MFD_RT4831 295 + help 296 + This enables support for Richtek RT4831 Backlight driver. 297 + It's commonly used to drive the display WLED. There're four channels 298 + inisde, and each channel can provide up to 30mA current. 299 + 292 300 config BACKLIGHT_SAHARA 293 301 tristate "Tabletkiosk Sahara Touch-iT Backlight Driver" 294 302 depends on X86
+1
drivers/video/backlight/Makefile
··· 49 49 obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o 50 50 obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o 51 51 obj-$(CONFIG_BACKLIGHT_QCOM_WLED) += qcom-wled.o 52 + obj-$(CONFIG_BACKLIGHT_RT4831) += rt4831-backlight.o 52 53 obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o 53 54 obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o 54 55 obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o
+203
drivers/video/backlight/rt4831-backlight.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <dt-bindings/leds/rt4831-backlight.h> 4 + #include <linux/backlight.h> 5 + #include <linux/bitops.h> 6 + #include <linux/kernel.h> 7 + #include <linux/module.h> 8 + #include <linux/platform_device.h> 9 + #include <linux/property.h> 10 + #include <linux/regmap.h> 11 + 12 + #define RT4831_REG_BLCFG 0x02 13 + #define RT4831_REG_BLDIML 0x04 14 + #define RT4831_REG_ENABLE 0x08 15 + 16 + #define RT4831_BLMAX_BRIGHTNESS 2048 17 + 18 + #define RT4831_BLOVP_MASK GENMASK(7, 5) 19 + #define RT4831_BLOVP_SHIFT 5 20 + #define RT4831_BLPWMEN_MASK BIT(0) 21 + #define RT4831_BLEN_MASK BIT(4) 22 + #define RT4831_BLCH_MASK GENMASK(3, 0) 23 + #define RT4831_BLDIML_MASK GENMASK(2, 0) 24 + #define RT4831_BLDIMH_MASK GENMASK(10, 3) 25 + #define RT4831_BLDIMH_SHIFT 3 26 + 27 + struct rt4831_priv { 28 + struct device *dev; 29 + struct regmap *regmap; 30 + struct backlight_device *bl; 31 + }; 32 + 33 + static int rt4831_bl_update_status(struct backlight_device *bl_dev) 34 + { 35 + struct rt4831_priv *priv = bl_get_data(bl_dev); 36 + int brightness = backlight_get_brightness(bl_dev); 37 + unsigned int enable = brightness ? RT4831_BLEN_MASK : 0; 38 + u8 v[2]; 39 + int ret; 40 + 41 + if (brightness) { 42 + v[0] = (brightness - 1) & RT4831_BLDIML_MASK; 43 + v[1] = ((brightness - 1) & RT4831_BLDIMH_MASK) >> RT4831_BLDIMH_SHIFT; 44 + 45 + ret = regmap_raw_write(priv->regmap, RT4831_REG_BLDIML, v, sizeof(v)); 46 + if (ret) 47 + return ret; 48 + } 49 + 50 + return regmap_update_bits(priv->regmap, RT4831_REG_ENABLE, RT4831_BLEN_MASK, enable); 51 + 52 + } 53 + 54 + static int rt4831_bl_get_brightness(struct backlight_device *bl_dev) 55 + { 56 + struct rt4831_priv *priv = bl_get_data(bl_dev); 57 + unsigned int val; 58 + u8 v[2]; 59 + int ret; 60 + 61 + ret = regmap_read(priv->regmap, RT4831_REG_ENABLE, &val); 62 + if (ret) 63 + return ret; 64 + 65 + if (!(val & RT4831_BLEN_MASK)) 66 + return 0; 67 + 68 + ret = regmap_raw_read(priv->regmap, RT4831_REG_BLDIML, v, sizeof(v)); 69 + if (ret) 70 + return ret; 71 + 72 + ret = (v[1] << RT4831_BLDIMH_SHIFT) + (v[0] & RT4831_BLDIML_MASK) + 1; 73 + 74 + return ret; 75 + } 76 + 77 + static const struct backlight_ops rt4831_bl_ops = { 78 + .options = BL_CORE_SUSPENDRESUME, 79 + .update_status = rt4831_bl_update_status, 80 + .get_brightness = rt4831_bl_get_brightness, 81 + }; 82 + 83 + static int rt4831_parse_backlight_properties(struct rt4831_priv *priv, 84 + struct backlight_properties *bl_props) 85 + { 86 + struct device *dev = priv->dev; 87 + u8 propval; 88 + u32 brightness; 89 + unsigned int val = 0; 90 + int ret; 91 + 92 + /* common properties */ 93 + ret = device_property_read_u32(dev, "max-brightness", &brightness); 94 + if (ret) 95 + brightness = RT4831_BLMAX_BRIGHTNESS; 96 + 97 + bl_props->max_brightness = min_t(u32, brightness, RT4831_BLMAX_BRIGHTNESS); 98 + 99 + ret = device_property_read_u32(dev, "default-brightness", &brightness); 100 + if (ret) 101 + brightness = bl_props->max_brightness; 102 + 103 + bl_props->brightness = min_t(u32, brightness, bl_props->max_brightness); 104 + 105 + /* vendor properties */ 106 + if (device_property_read_bool(dev, "richtek,pwm-enable")) 107 + val = RT4831_BLPWMEN_MASK; 108 + 109 + ret = regmap_update_bits(priv->regmap, RT4831_REG_BLCFG, RT4831_BLPWMEN_MASK, val); 110 + if (ret) 111 + return ret; 112 + 113 + ret = device_property_read_u8(dev, "richtek,bled-ovp-sel", &propval); 114 + if (ret) 115 + propval = RT4831_BLOVPLVL_21V; 116 + 117 + propval = min_t(u8, propval, RT4831_BLOVPLVL_29V); 118 + ret = regmap_update_bits(priv->regmap, RT4831_REG_BLCFG, RT4831_BLOVP_MASK, 119 + propval << RT4831_BLOVP_SHIFT); 120 + if (ret) 121 + return ret; 122 + 123 + ret = device_property_read_u8(dev, "richtek,channel-use", &propval); 124 + if (ret) { 125 + dev_err(dev, "richtek,channel-use DT property missing\n"); 126 + return ret; 127 + } 128 + 129 + if (!(propval & RT4831_BLCH_MASK)) { 130 + dev_err(dev, "No channel specified\n"); 131 + return -EINVAL; 132 + } 133 + 134 + return regmap_update_bits(priv->regmap, RT4831_REG_ENABLE, RT4831_BLCH_MASK, propval); 135 + } 136 + 137 + static int rt4831_bl_probe(struct platform_device *pdev) 138 + { 139 + struct rt4831_priv *priv; 140 + struct backlight_properties bl_props = { .type = BACKLIGHT_RAW, 141 + .scale = BACKLIGHT_SCALE_LINEAR }; 142 + int ret; 143 + 144 + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 145 + if (!priv) 146 + return -ENOMEM; 147 + 148 + priv->dev = &pdev->dev; 149 + 150 + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); 151 + if (!priv->regmap) { 152 + dev_err(&pdev->dev, "Failed to init regmap\n"); 153 + return -ENODEV; 154 + } 155 + 156 + ret = rt4831_parse_backlight_properties(priv, &bl_props); 157 + if (ret) { 158 + dev_err(&pdev->dev, "Failed to parse backlight properties\n"); 159 + return ret; 160 + } 161 + 162 + priv->bl = devm_backlight_device_register(&pdev->dev, pdev->name, &pdev->dev, priv, 163 + &rt4831_bl_ops, &bl_props); 164 + if (IS_ERR(priv->bl)) { 165 + dev_err(&pdev->dev, "Failed to register backlight\n"); 166 + return PTR_ERR(priv->bl); 167 + } 168 + 169 + backlight_update_status(priv->bl); 170 + platform_set_drvdata(pdev, priv); 171 + 172 + return 0; 173 + } 174 + 175 + static int rt4831_bl_remove(struct platform_device *pdev) 176 + { 177 + struct rt4831_priv *priv = platform_get_drvdata(pdev); 178 + struct backlight_device *bl_dev = priv->bl; 179 + 180 + bl_dev->props.brightness = 0; 181 + backlight_update_status(priv->bl); 182 + 183 + return 0; 184 + } 185 + 186 + static const struct of_device_id __maybe_unused rt4831_bl_of_match[] = { 187 + { .compatible = "richtek,rt4831-backlight", }, 188 + {} 189 + }; 190 + MODULE_DEVICE_TABLE(of, rt4831_bl_of_match); 191 + 192 + static struct platform_driver rt4831_bl_driver = { 193 + .driver = { 194 + .name = "rt4831-backlight", 195 + .of_match_table = rt4831_bl_of_match, 196 + }, 197 + .probe = rt4831_bl_probe, 198 + .remove = rt4831_bl_remove, 199 + }; 200 + module_platform_driver(rt4831_bl_driver); 201 + 202 + MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 203 + MODULE_LICENSE("GPL v2");