Skip to contentSkip to conversation

em28xx: improve the sensor device code

PATCH New Frank Schäfer 1 participant 10 messages Mar 27, 2013

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

cover em28xx: improve the sensor device code

This patch series improves the sensor device support/code.
The changes include
- splitting sensor detection, board hints and sensor initialization/configuration (patches 1 and 2)
- moving the sensor specific code to a separate source code file (patch 4)
- improving/extending sensor probing and identification (patches 5, 7 and 8)
- adding (basic) support for the OmniVision OV2640 sensor (patch 9)

Frank Schäfer (9):
em28xx: fix and separate the board hints for sensor devices
em28xx: separate sensor detection and initialization/configuration
em28xx: rename em28xx_hint_sensor() to em28xx_detect_sensor()
em28xx: move sensor code to a separate source code file

    em28xx-camera.c

em28xx: detect further Micron sensors
em28xx: move the probing of Micron sensors to a separate function
em28xx: add probing procedure for OmniVision sensors
em28xx: add comment about Samsung and Kodak sensor probing addresses
em28xx: add basic support for OmniVision OV2640 sensors

 drivers/media/usb/em28xx/Makefile        |    2 +-
 drivers/media/usb/em28xx/em28xx-camera.c |  424 ++++++++++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-cards.c  |  183 ++-----------
 drivers/media/usb/em28xx/em28xx.h        |    5 +
 4 Dateien geändert, 451 Zeilen hinzugefügt(+), 163 Zeilen entfernt(-)
 create mode 100644 drivers/media/usb/em28xx/em28xx-camera.c
Signature · 1 line
1.7.10.4
#1 lore raw

PATCH 1/9 em28xx: fix and separate the board hints for sensor devices

The current board hint code is mixed together with the sensor detection and initialization code. It actually selects a board depending on the detected sensor type only, with the result that 3 of the 6 webcam boards are currently dead.

Separate it and move it to em28xx_hint_board() which already contains the board hints for analog capturing+TV and DVB devices.
This way, we have all board hints at a common place which makes it easier to extend the code and reduces the risk of regressions.
It also makes it possible again to use the boards EM2750_BOARD_DLCW_130, EM2820_BOARD_VIDEOLOGY_20K14XUSB and EM2860_BOARD_NETGMBH_CAM (using the module parameter "card").

NOTE: the current board hint logic for webcams is preserved. Not more not less.

---
 drivers/media/usb/em28xx/em28xx-cards.c |   40 +++++++++++++++----------------

1 Datei geändert, 19 Zeilen hinzugefügt(+), 21 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-cards.c +19 -21
index 54e0362..e41ecb5 100644
@@ -2333,9 +2333,6 @@ static int em28xx_hint_sensor(struct em28xx *dev)
 	switch (version) {
 	case 0x8232:		/* mt9v011 640x480 1.3 Mpix sensor */
 	case 0x8243:		/* mt9v011 rev B 640x480 1.3 Mpix sensor */
-		dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
-		em28xx_set_model(dev);
-
 		sensor_name = "mt9v011";
 		dev->em28xx_sensor = EM28XX_MT9V011;
 		dev->sensor_xres = 640;
@@ -2359,9 +2356,6 @@ static int em28xx_hint_sensor(struct em28xx *dev)
 		break;
 
 	case 0x143a:    /* MT9M111 as found in the ECS G200 */
-		dev->model = EM2750_BOARD_UNKNOWN;
-		em28xx_set_model(dev);
-
 		sensor_name = "mt9m111";
 		dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
 		dev->em28xx_sensor = EM28XX_MT9M111;
@@ -2375,9 +2369,6 @@ static int em28xx_hint_sensor(struct em28xx *dev)
 		break;
 
 	case 0x8431:
-		dev->model = EM2750_BOARD_UNKNOWN;
-		em28xx_set_model(dev);
-
 		sensor_name = "mt9m001";
 		dev->em28xx_sensor = EM28XX_MT9M001;
 		em28xx_initialize_mt9m001(dev);
@@ -2394,11 +2385,7 @@ static int em28xx_hint_sensor(struct em28xx *dev)
 		return -EINVAL;
 	}
 
-	/* Setup webcam defaults */
-	em28xx_pre_card_setup(dev);
-
-	em28xx_errdev("Sensor is %s, using model %s entry.\n",
-		      sensor_name, em28xx_boards[dev->model].name);
+	em28xx_info("sensor %s detected\n", sensor_name);
 
 	return 0;
 }
@@ -2628,6 +2615,18 @@ static int em28xx_hint_board(struct em28xx *dev)
 {
 	int i;
 
+	if (dev->board.is_webcam) {
+		if (dev->em28xx_sensor == EM28XX_MT9V011) {
+			dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+		} else if (dev->em28xx_sensor == EM28XX_MT9M001 ||
+			   dev->em28xx_sensor == EM28XX_MT9M111) {
+			dev->model = EM2750_BOARD_UNKNOWN;
+		}
+		/* FIXME: IMPROVE ! */
+
+		return 0;
+	}
+
 	/* HINT method: EEPROM
 	 *
 	 * This method works only for boards with eeprom.
@@ -2719,10 +2718,10 @@ static void em28xx_card_setup(struct em28xx *dev)
 			dev->progressive = 1;
 	}
 
-	if (!dev->board.is_webcam) {
-		switch (dev->model) {
-		case EM2820_BOARD_UNKNOWN:
-		case EM2800_BOARD_UNKNOWN:
+	switch (dev->model) {
+	case EM2750_BOARD_UNKNOWN:
+	case EM2820_BOARD_UNKNOWN:
+	case EM2800_BOARD_UNKNOWN:
 		/*
 		 * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
 		 *
@@ -2743,9 +2742,8 @@ static void em28xx_card_setup(struct em28xx *dev)
 			em28xx_pre_card_setup(dev);
 		}
 		break;
-		default:
-			em28xx_set_model(dev);
-		}
+	default:
+		em28xx_set_model(dev);
 	}
 
 	em28xx_info("Identified as %s (card=%d)\n",
Signature · 1 line
1.7.10.4
#2 lore raw

PATCH 2/9 em28xx: separate sensor detection and initialization/configuration

In reply to Frank Schäfer

Sensor detection and initialization/configuration are currently mixed together. This works as long as all devices with a particular sensor are working with the same board configuration. In the long run, this will be not sufficient, so separate these both steps to make the code more flexible and future proof. This also makes the code more consistent, because the initialization of the MT9V011 sensor subdevice is already separated.

---
 drivers/media/usb/em28xx/em28xx-cards.c |  122 ++++++++++++++++++-------------

1 Datei geändert, 72 Zeilen hinzugefügt(+), 50 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-cards.c +72 -50
index e41ecb5..c8ad7e5 100644
@@ -2335,50 +2335,14 @@ static int em28xx_hint_sensor(struct em28xx *dev)
 	case 0x8243:		/* mt9v011 rev B 640x480 1.3 Mpix sensor */
 		sensor_name = "mt9v011";
 		dev->em28xx_sensor = EM28XX_MT9V011;
-		dev->sensor_xres = 640;
-		dev->sensor_yres = 480;
-		/*
-		 * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
-		 * the Silvercrest cam I have here for testing - for higher
-		 * resolutions, a high clock cause horizontal artifacts, so we
-		 * need to use a lower xclk frequency.
-		 * Yet, it would be possible to adjust xclk depending on the
-		 * desired resolution, since this affects directly the
-		 * frame rate.
-		 */
-		dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
-		dev->sensor_xtal = 4300000;
-
-		/* probably means GRGB 16 bit bayer */
-		dev->vinmode = 0x0d;
-		dev->vinctl = 0x00;
-
 		break;
-
 	case 0x143a:    /* MT9M111 as found in the ECS G200 */
 		sensor_name = "mt9m111";
-		dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
 		dev->em28xx_sensor = EM28XX_MT9M111;
-		em28xx_initialize_mt9m111(dev);
-		dev->sensor_xres = 640;
-		dev->sensor_yres = 512;
-
-		dev->vinmode = 0x0a;
-		dev->vinctl = 0x00;
-
 		break;
-
 	case 0x8431:
 		sensor_name = "mt9m001";
 		dev->em28xx_sensor = EM28XX_MT9M001;
-		em28xx_initialize_mt9m001(dev);
-		dev->sensor_xres = 1280;
-		dev->sensor_yres = 1024;
-
-		/* probably means BGGR 16 bit bayer */
-		dev->vinmode = 0x0c;
-		dev->vinctl = 0x00;
-
 		break;
 	default:
 		printk("Unknown Micron Sensor 0x%04x\n", version);
@@ -2611,6 +2575,76 @@ static void em28xx_tuner_setup(struct em28xx *dev)
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
 }
 
+static int em28xx_init_camera(struct em28xx *dev)
+{
+	switch (dev->em28xx_sensor) {
+	case EM28XX_MT9V011:
+	{
+		struct mt9v011_platform_data pdata;
+		struct i2c_board_info mt9v011_info = {
+			.type = "mt9v011",
+			.addr = dev->i2c_client[dev->def_i2c_bus].addr,
+			.platform_data = &pdata,
+		};
+
+		dev->sensor_xres = 640;
+		dev->sensor_yres = 480;
+
+		/*
+		 * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+		 * the Silvercrest cam I have here for testing - for higher
+		 * resolutions, a high clock cause horizontal artifacts, so we
+		 * need to use a lower xclk frequency.
+		 * Yet, it would be possible to adjust xclk depending on the
+		 * desired resolution, since this affects directly the
+		 * frame rate.
+		 */
+		dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+		em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
+		dev->sensor_xtal = 4300000;
+		pdata.xtal = dev->sensor_xtal;
+		if (NULL ==
+		    v4l2_i2c_new_subdev_board(&dev->v4l2_dev,
+					      &dev->i2c_adap[dev->def_i2c_bus],
+					      &mt9v011_info, NULL))
+			return -ENODEV;
+		/* probably means GRGB 16 bit bayer */
+		dev->vinmode = 0x0d;
+		dev->vinctl = 0x00;
+
+		break;
+	}
+	case EM28XX_MT9M001:
+		dev->sensor_xres = 1280;
+		dev->sensor_yres = 1024;
+
+		em28xx_initialize_mt9m001(dev);
+
+		/* probably means BGGR 16 bit bayer */
+		dev->vinmode = 0x0c;
+		dev->vinctl = 0x00;
+
+		break;
+	case EM28XX_MT9M111:
+		dev->sensor_xres = 640;
+		dev->sensor_yres = 512;
+
+		dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
+		em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
+		em28xx_initialize_mt9m111(dev);
+
+		dev->vinmode = 0x0a;
+		dev->vinctl = 0x00;
+
+		break;
+	case EM28XX_NOSENSOR:
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int em28xx_hint_board(struct em28xx *dev)
 {
 	int i;
@@ -2878,20 +2912,6 @@ static void em28xx_card_setup(struct em28xx *dev)
 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
 			"tvp5150", 0, tvp5150_addrs);
 
-	if (dev->em28xx_sensor == EM28XX_MT9V011) {
-		struct mt9v011_platform_data pdata;
-		struct i2c_board_info mt9v011_info = {
-			.type = "mt9v011",
-			.addr = 0xba >> 1,
-			.platform_data = &pdata,
-		};
-
-		pdata.xtal = dev->sensor_xtal;
-		v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
-				&mt9v011_info, NULL);
-	}
-
-
 	if (dev->board.adecoder == EM28XX_TVAUDIO)
 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
 			"tvaudio", dev->board.tvaudio_addr, NULL);
@@ -2925,6 +2945,8 @@ static void em28xx_card_setup(struct em28xx *dev)
 	}
 
 	em28xx_tuner_setup(dev);
+
+	em28xx_init_camera(dev);
 }
 
 
Signature · 1 line
1.7.10.4
#3 lore raw

PATCH 3/9 em28xx: rename em28xx_hint_sensor() to em28xx_detect_sensor()

In reply to Frank Schäfer

Now that the board hints and the sensor initialization/configuration have been separated, em28xx_detect_sensor() is the better name for this function.

---
 drivers/media/usb/em28xx/em28xx-cards.c |    7 +++----

1 Datei geändert, 3 Zeilen hinzugefügt(+), 4 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-cards.c +3 -4
index c8ad7e5..7bb760e 100644
@@ -2309,11 +2309,10 @@ static int em28xx_initialize_mt9m001(struct em28xx *dev)
 	return 0;
 }
 
-/* HINT method: webcam I2C chips
- *
+/*
  * This method works for webcams with Micron sensors
  */
-static int em28xx_hint_sensor(struct em28xx *dev)
+static int em28xx_detect_sensor(struct em28xx *dev)
 {
 	int rc;
 	char *sensor_name;
@@ -2746,7 +2745,7 @@ static void em28xx_card_setup(struct em28xx *dev)
 	 * If sensor is not found, then it isn't a webcam.
 	 */
 	if (dev->board.is_webcam) {
-		if (em28xx_hint_sensor(dev) < 0)
+		if (em28xx_detect_sensor(dev) < 0)
 			dev->board.is_webcam = 0;
 		else
 			dev->progressive = 1;
Signature · 1 line
1.7.10.4
#4 lore raw

PATCH 4/9 em28xx: move sensor code to a separate source code file em28xx-camera.c

In reply to Frank Schäfer

em28xx-cards.c is very large and the sensor/camera related code is growing, so move this code to a separate source code file em28xx-camera.c.

---
 drivers/media/usb/em28xx/Makefile        |    2 +-
 drivers/media/usb/em28xx/em28xx-camera.c |  189 ++++++++++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-cards.c  |  160 -------------------------
 drivers/media/usb/em28xx/em28xx.h        |    4 +
 4 Dateien geändert, 194 Zeilen hinzugefügt(+), 161 Zeilen entfernt(-)
 create mode 100644 drivers/media/usb/em28xx/em28xx-camera.c
drivers/media/usb/em28xx/Makefile +1 -1
index 634fb92..ad6d485 100644
@@ -1,5 +1,5 @@
 em28xx-y +=	em28xx-video.o em28xx-i2c.o em28xx-cards.o
-em28xx-y +=	em28xx-core.o  em28xx-vbi.o
+em28xx-y +=	em28xx-core.o  em28xx-vbi.o em28xx-camera.o
 
 em28xx-alsa-objs := em28xx-audio.o
 em28xx-rc-objs := em28xx-input.o
drivers/media/usb/em28xx/em28xx-camera.c new file +189 -0

189 lines not on this page · raw message

drivers/media/usb/em28xx/em28xx-cards.c +0 -160

178 lines not on this page · raw message

drivers/media/usb/em28xx/em28xx.h +4 -0
index 7be008f..a14492f 100644
@@ -722,6 +722,10 @@ void em28xx_release_resources(struct em28xx *dev);
 /* Provided by em28xx-vbi.c */
 extern struct vb2_ops em28xx_vbi_qops;
 
+/* Provided by em28xx-camera.c */
+int em28xx_detect_sensor(struct em28xx *dev);
+int em28xx_init_camera(struct em28xx *dev);
+
 /* printk macros */
 
 #define em28xx_err(fmt, arg...) do {\
Signature · 1 line
1.7.10.4
#5 lore raw

PATCH 5/9 em28xx: detect further Micron sensors

In reply to Frank Schäfer

Add further Micron chip IDs to be able to identify all Micron sensors listed by Empiatech.
Also probe the two alternate i2c addresses used by Micron sensors with 8 bit address and 16 bit register width.

---
 drivers/media/usb/em28xx/em28xx-camera.c |  126 ++++++++++++++++++++++--------

1 Datei geändert, 95 Zeilen hinzugefügt(+), 31 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-camera.c +95 -31
index 28dd848..2e4856a 100644
@@ -26,6 +26,15 @@
 #include "em28xx.h"
 
 
+/* Possible i2c addresses of Micron sensors */
+static unsigned short micron_sensor_addrs[] = {
+	0xb8 >> 1,   /* MT9V111, MT9V403 */
+	0xba >> 1,   /* MT9M001/011/111/112, MT9V011/012/112, MT9D011 */
+	0x90 >> 1,   /* MT9V012/112, MT9D011 (alternative address) */
+	I2C_CLIENT_END
+};
+
+
 /* FIXME: Should be replaced by a proper mt9m111 driver */
 static int em28xx_initialize_mt9m111(struct em28xx *dev)
 {
@@ -78,44 +87,99 @@ static int em28xx_initialize_mt9m001(struct em28xx *dev)
  */
 int em28xx_detect_sensor(struct em28xx *dev)
 {
-	int ret;
+	int ret, i;
 	char *name;
 	u8 reg;
 	__be16 id_be;
 	u16 id;
 
-	/* Micron sensor detection */
-	dev->i2c_client[dev->def_i2c_bus].addr = 0xba >> 1;
-	reg = 0;
-	i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], &reg, 1);
-	ret = i2c_master_recv(&dev->i2c_client[dev->def_i2c_bus],
-			      (char *)&id_be, 2);
-	if (ret != 2)
-		return -EINVAL;
-
-	id = be16_to_cpu(id_be);
-	switch (id) {
-	case 0x8232:		/* mt9v011 640x480 1.3 Mpix sensor */
-	case 0x8243:		/* mt9v011 rev B 640x480 1.3 Mpix sensor */
-		name = "mt9v011";
-		dev->em28xx_sensor = EM28XX_MT9V011;
-		break;
-	case 0x143a:    /* MT9M111 as found in the ECS G200 */
-		name = "mt9m111";
-		dev->em28xx_sensor = EM28XX_MT9M111;
-		break;
-	case 0x8431:
-		name = "mt9m001";
-		dev->em28xx_sensor = EM28XX_MT9M001;
-		break;
-	default:
-		em28xx_info("unknown Micron sensor detected: 0x%04x\n", id);
-		return -EINVAL;
+	struct i2c_client client = dev->i2c_client[dev->def_i2c_bus];
+
+	dev->em28xx_sensor = EM28XX_NOSENSOR;
+	/* Probe Micron sensors with 8 bit address and 16 bit register width */
+	for (i = 0; micron_sensor_addrs[i] != I2C_CLIENT_END; i++) {
+		client.addr = micron_sensor_addrs[i];
+		/* NOTE: i2c_smbus_read_word_data() doesn't work with BE data */
+		/* Read chip ID from register 0x00 */
+		reg = 0x00;
+		ret = i2c_master_send(&client, &reg, 1);
+		if (ret < 0) {
+			if (ret != -ENODEV)
+				em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+					      client.addr << 1, ret);
+			continue;
+		}
+		ret = i2c_master_recv(&client, (u8 *)&id_be, 2);
+		if (ret < 0) {
+			em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+				      client.addr << 1, ret);
+			continue;
+		}
+		id = be16_to_cpu(id_be);
+		/* Read chip ID from register 0xff */
+		reg = 0xff;
+		ret = i2c_master_send(&client, &reg, 1);
+		if (ret < 0) {
+			em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+				      client.addr << 1, ret);
+			continue;
+		}
+		ret = i2c_master_recv(&client, (u8 *)&id_be, 2);
+		if (ret < 0) {
+			em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+				      client.addr << 1, ret);
+			continue;
+		}
+		/* Validate chip ID to be sure we have a Micron device */
+		if (id != be16_to_cpu(id_be))
+			continue;
+		/* Check chip ID */
+		id = be16_to_cpu(id_be);
+		switch (id) {
+		case 0x1222:
+			name = "MT9V012"; /* MI370 */ /* 640x480 */
+			break;
+		case 0x1229:
+			name = "MT9V112"; /* 640x480 */
+			break;
+		case 0x1433:
+			name = "MT9M011"; /* 1280x1024 */
+			break;
+		case 0x143a:    /* found in the ECS G200 */
+			name = "MT9M111"; /* MI1310 */ /* 1280x1024 */
+			dev->em28xx_sensor = EM28XX_MT9M111;
+			break;
+		case 0x148c:
+			name = "MT9M112"; /* MI1320 */ /* 1280x1024 */
+			break;
+		case 0x1511:
+			name = "MT9D011"; /* MI2010 */ /* 1600x1200 */
+			break;
+		case 0x8232:
+		case 0x8243:	/* rev B */
+			name = "MT9V011"; /* MI360 */ /* 640x480 */
+			dev->em28xx_sensor = EM28XX_MT9V011;
+			break;
+		case 0x8431:
+			name = "MT9M001"; /* 1280x1024 */
+			dev->em28xx_sensor = EM28XX_MT9M001;
+			break;
+		default:
+			em28xx_info("unknown Micron sensor detected: 0x%04x\n",
+				    id);
+			return -EINVAL;
+		}
+
+		if (dev->em28xx_sensor == EM28XX_NOSENSOR)
+			em28xx_info("unsupported sensor detected: %s\n", name);
+		else
+			em28xx_info("sensor %s detected\n", name);
+
+		dev->i2c_client[dev->def_i2c_bus].addr = client.addr;
+		return 0;
 	}
 
-	em28xx_info("sensor %s detected\n", name);
-
-	return 0;
+	return -ENODEV;
 }
 
 int em28xx_init_camera(struct em28xx *dev)
Signature · 1 line
1.7.10.4
#6 lore raw

PATCH 6/9 em28xx: move the probing of Micron sensors to a separate function

In reply to Frank Schäfer

Other sensors like the ones from OmniVision need a different probing procedure, so it makes sense have separate functions for each manufacturer/sensor type.

---
 drivers/media/usb/em28xx/em28xx-camera.c |   23 +++++++++++++++++++----

1 Datei geändert, 19 Zeilen hinzugefügt(+), 4 Zeilen entfernt(-)

drivers/media/usb/em28xx/em28xx-camera.c +19 -4
index 2e4856a..d744af6 100644
@@ -83,9 +83,9 @@ static int em28xx_initialize_mt9m001(struct em28xx *dev)
 
 
 /*
- * This method works for webcams with Micron sensors
+ * Probes Micron sensors with 8 bit address and 16 bit register width
  */
-int em28xx_detect_sensor(struct em28xx *dev)
+static int em28xx_probe_sensor_micron(struct em28xx *dev)
 {
 	int ret, i;
 	char *name;
@@ -96,7 +96,6 @@ int em28xx_detect_sensor(struct em28xx *dev)
 	struct i2c_client client = dev->i2c_client[dev->def_i2c_bus];
 
 	dev->em28xx_sensor = EM28XX_NOSENSOR;
-	/* Probe Micron sensors with 8 bit address and 16 bit register width */
 	for (i = 0; micron_sensor_addrs[i] != I2C_CLIENT_END; i++) {
 		client.addr = micron_sensor_addrs[i];
 		/* NOTE: i2c_smbus_read_word_data() doesn't work with BE data */
@@ -167,7 +166,7 @@ int em28xx_detect_sensor(struct em28xx *dev)
 		default:
 			em28xx_info("unknown Micron sensor detected: 0x%04x\n",
 				    id);
-			return -EINVAL;
+			return 0;
 		}
 
 		if (dev->em28xx_sensor == EM28XX_NOSENSOR)
@@ -182,6 +181,22 @@ int em28xx_detect_sensor(struct em28xx *dev)
 	return -ENODEV;
 }
 
+/*
+ * This method works for webcams with Micron sensors
+ */
+int em28xx_detect_sensor(struct em28xx *dev)
+{
+	int ret;
+
+	ret = em28xx_probe_sensor_micron(dev);
+	if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) {
+		em28xx_info("No sensor detected\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
 int em28xx_init_camera(struct em28xx *dev)
 {
 	switch (dev->em28xx_sensor) {
Signature · 1 line
1.7.10.4
#7 lore raw

PATCH 7/9 em28xx: add probing procedure for OmniVision sensors

In reply to Frank Schäfer

OmniVision sensors are used as well in Empiatech based cameras such as the "SpeedLink Vicious And Devine Laplace" webcam (EM2765 + Omnivision OV2640). With this patch applied, OminiVision sensors with 8 bit address and register width are detected (recent models have a 16 bit address width and use different client addresses).
The most commonly used sensors (including the ones listed by Empiatech) are detected properly, although there is no support for them yet.

---
 drivers/media/usb/em28xx/em28xx-camera.c |  114 +++++++++++++++++++++++++++++-

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

drivers/media/usb/em28xx/em28xx-camera.c +113 -1
index d744af6..e8b3322 100644
@@ -34,6 +34,13 @@ static unsigned short micron_sensor_addrs[] = {
 	I2C_CLIENT_END
 };
 
+/* Possible i2c addresses of Omnivision sensors */
+static unsigned short omnivision_sensor_addrs[] = {
+	0x42 >> 1,   /* OV7725, OV7670/60/48 */
+	0x60 >> 1,   /* OV2640, OV9650/53/55 */
+	I2C_CLIENT_END
+};
+
 
 /* FIXME: Should be replaced by a proper mt9m111 driver */
 static int em28xx_initialize_mt9m111(struct em28xx *dev)
@@ -182,13 +189,118 @@ static int em28xx_probe_sensor_micron(struct em28xx *dev)
 }
 
 /*
- * This method works for webcams with Micron sensors
+ * Probes Omnivision sensors with 8 bit address and register width
  */
+static int em28xx_probe_sensor_omnivision(struct em28xx *dev)
+{
+	int ret, i;
+	char *name;
+	u8 reg;
+	u16 id;
+	struct i2c_client client = dev->i2c_client[dev->def_i2c_bus];
+
+	dev->em28xx_sensor = EM28XX_NOSENSOR;
+	/* NOTE: these devices have the register auto incrementation disabled
+	 * by default, so we have to use single byte reads !              */
+	for (i = 0; omnivision_sensor_addrs[i] != I2C_CLIENT_END; i++) {
+		client.addr = omnivision_sensor_addrs[i];
+		/* Read manufacturer ID from registers 0x1c-0x1d (BE) */
+		reg = 0x1c;
+		ret = i2c_smbus_read_byte_data(&client, reg);
+		if (ret < 0) {
+			if (ret != -ENODEV)
+				em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+					      client.addr << 1, ret);
+			continue;
+		}
+		id = ret << 8;
+		reg = 0x1d;
+		ret = i2c_smbus_read_byte_data(&client, reg);
+		if (ret < 0) {
+			em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+				      client.addr << 1, ret);
+			continue;
+		}
+		id += ret;
+		/* Check manufacturer ID */
+		if (id != 0x7fa2)
+			continue;
+		/* Read product ID from registers 0x0a-0x0b (BE) */
+		reg = 0x0a;
+		ret = i2c_smbus_read_byte_data(&client, reg);
+		if (ret < 0) {
+			em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+				      client.addr << 1, ret);
+			continue;
+		}
+		id = ret << 8;
+		reg = 0x0b;
+		ret = i2c_smbus_read_byte_data(&client, reg);
+		if (ret < 0) {
+			em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
+				      client.addr << 1, ret);
+			continue;
+		}
+		id += ret;
+		/* Check product ID */
+		switch (id) {
+		case 0x2642:
+			name = "OV2640";
+			break;
+		case 0x7648:
+			name = "OV7648";
+			break;
+		case 0x7660:
+			name = "OV7660";
+			break;
+		case 0x7673:
+			name = "OV7670";
+			break;
+		case 0x7720:
+			name = "OV7720";
+			break;
+		case 0x7721:
+			name = "OV7725";
+			break;
+		case 0x9648: /* Rev 2 */
+		case 0x9649: /* Rev 3 */
+			name = "OV9640";
+			break;
+		case 0x9650:
+		case 0x9652: /* OV9653 */
+			name = "OV9650";
+			break;
+		case 0x9656: /* Rev 4 */
+		case 0x9657: /* Rev 5 */
+			name = "OV9655";
+			break;
+		default:
+			em28xx_info("unknown OmniVision sensor detected: 0x%04x\n",
+				    id);
+			return 0;
+		}
+
+		if (dev->em28xx_sensor == EM28XX_NOSENSOR)
+			em28xx_info("unsupported sensor detected: %s\n", name);
+		else
+			em28xx_info("sensor %s detected\n", name);
+
+		dev->i2c_client[dev->def_i2c_bus].addr = client.addr;
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
 int em28xx_detect_sensor(struct em28xx *dev)
 {
 	int ret;
 
 	ret = em28xx_probe_sensor_micron(dev);
+
+	if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0)
+		ret = em28xx_probe_sensor_omnivision(dev);
+
 	if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) {
 		em28xx_info("No sensor detected\n");
 		return -ENODEV;
Signature · 1 line
1.7.10.4
#8 lore raw

PATCH 8/9 em28xx: add comment about Samsung and Kodak sensor probing addresses

In reply to Frank Schäfer

The Windows driver also probes at least two further i2c addresses (0x22 >> 1 and 0x66 >> 1). I've got some hints that they are very likely used by Samsung and Kodak sensors, which are known to be used in Empia devices, too. We havn't seen any devices using these sensors yet and don't know how to probe them properly, so leave a comment.

---
 drivers/media/usb/em28xx/em28xx-camera.c |    5 +++++

1 Datei geändert, 5 Zeilen hinzugefügt(+)

drivers/media/usb/em28xx/em28xx-camera.c +5 -0
index e8b3322..64b70d4 100644
@@ -301,6 +301,11 @@ int em28xx_detect_sensor(struct em28xx *dev)
 	if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0)
 		ret = em28xx_probe_sensor_omnivision(dev);
 
+	/*
+	 * NOTE: the Windows driver also probes i2c addresses
+	 *       0x22 (Samsung ?) and 0x66 (Kodak ?)
+	 */
+
 	if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) {
 		em28xx_info("No sensor detected\n");
 		return -ENODEV;
Signature · 1 line
1.7.10.4
#9 lore raw

PATCH 9/9 em28xx: add basic support for OmniVision OV2640 sensors

In reply to Frank Schäfer

This sensor is used by the "SpeedLink Vicious And Devine Laplace webcam" and others. It supports resolutions up to 1600x1200 (at 7-8 fps), but for resolutions higher than 640x480, further driver changes will be necessary, such as sensor output resolution switching (including further configuration changes), bridge xclk adjustment and disabling of 16 bit (12 bit) output formats at high resolutions. Image quality should also needs to be improved.

---
 drivers/media/usb/em28xx/em28xx-camera.c |   49 ++++++++++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx.h        |    1 +

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

drivers/media/usb/em28xx/em28xx-camera.c +49 -0
index 64b70d4..73cc50a 100644
@@ -20,6 +20,7 @@
 */
 
 #include <linux/i2c.h>
+#include <media/soc_camera.h>
 #include <media/mt9v011.h>
 #include <media/v4l2-common.h>
 
@@ -42,6 +43,13 @@ static unsigned short omnivision_sensor_addrs[] = {
 };
 
 
+static struct soc_camera_link camlink = {
+	.bus_id = 0,
+	.flags = 0,
+	.module_name = "em28xx",
+};
+
+
 /* FIXME: Should be replaced by a proper mt9m111 driver */
 static int em28xx_initialize_mt9m111(struct em28xx *dev)
 {
@@ -246,6 +254,7 @@ static int em28xx_probe_sensor_omnivision(struct em28xx *dev)
 		switch (id) {
 		case 0x2642:
 			name = "OV2640";
+			dev->em28xx_sensor = EM28XX_OV2640;
 			break;
 		case 0x7648:
 			name = "OV7648";
@@ -376,6 +385,46 @@ int em28xx_init_camera(struct em28xx *dev)
 		dev->vinctl = 0x00;
 
 		break;
+	case EM28XX_OV2640:
+	{
+		struct v4l2_subdev *subdev;
+		struct i2c_board_info ov2640_info = {
+			.type = "ov2640",
+			.flags = I2C_CLIENT_SCCB,
+			.addr = dev->i2c_client[dev->def_i2c_bus].addr,
+			.platform_data = &camlink,
+		};
+		struct v4l2_mbus_framefmt fmt;
+
+		/*
+		 * FIXME: sensor supports resolutions up to 1600x1200, but
+		 * resolution setting/switching needs to be modified to
+		 * - switch sensor output resolution (including further
+		 *   configuration changes)
+		 * - adjust bridge xclk
+		 * - disable 16 bit (12 bit) output formats on high resolutions
+		 */
+		dev->sensor_xres = 640;
+		dev->sensor_yres = 480;
+
+		subdev =
+		     v4l2_i2c_new_subdev_board(&dev->v4l2_dev,
+					       &dev->i2c_adap[dev->def_i2c_bus],
+					       &ov2640_info, NULL);
+
+		fmt.code = V4L2_MBUS_FMT_YUYV8_2X8;
+		fmt.width = 640;
+		fmt.height = 480;
+		v4l2_subdev_call(subdev, video, s_mbus_fmt, &fmt);
+
+		/* NOTE: for UXGA=1600x1200 switch to 12MHz */
+		dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ;
+		em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
+		dev->vinmode = 0x08;
+		dev->vinctl = 0x00;
+
+		break;
+	}
 	case EM28XX_NOSENSOR:
 	default:
 		return -EINVAL;
drivers/media/usb/em28xx/em28xx.h +1 -0
index a14492f..a9323b6 100644
@@ -364,6 +364,7 @@ enum em28xx_sensor {
 	EM28XX_MT9V011,
 	EM28XX_MT9M001,
 	EM28XX_MT9M111,
+	EM28XX_OV2640,
 };
 
 enum em28xx_adecoder {
Signature · 1 line
1.7.10.4