Linux-fbdev Archive on lore.kernel.org
 help / color / Atom feed
From: "Bruno Prémont" <bonbons@linux-vserver.org>
To: Jiri Kosina <jkosina@suse.cz>
Cc: linux-input@vger.kernel.org, linux-usb@vger.kernel.org,
	linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Rick L. Vinyard Jr." <rvinyard@cs.nmsu.edu>,
	Nicu Pavel <npavel@ituner.com>, Oliver Neukum <oliver@neukum.org>,
	Jaya Kumar <jayakumar.lkml@gmail.com>,
	Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	Richard Purdie <rpurdie@rpsys.net>
Subject: [PATCH v3 3/6] hid: add backlight support to PicoLCD device
Date: Wed, 24 Mar 2010 22:51:49 +0000
Message-ID: <20100324235149.3e38bcb2@neptune.home> (raw)
In-Reply-To: <20100324233707.7243b04d@neptune.home>

Add backlight support to PicoLCD device.

Backlight support depends on backlight class and is only being
compiled if backlight 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       |    2 +-
 drivers/hid/hid-picolcd.c |  136 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 136 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index a813ea9..588b9ac 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -278,8 +278,8 @@ config HID_PICOLCD
 	  - Keypad
 	  - Switching between Firmware and Flash mode
 	  - Framebuffer for monochrome 256x64 display
+	  - Backlight control    (needs CONFIG_BACKLIGHT_CLASS_DEVICE)
 	  Features that are not (yet) supported:
-	  - Backlight control
 	  - Contrast control
 	  - IR
 	  - General purpose outputs
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
index 6d17d89..2417a9d 100644
--- a/drivers/hid/hid-picolcd.c
+++ b/drivers/hid/hid-picolcd.c
@@ -26,6 +26,7 @@
 
 #include <linux/fb.h>
 #include <linux/vmalloc.h>
+#include <linux/backlight.h>
 
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
@@ -183,6 +184,11 @@ struct picolcd_data {
 	struct fb_info *fb_info;
 	struct fb_deferred_io fb_defio;
 #endif /* CONFIG_FB */
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
+	struct backlight_device *backlight;
+	u8 lcd_brightness;
+	u8 lcd_power;
+#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 
 	/* Housekeeping stuff */
 	spinlock_t lock;
@@ -703,7 +709,7 @@ static void picolcd_exit_framebuffer(struct picolcd_data *data)
 	kfree(fb_vbitmap);
 }
 
-
+#define picolcd_fbinfo(d) ((d)->fb_info)
 #else
 static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
 {
@@ -716,8 +722,120 @@ static inline int picolcd_init_framebuffer(struct picolcd_data *data)
 static void picolcd_exit_framebuffer(struct picolcd_data *data)
 {
 }
+#define picolcd_fbinfo(d) NULL
 #endif /* CONFIG_FB */
 
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
+/*
+ * backlight class device
+ */
+static int picolcd_get_brightness(struct backlight_device *bdev)
+{
+	struct picolcd_data *data = bl_get_data(bdev);
+	return data->lcd_brightness;
+}
+
+static int picolcd_set_brightness(struct backlight_device *bdev)
+{
+	struct picolcd_data *data = bl_get_data(bdev);
+	struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
+	unsigned long flags;
+
+	if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+		return -ENODEV;
+
+	data->lcd_brightness = bdev->props.brightness & 0x0ff;
+	data->lcd_power      = bdev->props.power;
+	spin_lock_irqsave(&data->lock, flags);
+	hid_set_field(report->field[0], 0, data->lcd_power = FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
+	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return 0;
+}
+
+static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
+{
+	return fb && fb = picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
+}
+
+static const struct backlight_ops picolcd_blops = {
+	.update_status  = picolcd_set_brightness,
+	.get_brightness = picolcd_get_brightness,
+	.check_fb       = picolcd_check_bl_fb,
+};
+
+static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
+{
+	struct device *dev = &data->hdev->dev;
+	struct backlight_device *bdev;
+	struct backlight_properties props;
+	if (!report)
+		return -ENODEV;
+	if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+			report->field[0]->report_size != 8) {
+		dev_err(dev, "unsupported BRIGHTNESS report");
+		return -EINVAL;
+	}
+
+	memset(&props, 0, sizeof(props));
+	props.max_brightness = 0xff;
+	bdev = backlight_device_register(dev_name(dev), dev, data,
+			&picolcd_blops, &props);
+	if (IS_ERR(bdev)) {
+		dev_err(dev, "failed to register backlight\n");
+		return PTR_ERR(bdev);
+	}
+	bdev->props.brightness     = 0xff;
+	data->lcd_brightness       = 0xff;
+	data->backlight            = bdev;
+	picolcd_set_brightness(bdev);
+	return 0;
+}
+
+static void picolcd_exit_backlight(struct picolcd_data *data)
+{
+	struct backlight_device *bdev = data->backlight;
+
+	data->backlight = NULL;
+	if (bdev)
+		backlight_device_unregister(bdev);
+}
+
+static inline int picolcd_resume_backlight(struct picolcd_data *data)
+{
+	if (!data->backlight)
+		return 0;
+	return picolcd_set_brightness(data->backlight);
+}
+
+static void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+	int bl_power = data->lcd_power;
+	if (!data->backlight)
+		return;
+
+	data->backlight->props.power = FB_BLANK_POWERDOWN;
+	picolcd_set_brightness(data->backlight);
+	data->lcd_power = data->backlight->props.power = bl_power;
+}
+#else
+static inline int picolcd_init_backlight(struct picolcd_data *data,
+		struct hid_report *report)
+{
+	return 0;
+}
+static inline void picolcd_exit_backlight(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_backlight(struct picolcd_data *data)
+{
+	return 0;
+}
+static void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
+
 /*
  * input class device
  */
@@ -853,6 +971,7 @@ static int picolcd_reset(struct hid_device *hdev)
 	if (error)
 		return error;
 
+	picolcd_resume_backlight(data);
 #if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE)
 	if (data->fb_info)
 		schedule_delayed_work(&data->fb_info->deferred_work, 0);
@@ -1447,12 +1566,17 @@ static int picolcd_raw_event(struct hid_device *hdev,
 #ifdef CONFIG_PM
 static int picolcd_suspend(struct hid_device *hdev)
 {
+	picolcd_suspend_backlight(hid_get_drvdata(hdev));
 	dbg_hid(PICOLCD_NAME " device ready for suspend\n");
 	return 0;
 }
 
 static int picolcd_resume(struct hid_device *hdev)
 {
+	int ret;
+	ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+	if (ret)
+		dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
 	return 0;
 }
 
@@ -1465,6 +1589,9 @@ static int picolcd_reset_resume(struct hid_device *hdev)
 	ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
 	if (ret)
 		dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
+	ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+	if (ret)
+		dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
 	return 0;
 }
 #endif
@@ -1569,6 +1696,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 	if (error)
 		goto err;
 
+	/* Setup backlight class device */
+	error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, 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)
@@ -1578,6 +1710,7 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 #endif
 	return 0;
 err:
+	picolcd_exit_backlight(data);
 	picolcd_exit_framebuffer(data);
 	picolcd_exit_cir(data);
 	picolcd_exit_keys(data);
@@ -1709,6 +1842,7 @@ static void picolcd_remove(struct hid_device *hdev)
 	spin_unlock_irqrestore(&data->lock, flags);
 
 	/* Clean up the framebuffer */
+	picolcd_exit_backlight(data);
 	picolcd_exit_framebuffer(data);
 	/* Cleanup input */
 	picolcd_exit_cir(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 ` Bruno Prémont [this message]
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   ` [PATCH v3 5/6] hid: add GPO (leds) support to " Bruno Prémont
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=20100324235149.3e38bcb2@neptune.home \
    --to=bonbons@linux-vserver.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jayakumar.lkml@gmail.com \
    --cc=jkosina@suse.cz \
    --cc=linux-fbdev@vger.kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=npavel@ituner.com \
    --cc=oliver@neukum.org \
    --cc=rpurdie@rpsys.net \
    --cc=rvinyard@cs.nmsu.edu \
    /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