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 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
= devm_kzalloc(&pdev
->dev
, pdata
->num_leds
* sizeof(*leds
),
54 for (i
= 0; i
< pdata
->num_leds
; i
++) {
55 struct pwmled
*led
= leds
+ i
;
56 const struct gpio_led
*dat
= pdata
->leds
+ i
;
59 led
->cdev
.name
= dat
->name
;
60 led
->cdev
.brightness
= LED_OFF
;
61 led
->cdev
.brightness_set
= pwmled_brightness
;
62 led
->cdev
.default_trigger
= dat
->default_trigger
;
64 led
->active_low
= dat
->active_low
;
66 status
= pwm_channel_alloc(dat
->gpio
, &led
->pwmc
);
71 * Prescale clock by 2^x, so PWM counts in low MHz.
72 * Start each cycle with the LED active, so increasing
73 * the duty cycle gives us more time on (== brighter).
78 pwm_channel_writel(&led
->pwmc
, PWM_CMR
, tmp
);
81 * Pick a period so PWM cycles at 100+ Hz; and a multiplier
82 * for scaling duty cycle: brightness * mult.
84 tmp
= (led
->pwmc
.mck
/ (1 << 5)) / 100;
87 pwm_channel_writel(&led
->pwmc
, PWM_CDTY
,
88 led
->cdev
.brightness
* 255);
89 pwm_channel_writel(&led
->pwmc
, PWM_CPRD
,
92 pwm_channel_enable(&led
->pwmc
);
94 /* Hand it over to the LED framework */
95 status
= led_classdev_register(&pdev
->dev
, &led
->cdev
);
97 pwm_channel_free(&led
->pwmc
);
102 platform_set_drvdata(pdev
, leds
);
107 for (i
= i
- 1; i
>= 0; i
--) {
108 led_classdev_unregister(&leds
[i
].cdev
);
109 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
);
132 platform_set_drvdata(pdev
, NULL
);
136 static struct platform_driver pwmled_driver
= {
138 .name
= "leds-atmel-pwm",
139 .owner
= THIS_MODULE
,
141 /* REVISIT add suspend() and resume() methods */
142 .probe
= pwmled_probe
,
143 .remove
= __exit_p(pwmled_remove
),
146 module_platform_driver(pwmled_driver
);
148 MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness");
149 MODULE_LICENSE("GPL");
150 MODULE_ALIAS("platform:leds-atmel-pwm");