1 #include <linux/kernel.h>
2 #include <linux/platform_device.h>
3 #include <linux/leds.h>
5 #include <linux/atmel_pwm.h>
9 struct led_classdev cdev
;
10 struct pwm_channel pwmc
;
11 struct gpio_led
*desc
;
18 * For simplicity, we use "brightness" as if it were a linear function
19 * of PWM duty cycle. However, a logarithmic function of duty cycle is
20 * probably a better match for perceived brightness: two is half as bright
21 * as four, four is half as bright as eight, etc
23 static void pwmled_brightness(struct led_classdev
*cdev
, enum led_brightness b
)
27 /* update the duty cycle for the *next* period */
28 led
= container_of(cdev
, struct pwmled
, cdev
);
29 pwm_channel_writel(&led
->pwmc
, PWM_CUPD
, led
->mult
* (unsigned) b
);
33 * NOTE: we reuse the platform_data structure of GPIO leds,
34 * but repurpose its "gpio" number as a PWM channel number.
36 static int __init
pwmled_probe(struct platform_device
*pdev
)
38 const struct gpio_led_platform_data
*pdata
;
43 pdata
= pdev
->dev
.platform_data
;
44 if (!pdata
|| pdata
->num_leds
< 1)
47 leds
= kcalloc(pdata
->num_leds
, sizeof(*leds
), GFP_KERNEL
);
51 for (i
= 0; i
< pdata
->num_leds
; i
++) {
52 struct pwmled
*led
= leds
+ i
;
53 const struct gpio_led
*dat
= pdata
->leds
+ i
;
56 led
->cdev
.name
= dat
->name
;
57 led
->cdev
.brightness
= LED_OFF
;
58 led
->cdev
.brightness_set
= pwmled_brightness
;
59 led
->cdev
.default_trigger
= dat
->default_trigger
;
61 led
->active_low
= dat
->active_low
;
63 status
= pwm_channel_alloc(dat
->gpio
, &led
->pwmc
);
68 * Prescale clock by 2^x, so PWM counts in low MHz.
69 * Start each cycle with the LED active, so increasing
70 * the duty cycle gives us more time on (== brighter).
75 pwm_channel_writel(&led
->pwmc
, PWM_CMR
, tmp
);
78 * Pick a period so PWM cycles at 100+ Hz; and a multiplier
79 * for scaling duty cycle: brightness * mult.
81 tmp
= (led
->pwmc
.mck
/ (1 << 5)) / 100;
84 pwm_channel_writel(&led
->pwmc
, PWM_CDTY
,
85 led
->cdev
.brightness
* 255);
86 pwm_channel_writel(&led
->pwmc
, PWM_CPRD
,
89 pwm_channel_enable(&led
->pwmc
);
91 /* Hand it over to the LED framework */
92 status
= led_classdev_register(&pdev
->dev
, &led
->cdev
);
94 pwm_channel_free(&led
->pwmc
);
99 platform_set_drvdata(pdev
, leds
);
104 for (i
= i
- 1; i
>= 0; i
--) {
105 led_classdev_unregister(&leds
[i
].cdev
);
106 pwm_channel_free(&leds
[i
].pwmc
);
114 static int __exit
pwmled_remove(struct platform_device
*pdev
)
116 const struct gpio_led_platform_data
*pdata
;
120 pdata
= pdev
->dev
.platform_data
;
121 leds
= platform_get_drvdata(pdev
);
123 for (i
= 0; i
< pdata
->num_leds
; i
++) {
124 struct pwmled
*led
= leds
+ i
;
126 led_classdev_unregister(&led
->cdev
);
127 pwm_channel_free(&led
->pwmc
);
131 platform_set_drvdata(pdev
, NULL
);
135 /* work with hotplug and coldplug */
136 MODULE_ALIAS("platform:leds-atmel-pwm");
138 static struct platform_driver pwmled_driver
= {
140 .name
= "leds-atmel-pwm",
141 .owner
= THIS_MODULE
,
143 /* REVISIT add suspend() and resume() methods */
144 .remove
= __exit_p(pwmled_remove
),
147 static int __init
modinit(void)
149 return platform_driver_probe(&pwmled_driver
, pwmled_probe
);
151 module_init(modinit
);
153 static void __exit
modexit(void)
155 platform_driver_unregister(&pwmled_driver
);
157 module_exit(modexit
);
159 MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness");
160 MODULE_LICENSE("GPL");