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