Skip to contentSkip to conversation

em28xx: add basic support for the SpeedLink Vicious And Devine Laplace webcams

PATCH In review Frank Schäfer 2 participants 9 messages Dec 1, 2013 – Dec 10, 2013

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover em28xx: add basic support for the SpeedLink Vicious And Devine Laplace webcams

The SpeedLink Vicious And Devine Laplace webcam is using an EM2765 bridge and an OV2640 sensor. It has a built-in microphone (STAC9753 AC97, USB standard device class) and provides 3 buttons (snapshot, illumination, mute) and 2 LEDs (capturing/mute and illumination/flash).
It is also equipped with an eeprom.
The device is available in two colors: white (1ae7:9003) and black (1ae7:9004). For further details see http://linuxtv.org/wiki/index.php/VAD_Laplace.

The first 6 patches improve and extend the support for device buttons and LEDs by
- abstracting and generalize the button and LED handling
- adding a software debouncing mechanism for buttons connected to ordinary GPI ports
- adding support for analog capturing and illumination LEDs
- adding support for illumination buttons

Support for the audio/video mute button can easily be added later, but that requires to fix several audio issues first. Fortunately, there is another bug that prevents the em28xx driver from detecting the audio part of these devices. :/

Patch 7 finally adds the USB IDs and the board defintion for these cameras.

The following limitations need to be addressed later:
- resolution currently limited to 640x480 (sensor supports 1600x1200)
- picture quality needs to be improved
- audio/video mute button doesn't work yet

Frank Schäfer (7):
em28xx: add support for GPO controlled analog capturing LEDs
em28xx: extend the support for device buttons
em28xx: add debouncing mechanism for GPI-connected buttons
em28xx: reduce the polling interval for buttons
em28xx: prepare for supporting multiple LEDs
em28xx: add support for illumination button and LED
em28xx: add support for the SpeedLink Vicious And Devine Laplace
webcams

 drivers/media/usb/em28xx/em28xx-cards.c |   92 +++++++++++++++-
 drivers/media/usb/em28xx/em28xx-core.c  |  105 +++++++++++++------
 drivers/media/usb/em28xx/em28xx-input.c |  175 ++++++++++++++++++++++++-------
 drivers/media/usb/em28xx/em28xx.h       |   49 ++++++++-

4 Dateien geändert, 346 Zeilen hinzugefügt(+), 75 Zeilen entfernt(-)

Signature · 1 line
1.7.10.4
#1 lore raw

PATCH 1/7 em28xx: add support for GPO controlled analog capturing LEDs

Some devices are equipped with a capturing status LED that needs to be switched on/off explicitly via a GPO port.

---
 drivers/media/usb/em28xx/em28xx-core.c |   69 +++++++++++++++++---------------
 drivers/media/usb/em28xx/em28xx.h      |    9 +++++

2 Dateien geändert, 46 Zeilen hinzugefügt(+), 32 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-core.c +37 -32
index fc157af..31d6ab2 100644
@@ -608,46 +608,51 @@ int em28xx_capture_start(struct em28xx *dev, int start)
 	    dev->chip_id == CHIP_ID_EM2884 ||
 	    dev->chip_id == CHIP_ID_EM28174) {
 		/* The Transport Stream Enable Register moved in em2874 */
-		if (!start) {
-			rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
-						   0x00,
-						   EM2874_TS1_CAPTURE_ENABLE);
-			return rc;
-		}
-
-		/* Enable Transport Stream */
 		rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
-					   EM2874_TS1_CAPTURE_ENABLE,
+					   start ?
+					       EM2874_TS1_CAPTURE_ENABLE : 0x00,
 					   EM2874_TS1_CAPTURE_ENABLE);
-		return rc;
-	}
-
+	} else {
+		/* FIXME: which is the best order? */
+		/* video registers are sampled by VREF */
+		rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
+					   start ? 0x10 : 0x00, 0x10);
+		if (rc < 0)
+			return rc;
 
-	/* FIXME: which is the best order? */
-	/* video registers are sampled by VREF */
-	rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
-				   start ? 0x10 : 0x00, 0x10);
-	if (rc < 0)
-		return rc;
+		if (start) {
+			if (dev->board.is_webcam)
+				rc = em28xx_write_reg(dev, 0x13, 0x0c);
 
-	if (!start) {
-		/* disable video capture */
-		rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
-		return rc;
-	}
+			/* Enable video capture */
+			rc = em28xx_write_reg(dev, 0x48, 0x00);
 
-	if (dev->board.is_webcam)
-		rc = em28xx_write_reg(dev, 0x13, 0x0c);
+			if (dev->mode == EM28XX_ANALOG_MODE)
+				rc = em28xx_write_reg(dev,
+						    EM28XX_R12_VINENABLE, 0x67);
+			else
+				rc = em28xx_write_reg(dev,
+						    EM28XX_R12_VINENABLE, 0x37);
 
-	/* enable video capture */
-	rc = em28xx_write_reg(dev, 0x48, 0x00);
+			msleep(6);
+		} else {
+			/* disable video capture */
+			rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
+		}
+	}
 
-	if (dev->mode == EM28XX_ANALOG_MODE)
-		rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
-	else
-		rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+	if (rc < 0)
+		return rc;
 
-	msleep(6);
+	/* Switch (explicitly controlled) analog capturing LED on/off */
+	if ((dev->mode == EM28XX_ANALOG_MODE)
+	    && dev->board.analog_capturing_led) {
+		struct em28xx_led *led = dev->board.analog_capturing_led;
+		em28xx_write_reg_bits(dev, led->gpio_reg,
+				      (!start ^ led->inverted) ?
+				      ~led->gpio_mask : led->gpio_mask,
+				      led->gpio_mask);
+	}
 
 	return rc;
 }
drivers/media/usb/em28xx/em28xx.h +9 -0
index f8726ad..8003c2f 100644
@@ -374,6 +374,12 @@ enum em28xx_adecoder {
 	EM28XX_TVAUDIO,
 };
 
+struct em28xx_led {
+	u8 gpio_reg;
+	u8 gpio_mask;
+	bool inverted;
+};
+
 struct em28xx_board {
 	char *name;
 	int vchannels;
@@ -410,6 +416,9 @@ struct em28xx_board {
 	struct em28xx_input       input[MAX_EM28XX_INPUT];
 	struct em28xx_input	  radio;
 	char			  *ir_codes;
+
+	/* LEDs that need to be controlled explicitly */
+	struct em28xx_led	  *analog_capturing_led;
 };
 
 struct em28xx_eeprom {
Signature · 1 line
1.7.10.4
#2 lore raw

PATCH 2/7 em28xx: extend the support for device buttons

In reply to Frank Schäfer

The current code supports only a single snapshot button assigned to register 0x0c bit 5. But devices may be equipped with multiple buttons with different functionalities and they can also be assigned to the various GPI-ports.

Extend the em28xx-input code to handle multiple buttons assigned to different GPI-ports / register addresses and bits.
Also make easier to extend the code with further button types.

---
 drivers/media/usb/em28xx/em28xx-cards.c |   20 ++++-
 drivers/media/usb/em28xx/em28xx-input.c |  150 +++++++++++++++++++++++--------
 drivers/media/usb/em28xx/em28xx.h       |   27 +++++-

3 Dateien geändert, 154 Zeilen hinzugefügt(+), 43 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-cards.c +17 -3
index a519669..ebb112c 100644
@@ -413,6 +413,20 @@ static struct em28xx_reg_seq pctv_520e[] = {
 };
 
 /*
+ *  Button definitions
+ */
+static struct em28xx_button std_snapshot_button[] = {
+	{
+		.role         = EM28XX_BUTTON_SNAPSHOT,
+		.reg_r        = EM28XX_R0C_USBSUSP,
+		.reg_clearing = EM28XX_R0C_USBSUSP,
+		.mask         = EM28XX_R0C_USBSUSP_SNAPSHOT,
+		.inverted     = 0,
+	},
+	{-1, 0, 0, 0, 0},
+};
+
+/*
  *  Board definitions
  */
 struct em28xx_board em28xx_boards[] = {
@@ -1391,7 +1405,7 @@ struct em28xx_board em28xx_boards[] = {
 	},
 	[EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
 		.name         = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0",
-		.has_snapshot_button = 1,
+		.buttons = std_snapshot_button,
 		.tda9887_conf = TDA9887_PRESENT,
 		.tuner_type   = TUNER_YMEC_TVF_5533MF,
 		.decoder      = EM28XX_SAA711X,
@@ -1413,7 +1427,7 @@ struct em28xx_board em28xx_boards[] = {
 	},
 	[EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = {
 		.name                = "EM2860/SAA711X Reference Design",
-		.has_snapshot_button = 1,
+		.buttons = std_snapshot_button,
 		.tuner_type          = TUNER_ABSENT,
 		.decoder             = EM28XX_SAA711X,
 		.input               = { {
@@ -2841,7 +2855,7 @@ static void request_module_async(struct work_struct *work)
 
 	if (dev->board.has_dvb)
 		request_module("em28xx-dvb");
-	if (dev->board.has_snapshot_button ||
+	if (dev->board.buttons ||
 	    ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir))
 		request_module("em28xx-rc");
 #endif /* CONFIG_MODULES */
drivers/media/usb/em28xx/em28xx-input.c +113 -37

212 lines not on this page · raw message

drivers/media/usb/em28xx/em28xx.h +24 -3
index 8003c2f..e185d00 100644
@@ -181,6 +181,9 @@
 /* time in msecs to wait for i2c writes to finish */
 #define EM2800_I2C_XFER_TIMEOUT		20
 
+/* max. number of button state polling addresses */
+#define EM28XX_NUM_BUTTON_ADDRESSES_MAX		5
+
 enum em28xx_mode {
 	EM28XX_SUSPEND,
 	EM28XX_ANALOG_MODE,
@@ -380,6 +383,19 @@ struct em28xx_led {
 	bool inverted;
 };
 
+enum em28xx_button_role {
+	EM28XX_BUTTON_SNAPSHOT = 0,
+	EM28XX_NUM_BUTTON_ROLES, /* must be the last */
+};
+
+struct em28xx_button {
+	enum em28xx_button_role role;
+	u8 reg_r;
+	u8 reg_clearing;
+	u8 mask;
+	bool inverted;
+};
+
 struct em28xx_board {
 	char *name;
 	int vchannels;
@@ -401,7 +417,6 @@ struct em28xx_board {
 	unsigned int mts_firmware:1;
 	unsigned int max_range_640_480:1;
 	unsigned int has_dvb:1;
-	unsigned int has_snapshot_button:1;
 	unsigned int is_webcam:1;
 	unsigned int valid:1;
 	unsigned int has_ir_i2c:1;
@@ -419,6 +434,9 @@ struct em28xx_board {
 
 	/* LEDs that need to be controlled explicitly */
 	struct em28xx_led	  *analog_capturing_led;
+
+	/* Buttons */
+	struct em28xx_button	  *buttons;
 };
 
 struct em28xx_eeprom {
@@ -648,10 +666,13 @@ struct em28xx {
 
 	enum em28xx_mode mode;
 
-	/* Snapshot button */
+	/* Button state polling */
+	struct delayed_work buttons_query_work;
+	u8 button_polling_addresses[EM28XX_NUM_BUTTON_ADDRESSES_MAX];
+	u8 num_button_polling_addresses;
+	/* Snapshot button input device */
 	char snapshot_button_path[30];	/* path of the input dev */
 	struct input_dev *sbutton_input_dev;
-	struct delayed_work sbutton_query_work;
 
 	struct em28xx_dvb *dvb;
 };
Signature · 1 line
1.7.10.4
#3 lore raw

PATCH 3/7 em28xx: add debouncing mechanism for GPI-connected buttons

In reply to Frank Schäfer

So far, the driver only supports a snapshot button which is assigned to register 0x0c bit 5. This special port has a built-in debouncing mechanism. For buttons connected to ordinary GPI ports, this patch implements a software debouncing mechanism.

---
 drivers/media/usb/em28xx/em28xx-input.c |   30 +++++++++++++++++++-----------
 drivers/media/usb/em28xx/em28xx.h       |    1 +

2 Dateien geändert, 20 Zeilen hinzugefügt(+), 11 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-input.c +19 -11
index 20c6a8a..ebc5387 100644
@@ -479,7 +479,7 @@ static void em28xx_query_buttons(struct work_struct *work)
 		container_of(work, struct em28xx, buttons_query_work.work);
 	u8 i, j;
 	int regval;
-	bool pressed;
+	bool is_pressed, was_pressed;
 
 	/* Poll and evaluate all addresses */
 	for (i = 0; i < dev->num_button_polling_addresses; i++) {
@@ -497,12 +497,21 @@ static void em28xx_query_buttons(struct work_struct *work)
 				j++;
 				continue;
 			}
-			/* Determine if button is pressed */
-			pressed = regval & button->mask;
-			if (button->inverted)
-				pressed = !pressed;
+			/* Determine if button is and was pressed last time */
+			is_pressed = regval & button->mask;
+			was_pressed = dev->button_polling_last_values[i]
+				       & button->mask;
+			if (button->inverted) {
+				is_pressed = !is_pressed;
+				was_pressed = !was_pressed;
+			}
+			/* Clear button state (if needed) */
+			if (is_pressed && button->reg_clearing)
+				em28xx_write_reg(dev, button->reg_clearing,
+						 (~regval & button->mask)
+						    | (regval & ~button->mask));
 			/* Handle button state */
-			if (!pressed) {
+			if (!is_pressed || was_pressed) {
 				j++;
 				continue;
 			}
@@ -518,14 +527,11 @@ static void em28xx_query_buttons(struct work_struct *work)
 			default:
 				WARN_ONCE(1, "BUG: unhandled button role.");
 			}
-			/* Clear button state (if needed) */
-			if (button->reg_clearing)
-				em28xx_write_reg(dev, button->reg_clearing,
-						 (~regval & button->mask)
-						    | (regval & ~button->mask));
 			/* Next button */
 			j++;
 		}
+		/* Save current value for comparison during the next polling */
+		dev->button_polling_last_values[i] = regval;
 	}
 	/* Schedule next poll */
 	schedule_delayed_work(&dev->buttons_query_work,
@@ -611,6 +617,8 @@ static void em28xx_init_buttons(struct em28xx *dev)
 
 	/* Start polling */
 	if (dev->num_button_polling_addresses) {
+		memset(dev->button_polling_last_values, 0,
+					       EM28XX_NUM_BUTTON_ADDRESSES_MAX);
 		INIT_DELAYED_WORK(&dev->buttons_query_work,
 							  em28xx_query_buttons);
 		schedule_delayed_work(&dev->buttons_query_work,
drivers/media/usb/em28xx/em28xx.h +1 -0
index e185d00..df828c6 100644
@@ -669,6 +669,7 @@ struct em28xx {
 	/* Button state polling */
 	struct delayed_work buttons_query_work;
 	u8 button_polling_addresses[EM28XX_NUM_BUTTON_ADDRESSES_MAX];
+	u8 button_polling_last_values[EM28XX_NUM_BUTTON_ADDRESSES_MAX];
 	u8 num_button_polling_addresses;
 	/* Snapshot button input device */
 	char snapshot_button_path[30];	/* path of the input dev */
Signature · 1 line
1.7.10.4
#4 lore raw

PATCH 4/7 em28xx: reduce the polling interval for buttons

In reply to Frank Schäfer

For GPI-connected buttons without (hardware) debouncing, the polling interval needs to be reduced to detect button presses properly.

---
 drivers/media/usb/em28xx/em28xx-input.c |    2 +-

1 Datei geändert, 1 Zeile hinzugefügt(+), 1 Zeile entfernt(-)

drivers/media/usb/em28xx/em28xx-input.c +1 -1
index ebc5387..c8f7ecb 100644
@@ -31,7 +31,7 @@
 #include "em28xx.h"
 
 #define EM28XX_SNAPSHOT_KEY KEY_CAMERA
-#define EM28XX_BUTTONS_QUERY_INTERVAL 500
+#define EM28XX_BUTTONS_QUERY_INTERVAL 100
 
 static unsigned int ir_debug;
 module_param(ir_debug, int, 0644);
Signature · 1 line
1.7.10.4
#5 lore raw

Re: [PATCH 4/7] em28xx: reduce the polling interval for buttons

Em Sun, 1 Dec 2013 22:06:54 +0100
Frank Schäfer <[email protected]> escreveu:

Quoted 13 lines

For GPI-connected buttons without (hardware) debouncing, the polling interval needs to be reduced to detect button presses properly.

---
 drivers/media/usb/em28xx/em28xx-input.c |    2 +-

1 Datei geändert, 1 Zeile hinzugefügt(+), 1 Zeile entfernt(-)

drivers/media/usb/em28xx/em28xx-input.c +0 -0
index ebc5387..c8f7ecb 100644

drivers/media/usb/em28xx/em28xx-input.c line 37

Quoted 8 lines
drivers/media/usb/em28xx/em28xx-input.c +1 -1
@@ -31,7 +31,7 @@
 #include "em28xx.h"
 
 #define EM28XX_SNAPSHOT_KEY KEY_CAMERA
-#define EM28XX_BUTTONS_QUERY_INTERVAL 500
+#define EM28XX_BUTTONS_QUERY_INTERVAL 100
 
 static unsigned int ir_debug;
 module_param(ir_debug, int, 0644);

I don't like this patch. If the reduced timeout is needed for the GPI connected buttons, you should not change it for the other buttons, as polling has a high cost, in terms of CPU sleep state (so, a more frequent polling time drains more power - meaning a lower time when battery is in usage).

Signature · 2 lines
Cheers,
Mauro
#6 lore raw

PATCH 5/7 em28xx: prepare for supporting multiple LEDs

In reply to Frank Schäfer

Introduce a LED role and store all LEDs in an array. Also provide a helper function to retrieve a specific LED.

---
 drivers/media/usb/em28xx/em28xx-core.c |   31 ++++++++++++++++++++++++-------
 drivers/media/usb/em28xx/em28xx.h      |   10 +++++++++-

2 Dateien geändert, 33 Zeilen hinzugefügt(+), 8 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-core.c +24 -7
index 31d6ab2..4a8179a 100644
@@ -600,6 +600,22 @@ int em28xx_colorlevels_set_default(struct em28xx *dev)
 	return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
 }
 
+const struct em28xx_led *em28xx_find_led(struct em28xx *dev,
+					 enum em28xx_led_role role)
+{
+	if (dev->board.leds) {
+		u8 k = 0;
+		while (dev->board.leds[k].role >= 0 &&
+			       dev->board.leds[k].role < EM28XX_NUM_LED_ROLES) {
+			if (dev->board.leds[k].role == role)
+				return &dev->board.leds[k];
+			k++;
+		}
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(em28xx_find_led);
+
 int em28xx_capture_start(struct em28xx *dev, int start)
 {
 	int rc;
@@ -645,13 +661,14 @@ int em28xx_capture_start(struct em28xx *dev, int start)
 		return rc;
 
 	/* Switch (explicitly controlled) analog capturing LED on/off */
-	if ((dev->mode == EM28XX_ANALOG_MODE)
-	    && dev->board.analog_capturing_led) {
-		struct em28xx_led *led = dev->board.analog_capturing_led;
-		em28xx_write_reg_bits(dev, led->gpio_reg,
-				      (!start ^ led->inverted) ?
-				      ~led->gpio_mask : led->gpio_mask,
-				      led->gpio_mask);
+	if (dev->mode == EM28XX_ANALOG_MODE) {
+		const struct em28xx_led *led;
+		led = em28xx_find_led(dev, EM28XX_LED_ANALOG_CAPTURING);
+		if (led)
+			em28xx_write_reg_bits(dev, led->gpio_reg,
+					      (!start ^ led->inverted) ?
+					      ~led->gpio_mask : led->gpio_mask,
+					      led->gpio_mask);
 	}
 
 	return rc;
drivers/media/usb/em28xx/em28xx.h +9 -1
index df828c6..f60f236 100644
@@ -377,7 +377,13 @@ enum em28xx_adecoder {
 	EM28XX_TVAUDIO,
 };
 
+enum em28xx_led_role {
+	EM28XX_LED_ANALOG_CAPTURING = 0,
+	EM28XX_NUM_LED_ROLES, /* must be the last */
+};
+
 struct em28xx_led {
+	enum em28xx_led_role role;
 	u8 gpio_reg;
 	u8 gpio_mask;
 	bool inverted;
@@ -433,7 +439,7 @@ struct em28xx_board {
 	char			  *ir_codes;
 
 	/* LEDs that need to be controlled explicitly */
-	struct em28xx_led	  *analog_capturing_led;
+	struct em28xx_led	  *leds;
 
 	/* Buttons */
 	struct em28xx_button	  *buttons;
@@ -711,6 +717,8 @@ int em28xx_audio_analog_set(struct em28xx *dev);
 int em28xx_audio_setup(struct em28xx *dev);
 
 int em28xx_colorlevels_set_default(struct em28xx *dev);
+const struct em28xx_led *em28xx_find_led(struct em28xx *dev,
+					 enum em28xx_led_role role);
 int em28xx_capture_start(struct em28xx *dev, int start);
 int em28xx_vbi_supported(struct em28xx *dev);
 int em28xx_set_outfmt(struct em28xx *dev);
Signature · 1 line
1.7.10.4
#7 lore raw

PATCH 6/7 em28xx: add support for illumination button and LED

In reply to Frank Schäfer

The SpeedLink VAD Laplace webcam is equipped with an illumination button and an illumination LED. When the button is pressed, the driver must toggle the LED state via the corresponding GPO port.

---
 drivers/media/usb/em28xx/em28xx-core.c  |   19 +++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-input.c |   19 ++++++++++++++++++-
 drivers/media/usb/em28xx/em28xx.h       |    3 +++

3 Dateien geändert, 40 Zeilen hinzugefügt(+), 1 Zeile entfernt(-)

drivers/media/usb/em28xx/em28xx-core.c +19 -0
index 4a8179a..fc82540 100644
@@ -226,6 +226,25 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
 EXPORT_SYMBOL_GPL(em28xx_write_reg_bits);
 
 /*
+ * em28xx_toggle_reg_bits()
+ * toggles/inverts the bits (specified by bitmask) of a register
+ */
+int em28xx_toggle_reg_bits(struct em28xx *dev, u16 reg, u8 bitmask)
+{
+	int oldval;
+	u8 newval;
+
+	oldval = em28xx_read_reg(dev, reg);
+	if (oldval < 0)
+		return oldval;
+
+	newval = (~oldval & bitmask) | (oldval & ~bitmask);
+
+	return em28xx_write_reg(dev, reg, newval);
+}
+EXPORT_SYMBOL_GPL(em28xx_toggle_reg_bits);
+
+/*
  * em28xx_is_ac97_ready()
  * Checks if ac97 is ready
  */
drivers/media/usb/em28xx/em28xx-input.c +18 -1
index c8f7ecb..d40fd34 100644
@@ -480,6 +480,7 @@ static void em28xx_query_buttons(struct work_struct *work)
 	u8 i, j;
 	int regval;
 	bool is_pressed, was_pressed;
+	const struct em28xx_led *led;
 
 	/* Poll and evaluate all addresses */
 	for (i = 0; i < dev->num_button_polling_addresses; i++) {
@@ -524,6 +525,15 @@ static void em28xx_query_buttons(struct work_struct *work)
 				input_report_key(dev->sbutton_input_dev,
 						 EM28XX_SNAPSHOT_KEY, 0);
 				break;
+			case EM28XX_BUTTON_ILLUMINATION:
+				led = em28xx_find_led(dev,
+						      EM28XX_LED_ILLUMINATION);
+				/* Switch illumination LED on/off */
+				if (led)
+					em28xx_toggle_reg_bits(dev,
+							       led->gpio_reg,
+							       led->gpio_mask);
+				break;
 			default:
 				WARN_ONCE(1, "BUG: unhandled button role.");
 			}
@@ -600,10 +610,17 @@ static void em28xx_init_buttons(struct em28xx *dev)
 			WARN_ONCE(1, "BUG: maximum number of button polling addresses exceeded.");
 			addr_new = 0;
 		}
-		/* Register input device (if needed) */
+		/* Button role specific checks and actions */
 		if (button->role == EM28XX_BUTTON_SNAPSHOT) {
+			/* Register input device */
 			if (em28xx_register_snapshot_button(dev) < 0)
 				addr_new = 0;
+		} else if (button->role == EM28XX_BUTTON_ILLUMINATION) {
+			/* Check sanity */
+			if (!em28xx_find_led(dev, EM28XX_LED_ILLUMINATION)) {
+				em28xx_errdev("BUG: illumination button defined, but no illumination LED.\n");
+				addr_new = 0;
+			}
 		}
 		/* Add read address to list of polling addresses */
 		if (addr_new) {
drivers/media/usb/em28xx/em28xx.h +3 -0
index f60f236..43e2968 100644
@@ -379,6 +379,7 @@ enum em28xx_adecoder {
 
 enum em28xx_led_role {
 	EM28XX_LED_ANALOG_CAPTURING = 0,
+	EM28XX_LED_ILLUMINATION,
 	EM28XX_NUM_LED_ROLES, /* must be the last */
 };
 
@@ -391,6 +392,7 @@ struct em28xx_led {
 
 enum em28xx_button_role {
 	EM28XX_BUTTON_SNAPSHOT = 0,
+	EM28XX_BUTTON_ILLUMINATION,
 	EM28XX_NUM_BUTTON_ROLES, /* must be the last */
 };
 
@@ -709,6 +711,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
 int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val);
 int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
 				 u8 bitmask);
+int em28xx_toggle_reg_bits(struct em28xx *dev, u16 reg, u8 bitmask);
 
 int em28xx_read_ac97(struct em28xx *dev, u8 reg);
 int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val);
Signature · 1 line
1.7.10.4
#8 lore raw

PATCH 7/7 em28xx: add support for the SpeedLink Vicious And Devine Laplace webcams

In reply to Frank Schäfer

The SpeedLink Vicious And Devine Laplace webcam is using an EM2765 bridge and an OV2640 sensor. It has a built-in microphone (USB standard device class) and provides 3 buttons (snapshot, illumination, mute) and 2 LEDs (capturing/mute and illumination/flash). It is also equipped with an eeprom.
The device is available in two colors: white (1ae7:9003) and black (1ae7:9004). For further details see http://linuxtv.org/wiki/index.php/VAD_Laplace.

Please note the following limitations that need to be addressed later:
- resolution limited to 640x480 (sensor supports 1600x1200)
- picture quality needs to be improved
- AV-mute button doesn't work yet

---
 drivers/media/usb/em28xx/em28xx-cards.c |   72 +++++++++++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx.h       |    1 +

2 Dateien geändert, 73 Zeilen hinzugefügt(+)

drivers/media/usb/em28xx/em28xx-cards.c +72 -0
index ebb112c..6454309 100644
@@ -412,6 +412,21 @@ static struct em28xx_reg_seq pctv_520e[] = {
 	{	-1,			-1,	-1,	-1},
 };
 
+/* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam
+ * reg 0x80/0x84:
+ * GPIO_0: capturing LED, 0=on, 1=off
+ * GPIO_2: AV mute button, 0=pressed, 1=unpressed
+ * GPIO 3: illumination button, 0=pressed, 1=unpressed
+ * GPIO_6: illumination/flash LED, 0=on, 1=off
+ * reg 0x81/0x85:
+ * GPIO_7: snapshot button, 0=pressed, 1=unpressed
+ */
+static struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = {
+	{EM2820_R08_GPIO_CTRL,		0xf7,	0xff,	10},
+	{EM2874_R80_GPIO_P0_CTRL,	0xff,	0xb2,	10},
+	{	-1,			-1,	-1,	-1},
+};
+
 /*
  *  Button definitions
  */
@@ -426,6 +441,41 @@ static struct em28xx_button std_snapshot_button[] = {
 	{-1, 0, 0, 0, 0},
 };
 
+static struct em28xx_button speedlink_vad_laplace_buttons[] = {
+	{
+		.role     = EM28XX_BUTTON_SNAPSHOT,
+		.reg_r    = EM2874_R85_GPIO_P1_STATE,
+		.mask     = 0x80,
+		.inverted = 1,
+	},
+	{
+		.role     = EM28XX_BUTTON_ILLUMINATION,
+		.reg_r    = EM2874_R84_GPIO_P0_STATE,
+		.mask     = 0x08,
+		.inverted = 1,
+	},
+	{-1, 0, 0, 0, 0},
+};
+
+/*
+ *  LED definitions
+ */
+static struct em28xx_led speedlink_vad_laplace_leds[] = {
+	{
+		.role      = EM28XX_LED_ANALOG_CAPTURING,
+		.gpio_reg  = EM2874_R80_GPIO_P0_CTRL,
+		.gpio_mask = 0x01,
+		.inverted  = 1,
+	},
+	{
+		.role      = EM28XX_LED_ILLUMINATION,
+		.gpio_reg  = EM2874_R80_GPIO_P0_CTRL,
+		.gpio_mask = 0x40,
+		.inverted  = 1,
+	},
+	{-1, 0, 0, 0},
+};
+
 /*
  *  Board definitions
  */
@@ -2057,6 +2107,24 @@ struct em28xx_board em28xx_boards[] = {
 		.tuner_gpio	= default_tuner_gpio,
 		.def_i2c_bus	= 1,
 	},
+	/* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam
+	 * Empia EM2765 + OmniVision OV2640 */
+	[EM2765_BOARD_SPEEDLINK_VAD_LAPLACE] = {
+		.name         = "SpeedLink Vicious And Devine Laplace webcam",
+		.xclk         = EM28XX_XCLK_FREQUENCY_24MHZ,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
+				EM28XX_I2C_FREQ_100_KHZ,
+		.def_i2c_bus  = 1,
+		.tuner_type   = TUNER_ABSENT,
+		.is_webcam    = 1,
+		.input        = { {
+			.type     = EM28XX_VMUX_COMPOSITE1,
+			.amux     = EM28XX_AMUX_VIDEO,
+			.gpio     = speedlink_vad_laplace_reg_seq,
+		} },
+		.buttons = speedlink_vad_laplace_buttons,
+		.leds = speedlink_vad_laplace_leds,
+	},
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
@@ -2222,6 +2290,10 @@ struct usb_device_id em28xx_id_table[] = {
 			.driver_info = EM2884_BOARD_PCTV_520E },
 	{ USB_DEVICE(0x1b80, 0xe1cc),
 			.driver_info = EM2874_BOARD_DELOCK_61959 },
+	{ USB_DEVICE(0x1ae7, 0x9003),
+			.driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE },
+	{ USB_DEVICE(0x1ae7, 0x9004),
+			.driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE },
 	{ },
 };
 MODULE_DEVICE_TABLE(usb, em28xx_id_table);
drivers/media/usb/em28xx/em28xx.h +1 -0
index 43e2968..6e26fba 100644
@@ -132,6 +132,7 @@
 #define EM2884_BOARD_C3TECH_DIGITAL_DUO		  88
 #define EM2874_BOARD_DELOCK_61959		  89
 #define EM2874_BOARD_KWORLD_UB435Q_V2		  90
+#define EM2765_BOARD_SPEEDLINK_VAD_LAPLACE	  91
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
Signature · 1 line
1.7.10.4