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"); 204