1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 4 * PCF50633 backlight device driver 5 */ 6 7 #include <linux/kernel.h> 8 #include <linux/module.h> 9 #include <linux/slab.h> 10 #include <linux/platform_device.h> 11 12 #include <linux/backlight.h> 13 14 #include <linux/mfd/pcf50633/core.h> 15 #include <linux/mfd/pcf50633/backlight.h> 16 17 struct pcf50633_bl { 18 struct pcf50633 *pcf; 19 struct backlight_device *bl; 20 21 unsigned int brightness; 22 unsigned int brightness_limit; 23 }; 24 25 /* 26 * pcf50633_bl_set_brightness_limit 27 * 28 * Update the brightness limit for the pc50633 backlight. The actual brightness 29 * will not go above the limit. This is useful to limit power drain for example 30 * on low battery. 31 * 32 * @dev: Pointer to a pcf50633 device 33 * @limit: The brightness limit. Valid values are 0-63 34 */ 35 int pcf50633_bl_set_brightness_limit(struct pcf50633 *pcf, unsigned int limit) 36 { 37 struct pcf50633_bl *pcf_bl = platform_get_drvdata(pcf->bl_pdev); 38 39 if (!pcf_bl) 40 return -ENODEV; 41 42 pcf_bl->brightness_limit = limit & 0x3f; 43 backlight_update_status(pcf_bl->bl); 44 45 return 0; 46 } 47 48 static int pcf50633_bl_update_status(struct backlight_device *bl) 49 { 50 struct pcf50633_bl *pcf_bl = bl_get_data(bl); 51 unsigned int new_brightness; 52 53 54 if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK) || 55 bl->props.power != BACKLIGHT_POWER_ON) 56 new_brightness = 0; 57 else if (bl->props.brightness < pcf_bl->brightness_limit) 58 new_brightness = bl->props.brightness; 59 else 60 new_brightness = pcf_bl->brightness_limit; 61 62 63 if (pcf_bl->brightness == new_brightness) 64 return 0; 65 66 if (new_brightness) { 67 pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDOUT, 68 new_brightness); 69 if (!pcf_bl->brightness) 70 pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 1); 71 } else { 72 pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 0); 73 } 74 75 pcf_bl->brightness = new_brightness; 76 77 return 0; 78 } 79 80 static int pcf50633_bl_get_brightness(struct backlight_device *bl) 81 { 82 struct pcf50633_bl *pcf_bl = bl_get_data(bl); 83 84 return pcf_bl->brightness; 85 } 86 87 static const struct backlight_ops pcf50633_bl_ops = { 88 .get_brightness = pcf50633_bl_get_brightness, 89 .update_status = pcf50633_bl_update_status, 90 .options = BL_CORE_SUSPENDRESUME, 91 }; 92 93 static int pcf50633_bl_probe(struct platform_device *pdev) 94 { 95 struct pcf50633_bl *pcf_bl; 96 struct device *parent = pdev->dev.parent; 97 struct pcf50633_platform_data *pcf50633_data = dev_get_platdata(parent); 98 struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data; 99 struct backlight_properties bl_props; 100 101 pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL); 102 if (!pcf_bl) 103 return -ENOMEM; 104 105 memset(&bl_props, 0, sizeof(bl_props)); 106 bl_props.type = BACKLIGHT_RAW; 107 bl_props.max_brightness = 0x3f; 108 bl_props.power = BACKLIGHT_POWER_ON; 109 110 if (pdata) { 111 bl_props.brightness = pdata->default_brightness; 112 pcf_bl->brightness_limit = pdata->default_brightness_limit; 113 } else { 114 bl_props.brightness = 0x3f; 115 pcf_bl->brightness_limit = 0x3f; 116 } 117 118 pcf_bl->pcf = dev_to_pcf50633(pdev->dev.parent); 119 120 pcf_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name, 121 &pdev->dev, pcf_bl, 122 &pcf50633_bl_ops, &bl_props); 123 124 if (IS_ERR(pcf_bl->bl)) 125 return PTR_ERR(pcf_bl->bl); 126 127 platform_set_drvdata(pdev, pcf_bl); 128 129 pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDDIM, pdata->ramp_time); 130 131 /* 132 * Should be different from bl_props.brightness, so we do not exit 133 * update_status early the first time it's called 134 */ 135 pcf_bl->brightness = pcf_bl->bl->props.brightness + 1; 136 137 backlight_update_status(pcf_bl->bl); 138 139 return 0; 140 } 141 142 static struct platform_driver pcf50633_bl_driver = { 143 .probe = pcf50633_bl_probe, 144 .driver = { 145 .name = "pcf50633-backlight", 146 }, 147 }; 148 149 module_platform_driver(pcf50633_bl_driver); 150 151 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 152 MODULE_DESCRIPTION("PCF50633 backlight driver"); 153 MODULE_LICENSE("GPL"); 154 MODULE_ALIAS("platform:pcf50633-backlight"); 155