2 * Copyright (C) 2008 Atmel Corporation
4 * Backlight driver using Atmel PWM peripheral.
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.
10 #include <linux/init.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
15 #include <linux/gpio.h>
16 #include <linux/backlight.h>
17 #include <linux/atmel_pwm.h>
18 #include <linux/atmel-pwm-bl.h>
19 #include <linux/slab.h>
22 const struct atmel_pwm_bl_platform_data
*pdata
;
23 struct backlight_device
*bldev
;
24 struct platform_device
*pdev
;
25 struct pwm_channel pwmc
;
29 static void atmel_pwm_bl_set_gpio_on(struct atmel_pwm_bl
*pwmbl
, int on
)
31 if (!gpio_is_valid(pwmbl
->gpio_on
))
34 gpio_set_value(pwmbl
->gpio_on
, on
^ pwmbl
->pdata
->on_active_low
);
37 static int atmel_pwm_bl_set_intensity(struct backlight_device
*bd
)
39 struct atmel_pwm_bl
*pwmbl
= bl_get_data(bd
);
40 int intensity
= bd
->props
.brightness
;
43 if (bd
->props
.power
!= FB_BLANK_UNBLANK
)
45 if (bd
->props
.fb_blank
!= FB_BLANK_UNBLANK
)
48 if (pwmbl
->pdata
->pwm_active_low
)
49 pwm_duty
= pwmbl
->pdata
->pwm_duty_min
+ intensity
;
51 pwm_duty
= pwmbl
->pdata
->pwm_duty_max
- intensity
;
53 if (pwm_duty
> pwmbl
->pdata
->pwm_duty_max
)
54 pwm_duty
= pwmbl
->pdata
->pwm_duty_max
;
55 if (pwm_duty
< pwmbl
->pdata
->pwm_duty_min
)
56 pwm_duty
= pwmbl
->pdata
->pwm_duty_min
;
59 atmel_pwm_bl_set_gpio_on(pwmbl
, 0);
60 pwm_channel_writel(&pwmbl
->pwmc
, PWM_CUPD
, pwm_duty
);
61 pwm_channel_disable(&pwmbl
->pwmc
);
63 pwm_channel_enable(&pwmbl
->pwmc
);
64 pwm_channel_writel(&pwmbl
->pwmc
, PWM_CUPD
, pwm_duty
);
65 atmel_pwm_bl_set_gpio_on(pwmbl
, 1);
71 static int atmel_pwm_bl_get_intensity(struct backlight_device
*bd
)
73 struct atmel_pwm_bl
*pwmbl
= bl_get_data(bd
);
77 cdty
= pwm_channel_readl(&pwmbl
->pwmc
, PWM_CDTY
);
78 if (pwmbl
->pdata
->pwm_active_low
)
79 intensity
= cdty
- pwmbl
->pdata
->pwm_duty_min
;
81 intensity
= pwmbl
->pdata
->pwm_duty_max
- cdty
;
83 return intensity
& 0xffff;
86 static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl
*pwmbl
)
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;
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).
98 prescale
= fls(prescale
);
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
);
109 dev_info(&pwmbl
->pdev
->dev
, "Atmel PWM backlight driver (%lu Hz)\n",
110 pwmbl
->pwmc
.mck
/ pwmbl
->pdata
->pwm_compare_max
/
113 return pwm_channel_enable(&pwmbl
->pwmc
);
116 static const struct backlight_ops atmel_pwm_bl_ops
= {
117 .get_brightness
= atmel_pwm_bl_get_intensity
,
118 .update_status
= atmel_pwm_bl_set_intensity
,
121 static int atmel_pwm_bl_probe(struct platform_device
*pdev
)
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
;
130 pdata
= dev_get_platdata(&pdev
->dev
);
134 if (pdata
->pwm_compare_max
< pdata
->pwm_duty_max
||
135 pdata
->pwm_duty_min
> pdata
->pwm_duty_max
||
136 pdata
->pwm_frequency
== 0)
139 pwmbl
= devm_kzalloc(&pdev
->dev
, sizeof(struct atmel_pwm_bl
),
145 pwmbl
->pdata
= pdata
;
146 pwmbl
->gpio_on
= pdata
->gpio_on
;
148 retval
= pwm_channel_alloc(pdata
->pwm_channel
, &pwmbl
->pwmc
);
152 if (gpio_is_valid(pwmbl
->gpio_on
)) {
153 /* Turn display off by default. */
154 if (pdata
->on_active_low
)
155 flags
= GPIOF_OUT_INIT_HIGH
;
157 flags
= GPIOF_OUT_INIT_LOW
;
159 retval
= devm_gpio_request_one(&pdev
->dev
, pwmbl
->gpio_on
,
160 flags
, "gpio_atmel_pwm_bl");
165 memset(&props
, 0, sizeof(struct backlight_properties
));
166 props
.type
= BACKLIGHT_RAW
;
167 props
.max_brightness
= pdata
->pwm_duty_max
- pdata
->pwm_duty_min
;
168 bldev
= devm_backlight_device_register(&pdev
->dev
, "atmel-pwm-bl",
169 &pdev
->dev
, pwmbl
, &atmel_pwm_bl_ops
,
172 retval
= PTR_ERR(bldev
);
176 pwmbl
->bldev
= bldev
;
178 platform_set_drvdata(pdev
, pwmbl
);
180 /* Power up the backlight by default at middle intesity. */
181 bldev
->props
.power
= FB_BLANK_UNBLANK
;
182 bldev
->props
.brightness
= bldev
->props
.max_brightness
/ 2;
184 retval
= atmel_pwm_bl_init_pwm(pwmbl
);
188 atmel_pwm_bl_set_intensity(bldev
);
193 pwm_channel_free(&pwmbl
->pwmc
);
198 static int atmel_pwm_bl_remove(struct platform_device
*pdev
)
200 struct atmel_pwm_bl
*pwmbl
= platform_get_drvdata(pdev
);
202 atmel_pwm_bl_set_gpio_on(pwmbl
, 0);
203 pwm_channel_disable(&pwmbl
->pwmc
);
204 pwm_channel_free(&pwmbl
->pwmc
);
209 static struct platform_driver atmel_pwm_bl_driver
= {
211 .name
= "atmel-pwm-bl",
213 /* REVISIT add suspend() and resume() */
214 .probe
= atmel_pwm_bl_probe
,
215 .remove
= atmel_pwm_bl_remove
,
218 module_platform_driver(atmel_pwm_bl_driver
);
220 MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>");
221 MODULE_DESCRIPTION("Atmel PWM backlight driver");
222 MODULE_LICENSE("GPL");
223 MODULE_ALIAS("platform:atmel-pwm-bl");