1 #include <linux/kernel.h>
2 #include <linux/platform_device.h>
3 #include <linux/leds.h>
5 #include <linux/atmel_pwm.h>
6 #include <linux/slab.h>
7 #include <linux/module.h>
11 struct led_classdev cdev
;
12 struct pwm_channel pwmc
;
13 struct gpio_led
*desc
;
20 * For simplicity, we use "brightness" as if it were a linear function
21 * of PWM duty cycle. However, a logarithmic function of duty cycle is
22 * probably a better match for perceived brightness: two is half as bright
23 * as four, four is half as bright as eight, etc
25 static void pwmled_brightness(struct led_classdev
*cdev
, enum led_brightness b
)
29 /* update the duty cycle for the *next* period */
30 led
= container_of(cdev
, struct pwmled
, cdev
);
31 pwm_channel_writel(&led
->pwmc
, PWM_CUPD
, led
->mult
* (unsigned) b
);
35 * NOTE: we reuse the platform_data structure of GPIO leds,
36 * but repurpose its "gpio" number as a PWM channel number.
38 static int __init
pwmled_probe(struct platform_device
*pdev
)
40 const struct gpio_led_platform_data
*pdata
;
45 pdata
= pdev
->dev
.platform_data
;
46 if (!pdata
|| pdata
->num_leds
< 1)
49 leds
= kcalloc(pdata
->num_leds
, sizeof(*leds
), GFP_KERNEL
);
53 for (i
= 0; i
< pdata
->num_leds
; i
++) {
54 struct pwmled
*led
= leds
+ i
;
55 const struct gpio_led
*dat
= pdata
->leds
+ i
;
58 led
->cdev
.name
= dat
->name
;
59 led
->cdev
.brightness
= LED_OFF
;
60 led
->cdev
.brightness_set
= pwmled_brightness
;
61 led
->cdev
.default_trigger
= dat
->default_trigger
;
63 led
->active_low
= dat
->active_low
;
65 status
= pwm_channel_alloc(dat
->gpio
, &led
->pwmc
);
70 * Prescale clock by 2^x, so PWM counts in low MHz.
71 * Start each cycle with the LED active, so increasing
72 * the duty cycle gives us more time on (== brighter).
77 pwm_channel_writel(&led
->pwmc
, PWM_CMR
, tmp
);
80 * Pick a period so PWM cycles at 100+ Hz; and a multiplier
81 * for scaling duty cycle: brightness * mult.
83 tmp
= (led
->pwmc
.mck
/ (1 << 5)) / 100;
86 pwm_channel_writel(&led
->pwmc
, PWM_CDTY
,
87 led
->cdev
.brightness
* 255);
88 pwm_channel_writel(&led
->pwmc
, PWM_CPRD
,
91 pwm_channel_enable(&led
->pwmc
);
93 /* Hand it over to the LED framework */
94 status
= led_classdev_register(&pdev
->dev
, &led
->cdev
);
96 pwm_channel_free(&led
->pwmc
);
101 platform_set_drvdata(pdev
, leds
);
106 for (i
= i
- 1; i
>= 0; i
--) {
107 led_classdev_unregister(&leds
[i
].cdev
);
108 pwm_channel_free(&leds
[i
].pwmc
);
116 static int __exit
pwmled_remove(struct platform_device
*pdev
)
118 const struct gpio_led_platform_data
*pdata
;
122 pdata
= pdev
->dev
.platform_data
;
123 leds
= platform_get_drvdata(pdev
);
125 for (i
= 0; i
< pdata
->num_leds
; i
++) {
126 struct pwmled
*led
= leds
+ i
;
128 led_classdev_unregister(&led
->cdev
);
129 pwm_channel_free(&led
->pwmc
);
133 platform_set_drvdata(pdev
, NULL
);
137 static struct platform_driver pwmled_driver
= {
139 .name
= "leds-atmel-pwm",
140 .owner
= THIS_MODULE
,
142 /* REVISIT add suspend() and resume() methods */
143 .probe
= pwmled_probe
,
144 .remove
= __exit_p(pwmled_remove
),
147 module_platform_driver(pwmled_driver
);
149 MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness");
150 MODULE_LICENSE("GPL");
151 MODULE_ALIAS("platform:leds-atmel-pwm");