From: Mauro Carvalho Chehab Date: Tue, 5 Mar 2013 09:55:28 +0000 (-0300) Subject: [media] em28xx: add support for registering multiple i2c buses X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=aab3125c43d8;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [media] em28xx: add support for registering multiple i2c buses Register both buses 0 and 1 via I2C API. For now, bus 0 is used only by eeprom on all known devices. Later patches will be needed if this changes in the future. Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 16ab4d7780b3..6e62b72376b0 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2235,8 +2235,8 @@ static inline void em28xx_set_model(struct em28xx *dev) dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ; - if (dev->board.def_i2c_bus == 1) - dev->board.i2c_speed |= EM2874_I2C_SECONDARY_BUS_SELECT; + /* Should be initialized early, for I2C to work */ + dev->def_i2c_bus = dev->board.def_i2c_bus; } @@ -2642,7 +2642,7 @@ static int em28xx_hint_board(struct em28xx *dev) /* user did not request i2c scanning => do it now */ if (!dev->i2c_hash) - em28xx_do_i2c_scan(dev); + em28xx_do_i2c_scan(dev, dev->def_i2c_bus); for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) { if (dev->i2c_hash == em28xx_i2c_hash[i].hash) { @@ -2953,7 +2953,9 @@ void em28xx_release_resources(struct em28xx *dev) em28xx_release_analog_resources(dev); - em28xx_i2c_unregister(dev); + if (dev->def_i2c_bus) + em28xx_i2c_unregister(dev, 1); + em28xx_i2c_unregister(dev, 0); v4l2_ctrl_handler_free(&dev->ctrl_handler); @@ -3109,14 +3111,25 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, v4l2_ctrl_handler_init(hdl, 8); dev->v4l2_dev.ctrl_handler = hdl; - /* register i2c bus */ - retval = em28xx_i2c_register(dev); + rt_mutex_init(&dev->i2c_bus_lock); + + /* register i2c bus 0 */ + retval = em28xx_i2c_register(dev, 0); if (retval < 0) { - em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n", + em28xx_errdev("%s: em28xx_i2c_register bus 0 - error [%d]!\n", __func__, retval); goto unregister_dev; } + if (dev->def_i2c_bus) { + retval = em28xx_i2c_register(dev, 1); + if (retval < 0) { + em28xx_errdev("%s: em28xx_i2c_register bus 1 - error [%d]!\n", + __func__, retval); + goto unregister_dev; + } + } + /* * Default format, used for tvp5150 or saa711x output formats */ @@ -3186,7 +3199,9 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return 0; fail: - em28xx_i2c_unregister(dev); + if (dev->def_i2c_bus) + em28xx_i2c_unregister(dev, 1); + em28xx_i2c_unregister(dev, 0); v4l2_ctrl_handler_free(&dev->ctrl_handler); unregister_dev: diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 9086e57914e6..d4a48cb1202e 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -280,11 +280,29 @@ static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { - struct em28xx *dev = i2c_adap->algo_data; + 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; - if (num <= 0) + rc = rt_mutex_trylock(&dev->i2c_bus_lock); + if (rc < 0) + return rc; + + /* Switch I2C bus if needed */ + if (bus != dev->cur_i2c_bus) { + if (bus == 1) + dev->cur_i2c_bus |= EM2874_I2C_SECONDARY_BUS_SELECT; + else + dev->cur_i2c_bus &= ~EM2874_I2C_SECONDARY_BUS_SELECT; + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->cur_i2c_bus); + dev->cur_i2c_bus = bus; + } + + if (num <= 0) { + rt_mutex_unlock(&dev->i2c_bus_lock); return 0; + } for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; if (i2c_debug) @@ -301,6 +319,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, 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) { @@ -336,12 +355,14 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, if (rc < 0) { if (i2c_debug) printk(" ERROR: %i\n", rc); + rt_mutex_unlock(&dev->i2c_bus_lock); return rc; } if (i2c_debug) printk("\n"); } + rt_mutex_unlock(&dev->i2c_bus_lock); return num; } @@ -372,8 +393,8 @@ static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits) /* Helper function to read data blocks from i2c clients with 8 or 16 bit * address width, 8 bit register width and auto incrementation been activated */ -static int em28xx_i2c_read_block(struct em28xx *dev, u16 addr, bool addr_w16, - u16 len, u8 *data) +static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr, + bool addr_w16, u16 len, u8 *data) { int remain = len, rsize, rsize_max, ret; u8 buf[2]; @@ -384,7 +405,7 @@ static int em28xx_i2c_read_block(struct em28xx *dev, u16 addr, bool addr_w16, /* Select address */ buf[0] = addr >> 8; buf[1] = addr & 0xff; - ret = i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], buf + !addr_w16, 1 + addr_w16); + ret = i2c_master_send(&dev->i2c_client[bus], buf + !addr_w16, 1 + addr_w16); if (ret < 0) return ret; /* Read data */ @@ -398,7 +419,7 @@ static int em28xx_i2c_read_block(struct em28xx *dev, u16 addr, bool addr_w16, else rsize = remain; - ret = i2c_master_recv(&dev->i2c_client[dev->def_i2c_bus], data, rsize); + ret = i2c_master_recv(&dev->i2c_client[bus], data, rsize); if (ret < 0) return ret; @@ -409,7 +430,8 @@ static int em28xx_i2c_read_block(struct em28xx *dev, u16 addr, bool addr_w16, return len; } -static int em28xx_i2c_eeprom(struct em28xx *dev, u8 **eedata, u16 *eedata_len) +static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, + u8 **eedata, u16 *eedata_len) { const u16 len = 256; /* FIXME common length/size for bytes to read, to display, hash @@ -422,10 +444,12 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, u8 **eedata, u16 *eedata_len) *eedata = NULL; *eedata_len = 0; - dev->i2c_client[dev->def_i2c_bus].addr = 0xa0 >> 1; + /* EEPROM is always on i2c bus 0 on all known devices. */ + + dev->i2c_client[bus].addr = 0xa0 >> 1; /* Check if board has eeprom */ - err = i2c_master_recv(&dev->i2c_client[dev->def_i2c_bus], &buf, 0); + err = i2c_master_recv(&dev->i2c_client[bus], &buf, 0); if (err < 0) { em28xx_info("board has no eeprom\n"); return -ENODEV; @@ -436,7 +460,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, u8 **eedata, u16 *eedata_len) return -ENOMEM; /* Read EEPROM content */ - err = em28xx_i2c_read_block(dev, 0x0000, dev->eeprom_addrwidth_16bit, + err = em28xx_i2c_read_block(dev, bus, 0x0000, + dev->eeprom_addrwidth_16bit, len, data); if (err != len) { em28xx_errdev("failed to read eeprom (err=%d)\n", err); @@ -485,7 +510,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, u8 **eedata, u16 *eedata_len) /* Read hardware config dataset offset from address * (microcode start + 46) */ - err = em28xx_i2c_read_block(dev, mc_start + 46, 1, 2, data); + err = em28xx_i2c_read_block(dev, bus, mc_start + 46, 1, 2, + data); if (err != 2) { em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n", err); @@ -501,7 +527,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, u8 **eedata, u16 *eedata_len) * the old eeprom and not longer than 256 bytes. * tveeprom is currently also limited to 256 bytes. */ - err = em28xx_i2c_read_block(dev, hwconf_offset, 1, len, data); + err = em28xx_i2c_read_block(dev, bus, hwconf_offset, 1, len, + data); if (err != len) { em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n", err); @@ -590,9 +617,11 @@ error: /* * functionality() */ -static u32 functionality(struct i2c_adapter *adap) +static u32 functionality(struct i2c_adapter *i2c_adap) { - struct em28xx *dev = adap->algo_data; + 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; @@ -643,7 +672,7 @@ static char *i2c_devs[128] = { * do_i2c_scan() * check i2c address range for devices */ -void em28xx_do_i2c_scan(struct em28xx *dev) +void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus) { u8 i2c_devicelist[128]; unsigned char buf; @@ -652,55 +681,66 @@ void em28xx_do_i2c_scan(struct em28xx *dev) memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist)); for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { - dev->i2c_client[dev->def_i2c_bus].addr = i; - rc = i2c_master_recv(&dev->i2c_client[dev->def_i2c_bus], &buf, 0); + dev->i2c_client[bus].addr = i; + rc = i2c_master_recv(&dev->i2c_client[bus], &buf, 0); if (rc < 0) continue; i2c_devicelist[i] = i; - em28xx_info("found i2c device @ 0x%x [%s]\n", - i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + em28xx_info("found i2c device @ 0x%x on bus %d [%s]\n", + i << 1, bus, i2c_devs[i] ? i2c_devs[i] : "???"); } - dev->i2c_hash = em28xx_hash_mem(i2c_devicelist, - ARRAY_SIZE(i2c_devicelist), 32); + if (bus == dev->def_i2c_bus) + dev->i2c_hash = em28xx_hash_mem(i2c_devicelist, + ARRAY_SIZE(i2c_devicelist), 32); } /* * em28xx_i2c_register() * register i2c bus */ -int em28xx_i2c_register(struct em28xx *dev) +int em28xx_i2c_register(struct em28xx *dev, unsigned bus) { int retval; BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg); BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req); - dev->i2c_adap[dev->def_i2c_bus] = em28xx_adap_template; - dev->i2c_adap[dev->def_i2c_bus].dev.parent = &dev->udev->dev; - strcpy(dev->i2c_adap[dev->def_i2c_bus].name, dev->name); - dev->i2c_adap[dev->def_i2c_bus].algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap[dev->def_i2c_bus], &dev->v4l2_dev); - retval = i2c_add_adapter(&dev->i2c_adap[dev->def_i2c_bus]); + if (bus >= NUM_I2C_BUSES) + return -ENODEV; + + dev->i2c_adap[bus] = em28xx_adap_template; + dev->i2c_adap[bus].dev.parent = &dev->udev->dev; + strcpy(dev->i2c_adap[bus].name, dev->name); + + dev->i2c_bus[bus].bus = bus; + 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); + + retval = i2c_add_adapter(&dev->i2c_adap[bus]); if (retval < 0) { em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n", __func__, retval); return retval; } - dev->i2c_client[dev->def_i2c_bus] = em28xx_client_template; - dev->i2c_client[dev->def_i2c_bus].adapter = &dev->i2c_adap[dev->def_i2c_bus]; + dev->i2c_client[bus] = em28xx_client_template; + dev->i2c_client[bus].adapter = &dev->i2c_adap[bus]; - retval = em28xx_i2c_eeprom(dev, &dev->eedata, &dev->eedata_len); - if ((retval < 0) && (retval != -ENODEV)) { - em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", - __func__, retval); + /* Up to now, all eeproms are at bus 0 */ + if (!bus) { + retval = em28xx_i2c_eeprom(dev, bus, &dev->eedata, &dev->eedata_len); + if ((retval < 0) && (retval != -ENODEV)) { + em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", + __func__, retval); - return retval; + return retval; + } } if (i2c_scan) - em28xx_do_i2c_scan(dev); + em28xx_do_i2c_scan(dev, bus); return 0; } @@ -709,8 +749,11 @@ int em28xx_i2c_register(struct em28xx *dev) * em28xx_i2c_unregister() * unregister i2c_bus */ -int em28xx_i2c_unregister(struct em28xx *dev) +int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus) { - i2c_del_adapter(&dev->i2c_adap[dev->def_i2c_bus]); + if (bus >= NUM_I2C_BUSES) + return -ENODEV; + + i2c_del_adapter(&dev->i2c_adap[bus]); return 0; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 43eb1c69e3f2..f6ac1df83816 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -375,7 +375,7 @@ struct em28xx_board { int vchannels; int tuner_type; int tuner_addr; - int def_i2c_bus; /* Default I2C bus */ + unsigned def_i2c_bus; /* Default I2C bus */ /* i2c flags */ unsigned int tda9887_conf; @@ -460,6 +460,13 @@ struct em28xx_fh { enum v4l2_buf_type type; }; +struct em28xx_i2c_bus { + struct em28xx *dev; + + unsigned bus; +}; + + /* main device struct */ struct em28xx { /* generic device properties */ @@ -515,8 +522,12 @@ struct em28xx { /* i2c i/o */ struct i2c_adapter i2c_adap[NUM_I2C_BUSES]; struct i2c_client i2c_client[NUM_I2C_BUSES]; + struct em28xx_i2c_bus i2c_bus[NUM_I2C_BUSES]; + unsigned char eeprom_addrwidth_16bit:1; - int def_i2c_bus; /* Default I2C bus */ + unsigned def_i2c_bus; /* Default I2C bus */ + unsigned cur_i2c_bus; /* Current I2C bus */ + struct rt_mutex i2c_bus_lock; /* video for linux */ int users; /* user count for exclusive use */ @@ -638,9 +649,9 @@ struct em28xx_ops { }; /* Provided by em28xx-i2c.c */ -void em28xx_do_i2c_scan(struct em28xx *dev); -int em28xx_i2c_register(struct em28xx *dev); -int em28xx_i2c_unregister(struct em28xx *dev); +void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus); +int em28xx_i2c_register(struct em28xx *dev, unsigned bus); +int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus); /* Provided by em28xx-core.c */ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,