rt4831-backlight.c (5256B)
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 27struct rt4831_priv { 28 struct device *dev; 29 struct regmap *regmap; 30 struct backlight_device *bl; 31}; 32 33static 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 54static 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 77static 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 83static 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 137static 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 175static 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 186static const struct of_device_id __maybe_unused rt4831_bl_of_match[] = { 187 { .compatible = "richtek,rt4831-backlight", }, 188 {} 189}; 190MODULE_DEVICE_TABLE(of, rt4831_bl_of_match); 191 192static 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}; 200module_platform_driver(rt4831_bl_driver); 201 202MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 203MODULE_LICENSE("GPL v2");