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/mod_devicetable.h> 8 #include <linux/module.h> 9 #include <linux/platform_device.h> 10 #include <linux/property.h> 11 #include <linux/regmap.h> 12 13 #define RT4831_REG_BLCFG 0x02 14 #define RT4831_REG_BLDIML 0x04 15 #define RT4831_REG_ENABLE 0x08 16 #define RT4831_REG_BLOPT2 0x11 17 18 #define RT4831_BLMAX_BRIGHTNESS 2048 19 20 #define RT4831_BLOVP_MASK GENMASK(7, 5) 21 #define RT4831_BLOVP_SHIFT 5 22 #define RT4831_BLPWMEN_MASK BIT(0) 23 #define RT4831_BLEN_MASK BIT(4) 24 #define RT4831_BLCH_MASK GENMASK(3, 0) 25 #define RT4831_BLDIML_MASK GENMASK(2, 0) 26 #define RT4831_BLDIMH_MASK GENMASK(10, 3) 27 #define RT4831_BLDIMH_SHIFT 3 28 #define RT4831_BLOCP_MASK GENMASK(1, 0) 29 30 #define RT4831_BLOCP_MINUA 900000 31 #define RT4831_BLOCP_MAXUA 1800000 32 #define RT4831_BLOCP_STEPUA 300000 33 34 struct rt4831_priv { 35 struct device *dev; 36 struct regmap *regmap; 37 struct backlight_device *bl; 38 }; 39 40 static int rt4831_bl_update_status(struct backlight_device *bl_dev) 41 { 42 struct rt4831_priv *priv = bl_get_data(bl_dev); 43 int brightness = backlight_get_brightness(bl_dev); 44 unsigned int enable = brightness ? RT4831_BLEN_MASK : 0; 45 u8 v[2]; 46 int ret; 47 48 if (brightness) { 49 v[0] = (brightness - 1) & RT4831_BLDIML_MASK; 50 v[1] = ((brightness - 1) & RT4831_BLDIMH_MASK) >> RT4831_BLDIMH_SHIFT; 51 52 ret = regmap_raw_write(priv->regmap, RT4831_REG_BLDIML, v, sizeof(v)); 53 if (ret) 54 return ret; 55 } 56 57 return regmap_update_bits(priv->regmap, RT4831_REG_ENABLE, RT4831_BLEN_MASK, enable); 58 59 } 60 61 static int rt4831_bl_get_brightness(struct backlight_device *bl_dev) 62 { 63 struct rt4831_priv *priv = bl_get_data(bl_dev); 64 unsigned int val; 65 u8 v[2]; 66 int ret; 67 68 ret = regmap_read(priv->regmap, RT4831_REG_ENABLE, &val); 69 if (ret) 70 return ret; 71 72 if (!(val & RT4831_BLEN_MASK)) 73 return 0; 74 75 ret = regmap_raw_read(priv->regmap, RT4831_REG_BLDIML, v, sizeof(v)); 76 if (ret) 77 return ret; 78 79 ret = (v[1] << RT4831_BLDIMH_SHIFT) + (v[0] & RT4831_BLDIML_MASK) + 1; 80 81 return ret; 82 } 83 84 static const struct backlight_ops rt4831_bl_ops = { 85 .options = BL_CORE_SUSPENDRESUME, 86 .update_status = rt4831_bl_update_status, 87 .get_brightness = rt4831_bl_get_brightness, 88 }; 89 90 static int rt4831_parse_backlight_properties(struct rt4831_priv *priv, 91 struct backlight_properties *bl_props) 92 { 93 struct device *dev = priv->dev; 94 u8 propval; 95 u32 brightness, ocp_uA; 96 unsigned int val = 0; 97 int ret; 98 99 /* common properties */ 100 ret = device_property_read_u32(dev, "max-brightness", &brightness); 101 if (ret) 102 brightness = RT4831_BLMAX_BRIGHTNESS; 103 104 bl_props->max_brightness = min_t(u32, brightness, RT4831_BLMAX_BRIGHTNESS); 105 106 ret = device_property_read_u32(dev, "default-brightness", &brightness); 107 if (ret) 108 brightness = bl_props->max_brightness; 109 110 bl_props->brightness = min_t(u32, brightness, bl_props->max_brightness); 111 112 /* vendor properties */ 113 if (device_property_read_bool(dev, "richtek,pwm-enable")) 114 val = RT4831_BLPWMEN_MASK; 115 116 ret = regmap_update_bits(priv->regmap, RT4831_REG_BLCFG, RT4831_BLPWMEN_MASK, val); 117 if (ret) 118 return ret; 119 120 ret = device_property_read_u8(dev, "richtek,bled-ovp-sel", &propval); 121 if (ret) 122 propval = RT4831_BLOVPLVL_21V; 123 124 propval = min_t(u8, propval, RT4831_BLOVPLVL_29V); 125 ret = regmap_update_bits(priv->regmap, RT4831_REG_BLCFG, RT4831_BLOVP_MASK, 126 propval << RT4831_BLOVP_SHIFT); 127 if (ret) 128 return ret; 129 130 /* 131 * This OCP level is used to protect and limit the inductor current. 132 * If inductor peak current reach the level, low-side MOSFET will be 133 * turned off. Meanwhile, the output channel current may be limited. 134 * To match the configured channel current, the inductor chosen must 135 * be higher than the OCP level. 136 * 137 * Not like the OVP level, the default 21V can be used in the most 138 * application. But if the chosen OCP level is smaller than needed, 139 * it will also affect the backlight channel output current to be 140 * smaller than the register setting. 141 */ 142 ret = device_property_read_u32(dev, "richtek,bled-ocp-microamp", 143 &ocp_uA); 144 if (!ret) { 145 ocp_uA = clamp_val(ocp_uA, RT4831_BLOCP_MINUA, 146 RT4831_BLOCP_MAXUA); 147 val = DIV_ROUND_UP(ocp_uA - RT4831_BLOCP_MINUA, 148 RT4831_BLOCP_STEPUA); 149 ret = regmap_update_bits(priv->regmap, RT4831_REG_BLOPT2, 150 RT4831_BLOCP_MASK, val); 151 if (ret) 152 return ret; 153 } 154 155 ret = device_property_read_u8(dev, "richtek,channel-use", &propval); 156 if (ret) { 157 dev_err(dev, "richtek,channel-use DT property missing\n"); 158 return ret; 159 } 160 161 if (!(propval & RT4831_BLCH_MASK)) { 162 dev_err(dev, "No channel specified\n"); 163 return -EINVAL; 164 } 165 166 return regmap_update_bits(priv->regmap, RT4831_REG_ENABLE, RT4831_BLCH_MASK, propval); 167 } 168 169 static int rt4831_bl_probe(struct platform_device *pdev) 170 { 171 struct rt4831_priv *priv; 172 struct backlight_properties bl_props = { .type = BACKLIGHT_RAW, 173 .scale = BACKLIGHT_SCALE_LINEAR }; 174 int ret; 175 176 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 177 if (!priv) 178 return -ENOMEM; 179 180 priv->dev = &pdev->dev; 181 182 priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); 183 if (!priv->regmap) { 184 dev_err(&pdev->dev, "Failed to init regmap\n"); 185 return -ENODEV; 186 } 187 188 ret = rt4831_parse_backlight_properties(priv, &bl_props); 189 if (ret) { 190 dev_err(&pdev->dev, "Failed to parse backlight properties\n"); 191 return ret; 192 } 193 194 priv->bl = devm_backlight_device_register(&pdev->dev, pdev->name, &pdev->dev, priv, 195 &rt4831_bl_ops, &bl_props); 196 if (IS_ERR(priv->bl)) { 197 dev_err(&pdev->dev, "Failed to register backlight\n"); 198 return PTR_ERR(priv->bl); 199 } 200 201 backlight_update_status(priv->bl); 202 platform_set_drvdata(pdev, priv); 203 204 return 0; 205 } 206 207 static void rt4831_bl_remove(struct platform_device *pdev) 208 { 209 struct rt4831_priv *priv = platform_get_drvdata(pdev); 210 struct backlight_device *bl_dev = priv->bl; 211 212 bl_dev->props.brightness = 0; 213 backlight_update_status(priv->bl); 214 } 215 216 static const struct of_device_id __maybe_unused rt4831_bl_of_match[] = { 217 { .compatible = "richtek,rt4831-backlight", }, 218 {} 219 }; 220 MODULE_DEVICE_TABLE(of, rt4831_bl_of_match); 221 222 static struct platform_driver rt4831_bl_driver = { 223 .driver = { 224 .name = "rt4831-backlight", 225 .of_match_table = rt4831_bl_of_match, 226 }, 227 .probe = rt4831_bl_probe, 228 .remove = rt4831_bl_remove, 229 }; 230 module_platform_driver(rt4831_bl_driver); 231 232 MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 233 MODULE_DESCRIPTION("Richtek RT4831 Backlight Driver"); 234 MODULE_LICENSE("GPL v2"); 235