Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.8-rc2 242 lines 6.2 kB view raw
1/* 2 * Copyright (C) 2008 Atmel Corporation 3 * 4 * Backlight driver using Atmel PWM peripheral. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published by 8 * the Free Software Foundation. 9 */ 10#include <linux/init.h> 11#include <linux/kernel.h> 12#include <linux/module.h> 13#include <linux/platform_device.h> 14#include <linux/fb.h> 15#include <linux/clk.h> 16#include <linux/gpio.h> 17#include <linux/backlight.h> 18#include <linux/atmel_pwm.h> 19#include <linux/atmel-pwm-bl.h> 20#include <linux/slab.h> 21 22struct atmel_pwm_bl { 23 const struct atmel_pwm_bl_platform_data *pdata; 24 struct backlight_device *bldev; 25 struct platform_device *pdev; 26 struct pwm_channel pwmc; 27 int gpio_on; 28}; 29 30static int atmel_pwm_bl_set_intensity(struct backlight_device *bd) 31{ 32 struct atmel_pwm_bl *pwmbl = bl_get_data(bd); 33 int intensity = bd->props.brightness; 34 int pwm_duty; 35 36 if (bd->props.power != FB_BLANK_UNBLANK) 37 intensity = 0; 38 if (bd->props.fb_blank != FB_BLANK_UNBLANK) 39 intensity = 0; 40 41 if (pwmbl->pdata->pwm_active_low) 42 pwm_duty = pwmbl->pdata->pwm_duty_min + intensity; 43 else 44 pwm_duty = pwmbl->pdata->pwm_duty_max - intensity; 45 46 if (pwm_duty > pwmbl->pdata->pwm_duty_max) 47 pwm_duty = pwmbl->pdata->pwm_duty_max; 48 if (pwm_duty < pwmbl->pdata->pwm_duty_min) 49 pwm_duty = pwmbl->pdata->pwm_duty_min; 50 51 if (!intensity) { 52 if (pwmbl->gpio_on != -1) { 53 gpio_set_value(pwmbl->gpio_on, 54 0 ^ pwmbl->pdata->on_active_low); 55 } 56 pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); 57 pwm_channel_disable(&pwmbl->pwmc); 58 } else { 59 pwm_channel_enable(&pwmbl->pwmc); 60 pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); 61 if (pwmbl->gpio_on != -1) { 62 gpio_set_value(pwmbl->gpio_on, 63 1 ^ pwmbl->pdata->on_active_low); 64 } 65 } 66 67 return 0; 68} 69 70static int atmel_pwm_bl_get_intensity(struct backlight_device *bd) 71{ 72 struct atmel_pwm_bl *pwmbl = bl_get_data(bd); 73 u8 intensity; 74 75 if (pwmbl->pdata->pwm_active_low) { 76 intensity = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY) - 77 pwmbl->pdata->pwm_duty_min; 78 } else { 79 intensity = pwmbl->pdata->pwm_duty_max - 80 pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY); 81 } 82 83 return intensity; 84} 85 86static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl) 87{ 88 unsigned long pwm_rate = pwmbl->pwmc.mck; 89 unsigned long prescale = DIV_ROUND_UP(pwm_rate, 90 (pwmbl->pdata->pwm_frequency * 91 pwmbl->pdata->pwm_compare_max)) - 1; 92 93 /* 94 * Prescale must be power of two and maximum 0xf in size because of 95 * hardware limit. PWM speed will be: 96 * PWM module clock speed / (2 ^ prescale). 97 */ 98 prescale = fls(prescale); 99 if (prescale > 0xf) 100 prescale = 0xf; 101 102 pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale); 103 pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY, 104 pwmbl->pdata->pwm_duty_min + 105 pwmbl->bldev->props.brightness); 106 pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD, 107 pwmbl->pdata->pwm_compare_max); 108 109 dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver (%lu Hz)\n", 110 pwmbl->pwmc.mck / pwmbl->pdata->pwm_compare_max / 111 (1 << prescale)); 112 113 return pwm_channel_enable(&pwmbl->pwmc); 114} 115 116static const struct backlight_ops atmel_pwm_bl_ops = { 117 .get_brightness = atmel_pwm_bl_get_intensity, 118 .update_status = atmel_pwm_bl_set_intensity, 119}; 120 121static int atmel_pwm_bl_probe(struct platform_device *pdev) 122{ 123 struct backlight_properties props; 124 const struct atmel_pwm_bl_platform_data *pdata; 125 struct backlight_device *bldev; 126 struct atmel_pwm_bl *pwmbl; 127 int retval; 128 129 pwmbl = devm_kzalloc(&pdev->dev, sizeof(struct atmel_pwm_bl), 130 GFP_KERNEL); 131 if (!pwmbl) 132 return -ENOMEM; 133 134 pwmbl->pdev = pdev; 135 136 pdata = pdev->dev.platform_data; 137 if (!pdata) { 138 retval = -ENODEV; 139 goto err_free_mem; 140 } 141 142 if (pdata->pwm_compare_max < pdata->pwm_duty_max || 143 pdata->pwm_duty_min > pdata->pwm_duty_max || 144 pdata->pwm_frequency == 0) { 145 retval = -EINVAL; 146 goto err_free_mem; 147 } 148 149 pwmbl->pdata = pdata; 150 pwmbl->gpio_on = pdata->gpio_on; 151 152 retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc); 153 if (retval) 154 goto err_free_mem; 155 156 if (pwmbl->gpio_on != -1) { 157 retval = devm_gpio_request(&pdev->dev, pwmbl->gpio_on, 158 "gpio_atmel_pwm_bl"); 159 if (retval) { 160 pwmbl->gpio_on = -1; 161 goto err_free_pwm; 162 } 163 164 /* Turn display off by default. */ 165 retval = gpio_direction_output(pwmbl->gpio_on, 166 0 ^ pdata->on_active_low); 167 if (retval) 168 goto err_free_pwm; 169 } 170 171 memset(&props, 0, sizeof(struct backlight_properties)); 172 props.type = BACKLIGHT_RAW; 173 props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min; 174 bldev = backlight_device_register("atmel-pwm-bl", &pdev->dev, pwmbl, 175 &atmel_pwm_bl_ops, &props); 176 if (IS_ERR(bldev)) { 177 retval = PTR_ERR(bldev); 178 goto err_free_pwm; 179 } 180 181 pwmbl->bldev = bldev; 182 183 platform_set_drvdata(pdev, pwmbl); 184 185 /* Power up the backlight by default at middle intesity. */ 186 bldev->props.power = FB_BLANK_UNBLANK; 187 bldev->props.brightness = bldev->props.max_brightness / 2; 188 189 retval = atmel_pwm_bl_init_pwm(pwmbl); 190 if (retval) 191 goto err_free_bl_dev; 192 193 atmel_pwm_bl_set_intensity(bldev); 194 195 return 0; 196 197err_free_bl_dev: 198 platform_set_drvdata(pdev, NULL); 199 backlight_device_unregister(bldev); 200err_free_pwm: 201 pwm_channel_free(&pwmbl->pwmc); 202err_free_mem: 203 return retval; 204} 205 206static int __exit atmel_pwm_bl_remove(struct platform_device *pdev) 207{ 208 struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev); 209 210 if (pwmbl->gpio_on != -1) 211 gpio_set_value(pwmbl->gpio_on, 0); 212 pwm_channel_disable(&pwmbl->pwmc); 213 pwm_channel_free(&pwmbl->pwmc); 214 backlight_device_unregister(pwmbl->bldev); 215 platform_set_drvdata(pdev, NULL); 216 217 return 0; 218} 219 220static struct platform_driver atmel_pwm_bl_driver = { 221 .driver = { 222 .name = "atmel-pwm-bl", 223 }, 224 /* REVISIT add suspend() and resume() */ 225 .remove = __exit_p(atmel_pwm_bl_remove), 226}; 227 228static int __init atmel_pwm_bl_init(void) 229{ 230 return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe); 231} 232module_init(atmel_pwm_bl_init); 233 234static void __exit atmel_pwm_bl_exit(void) 235{ 236 platform_driver_unregister(&atmel_pwm_bl_driver); 237} 238module_exit(atmel_pwm_bl_exit); 239 240MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>"); 241MODULE_DESCRIPTION("Atmel PWM backlight driver"); 242MODULE_LICENSE("GPL");