Created
June 2, 2012 19:43
-
-
Save fat-tire/2859726 to your computer and use it in GitHub Desktop.
differences between leds-omap-pwm.c in 2.6.32 (encore) and 3.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ diff -u nook_kernel/drivers/leds/leds-omap-pwm.c ~/android/ics/kernel/bn/encore/drivers/leds/leds-omap-pwm.c | |
--- nook_kernel/drivers/leds/leds-omap-pwm.c 2012-03-29 18:51:45.956450126 -0700 | |
+++ /home/fattire/android/ics/kernel/bn/encore/drivers/leds/leds-omap-pwm.c 2012-06-02 03:57:24.608694436 -0700 | |
@@ -19,21 +19,29 @@ | |
#include <linux/ctype.h> | |
#include <linux/sched.h> | |
#include <linux/interrupt.h> | |
-#include <asm/delay.h> | |
+#include <linux/slab.h> | |
+#include <linux/delay.h> | |
+#include <linux/clk.h> | |
#include <plat/board.h> | |
-#include <mach/dmtimer.h> | |
+#include <plat/dmtimer.h> | |
+#include "leds-omap-pwm.h" | |
+ | |
+/* 38400000 / (1 << (COUNTER_DEVIDER + 1)) */ | |
+#define COUNTER_DEVIDER 5 /* 600000 Hz counter in freq */ | |
+#define COUNTER_LOAD_VAL (0xFFFFFFFF - 4687 - 4) /* 128 Hz PWM out freq */ | |
+#define COUNTER_TO_MATCH_GUARD 80 | |
+ | |
+#define TIMER_INT_FLAGS (OMAP_TIMER_INT_MATCH | \ | |
+ OMAP_TIMER_INT_OVERFLOW) | |
#ifdef CONFIG_HAS_EARLYSUSPEND | |
static void omap_pwm_led_early_suspend(struct early_suspend *handler); | |
static void omap_pwm_led_late_resume(struct early_suspend *handler); | |
#endif | |
-#define NO_LED_FULL /* to avoid led flickering, never set brightness to FULL */ | |
- | |
struct omap_pwm_led { | |
struct led_classdev cdev; | |
struct work_struct work; | |
- struct delayed_work disable_int_work; | |
struct omap_pwm_led_platform_data *pdata; | |
struct omap_dm_timer *intensity_timer; | |
struct omap_dm_timer *blink_timer; | |
@@ -43,9 +51,14 @@ | |
#ifdef CONFIG_HAS_EARLYSUSPEND | |
struct early_suspend early_suspend; | |
#endif | |
- atomic_t cached_brightness; | |
+ atomic_t cached_match_val; | |
}; | |
+static inline unsigned int get_match_val(unsigned char index) | |
+{ | |
+ return match_data[index]; | |
+} | |
+ | |
static inline struct omap_pwm_led *pdev_to_omap_pwm_led(struct platform_device *pdev) | |
{ | |
return platform_get_drvdata(pdev); | |
@@ -63,10 +76,10 @@ | |
static void omap_pwm_led_set_blink(struct omap_pwm_led *led) | |
{ | |
- int def_on = 1; | |
+ int invert = 1; | |
- if ( led->pdata ) | |
- def_on = led->pdata->def_on; | |
+ if (led->pdata) | |
+ invert = led->pdata->invert; | |
if (!led->powered) | |
return; | |
@@ -80,70 +93,112 @@ | |
omap_dm_timer_stop(led->blink_timer); | |
omap_dm_timer_set_load(led->blink_timer, 1, -load_reg); | |
omap_dm_timer_set_match(led->blink_timer, 1, -cmp_reg); | |
- omap_dm_timer_set_pwm(led->blink_timer, | |
- def_on, 1, | |
+ omap_dm_timer_set_pwm(led->blink_timer, | |
+ invert, 1, | |
OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); | |
omap_dm_timer_write_counter(led->blink_timer, -2); | |
omap_dm_timer_start(led->blink_timer); | |
} else { | |
- omap_dm_timer_set_pwm(led->blink_timer, | |
- def_on, 1, | |
+ omap_dm_timer_set_pwm(led->blink_timer, | |
+ invert, 1, | |
OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); | |
omap_dm_timer_stop(led->blink_timer); | |
} | |
} | |
-/* | |
- Setting the timer match value when PWM is high (not yet toggled to low on MATCH) | |
- could cause the led flickering issue. Therefore, to eliminate the flickering we | |
- set the new brightness (match) value after PWM goes down, not immediately upon | |
- receiving the brightness change request, that is, do it in the MATCH interrupt handler. | |
- | |
- The MATCH interrupt is disabled after the new match value is set to avoid performance hit. | |
- However interrupt cannot be disabled immediately in the interrupt handler as it will cause | |
- flickering when the next change request comes in, for some unknown reasons. Therefore, we | |
- disable match interrupt in a delayed (1/8s) work. So the match interrupt will last for 1/8s, | |
- which is about 15 ISR calls. Performance wise this shall be OK. | |
-*/ | |
+static inline void omap_pwm_set_match(struct omap_dm_timer *timer, | |
+ unsigned int val) | |
+{ | |
+ omap_dm_timer_set_match(timer, 1, val); | |
+ omap_dm_timer_set_int_disable(timer, TIMER_INT_FLAGS); | |
+} | |
+ | |
static irqreturn_t intensity_timer_match_interrupt(int irq, void *arg) | |
{ | |
- struct omap_pwm_led *led = (struct omap_pwm_led*) arg; | |
- struct omap_dm_timer* timer = (struct omap_dm_timer*)led->intensity_timer; | |
+ struct omap_pwm_led *led; | |
+ struct omap_dm_timer *timer; | |
+ unsigned int counter; | |
+ unsigned int match_val; | |
+ unsigned int current_match_val; | |
+ unsigned int status; | |
+ | |
+ led = (struct omap_pwm_led *) arg; | |
+ timer = (struct omap_dm_timer *) led->intensity_timer; | |
+ match_val = atomic_read(&led->cached_match_val); | |
+ | |
+ /* disable interrupts */ | |
+ local_irq_disable(); | |
+ | |
+ /* get int status */ | |
+ status = omap_dm_timer_read_status(timer); | |
+ | |
+ /* get current match value */ | |
+ current_match_val = omap_dm_timer_get_match(timer); | |
+ | |
+ /* We must update match register only in case: | |
+ * - new match value is bigger than old one | |
+ * - when old match value is bigger than new one, current | |
+ * counter value must be bigger than old match value or | |
+ * lower than new match value. | |
+ * | |
+ * If this conditions are not met, we will write a match value, | |
+ * at moment when match event doesn't trigered yet and the new | |
+ * match value is lower than counter. This will result in missing | |
+ * the match event for this period. | |
+ */ | |
+ counter = omap_dm_timer_read_counter(timer); | |
+ | |
+ if ((counter + COUNTER_TO_MATCH_GUARD) < match_val) | |
+ omap_pwm_set_match(timer, match_val); | |
+ else if (counter > current_match_val) | |
+ omap_pwm_set_match(timer, match_val); | |
+ | |
+ /* acknowledge interrupts */ | |
+ omap_dm_timer_write_status(timer, status); | |
- /* clear interrupt status bit */ | |
- omap_dm_timer_write_status(timer, OMAP_TIMER_INT_MATCH); | |
+ /* enable interrupts */ | |
+ local_irq_enable(); | |
- /* set new match value */ | |
- omap_dm_timer_set_match(timer, 1, (0xffffff00) | atomic_read(&led->cached_brightness)); | |
- | |
return IRQ_HANDLED; | |
} | |
-static void omap_pwm_disable_int_work(struct work_struct *work) | |
+static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle) | |
{ | |
- struct delayed_work *dw = container_of(work, struct delayed_work, work); | |
- struct omap_pwm_led* led = container_of(dw, struct omap_pwm_led, disable_int_work); | |
- | |
- /* disable match interrupt*/ | |
- omap_dm_timer_set_int_enable(led->intensity_timer, 0); | |
+ struct omap_dm_timer *timer; | |
+ unsigned int match_val; | |
+ unsigned int current_match_val; | |
+ | |
+ timer = (struct omap_dm_timer *) led->intensity_timer; | |
+ | |
+ current_match_val = atomic_read(&led->cached_match_val); | |
+ match_val = get_match_val(cycle); | |
+ | |
+ if (current_match_val < match_val) { | |
+ omap_dm_timer_set_match(timer, 1, match_val); | |
+ atomic_set(&led->cached_match_val, match_val); | |
+ } else { | |
+ atomic_set(&led->cached_match_val, match_val); | |
+ omap_dm_timer_set_int_enable(timer, TIMER_INT_FLAGS); | |
+ } | |
} | |
static void omap_pwm_led_power_on(struct omap_pwm_led *led) | |
{ | |
+ int invert = 1; | |
+ unsigned int timerval; | |
int err; | |
- | |
+ | |
+ if (led->pdata) | |
+ invert = led->pdata->invert; | |
+ | |
if (led->powered) | |
return; | |
led->powered = 1; | |
- /* Select clock */ | |
+ /* Select clock source */ | |
omap_dm_timer_enable(led->intensity_timer); | |
- omap_dm_timer_set_source(led->intensity_timer, OMAP_TIMER_SRC_32_KHZ); | |
- | |
- /* Turn voltage on */ | |
- if (led->pdata->set_power != NULL) | |
- led->pdata->set_power(led->pdata, 1); | |
- | |
+ omap_dm_timer_set_source(led->intensity_timer, OMAP_TIMER_SRC_SYS_CLK); | |
+ omap_dm_timer_set_prescaler(led->intensity_timer, COUNTER_DEVIDER); | |
/* Enable PWM timers */ | |
if (led->blink_timer != NULL) { | |
omap_dm_timer_enable(led->blink_timer); | |
@@ -152,39 +207,52 @@ | |
omap_pwm_led_set_blink(led); | |
} | |
- // register timer match interrupt | |
- err = request_irq(omap_dm_timer_get_irq(led->intensity_timer), intensity_timer_match_interrupt, | |
- IRQF_DISABLED, "led intensity timer", (void*)led); | |
+ omap_dm_timer_set_pwm(led->intensity_timer, invert ? 0 : 1, 1, | |
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); | |
+ | |
+ omap_dm_timer_set_load(led->intensity_timer, 1, COUNTER_LOAD_VAL); | |
+ omap_dm_timer_start(led->intensity_timer); | |
+ omap_pwm_led_set_pwm_cycle(led, 0); | |
+ | |
+ timerval = omap_dm_timer_read_counter(led->intensity_timer); | |
+ if (timerval < COUNTER_LOAD_VAL) | |
+ omap_dm_timer_write_counter(led->intensity_timer, -2); | |
+ | |
+ /* Turn voltage on */ | |
+ if (led->pdata->set_power != NULL) | |
+ led->pdata->set_power(led->pdata, 1); | |
+ | |
+ /* register timer match and overflow interrupts */ | |
+ err = request_irq(omap_dm_timer_get_irq(led->intensity_timer), | |
+ intensity_timer_match_interrupt, | |
+ IRQF_DISABLED, "led intensity timer", (void *)led); | |
if (err) { | |
- printk(KERN_ERR "omap_pwm_led_power_on : unable to intensity gptimer IRQ\n"); | |
+ printk(KERN_ERR "%s(%s) : unable to get gptimer%d IRQ\n", | |
+ __func__, __FILE__, led->pdata->intensity_timer); | |
} | |
- | |
- omap_dm_timer_set_load(led->intensity_timer, 1, 0xffffff00); | |
} | |
static void omap_pwm_led_power_off(struct omap_pwm_led *led) | |
{ | |
- int def_on = 1; | |
+ int invert = 1; | |
if (!led->powered) | |
return; | |
led->powered = 0; | |
- /* cancel the pending disable interrupt work */ | |
- cancel_delayed_work_sync(&led->disable_int_work); | |
- /* disable timer match interrupt */ | |
- omap_dm_timer_set_int_enable(led->intensity_timer, 0); | |
- /* free irq, as corresponding to requst_irq() in omap_pwm_led_power_on*/ | |
- free_irq(omap_dm_timer_get_irq(led->intensity_timer), (void*)led); | |
- /* mark the next brightness request as first request */ | |
- atomic_set(&led->cached_brightness, -1); | |
+ if (led->pdata->set_power != NULL) | |
+ led->pdata->set_power(led->pdata, 0); | |
- if ( led->pdata ) | |
- def_on = led->pdata->def_on; | |
+ /* disable timer match interrupt */ | |
+ omap_dm_timer_set_int_disable(led->intensity_timer, | |
+ OMAP_TIMER_INT_MATCH); | |
+ free_irq(omap_dm_timer_get_irq(led->intensity_timer), (void *)led); | |
+ if (led->pdata) | |
+ invert = led->pdata->invert; | |
/* Everything off */ | |
- omap_dm_timer_set_pwm(led->intensity_timer, | |
- def_on ? 0 : 1, 1, | |
+ omap_dm_timer_set_pwm(led->intensity_timer, | |
+ invert ? 0 : 1, 1, | |
OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); | |
omap_dm_timer_stop(led->intensity_timer); | |
omap_dm_timer_disable(led->intensity_timer); | |
@@ -194,73 +262,7 @@ | |
omap_dm_timer_disable(led->blink_timer); | |
} | |
- if (led->pdata->set_power != NULL) | |
- led->pdata->set_power(led->pdata, 0); | |
-} | |
- | |
-static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle) | |
-{ | |
- unsigned int timerval; | |
- int def_on = 1; | |
- | |
- if ( led->pdata ) | |
- def_on = led->pdata->def_on; | |
- | |
-#ifdef NO_LED_FULL | |
- /* Reducing brightness starting from FULL (255) will result in | |
- backlight flickering. Therefore, we give up the LED_FULL value | |
- and never set it to FULL as the brightness difference between | |
- 255 (FULL) and 254 is ignorable. As a result the next | |
- if (cycle == LED_FULL) block is actually disabled. */ | |
- if (cycle == LED_FULL) { | |
- cycle = LED_FULL - 1; | |
- } | |
-#endif | |
- | |
- if (cycle == LED_FULL || cycle == LED_OFF) { | |
- omap_dm_timer_set_pwm(led->intensity_timer, | |
- def_on ? 1 : 0, 1, | |
- OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); | |
- omap_dm_timer_stop(led->intensity_timer); | |
- } else { | |
- /* If this is the first time to set the brightness after led power on? */ | |
- int first_request = (atomic_read(&led->cached_brightness) == -1)? 1 : 0; | |
- | |
- /* Cache the new brightness request and set it in the match interrupt handler afterwards. */ | |
- atomic_set(&led->cached_brightness, cycle); | |
- | |
- omap_dm_timer_set_pwm(led->intensity_timer, | |
- def_on ? 0 : 1, 1, | |
- OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); | |
- | |
- if (first_request) { | |
- /* if this is the first request after backlight is turned on, we have to set the match directly. */ | |
- omap_dm_timer_set_match(led->intensity_timer, 1, | |
- (0xffffff00) | cycle); | |
- } else { | |
- /* Don't set the new brightness here, but set it in the match interrupt handler. */ | |
- | |
- /* Cancel the pending disable interrupt work */ | |
- cancel_delayed_work_sync(&led->disable_int_work); | |
- | |
- /* At this point timer match interrupt must have been disabled, so enable it. */ | |
- omap_dm_timer_set_int_enable(led->intensity_timer, OMAP_TIMER_INT_MATCH); | |
- | |
- /* Schedule to disable interrupt to avoid performance hit. | |
- Note that we cannot disable interrupt immediately in the interrupt handler | |
- because for some unknown reason it will cause flickering when the next brightness | |
- request comes in. We have to let the interrupt last for 1/8 s and then disalbe it | |
- in the delayed work. */ | |
- schedule_delayed_work(&led->disable_int_work, HZ >> 3); | |
- } | |
- | |
- /* ensure timer value is in range */ | |
- timerval = omap_dm_timer_read_counter(led->intensity_timer); | |
- if (timerval < 0xffffff00) | |
- omap_dm_timer_write_counter(led->intensity_timer, -2); | |
- | |
- omap_dm_timer_start(led->intensity_timer); | |
- } | |
+ atomic_set(&led->cached_match_val, 0); | |
} | |
static void omap_pwm_led_set(struct led_classdev *led_cdev, | |
@@ -378,16 +380,14 @@ | |
led->cdev.name = pdata->name; | |
led->pdata = pdata; | |
led->brightness = pdata->def_brightness; | |
- atomic_set(&led->cached_brightness, -1); | |
INIT_WORK(&led->work, omap_pwm_led_work); | |
- INIT_DELAYED_WORK(&led->disable_int_work, omap_pwm_disable_int_work); | |
dev_info(&pdev->dev, "OMAP PWM LED (%s) at GP timer %d/%d\n", | |
pdata->name, pdata->intensity_timer, pdata->blink_timer); | |
- | |
- if (pdata->def_brightness) { | |
+ | |
+ if (pdata->def_brightness) | |
led->cdev.brightness = pdata->def_brightness; | |
- } | |
+ | |
/* register our new led device */ | |
ret = led_classdev_register(&pdev->dev, &led->cdev); | |
if (ret < 0) { | |
@@ -407,7 +407,8 @@ | |
if (pdata->blink_timer != 0) { | |
led->blink_timer = omap_dm_timer_request_specific(pdata->blink_timer); | |
if (led->blink_timer == NULL) { | |
- dev_err(&pdev->dev, "failed to request blinking pwm timer\n"); | |
+ dev_err(&pdev->dev, | |
+ "failed to request blinking pwm timer\n"); | |
ret = -ENODEV; | |
goto error_blink1; | |
} | |
@@ -415,12 +416,12 @@ | |
ret = device_create_file(led->cdev.dev, | |
&dev_attr_on_period); | |
- if(ret) | |
+ if (ret) | |
goto error_blink2; | |
ret = device_create_file(led->cdev.dev, | |
&dev_attr_off_period); | |
- if(ret) | |
+ if (ret) | |
goto error_blink3; | |
} | |
@@ -455,6 +456,7 @@ | |
static int omap_pwm_led_remove(struct platform_device *pdev) | |
{ | |
+ | |
struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev); | |
#ifdef CONFIG_HAS_EARLYSUSPEND | |
@@ -501,7 +503,8 @@ | |
#endif | |
#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) | |
-static int omap_pwm_led_suspend(struct platform_device *pdev, pm_message_t state) | |
+static int omap_pwm_led_suspend(struct platform_device *pdev, | |
+ pm_message_t state) | |
{ | |
struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment