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