diff --git a/linux/drivers/media/video/bt8xx/bttv-if.c b/linux/drivers/media/video/bt8xx/bttv-if.c --- a/linux/drivers/media/video/bt8xx/bttv-if.c +++ b/linux/drivers/media/video/bt8xx/bttv-if.c @@ -34,6 +34,7 @@ #include "bttvp.h" EXPORT_SYMBOL(bttv_get_pcidev); +EXPORT_SYMBOL(bttv_get_core); EXPORT_SYMBOL(bttv_gpio_enable); EXPORT_SYMBOL(bttv_read_gpio); EXPORT_SYMBOL(bttv_write_gpio); @@ -53,6 +54,12 @@ return bttvs[card]->c.pci; } +struct bttv_core *bttv_get_core(unsigned int card) +{ + if (card >= bttv_num) + return NULL; + return &bttvs[card]->c; +} int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data) { diff --git a/linux/drivers/media/video/bt8xx/bttv.h b/linux/drivers/media/video/bt8xx/bttv.h --- a/linux/drivers/media/video/bt8xx/bttv.h +++ b/linux/drivers/media/video/bt8xx/bttv.h @@ -301,6 +301,7 @@ interface below for new code */ extern struct pci_dev* bttv_get_pcidev(unsigned int card); +extern struct bttv_core *bttv_get_core(unsigned int card); /* sets GPOE register (BT848_GPIO_OUT_EN) to new value: data | (current_GPOE_value & ~mask) diff --git a/linux/sound/pci/bt87x.c b/linux/sound/pci/bt87x.c --- a/linux/sound/pci/bt87x.c +++ b/linux/sound/pci/bt87x.c @@ -36,6 +36,7 @@ #include #include #include +#include "bttv.h" #include "compat.h" #ifdef COMPAT_SND_CTL_BOOLEAN_MONO static int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, @@ -161,11 +162,22 @@ /* SYNC, one WRITE per line, one extra WRITE per page boundary, SYNC, JUMP */ #define MAX_RISC_SIZE ((1 + 255 + (PAGE_ALIGN(255 * 4092) / PAGE_SIZE - 1) + 1 + 1) * 8) +/* X9221 I2C POT, used in Osprey cards for gain control */ +#define X9221_ADDR 0x28 /* I2C address */ +#define X9221_PORT0 0x00 /* First port */ +#define X9221_PORT1 0x04 /* Second port */ +#define X9221_READ_WRC 0x90 /* read wiper control register */ +#define X9221_WRITE_WRC 0xa0 /* write wiper control register */ +#define X9221_G_WCR_TO_REG 0x80 /* global wcr to register */ +#define X9221_G_REG_TO_WRC 0x10 /* global register to wrc */ + + /* Cards with configuration information */ enum snd_bt87x_boardid { SND_BT87X_BOARD_UNKNOWN, SND_BT87X_BOARD_GENERIC, /* both an & dig interfaces, 32kHz */ SND_BT87X_BOARD_ANALOG, /* board with no external A/D */ + SND_BT87X_BOARD_OSPREY200, SND_BT87X_BOARD_OSPREY2x0, SND_BT87X_BOARD_OSPREY440, SND_BT87X_BOARD_AVPHONE98, @@ -177,8 +189,11 @@ u32 digital_fmt; /* Register settings for digital input */ unsigned no_analog:1; /* No analog input */ unsigned no_digital:1; /* No digital input */ + unsigned x9221:1; /* X9221 I2C POT for gain control */ }; +#define DIG_RATE_GPIO -1 + static __devinitdata struct snd_bt87x_board snd_bt87x_boards[] = { [SND_BT87X_BOARD_UNKNOWN] = { .dig_rate = 32000, /* just a guess */ @@ -189,14 +204,21 @@ [SND_BT87X_BOARD_ANALOG] = { .no_digital = 1, }, - [SND_BT87X_BOARD_OSPREY2x0] = { + [SND_BT87X_BOARD_OSPREY200] = { .dig_rate = 44100, .digital_fmt = CTL_DA_LRI | (1 << CTL_DA_LRD_SHIFT), }, - [SND_BT87X_BOARD_OSPREY440] = { - .dig_rate = 32000, + [SND_BT87X_BOARD_OSPREY2x0] = { + .dig_rate = DIG_RATE_GPIO, /* Controlled via GPIO */ .digital_fmt = CTL_DA_LRI | (1 << CTL_DA_LRD_SHIFT), .no_analog = 1, + .x9221 = 1, + }, + [SND_BT87X_BOARD_OSPREY440] = { + .dig_rate = DIG_RATE_GPIO, /* Controlled via GPIO */ + .digital_fmt = CTL_DA_LRI | (1 << CTL_DA_LRD_SHIFT), + .no_analog = 1, + .x9221 = 1, }, [SND_BT87X_BOARD_AVPHONE98] = { .dig_rate = 48000, @@ -207,6 +229,9 @@ struct snd_card *card; struct pci_dev *pci; struct snd_bt87x_board board; + struct bttv_core *core; + struct i2c_adapter *i2c; + int bttvnr; void __iomem *mmio; int irq; @@ -225,6 +250,7 @@ int current_line; int pci_parity_errors; + unsigned char x9221vol[2]; }; enum { DEVICE_DIGITAL, DEVICE_ANALOG }; @@ -409,9 +435,17 @@ { chip->reg_control |= CTL_DA_IOM_DA | CTL_A_PWRDN; runtime->hw = snd_bt87x_digital_hw; - runtime->hw.rates = snd_pcm_rate_to_rate_bit(chip->board.dig_rate); - runtime->hw.rate_min = chip->board.dig_rate; - runtime->hw.rate_max = chip->board.dig_rate; + if (chip->board.dig_rate == DIG_RATE_GPIO) { + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; + runtime->hw.rate_min = 32000; + runtime->hw.rate_max = 48000; + } else { + runtime->hw.rates = + snd_pcm_rate_to_rate_bit(chip->board.dig_rate); + runtime->hw.rate_min = chip->board.dig_rate; + runtime->hw.rate_max = chip->board.dig_rate; + } return 0; } @@ -509,11 +543,33 @@ int decimation; spin_lock_irq(&chip->reg_lock); - chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR); - decimation = (ANALOG_CLOCK + runtime->rate / 4) / runtime->rate; - chip->reg_control |= decimation << CTL_DA_SDR_SHIFT; - if (runtime->format == SNDRV_PCM_FORMAT_S8) - chip->reg_control |= CTL_DA_SBR; + if (substream->pcm->device == DEVICE_ANALOG) { + chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR); + decimation = (ANALOG_CLOCK + runtime->rate / 4) / + runtime->rate; + chip->reg_control |= decimation << CTL_DA_SDR_SHIFT; + if (runtime->format == SNDRV_PCM_FORMAT_S8) + chip->reg_control |= CTL_DA_SBR; + } else { + if (chip->board.dig_rate == DIG_RATE_GPIO) { + int bits = 0; + switch (runtime->rate) { + case 32000: bits = 0; break; + case 44100: bits = 1; break; + case 48000: bits = 2; break; + } + bttv_write_gpio(chip->bttvnr, 0x3, bits); + } else { + // The digital rate has been set fixed by module parameter digital_rate + int bits = 0; + switch (chip->board.dig_rate) { + case 32000: bits = 0; break; + case 44100: bits = 1; break; + case 48000: bits = 2; break; + } + bttv_write_gpio(chip->bttvnr, 0x3, bits); + } + } snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); spin_unlock_irq(&chip->reg_lock); return 0; @@ -720,6 +776,91 @@ return 0; } +static int snd_bt87x_dig_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 63; + return 0; +} + +static int snd_bt87x_dig_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); + char cmdR[1] = { X9221_READ_WRC|X9221_PORT0 }; + char cmdL[1] = { X9221_READ_WRC|X9221_PORT1 }; + char out; + struct i2c_msg msg[2] = { + { .addr = X9221_ADDR, .buf = cmdL, .len = 1, .flags = 0 }, + { .addr = X9221_ADDR, .buf = &out, .len = 1, + .flags = I2C_M_RD|I2C_M_NOSTART }, + }; + int err; + + err = i2c_transfer(chip->i2c, msg, 2); + if (err < 0) + return err; + chip->x9221vol[0] = value->value.integer.value[0] = out; + + msg[0].buf = cmdR; + err = i2c_transfer(chip->i2c, msg, 2); + if (err < 0) + return err; + chip->x9221vol[1] = value->value.integer.value[1] = out; + + return 0; +} + +static int snd_bt87x_dig_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); + char cmdL[] = + { X9221_WRITE_WRC|X9221_PORT1, value->value.integer.value[0] }; + char cmdR[] = + { X9221_WRITE_WRC|X9221_PORT0, value->value.integer.value[1] }; + struct i2c_msg msg[2] = { + { .addr = X9221_ADDR, .buf = cmdR, .len = sizeof(cmdR) }, + { .addr = X9221_ADDR, .buf = cmdL, .len = sizeof(cmdR) }, + }; + int err; + + err = i2c_transfer(chip->i2c, msg, 2); + if (err < 0) + return err; + + if (value->value.integer.value[0] != chip->x9221vol[0] || + value->value.integer.value[1] != chip->x9221vol[1]) { + chip->x9221vol[0] = value->value.integer.value[0]; + chip->x9221vol[1] = value->value.integer.value[1]; + return 1; + } + return 0; +} + +static struct snd_kcontrol_new snd_bt87x_dig_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = snd_bt87x_dig_volume_info, + .get = snd_bt87x_dig_volume_get, + .put = snd_bt87x_dig_volume_put, +}; + +static int attach_x9221(struct snd_bt87x *chip) +{ + chip->i2c = &chip->core->i2c_adap; + + /* Use a unique name if analog is using "Capture Volume" */ + if (!chip->board.no_analog) + snd_bt87x_dig_volume.name = "Digital Capture Volume"; + + return snd_ctl_add(chip->card, + snd_ctl_new1(&snd_bt87x_dig_volume, chip)); +} + static int snd_bt87x_dev_free(struct snd_device *device) { struct snd_bt87x *chip = device->device_data; @@ -812,6 +953,23 @@ return err; } +/* + * The the number of the v4l bttv driver's device that matches the audio + * device we are driving. + */ +static int get_bttvnr(struct snd_bt87x *chip) +{ + struct pci_dev *pci; + int i; + + for (i = 0; (pci = bttv_get_pcidev(i)); i++) { + if (pci->bus->number == chip->pci->bus->number && + PCI_SLOT(pci->devfn) == PCI_SLOT(chip->pci->devfn)) + return i; + } + return -ENODEV; +} + #define BT_DEVICE(chip, subvend, subdev, id) \ { .vendor = PCI_VENDOR_ID_BROOKTREE, \ .device = chip, \ @@ -824,8 +982,10 @@ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0x13eb, GENERIC), /* Hauppauge WinTV series */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, 0x0070, 0x13eb, GENERIC), - /* Viewcast Osprey 200 */ - BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, OSPREY2x0), + /* Viewcast Osprey 200/250 */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, OSPREY200), + /* Viewcast Osprey 210/220/230/240(?) */ + //BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, OSPREY2x0), /* Viewcast Osprey 440 (rate is configurable via gpio) */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff07, OSPREY440), /* ATI TV-Wonder */ @@ -922,6 +1082,20 @@ if (err < 0) goto _error; + chip->bttvnr = get_bttvnr(chip); + if (chip->bttvnr < 0) { + snd_printk(KERN_ERR + "bt87x%d: Unable to locate matching" + " video device\n", dev); + goto _error; + } + chip->core = bttv_get_core(chip->bttvnr); + if(boardid == SND_BT87X_BOARD_OSPREY200) { + if(chip->core->type == BTTV_BOARD_OSPREY2x0) { + // This is acctually a 210/220/230 card. The pci id is the same so bttv driver checks eeprom on card + boardid = SND_BT87X_BOARD_OSPREY2x0; + } + } memcpy(&chip->board, &snd_bt87x_boards[boardid], sizeof(chip->board)); if (!chip->board.no_digital) { @@ -933,6 +1107,12 @@ err = snd_bt87x_pcm(chip, DEVICE_DIGITAL, "Bt87x Digital"); if (err < 0) goto _error; + + if (chip->board.x9221) { + err = attach_x9221(chip); + if (err < 0) + goto _error; + } } if (!chip->board.no_analog) { err = snd_bt87x_pcm(chip, DEVICE_ANALOG, "Bt87x Analog");