From patchwork Sat Mar 23 17:27:08 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Frank Schaefer X-Patchwork-Id: 17584 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1UJSDD-0000HT-0C; Sat, 23 Mar 2013 18:26:35 +0100 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.75/mailfrontend-2) with esmtp id 1UJSDB-0005Lu-Ik; Sat, 23 Mar 2013 18:26:34 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751873Ab3CWR0a (ORCPT + 1 other); Sat, 23 Mar 2013 13:26:30 -0400 Received: from mail-ee0-f52.google.com ([74.125.83.52]:39966 "EHLO mail-ee0-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751864Ab3CWR02 (ORCPT ); Sat, 23 Mar 2013 13:26:28 -0400 Received: by mail-ee0-f52.google.com with SMTP id d49so98228eek.11 for ; Sat, 23 Mar 2013 10:26:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:mime-version:content-type:content-transfer-encoding; bh=oIBS5UWaNf4MG33givXRsLwXfFdrDfA4+IaFZJHS4tY=; b=bCGLVcyV3kW3qAe6c8eAp1LHCbistx79Ys/lFNEhI6MX/MF25Vd4a5HpFvtNl2Wh8H qnzVkZqOeiBcT8wSKGdmBGjMk8dXZ0DeLEg+Jbn7To/e/BmCyC5kWuhOndHv97NUQDTl mHTyV2RVybWYAwA8Jtmhrdo+urMvZnRNQFiLw7+b6a7srLzab+BQrTN8uLnk+YsCYesB n5ryuPmtbZMUPQac/fbB9wpNIww772StX22kh83sOdTNugWEGL2GkMOCnI6MTUqZogAE pfTr462e+tqPJas+ZHfYwE22IwO7483RQ3ee1nTp4v9FKsRaCx9KnZSgskSCB93tlKoT zGSg== X-Received: by 10.14.223.199 with SMTP id v47mr16194700eep.18.1364059586670; Sat, 23 Mar 2013 10:26:26 -0700 (PDT) Received: from Athlon64X2-5000.site (ip-109-90-247-70.unitymediagroup.de. [109.90.247.70]) by mx.google.com with ESMTPS id f47sm9283468eep.13.2013.03.23.10.26.25 (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sat, 23 Mar 2013 10:26:26 -0700 (PDT) From: =?UTF-8?q?Frank=20Sch=C3=A4fer?= To: mchehab@redhat.com Cc: linux-media@vger.kernel.org, =?UTF-8?q?Frank=20Sch=C3=A4fer?= Subject: [PATCH v2 1/5] em28xx: add support for em25xx i2c bus B read/write/check device operations Date: Sat, 23 Mar 2013 18:27:08 +0100 Message-Id: <1364059632-29070-2-git-send-email-fschaefer.oss@googlemail.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1364059632-29070-1-git-send-email-fschaefer.oss@googlemail.com> References: <1364059632-29070-1-git-send-email-fschaefer.oss@googlemail.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 5.6.1.2065439, Antispam-Engine: 2.7.2.376379, Antispam-Data: 2013.3.23.172118 X-PMX-Spam: Gauge=IIIIIIII, Probability=8%, Report=' MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, CT_TEXT_PLAIN_UTF8_CAPS 0, DKIM_SIGNATURE 0, __ANY_URI 0, __CT 0, __CTE 0, __CT_TEXT_PLAIN 0, __FRAUD_BODY_WEBMAIL 0, __FRAUD_WEBMAIL 0, __FRAUD_WEBMAIL_FROM 0, __HAS_FROM 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __IN_REP_TO 0, __MIME_TEXT_ONLY 0, __MIME_VERSION 0, __MULTIPLE_RCPTS_CC_X2 0, __PHISH_SPEAR_STRUCTURE_1 0, __SANE_MSGID 0, __STOCK_PHRASE_7 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NO_PATH 0, __URI_NO_WWW 0, __URI_NS , __YOUTUBE_RCVD 0' The webcam "SpeedLink VAD Laplace" (em2765 + ov2640) uses a special algorithm for i2c communication with the sensor, which is connected to a second i2c bus. We don't know yet how to find out which devices support/use it. It's very likely used by all em25xx and em276x+ bridges. Tests with other em28xx chips (em2820, em2882/em2883) show, that this algorithm always succeeds there although no slave device is connected. The algorithm likely also works for real i2c client devices (OV2640 uses SCCB), because the Windows driver seems to use it for probing Samsung and Kodak sensors. Signed-off-by: Frank Schäfer --- drivers/media/usb/em28xx/em28xx-cards.c | 8 +- drivers/media/usb/em28xx/em28xx-i2c.c | 229 +++++++++++++++++++++++++------ drivers/media/usb/em28xx/em28xx.h | 10 +- 3 Dateien geändert, 205 Zeilen hinzugefügt(+), 42 Zeilen entfernt(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index cb7cdd3..033b6cb 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3139,15 +3139,19 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, rt_mutex_init(&dev->i2c_bus_lock); /* register i2c bus 0 */ - retval = em28xx_i2c_register(dev, 0); + if (dev->board.is_em2800) + retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM2800); + else + retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM28XX); if (retval < 0) { em28xx_errdev("%s: em28xx_i2c_register bus 0 - error [%d]!\n", __func__, retval); goto unregister_dev; } + /* register i2c bus 1 */ if (dev->def_i2c_bus) { - retval = em28xx_i2c_register(dev, 1); + retval = em28xx_i2c_register(dev, 1, EM28XX_I2C_ALGO_EM28XX); if (retval < 0) { em28xx_errdev("%s: em28xx_i2c_register bus 1 - error [%d]!\n", __func__, retval); diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 9e2fa41..ab14ac3 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -5,6 +5,7 @@ Markus Rechberger Mauro Carvalho Chehab Sascha Sommer + Copyright (C) 2013 Frank Schäfer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -274,6 +275,176 @@ static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) } /* + * em25xx_bus_B_send_bytes + * write bytes to the i2c device + */ +static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len) +{ + int ret; + + if (len < 1 || len > 64) + return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ + + /* Set register and write value */ + ret = dev->em28xx_write_regs_req(dev, 0x06, addr, buf, len); + /* NOTE: + * 0 byte writes always succeed, even if no device is connected. */ + if (ret != len) { + if (ret < 0) { + em28xx_warn("writing to i2c device at 0x%x failed " + "(error=%i)\n", addr, ret); + return ret; + } else { + em28xx_warn("%i bytes write to i2c device at 0x%x " + "requested, but %i bytes written\n", + len, addr, ret); + return -EIO; + } + } + /* Check success */ + ret = dev->em28xx_read_reg_req(dev, 0x08, 0x0000); + /* NOTE: the only error we've seen so far is + * 0x01 when the slave device is not present */ + if (ret == 0x00) { + return len; + } else if (ret > 0) { + return -ENODEV; + } + + return ret; + /* NOTE: With chips which do not support this operation, + * it seems to succeed ALWAYS ! (even if no device connected) */ +} + +/* + * em25xx_bus_B_recv_bytes + * read bytes from the i2c device + */ +static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len) +{ + int ret; + + if (len < 1 || len > 64) + return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ + + /* Read value */ + ret = dev->em28xx_read_reg_req_len(dev, 0x06, addr, buf, len); + /* NOTE: + * 0 byte reads always succeed, even if no device is connected. */ + if (ret < 0) { + em28xx_warn("reading from i2c device at 0x%x failed (error=%i)\n", + addr, ret); + return ret; + } + /* NOTE: some devices with two i2c busses have the bad habit to return 0 + * bytes if we are on bus B AND there was no write attempt to the + * specified slave address before AND no device is present at the + * requested slave address. + * Anyway, the next check will fail with -ENODEV in this case, so avoid + * spamming the system log on device probing and do nothing here. + */ + + /* Check success */ + ret = dev->em28xx_read_reg_req(dev, 0x08, 0x0000); + /* NOTE: the only error we've seen so far is + * 0x01 when the slave device is not present */ + if (ret == 0x00) { + return len; + } else if (ret > 0) { + return -ENODEV; + } + + return ret; + /* NOTE: With chips which do not support this operation, + * it seems to succeed ALWAYS ! (even if no device connected) */ +} + +/* + * em25xx_bus_B_check_for_device() + * check if there is a i2c device at the supplied address + */ +static int em25xx_bus_B_check_for_device(struct em28xx *dev, u16 addr) +{ + u8 buf; + int ret; + + ret = em25xx_bus_B_recv_bytes(dev, addr, &buf, 1); + if (ret < 0) + return ret; + + return 0; + /* NOTE: With chips which do not support this operation, + * it seems to succeed ALWAYS ! (even if no device connected) */ +} + +static inline int i2c_check_for_device(struct em28xx_i2c_bus *i2c_bus, u16 addr) +{ + struct em28xx *dev = i2c_bus->dev; + int rc = -EOPNOTSUPP; + + if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) { + rc = em28xx_i2c_check_for_device(dev, addr); + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) { + rc = em2800_i2c_check_for_device(dev, addr); + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) { + rc = em25xx_bus_B_check_for_device(dev, addr); + } + if (rc == -ENODEV) { + if (i2c_debug) + printk(" no device\n"); + } + return rc; +} + +static inline int i2c_recv_bytes(struct em28xx_i2c_bus *i2c_bus, + struct i2c_msg msg) +{ + struct em28xx *dev = i2c_bus->dev; + u16 addr = msg.addr << 1; + int byte, rc = -EOPNOTSUPP; + + if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) { + rc = em28xx_i2c_recv_bytes(dev, addr, msg.buf, msg.len); + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) { + rc = em2800_i2c_recv_bytes(dev, addr, msg.buf, msg.len); + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) { + rc = em25xx_bus_B_recv_bytes(dev, addr, msg.buf, msg.len); + } + if (i2c_debug) { + for (byte = 0; byte < msg.len; byte++) + printk(" %02x", msg.buf[byte]); + } + return rc; +} + +static inline int i2c_send_bytes(struct em28xx_i2c_bus *i2c_bus, + struct i2c_msg msg, int stop) +{ + struct em28xx *dev = i2c_bus->dev; + u16 addr = msg.addr << 1; + int byte, rc = -EOPNOTSUPP; + + if (i2c_debug) { + for (byte = 0; byte < msg.len; byte++) + printk(" %02x", msg.buf[byte]); + } + if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) { + rc = em28xx_i2c_send_bytes(dev, addr, msg.buf, msg.len, stop); + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) { + rc = em2800_i2c_send_bytes(dev, addr, msg.buf, msg.len); + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) { + rc = em25xx_bus_B_send_bytes(dev, addr, msg.buf, msg.len); + } + return rc; +} + +/* * em28xx_i2c_xfer() * the main i2c transfer function */ @@ -283,7 +454,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data; struct em28xx *dev = i2c_bus->dev; unsigned bus = i2c_bus->bus; - int addr, rc, i, byte; + int addr, rc, i; u8 reg; rc = rt_mutex_trylock(&dev->i2c_bus_lock); @@ -291,7 +462,8 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, return rc; /* Switch I2C bus if needed */ - if (bus != dev->cur_i2c_bus) { + if (bus != dev->cur_i2c_bus && + i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) { if (bus == 1) reg = EM2874_I2C_SECONDARY_BUS_SELECT; else @@ -314,45 +486,17 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); if (!msgs[i].len) { /* no len: check only for device presence */ - if (dev->board.is_em2800) - rc = em2800_i2c_check_for_device(dev, addr); - else - rc = em28xx_i2c_check_for_device(dev, addr); + rc = i2c_check_for_device(i2c_bus, addr); if (rc == -ENODEV) { - if (i2c_debug) - printk(" no device\n"); rt_mutex_unlock(&dev->i2c_bus_lock); return rc; } } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ - if (dev->board.is_em2800) - rc = em2800_i2c_recv_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - else - rc = em28xx_i2c_recv_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - if (i2c_debug) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } + rc = i2c_recv_bytes(i2c_bus, msgs[i]); } else { /* write bytes */ - if (i2c_debug) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } - if (dev->board.is_em2800) - rc = em2800_i2c_send_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - else - rc = em28xx_i2c_send_bytes(dev, addr, - msgs[i].buf, - msgs[i].len, - i == num - 1); + rc = i2c_send_bytes(i2c_bus, msgs[i], i == num - 1); } if (rc < 0) { if (i2c_debug) @@ -622,12 +766,17 @@ error: static u32 functionality(struct i2c_adapter *i2c_adap) { struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data; - struct em28xx *dev = i2c_bus->dev; - u32 func_flags = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; - if (dev->board.is_em2800) - func_flags &= ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA; - return func_flags; + if ((i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) || + (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B)) { + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) { + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL) & + ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA; + } + + WARN(1, "Unknown i2c bus algorithm.\n"); + return 0; } static struct i2c_algorithm em28xx_algo = { @@ -701,7 +850,8 @@ void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus) * em28xx_i2c_register() * register i2c bus */ -int em28xx_i2c_register(struct em28xx *dev, unsigned bus) +int em28xx_i2c_register(struct em28xx *dev, unsigned bus, + enum em28xx_i2c_algo_type algo_type) { int retval; @@ -716,6 +866,7 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus) strcpy(dev->i2c_adap[bus].name, dev->name); dev->i2c_bus[bus].bus = bus; + dev->i2c_bus[bus].algo_type = algo_type; dev->i2c_bus[bus].dev = dev; dev->i2c_adap[bus].algo_data = &dev->i2c_bus[bus]; i2c_set_adapdata(&dev->i2c_adap[bus], &dev->v4l2_dev); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 4c667fd..aeee896 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -461,10 +461,17 @@ struct em28xx_fh { enum v4l2_buf_type type; }; +enum em28xx_i2c_algo_type { + EM28XX_I2C_ALGO_EM28XX = 0, + EM28XX_I2C_ALGO_EM2800, + EM28XX_I2C_ALGO_EM25XX_BUS_B, +}; + struct em28xx_i2c_bus { struct em28xx *dev; unsigned bus; + enum em28xx_i2c_algo_type algo_type; }; @@ -651,7 +658,8 @@ struct em28xx_ops { /* Provided by em28xx-i2c.c */ void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus); -int em28xx_i2c_register(struct em28xx *dev, unsigned bus); +int em28xx_i2c_register(struct em28xx *dev, unsigned bus, + enum em28xx_i2c_algo_type algo_type); int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus); /* Provided by em28xx-core.c */