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