From: Steven Toth Date: Mon, 23 Mar 2015 18:45:48 +0000 (-0300) Subject: [media] saa7164: Add Digital TV support for the HVR2255 and HVR2205 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=504b29cbb0cc0fb7169c276054a72110b57660c0;p=linux-beck.git [media] saa7164: Add Digital TV support for the HVR2255 and HVR2205 Adding support for the Hauppauge HVR2255 and Hauppauge HVR2205 PCIe cards. Digital TV for DVB-T and ATSC/US-Cable is known to work. Support for analog RF tuners does not work due to a lack of analog support in the upstream SI2157 driver. Signed-off-by: Steven Toth Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/media/pci/saa7164/saa7164-cards.c b/drivers/media/pci/saa7164/saa7164-cards.c index 5b72da5ce418..2ca5e5d5580d 100644 --- a/drivers/media/pci/saa7164/saa7164-cards.c +++ b/drivers/media/pci/saa7164/saa7164-cards.c @@ -30,6 +30,7 @@ * attached I2C devices, so we can simplify the virtual i2c mechansms * and keep the -i2c.c implementation clean. */ +#define REGLEN_0bit 0 #define REGLEN_8bit 1 #define REGLEN_16bit 2 @@ -499,6 +500,144 @@ struct saa7164_board saa7164_boards[] = { .i2c_reg_len = REGLEN_8bit, } }, }, + [SAA7164_BOARD_HAUPPAUGE_HVR2255proto] = { + .name = "Hauppauge WinTV-HVR2255(proto)", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .portc = SAA7164_MPEG_ENCODER, + .portd = SAA7164_MPEG_ENCODER, + .porte = SAA7164_MPEG_VBI, + .portf = SAA7164_MPEG_VBI, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x27, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-1", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x06, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xb2 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x24, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-2", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x26, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x1c >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2255] = { + .name = "Hauppauge WinTV-HVR2255", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .portc = SAA7164_MPEG_ENCODER, + .portd = SAA7164_MPEG_ENCODER, + .porte = SAA7164_MPEG_VBI, + .portf = SAA7164_MPEG_VBI, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x28, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-1", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x06, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306-1", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xb2 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x25, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-2", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x27, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x1c >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2205] = { + .name = "Hauppauge WinTV-HVR2205", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .portc = SAA7164_MPEG_ENCODER, + .portd = SAA7164_MPEG_ENCODER, + .porte = SAA7164_MPEG_VBI, + .portf = SAA7164_MPEG_VBI, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x28, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-1", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x06, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "SI2168-1", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc8 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x25, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-2", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x27, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "SI2168-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xcc >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, }; const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards); @@ -546,6 +685,21 @@ struct saa7164_subid saa7164_subids[] = { .subvendor = 0x0070, .subdevice = 0x8953, .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_5, + }, { + .subvendor = 0x0070, + .subdevice = 0xf111, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2255, + /* Prototype card left here for documenation purposes. + .card = SAA7164_BOARD_HAUPPAUGE_HVR2255proto, + */ + }, { + .subvendor = 0x0070, + .subdevice = 0xf123, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2205, + }, { + .subvendor = 0x0070, + .subdevice = 0xf120, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2205, }, }; const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids); @@ -594,12 +748,26 @@ void saa7164_gpio_setup(struct saa7164_dev *dev) case SAA7164_BOARD_HAUPPAUGE_HVR2250: case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2255proto: + case SAA7164_BOARD_HAUPPAUGE_HVR2255: + case SAA7164_BOARD_HAUPPAUGE_HVR2205: /* + HVR2200 / HVR2250 GPIO 2: s5h1411 / tda10048-1 demod reset GPIO 3: s5h1411 / tda10048-2 demod reset GPIO 7: IRBlaster Zilog reset */ + /* HVR2255 + * GPIO 2: lgdg3306-1 demod reset + * GPIO 3: lgdt3306-2 demod reset + */ + + /* HVR2205 + * GPIO 2: si2168-1 demod reset + * GPIO 3: si2168-2 demod reset + */ + /* Reset parts by going in and out of reset */ saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2); saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3); @@ -647,6 +815,21 @@ static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data) /* WinTV-HVR2200 (PCIe, Retail, half-height) * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ break; + case 151009: + /* First production board rev B2I6 */ + /* WinTV-HVR2205 (PCIe, Retail, full-height bracket) + * DVB-T/T2/C (SI2157/SI2168) and basic analog, FM */ + break; + case 151609: + /* First production board rev B2I6 */ + /* WinTV-HVR2205 (PCIe, Retail, half-height bracket) + * DVB-T/T2/C (SI2157/SI2168) and basic analog, FM */ + break; + case 151061: + /* First production board rev B1I6 */ + /* WinTV-HVR2255 (PCIe, Retail, full-height bracket) + * ATSC/QAM (SI2157/LGDT3306) and basic analog, FM */ + break; default: printk(KERN_ERR "%s: Warning: Unknown Hauppauge model #%d\n", dev->name, tv.model); @@ -676,6 +859,9 @@ void saa7164_card_setup(struct saa7164_dev *dev) case SAA7164_BOARD_HAUPPAUGE_HVR2250: case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2255proto: + case SAA7164_BOARD_HAUPPAUGE_HVR2255: + case SAA7164_BOARD_HAUPPAUGE_HVR2205: hauppauge_eeprom(dev, &eeprom[0]); break; } diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c index 16ae71592e8c..89fa2999633f 100644 --- a/drivers/media/pci/saa7164/saa7164-dvb.c +++ b/drivers/media/pci/saa7164/saa7164-dvb.c @@ -24,6 +24,9 @@ #include "tda10048.h" #include "tda18271.h" #include "s5h1411.h" +#include "si2157.h" +#include "si2168.h" +#include "lgdt3306a.h" #define DRIVER_NAME "saa7164" @@ -82,6 +85,64 @@ static struct s5h1411_config hauppauge_s5h1411_config = { .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, }; +static struct lgdt3306a_config hauppauge_hvr2255a_config = { + .i2c_addr = 0xb2 >> 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .deny_i2c_rptr = 1, /* Disabled */ + .spectral_inversion = 0, /* Disabled */ + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, /* 24 or 25 */ +}; + +static struct lgdt3306a_config hauppauge_hvr2255b_config = { + .i2c_addr = 0x1c >> 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .deny_i2c_rptr = 1, /* Disabled */ + .spectral_inversion = 0, /* Disabled */ + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, /* 24 or 25 */ +}; + +static struct si2157_config hauppauge_hvr2255_tuner_config = { + .inversion = 0, +}; + +static int si2157_attach(struct saa7164_port *port, struct i2c_adapter *adapter, + struct dvb_frontend *fe, u8 addr8bit, struct si2157_config *cfg) +{ + struct i2c_board_info bi; + struct i2c_client *tuner; + + cfg->fe = fe; + + memset(&bi, 0, sizeof(bi)); + + strlcpy(bi.type, "si2157", I2C_NAME_SIZE); + bi.platform_data = cfg; + bi.addr = addr8bit >> 1; + + request_module(bi.type); + + tuner = i2c_new_device(adapter, &bi); + if (tuner == NULL || tuner->dev.driver == NULL) + return -ENODEV; + + if (!try_module_get(tuner->dev.driver->owner)) { + i2c_unregister_device(tuner); + return -ENODEV; + } + + port->i2c_client_tuner = tuner; + + return 0; +} + static int saa7164_dvb_stop_port(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; @@ -242,14 +303,16 @@ static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - mutex_lock(&dvb->lock); - if (dvb->feeding++ == 0) { - /* Start transport */ - ret = saa7164_dvb_start_port(port); + if (dvb) { + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + ret = saa7164_dvb_start_port(port); + } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); } - mutex_unlock(&dvb->lock); - dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", - __func__, port->nr, dvb->feeding); return ret; } @@ -264,14 +327,16 @@ static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed) dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); - mutex_lock(&dvb->lock); - if (--dvb->feeding == 0) { - /* Stop transport */ - ret = saa7164_dvb_stop_streaming(port); + if (dvb) { + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + ret = saa7164_dvb_stop_streaming(port); + } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); } - mutex_unlock(&dvb->lock); - dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", - __func__, port->nr, dvb->feeding); return ret; } @@ -425,6 +490,7 @@ int saa7164_dvb_unregister(struct saa7164_port *port) struct saa7164_dev *dev = port->dev; struct saa7164_buffer *b; struct list_head *c, *n; + struct i2c_client *client; dprintk(DBGLVL_DVB, "%s()\n", __func__); @@ -443,6 +509,20 @@ int saa7164_dvb_unregister(struct saa7164_port *port) if (dvb->frontend == NULL) return 0; + /* remove I2C client for tuner */ + client = port->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + /* remove I2C client for demodulator */ + client = port->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + dvb_net_release(&dvb->net); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); @@ -462,6 +542,12 @@ int saa7164_dvb_register(struct saa7164_port *port) struct saa7164_dev *dev = port->dev; struct saa7164_dvb *dvb = &port->dvb; struct saa7164_i2c *i2c_bus = NULL; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + struct i2c_adapter *adapter; + struct i2c_board_info info; + struct i2c_client *client_demod; + struct i2c_client *client_tuner; int ret; dprintk(DBGLVL_DVB, "%s()\n", __func__); @@ -527,6 +613,118 @@ int saa7164_dvb_register(struct saa7164_port *port) } } + break; + case SAA7164_BOARD_HAUPPAUGE_HVR2255proto: + case SAA7164_BOARD_HAUPPAUGE_HVR2255: + i2c_bus = &dev->i2c_bus[2]; + + if (port->nr == 0) { + port->dvb.frontend = dvb_attach(lgdt3306a_attach, + &hauppauge_hvr2255a_config, &i2c_bus->i2c_adap); + } else { + port->dvb.frontend = dvb_attach(lgdt3306a_attach, + &hauppauge_hvr2255b_config, &i2c_bus->i2c_adap); + } + + if (port->dvb.frontend != NULL) { + + if (port->nr == 0) { + si2157_attach(port, &dev->i2c_bus[0].i2c_adap, port->dvb.frontend, + 0xc0, &hauppauge_hvr2255_tuner_config); + } else { + si2157_attach(port, &dev->i2c_bus[1].i2c_adap, port->dvb.frontend, + 0xc0, &hauppauge_hvr2255_tuner_config); + } + } + break; + case SAA7164_BOARD_HAUPPAUGE_HVR2205: + + if (port->nr == 0) { + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &port->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0xc8 >> 1; + info.platform_data = &si2168_config; + request_module(info.type); + client_demod = i2c_new_device(&dev->i2c_bus[2].i2c_adap, &info); + if (client_demod == NULL || client_demod->dev.driver == NULL) { + goto frontend_detach; + } + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = port->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0xc0 >> 1; + info.platform_data = &si2157_config; + request_module(info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info); + if (client_tuner == NULL || client_tuner->dev.driver == NULL) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + } else { + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &port->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0xcc >> 1; + info.platform_data = &si2168_config; + request_module(info.type); + client_demod = i2c_new_device(&dev->i2c_bus[2].i2c_adap, &info); + if (client_demod == NULL || client_demod->dev.driver == NULL) { + goto frontend_detach; + } + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = port->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0xc0 >> 1; + info.platform_data = &si2157_config; + request_module(info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info); + if (client_tuner == NULL || client_tuner->dev.driver == NULL) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + } + break; default: printk(KERN_ERR "%s: The frontend isn't supported\n", @@ -548,5 +746,9 @@ int saa7164_dvb_register(struct saa7164_port *port) } return 0; + +frontend_detach: + printk(KERN_ERR "%s() Frontend/I2C initialization failed\n", __func__); + return -1; } diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h index cd1a07ce27cb..33e30b3c5088 100644 --- a/drivers/media/pci/saa7164/saa7164.h +++ b/drivers/media/pci/saa7164/saa7164.h @@ -83,6 +83,9 @@ #define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8 #define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9 #define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10 +#define SAA7164_BOARD_HAUPPAUGE_HVR2255proto 11 +#define SAA7164_BOARD_HAUPPAUGE_HVR2255 12 +#define SAA7164_BOARD_HAUPPAUGE_HVR2205 13 #define SAA7164_MAX_UNITS 8 #define SAA7164_TS_NUMBER_OF_LINES 312 @@ -371,6 +374,8 @@ struct saa7164_port { /* --- DVB Transport Specific --- */ struct saa7164_dvb dvb; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; /* --- Encoder/V4L related attributes --- */ /* Encoder */