0001
0002
0003
0004
0005
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
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
0042
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, ®_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, ®_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");