1 /* 2 * linux/drivers/video/backlight/pwm_bl.c 3 * 4 * simple PWM based backlight control, board code has to setup 5 * 1) pin configuration so PWM waveforms can output 6 * 2) platform_data being correctly configured 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/kernel.h> 15 #include <linux/init.h> 16 #include <linux/platform_device.h> 17 #include <linux/fb.h> 18 #include <linux/backlight.h> 19 #include <linux/err.h> 20 #include <linux/pwm.h> 21 #include <linux/pwm_backlight.h> 22 #include <linux/slab.h> 23 24 struct pwm_bl_data { 25 struct pwm_device *pwm; 26 struct device *dev; 27 unsigned int period; 28 int (*notify)(struct device *, 29 int brightness); 30 }; 31 32 static int pwm_backlight_update_status(struct backlight_device *bl) 33 { 34 struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); 35 int brightness = bl->props.brightness; 36 int max = bl->props.max_brightness; 37 38 if (bl->props.power != FB_BLANK_UNBLANK) 39 brightness = 0; 40 41 if (bl->props.fb_blank != FB_BLANK_UNBLANK) 42 brightness = 0; 43 44 if (pb->notify) 45 brightness = pb->notify(pb->dev, brightness); 46 47 if (brightness == 0) { 48 pwm_config(pb->pwm, 0, pb->period); 49 pwm_disable(pb->pwm); 50 } else { 51 pwm_config(pb->pwm, brightness * pb->period / max, pb->period); 52 pwm_enable(pb->pwm); 53 } 54 return 0; 55 } 56 57 static int pwm_backlight_get_brightness(struct backlight_device *bl) 58 { 59 return bl->props.brightness; 60 } 61 62 static const struct backlight_ops pwm_backlight_ops = { 63 .update_status = pwm_backlight_update_status, 64 .get_brightness = pwm_backlight_get_brightness, 65 }; 66 67 static int pwm_backlight_probe(struct platform_device *pdev) 68 { 69 struct backlight_properties props; 70 struct platform_pwm_backlight_data *data = pdev->dev.platform_data; 71 struct backlight_device *bl; 72 struct pwm_bl_data *pb; 73 int ret; 74 75 if (!data) { 76 dev_err(&pdev->dev, "failed to find platform data\n"); 77 return -EINVAL; 78 } 79 80 if (data->init) { 81 ret = data->init(&pdev->dev); 82 if (ret < 0) 83 return ret; 84 } 85 86 pb = kzalloc(sizeof(*pb), GFP_KERNEL); 87 if (!pb) { 88 dev_err(&pdev->dev, "no memory for state\n"); 89 ret = -ENOMEM; 90 goto err_alloc; 91 } 92 93 pb->period = data->pwm_period_ns; 94 pb->notify = data->notify; 95 pb->dev = &pdev->dev; 96 97 pb->pwm = pwm_request(data->pwm_id, "backlight"); 98 if (IS_ERR(pb->pwm)) { 99 dev_err(&pdev->dev, "unable to request PWM for backlight\n"); 100 ret = PTR_ERR(pb->pwm); 101 goto err_pwm; 102 } else 103 dev_dbg(&pdev->dev, "got pwm for backlight\n"); 104 105 memset(&props, 0, sizeof(struct backlight_properties)); 106 props.max_brightness = data->max_brightness; 107 bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb, 108 &pwm_backlight_ops, &props); 109 if (IS_ERR(bl)) { 110 dev_err(&pdev->dev, "failed to register backlight\n"); 111 ret = PTR_ERR(bl); 112 goto err_bl; 113 } 114 115 bl->props.brightness = data->dft_brightness; 116 backlight_update_status(bl); 117 118 platform_set_drvdata(pdev, bl); 119 return 0; 120 121 err_bl: 122 pwm_free(pb->pwm); 123 err_pwm: 124 kfree(pb); 125 err_alloc: 126 if (data->exit) 127 data->exit(&pdev->dev); 128 return ret; 129 } 130 131 static int pwm_backlight_remove(struct platform_device *pdev) 132 { 133 struct platform_pwm_backlight_data *data = pdev->dev.platform_data; 134 struct backlight_device *bl = platform_get_drvdata(pdev); 135 struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); 136 137 backlight_device_unregister(bl); 138 pwm_config(pb->pwm, 0, pb->period); 139 pwm_disable(pb->pwm); 140 pwm_free(pb->pwm); 141 kfree(pb); 142 if (data->exit) 143 data->exit(&pdev->dev); 144 return 0; 145 } 146 147 #ifdef CONFIG_PM 148 static int pwm_backlight_suspend(struct platform_device *pdev, 149 pm_message_t state) 150 { 151 struct backlight_device *bl = platform_get_drvdata(pdev); 152 struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); 153 154 if (pb->notify) 155 pb->notify(pb->dev, 0); 156 pwm_config(pb->pwm, 0, pb->period); 157 pwm_disable(pb->pwm); 158 return 0; 159 } 160 161 static int pwm_backlight_resume(struct platform_device *pdev) 162 { 163 struct backlight_device *bl = platform_get_drvdata(pdev); 164 165 backlight_update_status(bl); 166 return 0; 167 } 168 #else 169 #define pwm_backlight_suspend NULL 170 #define pwm_backlight_resume NULL 171 #endif 172 173 static struct platform_driver pwm_backlight_driver = { 174 .driver = { 175 .name = "pwm-backlight", 176 .owner = THIS_MODULE, 177 }, 178 .probe = pwm_backlight_probe, 179 .remove = pwm_backlight_remove, 180 .suspend = pwm_backlight_suspend, 181 .resume = pwm_backlight_resume, 182 }; 183 184 static int __init pwm_backlight_init(void) 185 { 186 return platform_driver_register(&pwm_backlight_driver); 187 } 188 module_init(pwm_backlight_init); 189 190 static void __exit pwm_backlight_exit(void) 191 { 192 platform_driver_unregister(&pwm_backlight_driver); 193 } 194 module_exit(pwm_backlight_exit); 195 196 MODULE_DESCRIPTION("PWM based Backlight Driver"); 197 MODULE_LICENSE("GPL"); 198 MODULE_ALIAS("platform:pwm-backlight"); 199 200