public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] backlight dimmer
@ 2007-10-28 16:10 jack
  2007-10-28 16:15 ` Samuel Tardieu
  2007-10-30 14:48 ` Pavel Machek
  0 siblings, 2 replies; 7+ messages in thread
From: jack @ 2007-10-28 16:10 UTC (permalink / raw)
  To: linux-kernel; +Cc: rpurdie

Hello,
 this patch implements a macbook like backlight dimmer on top of 
backlight.c.

The dimmer is entirely in kernelspace and is suitable for an embedded 
context in order to avoid the overhead of a daemon controlling the 
backlight. Implementing this functionality in userspace has other 
advantages and is a perfectly reasonable alternative, so this patch is
not the definitive solution.

activate dimmer:
echo 1 > /sys/devices/virtual/backlight/*/dimmer_control

other attributes (britness levels & timeout):
/sys/devices/virtual/backlight/*/dimmer_high_level
/sys/devices/virtual/backlight/*/dimmer_low_level
/sys/devices/virtual/backlight/*/dimmer_timeout

regards,
jacopo antonello



******************************* diff follows
--- linux-2.6.23.1/include/linux/backlight.h	2007-10-12 18:43:44.000000000 +0200
+++ b/include/linux/backlight.h	2007-10-28 13:45:21.000000000 +0100
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
+#include <linux/timeout.h>

/* Notes on locking:
 *
@@ -70,6 +71,12 @@ struct backlight_device {
	/* The framebuffer notifier block */
	struct notifier_block fb_notif;

+  /* dimmer stuff */
+  struct timeout *timeout;
+  struct input_handler *input_handler;
+  unsigned short dimmer_low_level;
+  unsigned short dimmer_high_level;
+
	struct device dev;
};

--- linux-2.6.23.1/drivers/video/backlight/Makefile	2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/video/backlight/Makefile	2007-10-28 13:45:21.000000000 +0100
@@ -1,7 +1,7 @@
# Backlight & LCD drivers

obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o
-obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o timeout.o
obj-$(CONFIG_BACKLIGHT_CORGI)	+= corgi_bl.o
obj-$(CONFIG_BACKLIGHT_HP680)	+= hp680_bl.o
obj-$(CONFIG_BACKLIGHT_LOCOMO)	+= locomolcd.o
--- linux-2.6.23.1/include/linux/timeout.h	1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/timeout.h	2007-10-28 13:45:21.000000000 +0100
@@ -0,0 +1,66 @@
+/*
+ *  timeout.h - simple timeout
+ *
+ *
+ *  Copyright (C) 2007 Jacopo Antonello <jack@antonello.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _TIMEOUT_H_
+#define _TIMEOUT_H_
+
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+enum timeout_state {
+  TIMEOUT_RUNNING,
+  TIMEOUT_TRIGGERED,
+  TIMEOUT_FINILIZED
+};
+
+struct timeout {
+  struct mutex lock;
+  spinlock_t latest_lock;
+  struct delayed_work trigger_worker;
+  struct work_struct start_worker;
+  unsigned long latest;
+  enum timeout_state state;
+
+  unsigned long duration; /* client defined duration */
+  unsigned long data; /* client data */
+  void (*trigger)(unsigned long); /* client function */
+  void (*start)(unsigned long); /* client function */
+};
+
+extern void timeout_touch(struct timeout *timeout);
+
+extern void timeout_init(struct timeout *timeout);
+
+extern void timeout_kill(struct timeout *timeout);
+
+static inline int timeout_sec2jiffies(int secs)
+{
+    return secs * HZ;
+}
+
+static inline int timeout_jif2sec(unsigned long jif)
+{
+    return jif / HZ;
+}
+
+#endif
--- linux-2.6.23.1/drivers/video/backlight/timeout.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/video/backlight/timeout.c	2007-10-28 13:45:21.000000000 +0100
@@ -0,0 +1,143 @@
+/*
+ *  timeout.c - simple timeout
+ *
+ *
+ *  Copyright (C) 2007 Jacopo Antonello <jack@antonello.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/timeout.h>
+
+
+/* atomic context helpers for timeout->latest */
+static inline unsigned long read_latest(struct timeout *timeout)
+{
+  unsigned long ret;
+  spin_lock(&timeout->latest_lock);
+  ret = timeout->latest;
+  spin_unlock(&timeout->latest_lock);
+  return ret;
+}
+
+static inline unsigned long elapsed(struct timeout *timeout)
+{
+  unsigned long elapsed;
+  spin_lock(&timeout->latest_lock);
+  elapsed = jiffies - timeout->latest;
+  spin_unlock(&timeout->latest_lock);
+  return elapsed;
+}
+
+static inline void touch_latest(struct timeout *timeout)
+{
+  spin_lock(&timeout->latest_lock);
+  timeout->latest = jiffies;
+  spin_unlock(&timeout->latest_lock);
+}
+
+/* non-atomic context */
+static void trigger_worker_function(struct work_struct *work)
+{
+  struct timeout *timeout = container_of(work, struct timeout, trigger_worker.work);
+
+  mutex_lock(&timeout->lock);
+
+  /* flag to kill worker */
+  if (timeout->state != TIMEOUT_RUNNING)
+    goto exit;
+
+  /* TODO beware of jiffies calculations! use helpers */
+  if ( elapsed(timeout) >= timeout->duration ) {
+    /*
+    printk(KERN_DEBUG "tigger_worker_function() -> timeout->trigger() (elapsed: %f) \n",
+        (elapsed(timeout) / (float) (HZ)));
+        */
+    printk(KERN_DEBUG "tigger_worker_function() -> timeout->trigger() (elapsed: %ld ms) \n",
+        (elapsed(timeout) * 1000 / (HZ)));
+    timeout->state = TIMEOUT_TRIGGERED;
+    timeout->trigger(timeout->data);
+  }
+  else {
+    unsigned long new_delay = ( read_latest(timeout) - jiffies ) + timeout->duration;
+    /*
+    printk(KERN_DEBUG "tigger_worker_function() -> schedule_delayed_work() (new_delay: %ld %f)\n",
+        new_delay, new_delay / (float) (HZ));
+        */
+    printk(KERN_DEBUG "tigger_worker_function() -> schedule_delayed_work() (new_delay: %ld jif %ld ms)\n",
+        new_delay, new_delay * 1000 / (HZ));
+    schedule_delayed_work(&timeout->trigger_worker,
+/*        ( jiffies - read_latest(timeout) ) + timeout->duration ); */
+        new_delay );
+
+  }
+
+  exit:
+    mutex_unlock(&timeout->lock);
+}
+
+/* non-atomic context */
+static void start_worker_function(struct work_struct *work)
+{
+  struct timeout *timeout = container_of(work, struct timeout, start_worker);
+
+  printk(KERN_DEBUG "start_worker_function() -> timeout->start()\n");
+  timeout->start(timeout->data);
+  schedule_delayed_work(&timeout->trigger_worker, timeout->duration);
+}
+
+/* atomic context touch */
+void timeout_touch(struct timeout *timeout)
+{
+  touch_latest(timeout);
+
+  if (timeout->state == TIMEOUT_TRIGGERED &&
+      mutex_trylock(&timeout->lock)) {
+
+    if (timeout->state == TIMEOUT_TRIGGERED) {
+      timeout->state = TIMEOUT_RUNNING;
+      schedule_work(&timeout->start_worker);
+    }
+
+    mutex_unlock(&timeout->lock);
+  }
+}
+EXPORT_SYMBOL(timeout_touch);
+
+void timeout_init(struct timeout *timeout)
+{
+  mutex_init(&timeout->lock);
+  spin_lock_init(&timeout->latest_lock);
+  INIT_DELAYED_WORK(&timeout->trigger_worker, trigger_worker_function);
+  INIT_WORK(&timeout->start_worker, start_worker_function);
+  timeout->state = TIMEOUT_TRIGGERED;
+}
+EXPORT_SYMBOL(timeout_init);
+
+void timeout_kill(struct timeout *timeout)
+{
+  mutex_lock(&timeout->lock);
+  if (timeout->state == TIMEOUT_RUNNING)
+    cancel_delayed_work_sync(&timeout->trigger_worker);
+
+  flush_scheduled_work();
+  timeout->state = TIMEOUT_FINILIZED;
+  mutex_unlock(&timeout->lock);
+}
+EXPORT_SYMBOL(timeout_kill);
--- linux-2.6.23.1/drivers/video/backlight/backlight.c	2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/video/backlight/backlight.c	2007-10-28 13:45:21.000000000 +0100
@@ -8,7 +8,8 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
-#include <linux/backlight.h>
+#include <linux/input.h>
+#include <linux/backlight.h> 
#include <linux/notifier.h>
#include <linux/ctype.h>
#include <linux/err.h>
@@ -172,6 +173,267 @@ static void bl_device_release(struct dev
	kfree(bd);
}

+/*
+ * ---------------------------------------------------
+ *           backlight dimmer
+ * --------------------------------------------------
+ */
+
+static const struct input_device_id idi_dimmer[] = {
+  {
+    .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+    .evbit = { BIT(EV_KEY) },
+  }, /* key event */
+  {
+    .flags = INPUT_DEVICE_ID_MATCH_RELBIT,
+    .relbit = { BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) },
+  },  /* rel axes */
+  {
+    .flags = INPUT_DEVICE_ID_MATCH_ABSBIT,
+    .absbit = { BIT(ABS_X) | BIT(ABS_Y) },
+  },  /* abs axes */
+  { }
+};
+
+static void handler_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+  struct backlight_device *dev = (struct backlight_device *)handle->private;
+
+  switch(type) {
+    case EV_KEY:
+    case EV_REL:
+      timeout_touch(dev->timeout);
+  }
+}
+
+static void dimmer_start(unsigned long data)
+{
+	struct backlight_device *bd = (struct backlight_device *)data;
+
+  printk(KERN_DEBUG "SWITCH TO HIGH\n");
+  bd->props.brightness = bd->dimmer_high_level;
+  backlight_update_status(bd);
+}
+
+static void dimmer_trigger(unsigned long data)
+{
+	struct backlight_device *bd = (struct backlight_device *)data;
+
+  printk(KERN_DEBUG "SWITCH TO LOW\n");
+  bd->props.brightness = bd->dimmer_low_level;
+  backlight_update_status(bd);
+}
+
+static int handler_connect(struct input_handler *handler, struct input_dev *dev,
+    const struct input_device_id *id)
+{
+  int error = 0;
+  struct input_handle *handle;
+
+
+  handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+  if (!handle)
+    return -ENOMEM;
+
+  handle->handler = handler;
+  handle->dev = dev;
+  handle->name = "backlight";
+  handle->private = handler->private;
+
+  error = input_register_handle(handle);
+  if (error)
+    goto err_free_handle;
+
+  error = input_open_device(handle);
+  if (error)
+    goto err_unregister_handle;
+
+  return 0;
+
+  err_unregister_handle:
+    input_unregister_handle(handle);
+  err_free_handle:
+    kfree(handle);
+  return error;
+}
+
+static void handler_disconnect(struct input_handle *handle)
+{
+  input_close_device(handle);
+  input_unregister_handle(handle);
+  kfree(handle);
+}
+
+static int enable_dimmer(struct backlight_device *dev)
+{
+  printk(KERN_DEBUG "enable_dimmer()...\n");
+  dev->input_handler = kzalloc(sizeof(struct input_handler), GFP_KERNEL);
+  if (!dev->input_handler)
+    return -ENOMEM;
+
+  if (!dev->dimmer_low_level)
+    dev->dimmer_low_level = dev->props.max_brightness/2;
+  if (!dev->dimmer_high_level)
+    dev->dimmer_high_level = dev->props.max_brightness;
+
+  dev->input_handler->event = handler_event;
+  dev->input_handler->connect = handler_connect;
+  dev->input_handler->disconnect = handler_disconnect;
+  dev->input_handler->name = "backlight";
+  dev->input_handler->id_table = idi_dimmer;
+  dev->input_handler->private = dev;
+
+  dev->timeout = kzalloc(sizeof(struct timeout), GFP_KERNEL);
+  if (!dev->timeout)
+    goto free_handler;
+
+  timeout_init(dev->timeout);
+  dev->timeout->duration = timeout_sec2jiffies(5);
+  dev->timeout->data = (unsigned long)dev;
+  dev->timeout->trigger = dimmer_trigger;
+  dev->timeout->start = dimmer_start;
+
+  if (!input_register_handler(dev->input_handler))
+    return 0; /* success */
+  /* else go on & cleanup */
+
+  free_handler:
+    kfree(dev->input_handler);
+    dev->input_handler=NULL;
+    return -ENOMEM;
+}
+
+static void disable_dimmer(struct backlight_device *dev)
+{
+  if (!dev->input_handler)
+    return;
+
+  input_unregister_handler(dev->input_handler);
+  timeout_kill(dev->timeout);
+  kfree(dev->input_handler);
+  kfree(dev->timeout);
+  dev->input_handler = NULL;
+  dev->timeout = NULL;
+  printk(KERN_DEBUG "disable_dimmer()\n");
+}
+
+/* show */
+static ssize_t show_dimmer_control(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  return snprintf(buf, PAGE_SIZE, "%d\n",
+      (bd->input_handler != NULL));
+}
+
+static ssize_t show_dimmer_timeout(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  if (bd->timeout)
+    return snprintf(buf, PAGE_SIZE, "%d\n",
+        timeout_jif2sec(bd->timeout->duration));
+  else
+    return 0;
+}
+
+static ssize_t show_dimmer_low_level(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  return snprintf(buf, PAGE_SIZE, "%d\n",
+      bd->dimmer_low_level);
+}
+
+static ssize_t show_dimmer_high_level(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  return snprintf(buf, PAGE_SIZE, "%d\n",
+      bd->dimmer_high_level);
+}
+
+/* store */
+static ssize_t store_dimmer_control(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  char *endp;
+  int control = simple_strtoul(buf, &endp, 0);
+
+	mutex_lock(&bd->ops_lock);
+  if (control == 1 && !bd->input_handler)
+    enable_dimmer(bd);
+  else if (control == 0 && bd->input_handler)
+    disable_dimmer(bd);
+  mutex_unlock(&bd->ops_lock);
+
+	return count;
+}
+
+static ssize_t store_dimmer_timeout(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  char *endp;
+  int timeout = simple_strtoul(buf, &endp, 0);
+
+  if (timeout <= 0 || timeout >= 60*30)
+    return -ENXIO;
+
+	mutex_lock(&bd->ops_lock);
+  if (!bd->timeout) {
+	  mutex_unlock(&bd->ops_lock);
+    return -ENXIO;
+  }
+  bd->timeout->duration = timeout_sec2jiffies(timeout);
+	mutex_unlock(&bd->ops_lock);
+
+  return count;
+}
+
+static ssize_t store_dimmer_low_level(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  char *endp;
+  int low = simple_strtoul(buf, &endp, 0);
+
+  if (low < 0 || low > bd->props.max_brightness)
+    return -ENXIO;
+
+	mutex_lock(&bd->ops_lock);
+  bd->dimmer_low_level = low;
+	mutex_unlock(&bd->ops_lock);
+
+  return count;
+}
+
+static ssize_t store_dimmer_high_level(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+  char *endp;
+  int high = simple_strtoul(buf, &endp, 0);
+
+  if (high < 0 || high > bd->props.max_brightness)
+    return -ENXIO;
+
+	mutex_lock(&bd->ops_lock);
+  bd->dimmer_high_level = high;
+	mutex_unlock(&bd->ops_lock);
+
+  return count;
+}
+
static struct device_attribute bl_device_attributes[] = {
	__ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
	__ATTR(brightness, 0644, backlight_show_brightness,
@@ -179,6 +441,10 @@ static struct device_attribute bl_device
	__ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
		     NULL),
	__ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
+	__ATTR(dimmer_control, 0666, show_dimmer_control, store_dimmer_control),
+	__ATTR(dimmer_timeout, 0666, show_dimmer_timeout, store_dimmer_timeout),
+	__ATTR(dimmer_low_level, 0666, show_dimmer_low_level, store_dimmer_low_level),
+	__ATTR(dimmer_high_level, 0666, show_dimmer_high_level, store_dimmer_high_level),
	__ATTR_NULL,
};

@@ -259,6 +525,7 @@ void backlight_device_unregister(struct 
#endif
	mutex_lock(&bd->ops_lock);
	bd->ops = NULL;
+  disable_dimmer(bd);
	mutex_unlock(&bd->ops_lock);

	backlight_unregister_fb(bd);




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] backlight dimmer
  2007-10-28 16:10 [PATCH] backlight dimmer jack
@ 2007-10-28 16:15 ` Samuel Tardieu
  2007-10-28 21:30   ` lists
  2007-10-30 14:48 ` Pavel Machek
  1 sibling, 1 reply; 7+ messages in thread
From: Samuel Tardieu @ 2007-10-28 16:15 UTC (permalink / raw)
  To: linux-kernel

>>>>> "Jacopo" == jack  <jack@antonello.org> writes:

Jacopo> this patch implements a macbook like backlight dimmer on top
Jacopo> of backlight.c.

Jacopo, if you want your patch to be reviewed, you should first put it
in an acceptable form. You can start by running it through
scripts/checkpatch.pl.

  Sam
-- 
Samuel Tardieu -- sam@rfc1149.net -- http://www.rfc1149.net/


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] backlight dimmer
  2007-10-28 16:15 ` Samuel Tardieu
@ 2007-10-28 21:30   ` lists
  2007-10-28 21:34     ` Arjan van de Ven
  0 siblings, 1 reply; 7+ messages in thread
From: lists @ 2007-10-28 21:30 UTC (permalink / raw)
  To: Samuel Tardieu; +Cc: linux-kernel

Ok,
  now checkpatch.pl only complains about a missing signed-off-by.
Is this ok for review?

jacopo


--- linux-2.6.23.1/include/linux/backlight.h	2007-10-12 18:43:44.000000000 +0200
+++ b/include/linux/backlight.h	2007-10-28 21:14:21.000000000 +0100
@@ -11,6 +11,7 @@
 #include <linux/device.h>
 #include <linux/mutex.h>
 #include <linux/notifier.h>
+#include <linux/timeout.h>
 
 /* Notes on locking:
  *
@@ -70,6 +71,12 @@ struct backlight_device {
 	/* The framebuffer notifier block */
 	struct notifier_block fb_notif;
 
+  /* dimmer stuff */
+	struct timeout *timeout;
+	struct input_handler *input_handler;
+	unsigned short dimmer_low_level;
+	unsigned short dimmer_high_level;
+
 	struct device dev;
 };
 
--- linux-2.6.23.1/drivers/video/backlight/Makefile	2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/video/backlight/Makefile	2007-10-28 16:42:09.000000000 +0100
@@ -1,7 +1,7 @@
 # Backlight & LCD drivers
 
 obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o
-obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o timeout.o
 obj-$(CONFIG_BACKLIGHT_CORGI)	+= corgi_bl.o
 obj-$(CONFIG_BACKLIGHT_HP680)	+= hp680_bl.o
 obj-$(CONFIG_BACKLIGHT_LOCOMO)	+= locomolcd.o
--- linux-2.6.23.1/include/linux/timeout.h	1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/timeout.h	2007-10-28 21:14:27.000000000 +0100
@@ -0,0 +1,68 @@
+/*
+ *  timeout.h - simple timeout
+ *
+ *
+ *  Copyright (C) 2007 Jacopo Antonello <jack@antonello.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _TIMEOUT_H_
+#define _TIMEOUT_H_
+
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+enum timeout_state {
+	TIMEOUT_RUNNING,
+	TIMEOUT_TRIGGERED,
+	TIMEOUT_FINILIZED
+};
+
+struct timeout {
+	/* allow either start() or trigger() at a time */
+	struct mutex lock;
+	/* serialize updates to latest */
+	spinlock_t latest_lock;
+	struct delayed_work trigger_worker;
+	struct work_struct start_worker;
+	unsigned long latest;
+	enum timeout_state state;
+
+	unsigned long duration; /* client defined duration */
+	unsigned long data; /* client data */
+	void (*trigger)(unsigned long); /* client function */
+	void (*start)(unsigned long); /* client function */
+};
+
+extern void timeout_touch(struct timeout *timeout);
+
+extern void timeout_init(struct timeout *timeout);
+
+extern void timeout_kill(struct timeout *timeout);
+
+static inline int timeout_sec2jiffies(int secs)
+{
+	return secs * HZ;
+}
+
+static inline int timeout_jif2sec(unsigned long jif)
+{
+	return jif / HZ;
+}
+
+#endif
--- linux-2.6.23.1/drivers/video/backlight/timeout.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/video/backlight/timeout.c	2007-10-28 21:18:09.000000000 +0100
@@ -0,0 +1,135 @@
+/*
+ *  timeout.c - simple timeout
+ *
+ *
+ *  Copyright (C) 2007 Jacopo Antonello <jack@antonello.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/timeout.h>
+
+
+/* atomic context helpers for timeout->latest */
+static inline unsigned long read_latest(struct timeout *timeout)
+{
+	unsigned long ret;
+	spin_lock(&timeout->latest_lock);
+	ret = timeout->latest;
+	spin_unlock(&timeout->latest_lock);
+	return ret;
+}
+
+static inline unsigned long elapsed(struct timeout *timeout)
+{
+	unsigned long elapsed;
+	spin_lock(&timeout->latest_lock);
+	elapsed = jiffies - timeout->latest;
+	spin_unlock(&timeout->latest_lock);
+	return elapsed;
+}
+
+static inline void touch_latest(struct timeout *timeout)
+{
+	spin_lock(&timeout->latest_lock);
+	timeout->latest = jiffies;
+	spin_unlock(&timeout->latest_lock);
+}
+
+/* non-atomic context */
+static void trigger_worker_function(struct work_struct *work)
+{
+	struct timeout *timeout = container_of(work, struct timeout,
+			trigger_worker.work);
+
+	mutex_lock(&timeout->lock);
+
+	/* flag to kill worker */
+	if (timeout->state != TIMEOUT_RUNNING)
+		goto exit;
+
+	if (elapsed(timeout) >= timeout->duration) {
+    pr_debug("tigger_worker_function() -> timeout->trigger()"
+				" (elapsed: %ld ms) \n",
+				(elapsed(timeout) * 1000 / (HZ)));
+		timeout->state = TIMEOUT_TRIGGERED;
+		timeout->trigger(timeout->data);
+	} else {
+		unsigned long new_delay = (read_latest(timeout) - jiffies) +
+			timeout->duration;
+		pr_debug("tigger_worker_function() -> schedule_delayed_work() "
+				"(new_delay: %ld jif %ld ms)\n",
+				new_delay, new_delay * 1000 / (HZ));
+		schedule_delayed_work(&timeout->trigger_worker, new_delay);
+	}
+
+exit:
+		mutex_unlock(&timeout->lock);
+}
+
+/* non-atomic context */
+static void start_worker_function(struct work_struct *work)
+{
+	struct timeout *timeout = container_of(work,
+			struct timeout, start_worker);
+
+	pr_debug("start_worker_function() -> timeout->start()\n");
+	timeout->start(timeout->data);
+	schedule_delayed_work(&timeout->trigger_worker, timeout->duration);
+}
+
+/* atomic context touch */
+void timeout_touch(struct timeout *timeout)
+{
+	touch_latest(timeout);
+
+	if (timeout->state == TIMEOUT_TRIGGERED &&
+			mutex_trylock(&timeout->lock)) {
+
+		if (timeout->state == TIMEOUT_TRIGGERED) {
+			timeout->state = TIMEOUT_RUNNING;
+			schedule_work(&timeout->start_worker);
+		}
+
+		mutex_unlock(&timeout->lock);
+	}
+}
+EXPORT_SYMBOL(timeout_touch);
+
+void timeout_init(struct timeout *timeout)
+{
+	mutex_init(&timeout->lock);
+	spin_lock_init(&timeout->latest_lock);
+	INIT_DELAYED_WORK(&timeout->trigger_worker, trigger_worker_function);
+	INIT_WORK(&timeout->start_worker, start_worker_function);
+	timeout->state = TIMEOUT_TRIGGERED;
+}
+EXPORT_SYMBOL(timeout_init);
+
+void timeout_kill(struct timeout *timeout)
+{
+	mutex_lock(&timeout->lock);
+	if (timeout->state == TIMEOUT_RUNNING)
+		cancel_delayed_work_sync(&timeout->trigger_worker);
+
+	flush_scheduled_work();
+	timeout->state = TIMEOUT_FINILIZED;
+	mutex_unlock(&timeout->lock);
+}
+EXPORT_SYMBOL(timeout_kill);
--- linux-2.6.23.1/drivers/video/backlight/backlight.c	2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/video/backlight/backlight.c	2007-10-28 21:29:18.000000000 +0100
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/device.h>
+#include <linux/input.h>
 #include <linux/backlight.h>
 #include <linux/notifier.h>
 #include <linux/ctype.h>
@@ -172,13 +173,282 @@ static void bl_device_release(struct dev
 	kfree(bd);
 }
 
+/*
+ * ---------------------------------------------------
+ *           backlight dimmer
+ * --------------------------------------------------
+ */
+
+static const struct input_device_id idi_dimmer[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT(EV_KEY) },
+	}, /* key event */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_RELBIT,
+		.relbit = { BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) },
+	},	/* rel axes */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
+	},	/* abs axes */
+	{ }
+};
+
+static void handler_event(struct input_handle *handle, unsigned int type,
+		unsigned int code, int value)
+{
+	struct backlight_device *dev =
+		(struct backlight_device *)handle->private;
+
+	switch (type) {
+	case EV_KEY:
+	case EV_REL:
+			timeout_touch(dev->timeout);
+	}
+}
+
+static void dimmer_start(unsigned long data)
+{
+	struct backlight_device *bd = (struct backlight_device *)data;
+
+	pr_debug("switch to high level\n");
+	bd->props.brightness = bd->dimmer_high_level;
+	backlight_update_status(bd);
+}
+
+static void dimmer_trigger(unsigned long data)
+{
+	struct backlight_device *bd = (struct backlight_device *)data;
+
+	pr_debug("switch to low level\n");
+	bd->props.brightness = bd->dimmer_low_level;
+	backlight_update_status(bd);
+}
+
+static int handler_connect(struct input_handler *handler, struct input_dev *dev,
+		const struct input_device_id *id)
+{
+	int error = 0;
+	struct input_handle *handle;
+
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->handler = handler;
+	handle->dev = dev;
+	handle->name = "backlight";
+	handle->private = handler->private;
+
+	error = input_register_handle(handle);
+	if (error)
+		goto err_free_handle;
+
+	error = input_open_device(handle);
+	if (error)
+		goto err_unregister_handle;
+
+	return 0;
+
+err_unregister_handle:
+		input_unregister_handle(handle);
+err_free_handle:
+		kfree(handle);
+	return error;
+}
+
+static void handler_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static int enable_dimmer(struct backlight_device *dev)
+{
+	pr_debug("enable_dimmer()\n");
+	dev->input_handler = kzalloc(sizeof(struct input_handler), GFP_KERNEL);
+	if (!dev->input_handler)
+		return -ENOMEM;
+
+	if (!dev->dimmer_low_level)
+		dev->dimmer_low_level = dev->props.max_brightness/2;
+	if (!dev->dimmer_high_level)
+		dev->dimmer_high_level = dev->props.max_brightness;
+
+	dev->input_handler->event = handler_event;
+	dev->input_handler->connect = handler_connect;
+	dev->input_handler->disconnect = handler_disconnect;
+	dev->input_handler->name = "backlight";
+	dev->input_handler->id_table = idi_dimmer;
+	dev->input_handler->private = dev;
+
+	dev->timeout = kzalloc(sizeof(struct timeout), GFP_KERNEL);
+	if (!dev->timeout)
+		goto free_handler;
+
+	timeout_init(dev->timeout);
+	dev->timeout->duration = timeout_sec2jiffies(5);
+	dev->timeout->data = (unsigned long)dev;
+	dev->timeout->trigger = dimmer_trigger;
+	dev->timeout->start = dimmer_start;
+
+	if (!input_register_handler(dev->input_handler))
+		return 0; /* success */
+	/* else go on & cleanup */
+
+free_handler:
+		kfree(dev->input_handler);
+		dev->input_handler = NULL;
+		return -ENOMEM;
+}
+
+static void disable_dimmer(struct backlight_device *dev)
+{
+	if (!dev->input_handler)
+		return;
+
+	input_unregister_handler(dev->input_handler);
+	timeout_kill(dev->timeout);
+	kfree(dev->input_handler);
+	kfree(dev->timeout);
+	dev->input_handler = NULL;
+	dev->timeout = NULL;
+	pr_debug("disable_dimmer()\n");
+}
+
+/* show */
+static ssize_t show_dimmer_control(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			(bd->input_handler != NULL));
+}
+
+static ssize_t show_dimmer_timeout(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	if (bd->timeout)
+		return snprintf(buf, PAGE_SIZE, "%d\n",
+				timeout_jif2sec(bd->timeout->duration));
+	else
+		return 0;
+}
+
+static ssize_t show_dimmer_low_level(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			bd->dimmer_low_level);
+}
+
+static ssize_t show_dimmer_high_level(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			bd->dimmer_high_level);
+}
+
+/* store */
+static ssize_t store_dimmer_control(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	char *endp;
+	int control = simple_strtoul(buf, &endp, 0);
+
+	mutex_lock(&bd->ops_lock);
+	if (control == 1 && !bd->input_handler)
+		enable_dimmer(bd);
+	else if (control == 0 && bd->input_handler)
+		disable_dimmer(bd);
+	mutex_unlock(&bd->ops_lock);
+
+	return count;
+}
+
+static ssize_t store_dimmer_timeout(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	char *endp;
+	int timeout = simple_strtoul(buf, &endp, 0);
+
+	if (timeout <= 0 || timeout >= 60*30)
+		return -ENXIO;
+
+	mutex_lock(&bd->ops_lock);
+	if (!bd->timeout) {
+		mutex_unlock(&bd->ops_lock);
+		return -ENXIO;
+	}
+	bd->timeout->duration = timeout_sec2jiffies(timeout);
+	mutex_unlock(&bd->ops_lock);
+
+	return count;
+}
+
+static ssize_t store_dimmer_low_level(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	char *endp;
+	int low = simple_strtoul(buf, &endp, 0);
+
+	if (low < 0 || low > bd->props.max_brightness)
+		return -ENXIO;
+
+	mutex_lock(&bd->ops_lock);
+	bd->dimmer_low_level = low;
+	mutex_unlock(&bd->ops_lock);
+
+	return count;
+}
+
+static ssize_t store_dimmer_high_level(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct backlight_device *bd = to_backlight_device(dev);
+
+	char *endp;
+	int high = simple_strtoul(buf, &endp, 0);
+
+	if (high < 0 || high > bd->props.max_brightness)
+		return -ENXIO;
+
+	mutex_lock(&bd->ops_lock);
+	bd->dimmer_high_level = high;
+	mutex_unlock(&bd->ops_lock);
+
+	return count;
+}
+
 static struct device_attribute bl_device_attributes[] = {
 	__ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
 	__ATTR(brightness, 0644, backlight_show_brightness,
-		     backlight_store_brightness),
+				 backlight_store_brightness),
 	__ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
-		     NULL),
+				 NULL),
 	__ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
+	__ATTR(dimmer_control, 0666, show_dimmer_control, store_dimmer_control),
+	__ATTR(dimmer_timeout, 0666, show_dimmer_timeout, store_dimmer_timeout),
+	__ATTR(dimmer_low_level, 0666, show_dimmer_low_level,
+			store_dimmer_low_level),
+	__ATTR(dimmer_high_level, 0666, show_dimmer_high_level,
+			store_dimmer_high_level),
 	__ATTR_NULL,
 };
 
@@ -259,6 +529,7 @@ void backlight_device_unregister(struct 
 #endif
 	mutex_lock(&bd->ops_lock);
 	bd->ops = NULL;
+	disable_dimmer(bd);
 	mutex_unlock(&bd->ops_lock);
 
 	backlight_unregister_fb(bd);


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] backlight dimmer
  2007-10-28 21:30   ` lists
@ 2007-10-28 21:34     ` Arjan van de Ven
  2007-10-28 22:09       ` jack
  0 siblings, 1 reply; 7+ messages in thread
From: Arjan van de Ven @ 2007-10-28 21:34 UTC (permalink / raw)
  To: lists@antonello.org; +Cc: Samuel Tardieu, linux-kernel

On Sun, 28 Oct 2007 22:30:55 +0100
"lists@antonello.org" <lists@antonello.org> wrote:

> Ok,
>   now checkpatch.pl only complains about a missing signed-off-by.
> Is this ok for review?


hi,

when going over your patch.. is there a reason you introduce yet
another timeout infrastructure? Is there something wrong with the
existing ones that maybe should be fixed instead?
Either way.. please put justification for such new mechanism in the
patch changelog....

Greetings,
   Arjan van de Ven

-- 
If you want to reach me at my work email, use arjan@linux.intel.com
For development, discussion and tips for power savings, 
visit http://www.lesswatts.org

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] backlight dimmer
  2007-10-28 21:34     ` Arjan van de Ven
@ 2007-10-28 22:09       ` jack
  0 siblings, 0 replies; 7+ messages in thread
From: jack @ 2007-10-28 22:09 UTC (permalink / raw)
  To: Arjan van de Ven; +Cc: lists@antonello.org, Samuel Tardieu, linux-kernel

Arjan van de Ven wrote:
> On Sun, 28 Oct 2007 22:30:55 +0100
> "lists@antonello.org" <lists@antonello.org> wrote:
> 
>> Ok,
>>   now checkpatch.pl only complains about a missing signed-off-by.
>> Is this ok for review?
> 
> 
> hi,
> 
> when going over your patch.. is there a reason you introduce yet
> another timeout infrastructure? Is there something wrong with the
> existing ones that maybe should be fixed instead?
> Either way.. please put justification for such new mechanism in the
> patch changelog....
> 
> Greetings,
>    Arjan van de Ven
> 

hi,

i don't think there are similar infrastructures. This timeout is
not quite a timer.

The timeout starts counting when timeout_touch() is first called. At
this point the start() function is executed in non-atomic context.
Then either it is reset if timeout_touch() is called in time (and
thus starts to count again). Else it triggers, and executes the
trigger() function in non-atomic context and it stays idle unless
timeout_touch() is called again.

The non-atomic context is needed to use backlight.c mutexes and
that is enabled with the use of workqueues.

I don't mean to add some new infrastructure to the base kernel, but
it seemed a general functionality to me. In fact it may also be fully
included in backlight.c. Since backlight.h is in include/linux, i was
forced to put timeout.h in include/linux also. But this is just
a temporary fix.

jacopo


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] backlight dimmer
  2007-10-28 16:10 [PATCH] backlight dimmer jack
  2007-10-28 16:15 ` Samuel Tardieu
@ 2007-10-30 14:48 ` Pavel Machek
  2007-11-01 23:18   ` jack
  1 sibling, 1 reply; 7+ messages in thread
From: Pavel Machek @ 2007-10-30 14:48 UTC (permalink / raw)
  To: jack; +Cc: linux-kernel, rpurdie

On Sun 2007-10-28 17:10:53, jack@antonello.org wrote:
> Hello,
> this patch implements a macbook like backlight dimmer on 
> top of backlight.c.
> 
> The dimmer is entirely in kernelspace and is suitable 
> for an embedded context in order to avoid the overhead 
> of a daemon controlling the backlight. Implementing this 
> functionality in userspace has other advantages and is a 
> perfectly reasonable alternative, so this patch is
> not the definitive solution.
> 
> activate dimmer:
> echo 1 > /sys/devices/virtual/backlight/*/dimmer_control
> 
> other attributes (britness levels & timeout):
> /sys/devices/virtual/backlight/*/dimmer_high_level
> /sys/devices/virtual/backlight/*/dimmer_low_level
> /sys/devices/virtual/backlight/*/dimmer_timeout

I'd say that userspace makes sense here. I'd want backlight to go down
slowly, for example.

But... maybe undimming should be done in kernel, so it keeps
low,latency?

Hmm, maybe existing screen blanking infrastructure can be reused?

> --- linux-2.6.23.1/include/linux/backlight.h 2007-10-12 
> 18:43:44.000000000 +0200
> +++ b/include/linux/backlight.h	2007-10-28 
> 13:45:21.000000000 +0100
> @@ -11,6 +11,7 @@
> #include <linux/device.h>
> #include <linux/mutex.h>
> #include <linux/notifier.h>
> +#include <linux/timeout.h>
> 
> /* Notes on locking:

Your mail client damages patches?

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] backlight dimmer
  2007-10-30 14:48 ` Pavel Machek
@ 2007-11-01 23:18   ` jack
  0 siblings, 0 replies; 7+ messages in thread
From: jack @ 2007-11-01 23:18 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, rpurdie

Pavel Machek wrote:
> On Sun 2007-10-28 17:10:53, jack@antonello.org wrote:
>> Hello,
>> this patch implements a macbook like backlight dimmer on 
>> top of backlight.c.
>>
>> The dimmer is entirely in kernelspace and is suitable 
>> for an embedded context in order to avoid the overhead 
>> of a daemon controlling the backlight. Implementing this 
>> functionality in userspace has other advantages and is a 
>> perfectly reasonable alternative, so this patch is
>> not the definitive solution.
>>
>> activate dimmer:
>> echo 1 > /sys/devices/virtual/backlight/*/dimmer_control
>>
>> other attributes (britness levels & timeout):
>> /sys/devices/virtual/backlight/*/dimmer_high_level
>> /sys/devices/virtual/backlight/*/dimmer_low_level
>> /sys/devices/virtual/backlight/*/dimmer_timeout
> 
> I'd say that userspace makes sense here. I'd want backlight to go down
> slowly, for example.
> 
> But... maybe undimming should be done in kernel, so it keeps
> low,latency?
> 
> Hmm, maybe existing screen blanking infrastructure can be reused?
> 
>> --- linux-2.6.23.1/include/linux/backlight.h 2007-10-12 
>> 18:43:44.000000000 +0200
>> +++ b/include/linux/backlight.h	2007-10-28 
>> 13:45:21.000000000 +0100
>> @@ -11,6 +11,7 @@
>> #include <linux/device.h>
>> #include <linux/mutex.h>
>> #include <linux/notifier.h>
>> +#include <linux/timeout.h>
>>
>> /* Notes on locking:
> 
> Your mail client damages patches?
> 

Hi,
incremental dimming can be easily implemented on top of this patch. 
It's just about posting the timeout repeatedly. I will post something 
in the near future :).

I believe the userspace vs kernelspace argument is very arbitrary.

As i said, the dimmer can be completely coded in userspace. There are 
good reasons for that. Userspace apps can work out better than the 
kernel what the user is doing. If the user is watching a movie for 
instance, the dimmer should be stopped.

However, the patch is just a light addition to backlight.c. If the 
dimmer is stopped/unused, just a few pointers and some code are wasted.

Userspace apps can start, stop and change the timeout value for the 
dimmer on the fly, as if they were doing the dimming themselves. In 
this case, userspace only notifies the kernel to stop the dimmer 
whenever such action is deemed right. Instead when the dimmer is on, 
which is most of the time, no kernespace/userspace switch is necessary.

Changing the brightness might occur every 10 seconds or so. IMHO this 
is more of a realtime (and thus kernelspace) requirement rather than 
userspace. This also saves us from having a whole (heavyweight) 
dedicated process managing such a basic functionality.

Finally, implementing the dimmer in kernelspace delivers this 
functionality everywhere provided Linux is used. No additional 
software is necessary. This latter would usually have to be tailored 
to different audiences such as desktop, embedded, whatever.

I don't think thunderbird is breaking the patch, since i was able to 
apply it from my previous posts. However here is a web location for 
it, just in case: http://www.antonello.org/dimmer/. Please let me know 
if you can't test it!

jacopo


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2007-11-01 23:19 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-10-28 16:10 [PATCH] backlight dimmer jack
2007-10-28 16:15 ` Samuel Tardieu
2007-10-28 21:30   ` lists
2007-10-28 21:34     ` Arjan van de Ven
2007-10-28 22:09       ` jack
2007-10-30 14:48 ` Pavel Machek
2007-11-01 23:18   ` jack

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox