Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs
0004  *
0005  * Copyright 2009 Analog Devices Inc.
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/init.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/fb.h>
0012 #include <linux/backlight.h>
0013 #include <linux/mfd/adp5520.h>
0014 #include <linux/slab.h>
0015 #include <linux/module.h>
0016 
0017 struct adp5520_bl {
0018     struct device *master;
0019     struct adp5520_backlight_platform_data *pdata;
0020     struct mutex lock;
0021     unsigned long cached_daylight_max;
0022     int id;
0023     int current_brightness;
0024 };
0025 
0026 static int adp5520_bl_set(struct backlight_device *bl, int brightness)
0027 {
0028     struct adp5520_bl *data = bl_get_data(bl);
0029     struct device *master = data->master;
0030     int ret = 0;
0031 
0032     if (data->pdata->en_ambl_sens) {
0033         if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) {
0034             /* Disable Ambient Light auto adjust */
0035             ret |= adp5520_clr_bits(master, ADP5520_BL_CONTROL,
0036                     ADP5520_BL_AUTO_ADJ);
0037             ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
0038                     brightness);
0039         } else {
0040             /*
0041              * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
0042              * restore daylight l3 sysfs brightness
0043              */
0044             ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
0045                      data->cached_daylight_max);
0046             ret |= adp5520_set_bits(master, ADP5520_BL_CONTROL,
0047                      ADP5520_BL_AUTO_ADJ);
0048         }
0049     } else {
0050         ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, brightness);
0051     }
0052 
0053     if (data->current_brightness && brightness == 0)
0054         ret |= adp5520_set_bits(master,
0055                 ADP5520_MODE_STATUS, ADP5520_DIM_EN);
0056     else if (data->current_brightness == 0 && brightness)
0057         ret |= adp5520_clr_bits(master,
0058                 ADP5520_MODE_STATUS, ADP5520_DIM_EN);
0059 
0060     if (!ret)
0061         data->current_brightness = brightness;
0062 
0063     return ret;
0064 }
0065 
0066 static int adp5520_bl_update_status(struct backlight_device *bl)
0067 {
0068     return adp5520_bl_set(bl, backlight_get_brightness(bl));
0069 }
0070 
0071 static int adp5520_bl_get_brightness(struct backlight_device *bl)
0072 {
0073     struct adp5520_bl *data = bl_get_data(bl);
0074     int error;
0075     uint8_t reg_val;
0076 
0077     error = adp5520_read(data->master, ADP5520_BL_VALUE, &reg_val);
0078 
0079     return error ? data->current_brightness : reg_val;
0080 }
0081 
0082 static const struct backlight_ops adp5520_bl_ops = {
0083     .update_status  = adp5520_bl_update_status,
0084     .get_brightness = adp5520_bl_get_brightness,
0085 };
0086 
0087 static int adp5520_bl_setup(struct backlight_device *bl)
0088 {
0089     struct adp5520_bl *data = bl_get_data(bl);
0090     struct device *master = data->master;
0091     struct adp5520_backlight_platform_data *pdata = data->pdata;
0092     int ret = 0;
0093 
0094     ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
0095                 pdata->l1_daylight_max);
0096     ret |= adp5520_write(master, ADP5520_DAYLIGHT_DIM,
0097                 pdata->l1_daylight_dim);
0098 
0099     if (pdata->en_ambl_sens) {
0100         data->cached_daylight_max = pdata->l1_daylight_max;
0101         ret |= adp5520_write(master, ADP5520_OFFICE_MAX,
0102                 pdata->l2_office_max);
0103         ret |= adp5520_write(master, ADP5520_OFFICE_DIM,
0104                 pdata->l2_office_dim);
0105         ret |= adp5520_write(master, ADP5520_DARK_MAX,
0106                 pdata->l3_dark_max);
0107         ret |= adp5520_write(master, ADP5520_DARK_DIM,
0108                 pdata->l3_dark_dim);
0109         ret |= adp5520_write(master, ADP5520_L2_TRIP,
0110                 pdata->l2_trip);
0111         ret |= adp5520_write(master, ADP5520_L2_HYS,
0112                 pdata->l2_hyst);
0113         ret |= adp5520_write(master, ADP5520_L3_TRIP,
0114                  pdata->l3_trip);
0115         ret |= adp5520_write(master, ADP5520_L3_HYS,
0116                 pdata->l3_hyst);
0117         ret |= adp5520_write(master, ADP5520_ALS_CMPR_CFG,
0118                 ALS_CMPR_CFG_VAL(pdata->abml_filt,
0119                 ADP5520_L3_EN));
0120     }
0121 
0122     ret |= adp5520_write(master, ADP5520_BL_CONTROL,
0123             BL_CTRL_VAL(pdata->fade_led_law,
0124                     pdata->en_ambl_sens));
0125 
0126     ret |= adp5520_write(master, ADP5520_BL_FADE, FADE_VAL(pdata->fade_in,
0127             pdata->fade_out));
0128 
0129     ret |= adp5520_set_bits(master, ADP5520_MODE_STATUS,
0130             ADP5520_BL_EN | ADP5520_DIM_EN);
0131 
0132     return ret;
0133 }
0134 
0135 static ssize_t adp5520_show(struct device *dev, char *buf, int reg)
0136 {
0137     struct adp5520_bl *data = dev_get_drvdata(dev);
0138     int ret;
0139     uint8_t reg_val;
0140 
0141     mutex_lock(&data->lock);
0142     ret = adp5520_read(data->master, reg, &reg_val);
0143     mutex_unlock(&data->lock);
0144 
0145     if (ret < 0)
0146         return ret;
0147 
0148     return sprintf(buf, "%u\n", reg_val);
0149 }
0150 
0151 static ssize_t adp5520_store(struct device *dev, const char *buf,
0152              size_t count, int reg)
0153 {
0154     struct adp5520_bl *data = dev_get_drvdata(dev);
0155     unsigned long val;
0156     int ret;
0157 
0158     ret = kstrtoul(buf, 10, &val);
0159     if (ret)
0160         return ret;
0161 
0162     mutex_lock(&data->lock);
0163     adp5520_write(data->master, reg, val);
0164     mutex_unlock(&data->lock);
0165 
0166     return count;
0167 }
0168 
0169 static ssize_t adp5520_bl_dark_max_show(struct device *dev,
0170             struct device_attribute *attr, char *buf)
0171 {
0172     return adp5520_show(dev, buf, ADP5520_DARK_MAX);
0173 }
0174 
0175 static ssize_t adp5520_bl_dark_max_store(struct device *dev,
0176             struct device_attribute *attr,
0177             const char *buf, size_t count)
0178 {
0179     return adp5520_store(dev, buf, count, ADP5520_DARK_MAX);
0180 }
0181 static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show,
0182             adp5520_bl_dark_max_store);
0183 
0184 static ssize_t adp5520_bl_office_max_show(struct device *dev,
0185             struct device_attribute *attr, char *buf)
0186 {
0187     return adp5520_show(dev, buf, ADP5520_OFFICE_MAX);
0188 }
0189 
0190 static ssize_t adp5520_bl_office_max_store(struct device *dev,
0191             struct device_attribute *attr,
0192             const char *buf, size_t count)
0193 {
0194     return adp5520_store(dev, buf, count, ADP5520_OFFICE_MAX);
0195 }
0196 static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
0197             adp5520_bl_office_max_store);
0198 
0199 static ssize_t adp5520_bl_daylight_max_show(struct device *dev,
0200             struct device_attribute *attr, char *buf)
0201 {
0202     return adp5520_show(dev, buf, ADP5520_DAYLIGHT_MAX);
0203 }
0204 
0205 static ssize_t adp5520_bl_daylight_max_store(struct device *dev,
0206             struct device_attribute *attr,
0207             const char *buf, size_t count)
0208 {
0209     struct adp5520_bl *data = dev_get_drvdata(dev);
0210     int ret;
0211 
0212     ret = kstrtoul(buf, 10, &data->cached_daylight_max);
0213     if (ret < 0)
0214         return ret;
0215 
0216     return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_MAX);
0217 }
0218 static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show,
0219             adp5520_bl_daylight_max_store);
0220 
0221 static ssize_t adp5520_bl_dark_dim_show(struct device *dev,
0222             struct device_attribute *attr, char *buf)
0223 {
0224     return adp5520_show(dev, buf, ADP5520_DARK_DIM);
0225 }
0226 
0227 static ssize_t adp5520_bl_dark_dim_store(struct device *dev,
0228             struct device_attribute *attr,
0229             const char *buf, size_t count)
0230 {
0231     return adp5520_store(dev, buf, count, ADP5520_DARK_DIM);
0232 }
0233 static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show,
0234             adp5520_bl_dark_dim_store);
0235 
0236 static ssize_t adp5520_bl_office_dim_show(struct device *dev,
0237             struct device_attribute *attr, char *buf)
0238 {
0239     return adp5520_show(dev, buf, ADP5520_OFFICE_DIM);
0240 }
0241 
0242 static ssize_t adp5520_bl_office_dim_store(struct device *dev,
0243             struct device_attribute *attr,
0244             const char *buf, size_t count)
0245 {
0246     return adp5520_store(dev, buf, count, ADP5520_OFFICE_DIM);
0247 }
0248 static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show,
0249             adp5520_bl_office_dim_store);
0250 
0251 static ssize_t adp5520_bl_daylight_dim_show(struct device *dev,
0252             struct device_attribute *attr, char *buf)
0253 {
0254     return adp5520_show(dev, buf, ADP5520_DAYLIGHT_DIM);
0255 }
0256 
0257 static ssize_t adp5520_bl_daylight_dim_store(struct device *dev,
0258             struct device_attribute *attr,
0259             const char *buf, size_t count)
0260 {
0261     return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_DIM);
0262 }
0263 static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show,
0264             adp5520_bl_daylight_dim_store);
0265 
0266 static struct attribute *adp5520_bl_attributes[] = {
0267     &dev_attr_dark_max.attr,
0268     &dev_attr_dark_dim.attr,
0269     &dev_attr_office_max.attr,
0270     &dev_attr_office_dim.attr,
0271     &dev_attr_daylight_max.attr,
0272     &dev_attr_daylight_dim.attr,
0273     NULL
0274 };
0275 
0276 static const struct attribute_group adp5520_bl_attr_group = {
0277     .attrs = adp5520_bl_attributes,
0278 };
0279 
0280 static int adp5520_bl_probe(struct platform_device *pdev)
0281 {
0282     struct backlight_properties props;
0283     struct backlight_device *bl;
0284     struct adp5520_bl *data;
0285     int ret = 0;
0286 
0287     data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
0288     if (data == NULL)
0289         return -ENOMEM;
0290 
0291     data->master = pdev->dev.parent;
0292     data->pdata = dev_get_platdata(&pdev->dev);
0293 
0294     if (data->pdata  == NULL) {
0295         dev_err(&pdev->dev, "missing platform data\n");
0296         return -ENODEV;
0297     }
0298 
0299     data->id = pdev->id;
0300     data->current_brightness = 0;
0301 
0302     mutex_init(&data->lock);
0303 
0304     memset(&props, 0, sizeof(struct backlight_properties));
0305     props.type = BACKLIGHT_RAW;
0306     props.max_brightness = ADP5020_MAX_BRIGHTNESS;
0307     bl = devm_backlight_device_register(&pdev->dev, pdev->name,
0308                     data->master, data, &adp5520_bl_ops,
0309                     &props);
0310     if (IS_ERR(bl)) {
0311         dev_err(&pdev->dev, "failed to register backlight\n");
0312         return PTR_ERR(bl);
0313     }
0314 
0315     bl->props.brightness = ADP5020_MAX_BRIGHTNESS;
0316     if (data->pdata->en_ambl_sens)
0317         ret = sysfs_create_group(&bl->dev.kobj,
0318             &adp5520_bl_attr_group);
0319 
0320     if (ret) {
0321         dev_err(&pdev->dev, "failed to register sysfs\n");
0322         return ret;
0323     }
0324 
0325     platform_set_drvdata(pdev, bl);
0326     ret = adp5520_bl_setup(bl);
0327     if (ret) {
0328         dev_err(&pdev->dev, "failed to setup\n");
0329         if (data->pdata->en_ambl_sens)
0330             sysfs_remove_group(&bl->dev.kobj,
0331                     &adp5520_bl_attr_group);
0332         return ret;
0333     }
0334 
0335     backlight_update_status(bl);
0336 
0337     return 0;
0338 }
0339 
0340 static int adp5520_bl_remove(struct platform_device *pdev)
0341 {
0342     struct backlight_device *bl = platform_get_drvdata(pdev);
0343     struct adp5520_bl *data = bl_get_data(bl);
0344 
0345     adp5520_clr_bits(data->master, ADP5520_MODE_STATUS, ADP5520_BL_EN);
0346 
0347     if (data->pdata->en_ambl_sens)
0348         sysfs_remove_group(&bl->dev.kobj,
0349                 &adp5520_bl_attr_group);
0350 
0351     return 0;
0352 }
0353 
0354 #ifdef CONFIG_PM_SLEEP
0355 static int adp5520_bl_suspend(struct device *dev)
0356 {
0357     struct backlight_device *bl = dev_get_drvdata(dev);
0358 
0359     return adp5520_bl_set(bl, 0);
0360 }
0361 
0362 static int adp5520_bl_resume(struct device *dev)
0363 {
0364     struct backlight_device *bl = dev_get_drvdata(dev);
0365 
0366     backlight_update_status(bl);
0367     return 0;
0368 }
0369 #endif
0370 
0371 static SIMPLE_DEV_PM_OPS(adp5520_bl_pm_ops, adp5520_bl_suspend,
0372             adp5520_bl_resume);
0373 
0374 static struct platform_driver adp5520_bl_driver = {
0375     .driver     = {
0376         .name   = "adp5520-backlight",
0377         .pm = &adp5520_bl_pm_ops,
0378     },
0379     .probe      = adp5520_bl_probe,
0380     .remove     = adp5520_bl_remove,
0381 };
0382 
0383 module_platform_driver(adp5520_bl_driver);
0384 
0385 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
0386 MODULE_DESCRIPTION("ADP5520(01) Backlight Driver");
0387 MODULE_LICENSE("GPL");
0388 MODULE_ALIAS("platform:adp5520-backlight");