From patchwork Sat Feb 27 00:20:54 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsODwqltZXRoIE3Dg8KhcnRvbg==?= X-Patchwork-Id: 82518 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1R0Kx6a025777 for ; Sat, 27 Feb 2010 00:21:01 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S966684Ab0B0AU6 (ORCPT ); Fri, 26 Feb 2010 19:20:58 -0500 Received: from relay02.digicable.hu ([92.249.128.188]:59595 "EHLO relay02.digicable.hu" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S966663Ab0B0AU6 (ORCPT ); Fri, 26 Feb 2010 19:20:58 -0500 Received: from [92.249.229.131] by relay02.digicable.hu with esmtpa id 1NlAQS-0005P0-0V ; Sat, 27 Feb 2010 01:20:56 +0100 Message-ID: <4B886566.8000600@freemail.hu> Date: Sat, 27 Feb 2010 01:20:54 +0100 From: =?UTF-8?B?TsOpbWV0aCBNw6FydG9u?= User-Agent: Mozilla/5.0 (X11; U; Linux i686; hu-HU; rv:1.8.1.21) Gecko/20090402 SeaMonkey/1.1.16 MIME-Version: 1.0 To: Jean-Francois Moine , Richard Purdie CC: V4L Mailing List Subject: [PATCH 1/2] gspca pac7302: allow controlling LED separately References: <4B84CC9E.4030600@freemail.hu> <20100224082238.53c8f6f8@tele> In-Reply-To: <20100224082238.53c8f6f8@tele> X-Original: 92.249.229.131 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Sat, 27 Feb 2010 00:21:01 +0000 (UTC) diff -r 4f102b2f7ac1 linux/drivers/media/video/gspca/pac7302.c --- a/linux/drivers/media/video/gspca/pac7302.c Thu Jan 28 20:35:40 2010 +0100 +++ b/linux/drivers/media/video/gspca/pac7302.c Sat Feb 27 00:57:32 2010 +0100 @@ -6,6 +6,7 @@ * * Separated from Pixart PAC7311 library by Márton Németh * Camera button input handling by Márton Németh + * LED control by Márton Németh * Copyright (C) 2009-2010 Márton Németh * * This program is free software; you can redistribute it and/or modify @@ -62,6 +63,7 @@ 0 | 0xc6 | setwhitebalance() 0 | 0xc7 | setbluebalance() 0 | 0xdc | setbrightcont(), setcolors() + 1 | 0x78 | set_streaming_led() 3 | 0x02 | setexposure() 3 | 0x10 | setgain() 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() @@ -72,6 +74,8 @@ #include #include +#include +#include #include "gspca.h" MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); @@ -91,6 +95,7 @@ unsigned char gain; unsigned char exposure; unsigned char autogain; + unsigned char led; __u8 hflip; __u8 vflip; u8 flags; @@ -101,6 +106,16 @@ u8 autogain_ignore_frames; atomic_t avg_lum; + +#ifdef CONFIG_LEDS_CLASS + struct led_classdev led_cdev; + struct work_struct led_work; + char name[32]; +#ifdef CONFIG_LEDS_TRIGGERS + struct led_trigger led_trigger; + char trigger_name[32]; +#endif +#endif }; /* V4L2 controls supported by the driver */ @@ -572,6 +587,7 @@ sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; sd->autogain = AUTOGAIN_DEF; + sd->led = 0; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; sd->flags = id->driver_info; @@ -716,6 +732,58 @@ reg_w(gspca_dev, 0x11, 0x01); } +static void set_streaming_led(struct gspca_dev *gspca_dev, u8 streaming) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = 0; + + if (sd->led) { + if (streaming) + data = 0x01; + else + data = 0x00; + } else { + if (streaming) + data = 0x41; + else + data = 0x40; + } + + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, data); +} + +#ifdef CONFIG_LEDS_CLASS +/* Set the LED, may sleep */ +static void led_work(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, led_work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + mutex_lock(&gspca_dev->usb_lock); + set_streaming_led(gspca_dev, gspca_dev->streaming); + mutex_unlock(&gspca_dev->usb_lock); +} + +/* LED state set request, must not sleep */ +static void led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + u8 new_value; + struct sd *sd = container_of(led_cdev, struct sd, led_cdev); + + if (value == LED_OFF) + new_value = 0; + else + new_value = 1; + + if (sd->led != new_value) { + sd->led = new_value; + schedule_work(&sd->led_work); + } +} +#endif + /* this function is called at probe and resume time for pac7302 */ static int sd_init(struct gspca_dev *gspca_dev) { @@ -747,27 +815,60 @@ atomic_set(&sd->avg_lum, -1); /* start stream */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x01); + +#if defined(CONFIG_LEDS_CLASS) && defined(CONFIG_LEDS_TRIGGERS) + led_trigger_event(&sd->led_trigger, LED_FULL); +#elif defined(CONFIG_LEDS_CLASS) + sd->led_cdev.brightness = sd->led_cdev.max_brightness; + if (!(sd->led_cdev.flags & LED_SUSPENDED)) + sd->led_cdev.brightness_set(&sd->led_cdev, + sd->led_cdev.brightness); +#else + sd->led = 1; +#endif + set_streaming_led(gspca_dev, 1); return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) { + struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev); +#ifndef CONFIG_LEDS_CLASS + sd->led = 0; +#endif /* stop stream */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x00); + set_streaming_led(gspca_dev, 0); +#if defined(CONFIG_LEDS_CLASS) && defined(CONFIG_LEDS_TRIGGERS) + led_trigger_event(&sd->led_trigger, LED_OFF); +#elif defined(CONFIG_LEDS_CLASS) + sd->led_cdev.brightness = LED_OFF; + if (!(sd->led_cdev.flags & LED_SUSPENDED)) + sd->led_cdev.brightness_set(&sd->led_cdev, + sd->led_cdev.brightness); +#endif } /* called on streamoff with alt 0 and on disconnect for pac7302 */ static void sd_stop0(struct gspca_dev *gspca_dev) { + struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev); + if (!gspca_dev->present) return; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x40); +#ifndef CONFIG_LEDS_CLASS + sd->led = 0; +#endif + set_streaming_led(gspca_dev, 0); +#if defined(CONFIG_LEDS_CLASS) && defined(CONFIG_LEDS_TRIGGERS) + led_trigger_event(&sd->led_trigger, LED_OFF); +#elif defined(CONFIG_LEDS_CLASS) + sd->led_cdev.brightness = LED_OFF; + if (!(sd->led_cdev.flags & LED_SUSPENDED)) + sd->led_cdev.brightness_set(&sd->led_cdev, + sd->led_cdev.brightness); +#endif } /* Include pac common sof detection functions */ @@ -1239,15 +1340,65 @@ static int __devinit sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + int ret; + + ret = gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), THIS_MODULE); +#ifdef CONFIG_LEDS_CLASS + if (ret == 0) { + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev); + +#ifdef CONFIG_LEDS_TRIGGERS + snprintf(sd->trigger_name, sizeof(sd->trigger_name), + "pac7302-%u", gspca_dev->vdev.num); + sd->led_trigger.name = sd->trigger_name; + sd->led_cdev.default_trigger = sd->trigger_name; +#endif + snprintf(sd->name, sizeof(sd->name), + "pac7302-%u::camera", gspca_dev->vdev.num); + sd->led_cdev.name = sd->name; + sd->led_cdev.brightness_set = led_set; + sd->led_cdev.blink_set = NULL; + sd->led_cdev.flags = LED_CORE_SUSPENDRESUME; + INIT_WORK(&sd->led_work, led_work); + ret = led_classdev_register(&gspca_dev->dev->dev, + &sd->led_cdev); + if (ret) + gspca_disconnect(intf); + else { +#ifdef CONFIG_LEDS_TRIGGERS + led_trigger_register(&sd->led_trigger); +#endif + } + } +#endif + + return ret; } +#ifdef CONFIG_LEDS_CLASS +static void sd_disconnect(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev); + +#ifdef CONFIG_LEDS_TRIGGERS + led_trigger_unregister(&sd->led_trigger); +#endif + led_classdev_unregister(&sd->led_cdev); + cancel_work_sync(&sd->led_work); + gspca_disconnect(intf); +} +#else +#define sd_disconnect gspca_disconnect +#endif + static struct usb_driver sd_driver = { .name = MODULE_NAME, .id_table = device_table, .probe = sd_probe, - .disconnect = gspca_disconnect, + .disconnect = sd_disconnect, #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume,