Index: linux-2.6.22.5-moko/drivers/leds/Kconfig =================================================================== --- linux-2.6.22.5-moko.orig/drivers/leds/Kconfig +++ linux-2.6.22.5-moko/drivers/leds/Kconfig @@ -101,6 +101,12 @@ help This option enables support for the Vibrator on the FIC Neo1973. +config LEDS_GTA02 + tristate "LED Support for the FIC Neo1973 (GTA02)" + depends on LEDS_CLASS && MACH_NEO1973_GTA02 + help + This option enables support for the LEDs on the FIC Neo1973. + comment "LED Triggers" config LEDS_TRIGGERS Index: linux-2.6.22.5-moko/drivers/leds/Makefile =================================================================== --- linux-2.6.22.5-moko.orig/drivers/leds/Makefile +++ linux-2.6.22.5-moko/drivers/leds/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o obj-$(CONFIG_LEDS_GTA01) += leds-gta01.o +obj-$(CONFIG_LEDS_GTA02) += leds-gta02.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o Index: linux-2.6.22.5-moko/drivers/leds/leds-gta02.c =================================================================== --- /dev/null +++ linux-2.6.22.5-moko/drivers/leds/leds-gta02.c @@ -0,0 +1,215 @@ +/* + * LED driver for the FIC GTA02 (Neo1973) GSM Phone + * + * (C) 2006-2007 by OpenMoko, Inc. + * Author: Harald Welte + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_LEDS 3 +#define COUNTER 256 + +struct gta02_led_priv +{ + struct mutex mutex; + struct led_classdev cdev; + struct s3c2410_pwm pwm; + unsigned int gpio; + unsigned int has_pwm; +}; + +struct gta02_led_bundle +{ + int num_leds; + struct gta02_led_priv led[MAX_LEDS]; +}; + +static inline struct gta02_led_priv *to_priv(struct led_classdev *led_cdev) +{ + return container_of(led_cdev, struct gta02_led_priv, cdev); +} + +static inline struct gta02_led_bundle *to_bundle(struct led_classdev *led_cdev) +{ + return dev_get_drvdata(led_cdev->class_dev->dev); +} + +static void gta02led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct gta02_led_priv *lp = to_priv(led_cdev); + + /* + * value == 255 -> 99% duty cycle (full power) + * value == 128 -> 50% duty cycle (medium power) + * value == 0 -> 0% duty cycle (zero power) + */ + mutex_lock(&lp->mutex); + if (lp->has_pwm) { + s3c2410_pwm_duty_cycle(value, &lp->pwm); + } else { + if (value) + s3c2410_gpio_setpin(lp->gpio, 1); + else + s3c2410_gpio_setpin(lp->gpio, 0); + } + mutex_unlock(&lp->mutex); +} + +#ifdef CONFIG_PM +static int gta02led_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gta02_led_bundle *bundle = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < bundle->num_leds; i++) + led_classdev_suspend(&bundle->led[i].cdev); + + return 0; +} + +static int gta02led_resume(struct platform_device *pdev) +{ + struct gta02_led_bundle *bundle = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < bundle->num_leds; i++) + led_classdev_resume(&bundle->led[i].cdev); + + return 0; +} +#endif + +static int __init gta02led_probe(struct platform_device *pdev) +{ + int i, rc; + struct gta02_led_bundle *bundle; + + if (!machine_is_neo1973_gta02()) + return -EIO; + + bundle = kzalloc(sizeof(struct gta02_led_bundle), GFP_KERNEL); + if (!bundle) + return -ENOMEM; + platform_set_drvdata(pdev, bundle); + + for (i = 0; i < pdev->num_resources; i++) { + struct gta02_led_priv *lp; + struct resource *r; + + if (i >= MAX_LEDS) + break; + + r = platform_get_resource(pdev, 0, i); + if (!r || !r->start || !r->name) + continue; + + lp = &bundle->led[i]; + + lp->gpio = r->start; + lp->cdev.name = r->name; + lp->cdev.brightness_set = gta02led_set; + + switch (lp->gpio) { + case S3C2410_GPB0: + lp->has_pwm = 1; + lp->pwm.timerid = PWM0; + s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB0_TOUT0); + break; + case S3C2410_GPB1: + lp->has_pwm = 1; + lp->pwm.timerid = PWM1; + s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB1_TOUT1); + break; + case S3C2410_GPB2: + lp->has_pwm = 1; + lp->pwm.timerid = PWM2; + s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB2_TOUT2); + break; + case S3C2410_GPB3: + lp->has_pwm = 1; + lp->pwm.timerid = PWM3; + s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB3_TOUT3); + break; + default: + break; + } + + if (lp->has_pwm) { + lp->pwm.prescaler = 0; + lp->pwm.divider = S3C2410_TCFG1_MUX3_DIV8; + lp->pwm.counter = COUNTER; + lp->pwm.comparer = COUNTER; + s3c2410_pwm_enable(&lp->pwm); + s3c2410_pwm_start(&lp->pwm); + } + + mutex_init(&lp->mutex); + rc = led_classdev_register(&pdev->dev, &lp->cdev); + } + + return 0; +} + +static int gta02led_remove(struct platform_device *pdev) +{ + struct gta02_led_bundle *bundle = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < bundle->num_leds; i++) { + struct gta02_led_priv *lp = &bundle->led[i]; + if (lp->has_pwm) + s3c2410_pwm_disable(&lp->pwm); + + led_classdev_unregister(&lp->cdev); + mutex_destroy(&lp->mutex); + } + + platform_set_drvdata(pdev, NULL); + kfree(bundle); + + return 0; +} + +static struct platform_driver gta02led_driver = { + .probe = gta02led_probe, + .remove = gta02led_remove, +#ifdef CONFIG_PM + .suspend = gta02led_suspend, + .resume = gta02led_resume, +#endif + .driver = { + .name = "gta02-led", + }, +}; + +static int __init gta02led_init(void) +{ + return platform_driver_register(>a02led_driver); +} + +static void __exit gta02led_exit(void) +{ + platform_driver_unregister(>a02led_driver); +} + +module_init(gta02led_init); +module_exit(gta02led_exit); + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("FIC GTA02 LED driver"); +MODULE_LICENSE("GPL");