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.1-rc3 248 lines 6.3 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 " 110 "(%lu Hz)\n", pwmbl->pwmc.mck / 111 pwmbl->pdata->pwm_compare_max / 112 (1 << prescale)); 113 114 return pwm_channel_enable(&pwmbl->pwmc); 115} 116 117static const struct backlight_ops atmel_pwm_bl_ops = { 118 .get_brightness = atmel_pwm_bl_get_intensity, 119 .update_status = atmel_pwm_bl_set_intensity, 120}; 121 122static int atmel_pwm_bl_probe(struct platform_device *pdev) 123{ 124 struct backlight_properties props; 125 const struct atmel_pwm_bl_platform_data *pdata; 126 struct backlight_device *bldev; 127 struct atmel_pwm_bl *pwmbl; 128 int retval; 129 130 pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), 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 = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl"); 158 if (retval) { 159 pwmbl->gpio_on = -1; 160 goto err_free_pwm; 161 } 162 163 /* Turn display off by default. */ 164 retval = gpio_direction_output(pwmbl->gpio_on, 165 0 ^ pdata->on_active_low); 166 if (retval) 167 goto err_free_gpio; 168 } 169 170 memset(&props, 0, sizeof(struct backlight_properties)); 171 props.type = BACKLIGHT_RAW; 172 props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min; 173 bldev = backlight_device_register("atmel-pwm-bl", &pdev->dev, pwmbl, 174 &atmel_pwm_bl_ops, &props); 175 if (IS_ERR(bldev)) { 176 retval = PTR_ERR(bldev); 177 goto err_free_gpio; 178 } 179 180 pwmbl->bldev = bldev; 181 182 platform_set_drvdata(pdev, pwmbl); 183 184 /* Power up the backlight by default at middle intesity. */ 185 bldev->props.power = FB_BLANK_UNBLANK; 186 bldev->props.brightness = bldev->props.max_brightness / 2; 187 188 retval = atmel_pwm_bl_init_pwm(pwmbl); 189 if (retval) 190 goto err_free_bl_dev; 191 192 atmel_pwm_bl_set_intensity(bldev); 193 194 return 0; 195 196err_free_bl_dev: 197 platform_set_drvdata(pdev, NULL); 198 backlight_device_unregister(bldev); 199err_free_gpio: 200 if (pwmbl->gpio_on != -1) 201 gpio_free(pwmbl->gpio_on); 202err_free_pwm: 203 pwm_channel_free(&pwmbl->pwmc); 204err_free_mem: 205 kfree(pwmbl); 206 return retval; 207} 208 209static int __exit atmel_pwm_bl_remove(struct platform_device *pdev) 210{ 211 struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev); 212 213 if (pwmbl->gpio_on != -1) { 214 gpio_set_value(pwmbl->gpio_on, 0); 215 gpio_free(pwmbl->gpio_on); 216 } 217 pwm_channel_disable(&pwmbl->pwmc); 218 pwm_channel_free(&pwmbl->pwmc); 219 backlight_device_unregister(pwmbl->bldev); 220 platform_set_drvdata(pdev, NULL); 221 kfree(pwmbl); 222 223 return 0; 224} 225 226static struct platform_driver atmel_pwm_bl_driver = { 227 .driver = { 228 .name = "atmel-pwm-bl", 229 }, 230 /* REVISIT add suspend() and resume() */ 231 .remove = __exit_p(atmel_pwm_bl_remove), 232}; 233 234static int __init atmel_pwm_bl_init(void) 235{ 236 return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe); 237} 238module_init(atmel_pwm_bl_init); 239 240static void __exit atmel_pwm_bl_exit(void) 241{ 242 platform_driver_unregister(&atmel_pwm_bl_driver); 243} 244module_exit(atmel_pwm_bl_exit); 245 246MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>"); 247MODULE_DESCRIPTION("Atmel PWM backlight driver"); 248MODULE_LICENSE("GPL");