Skip to content

Instantly share code, notes, and snippets.

@fat-tire
Created June 2, 2012 19:43
Show Gist options
  • Save fat-tire/2859726 to your computer and use it in GitHub Desktop.
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
$ 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