aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank Schaefer2013-03-27 17:06:34 -0300
committerMauro Carvalho Chehab2013-03-29 06:40:10 -0300
commitbde03684323d12b5da0834475bf9964f064a403b (patch)
tree17956683772b40dd12a67b8a083598259b1dba14
parent0af0b25a64458c3ee002efe6e7542013b94a268a (diff)
[media] em28xx: add probing procedure for OmniVision sensors
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. Signed-off-by: Frank Schäfer <fschaefer.oss@googlemail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c114
1 files changed, 113 insertions, 1 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index d744af6f7d08..e8b3322242c0 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/drivers/media/usb/em28xx/em28xx-camera.c
@@ -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;