Linux-fbdev Archive on lore.kernel.org
 help / color / Atom feed
From: "Bruno Prémont" <bonbons@linux-vserver.org>
To: Jiri Kosina <jkosina-AlSwsSmVLrQ@public.gmane.org>
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	"Rick L. Vinyard Jr."
	<rvinyard-qcTL/1vZYtiVc3sceRu5cw@public.gmane.org>,
	Nicu Pavel <npavel-VxACSXvuqMTQT0dZR+AlfA@public.gmane.org>,
	Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>,
	Jaya Kumar
	<jayakumar.lkml-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
	Dmitry Torokhov
	<dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
	Richard Purdie <rpurdie-Fm38FmjxZ/leoWH0uzbU5w@public.gmane.org>
Subject: [PATCH v3 5/6] hid: add GPO (leds) support to PicoLCD device
Date: Wed, 24 Mar 2010 22:55:56 +0000
Message-ID: <20100324235556.1608ee39@neptune.home> (raw)
In-Reply-To: <20100324233707.7243b04d-hY15tx4IgV39zxVx7UNMDg@public.gmane.org>

Add leds support to PicoLCD device to drive the GPO pins.

GPO support depends on leds class and is only being
compiled if leds class has been selected.

Changes since v2:
 - Drop inline keyword on non-stub functions

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/hid/Kconfig       |    6 +-
 drivers/hid/hid-picolcd.c |  164 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 167 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 399edc5..34f6593 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -278,11 +278,11 @@ config HID_PICOLCD
 	  - Keypad
 	  - Switching between Firmware and Flash mode
 	  - Framebuffer for monochrome 256x64 display
-	  - Backlight control    (needs CONFIG_BACKLIGHT_CLASS_DEVICE)
-	  - Contrast control     (needs CONFIG_LCD_CLASS_DEVICE)
+	  - Backlight control         (needs CONFIG_BACKLIGHT_CLASS_DEVICE)
+	  - Contrast control          (needs CONFIG_LCD_CLASS_DEVICE)
+	  - General purpose outputs   (needs CONFIG_LEDS_CLASS)
 	  Features that are not (yet) supported:
 	  - IR
-	  - General purpose outputs
 	  - EEProm / Flash access
 
 config HID_QUANTA
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
index 0cc0d71..ff70aa2 100644
--- a/drivers/hid/hid-picolcd.c
+++ b/drivers/hid/hid-picolcd.c
@@ -29,6 +29,8 @@
 #include <linux/backlight.h>
 #include <linux/lcd.h>
 
+#include <linux/leds.h>
+
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
 
@@ -194,6 +196,11 @@ struct picolcd_data {
 	u8 lcd_brightness;
 	u8 lcd_power;
 #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+	/* LED stuff */
+	u8 led_state;
+	struct led_classdev *led[8];
+#endif /* CONFIG_LEDS_CLASS */
 
 	/* Housekeeping stuff */
 	spinlock_t lock;
@@ -934,6 +941,153 @@ static inline int picolcd_resume_lcd(struct picolcd_data *data)
 }
 #endif /* CONFIG_LCD_CLASS_DEVICE */
 
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+/**
+ * LED class device
+ */
+static void picolcd_leds_set(struct picolcd_data *data)
+{
+	struct hid_report *report;
+	unsigned long flags;
+
+	if (!data->led[0])
+		return;
+	report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
+	if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+		return;
+
+	spin_lock_irqsave(&data->lock, flags);
+	hid_set_field(report->field[0], 0, data->led_state);
+	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
+			enum led_brightness value)
+{
+	struct device *dev;
+	struct hid_device *hdev;
+	struct picolcd_data *data;
+	int i, state = 0;
+
+	dev  = led_cdev->dev->parent;
+	hdev = container_of(dev, struct hid_device, dev);
+	data = hid_get_drvdata(hdev);
+	for (i = 0; i < 8; i++) {
+		if (led_cdev != data->led[i])
+			continue;
+		state = (data->led_state >> i) & 1;
+		if (value = LED_OFF && state) {
+			data->led_state &= ~(1 << i);
+			picolcd_leds_set(data);
+		} else if (value != LED_OFF && !state) {
+			data->led_state |= 1 << i;
+			picolcd_leds_set(data);
+		}
+		break;
+	}
+}
+
+static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
+{
+	struct device *dev;
+	struct hid_device *hdev;
+	struct picolcd_data *data;
+	int i, value = 0;
+
+	dev  = led_cdev->dev->parent;
+	hdev = container_of(dev, struct hid_device, dev);
+	data = hid_get_drvdata(hdev);
+	for (i = 0; i < 8; i++)
+		if (led_cdev = data->led[i]) {
+			value = (data->led_state >> i) & 1;
+			break;
+		}
+	return value ? LED_FULL : LED_OFF;
+}
+
+static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
+{
+	struct device *dev = &data->hdev->dev;
+	struct led_classdev *led;
+	size_t name_sz = strlen(dev_name(dev)) + 8;
+	char *name;
+	int i, ret = 0;
+
+	if (!report)
+		return -ENODEV;
+	if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+			report->field[0]->report_size != 8) {
+		dev_err(dev, "unsupported LED_STATE report");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < 8; i++) {
+		led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
+		if (!led) {
+			dev_err(dev, "can't allocate memory for LED %d\n", i);
+			ret = -ENOMEM;
+			goto err;
+		}
+		name = (void *)(&led[1]);
+		snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
+		led->name = name;
+		led->brightness = 0;
+		led->max_brightness = 1;
+		led->brightness_get = picolcd_led_get_brightness;
+		led->brightness_set = picolcd_led_set_brightness;
+
+		data->led[i] = led;
+		ret = led_classdev_register(dev, data->led[i]);
+		if (ret) {
+			data->led[i] = NULL;
+			kfree(led);
+			dev_err(dev, "can't register LED %d\n", i);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	for (i = 0; i < 8; i++)
+		if (data->led[i]) {
+			led = data->led[i];
+			data->led[i] = NULL;
+			led_classdev_unregister(led);
+			kfree(led);
+		}
+	return ret;
+}
+
+static void picolcd_exit_leds(struct picolcd_data *data)
+{
+	struct led_classdev *led;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		led = data->led[i];
+		data->led[i] = NULL;
+		if (!led)
+			continue;
+		led_classdev_unregister(led);
+		kfree(led);
+	}
+}
+
+#else
+static inline int picolcd_init_leds(struct picolcd_data *data,
+		struct hid_report *report)
+{
+	return 0;
+}
+static void picolcd_exit_leds(struct picolcd_data *data)
+{
+}
+static inline int picolcd_leds_set(struct picolcd_data *data)
+{
+	return 0;
+}
+#endif /* CONFIG_LEDS_CLASS */
+
 /*
  * input class device
  */
@@ -1076,6 +1230,7 @@ static int picolcd_reset(struct hid_device *hdev)
 		schedule_delayed_work(&data->fb_info->deferred_work, 0);
 #endif /* CONFIG_FB */
 
+	picolcd_leds_set(data);
 	return 0;
 }
 
@@ -1694,6 +1849,7 @@ static int picolcd_reset_resume(struct hid_device *hdev)
 	ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
 	if (ret)
 		dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+	picolcd_leds_set(hid_get_drvdata(hdev));
 	return 0;
 }
 #endif
@@ -1808,6 +1964,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 	if (error)
 		goto err;
 
+	/* Setup the LED class devices */
+	error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
+	if (error)
+		goto err;
+
 #ifdef CONFIG_DEBUG_FS
 	report = picolcd_out_report(REPORT_READ_MEMORY, hdev);
 	if (report && report->maxfield = 1 && report->field[0]->report_size = 8)
@@ -1817,6 +1978,7 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 #endif
 	return 0;
 err:
+	picolcd_exit_leds(data);
 	picolcd_exit_backlight(data);
 	picolcd_exit_lcd(data);
 	picolcd_exit_framebuffer(data);
@@ -1949,6 +2111,8 @@ static void picolcd_remove(struct hid_device *hdev)
 		complete(&data->pending->ready);
 	spin_unlock_irqrestore(&data->lock, flags);
 
+	/* Cleanup LED */
+	picolcd_exit_leds(data);
 	/* Clean up the framebuffer */
 	picolcd_exit_backlight(data);
 	picolcd_exit_lcd(data);
-- 
1.6.4.4


  parent reply index

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-03-24 22:37 [PATCH v3 0/6] hid: new driver for " Bruno Prémont
2010-03-24 22:49 ` [PATCH v3 2/6] hid: add framebuffer support to " Bruno Prémont
2010-03-27  0:22   ` [PATCH v4 " Bruno Prémont
2010-03-29 20:30     ` [PATCH v5 " Bruno Prémont
2010-03-24 22:51 ` [PATCH v3 3/6] hid: add backlight " Bruno Prémont
2010-03-24 22:54 ` [PATCH v3 4/6] hid: add lcd " Bruno Prémont
     [not found] ` <20100324233707.7243b04d-hY15tx4IgV39zxVx7UNMDg@public.gmane.org>
2010-03-24 22:40   ` [PATCH v3 1/6] hid: new driver for " Bruno Prémont
2010-03-26  6:56     ` Dmitry Torokhov
2010-03-26  9:29       ` Bruno Prémont
2010-03-26 20:59         ` Jiri Kosina
2010-03-26 21:16           ` Dmitry Torokhov
2010-03-26 21:39             ` Bruno Prémont
2010-03-27  0:22           ` [PATCH v4 " Bruno Prémont
2010-03-29  9:47             ` Jiri Kosina
2010-03-29 10:16               ` Bruno Prémont
2010-03-30  8:12                 ` Jiri Kosina
2010-03-30 20:32                   ` [PATCH v6 0/8] " Bruno Prémont
2010-03-30 20:33                     ` [PATCH v6 1/8] " Bruno Prémont
2010-04-01 16:58                       ` Oliver Neukum
2010-04-25 19:29                         ` [PATCH] hid: split picolcd's operation_mode sysfs attribute Bruno Prémont
2010-04-27 13:32                           ` Jiri Kosina
2010-03-30 20:34                     ` [PATCH v6 2/8] hid: add framebuffer support to PicoLCD device Bruno Prémont
     [not found]                     ` <20100330223224.18fe4f3e-hY15tx4IgV39zxVx7UNMDg@public.gmane.org>
2010-03-30 20:35                       ` [PATCH v6 3/8] hid: add backlight " Bruno Prémont
2010-03-30 20:36                     ` [PATCH v6 4/8] hid: add lcd " Bruno Prémont
2010-03-30 20:36                     ` [PATCH v6 5/8] hid: add GPO (leds) " Bruno Prémont
2010-03-30 20:38                     ` [PATCH v6 6/8] hid: add experimental access to PicoLCD device's Bruno Prémont
2010-03-30 20:42                     ` [PATCH v6 7/8, needs improvement] hid: add suspend/resume hooks for Bruno Prémont
2010-03-31 12:15                       ` [PATCH v6 7/8, needs improvement] hid: add suspend/resume hooks Jiri Kosina
2010-03-30 20:43                     ` [PATCH v6 8/8] hid: add PM support to PicoLCD device Bruno Prémont
2010-03-31  9:28                     ` [PATCH v6 0/8] hid: new driver for " Jiri Kosina
2010-03-24 22:55   ` Bruno Prémont [this message]
2010-03-24 22:58   ` [PATCH v3 6/6] hid: add experimental access to PicoLCD device's Bruno Prémont
2010-03-29  9:44     ` Jiri Kosina
     [not found]       ` <alpine.LNX.2.00.1003291142400.24576-ztGlSCb7Y1iN3ZZ/Hiejyg@public.gmane.org>
2010-03-29 20:34         ` [PATCH v4 " Bruno Prémont

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20100324235556.1608ee39@neptune.home \
    --to=bonbons@linux-vserver.org \
    --cc=dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=jayakumar.lkml-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=jkosina-AlSwsSmVLrQ@public.gmane.org \
    --cc=linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=npavel-VxACSXvuqMTQT0dZR+AlfA@public.gmane.org \
    --cc=oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org \
    --cc=rpurdie-Fm38FmjxZ/leoWH0uzbU5w@public.gmane.org \
    --cc=rvinyard-qcTL/1vZYtiVc3sceRu5cw@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Linux-fbdev Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-fbdev/0 linux-fbdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-fbdev linux-fbdev/ https://lore.kernel.org/linux-fbdev \
		linux-fbdev@vger.kernel.org
	public-inbox-index linux-fbdev

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-fbdev


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git