diff options
Diffstat (limited to 'drivers/media/video')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-core.c | 253 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-video.c | 63 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx.h | 27 |
3 files changed, 226 insertions, 117 deletions
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 4de1b2d433a..2f3257e8702 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -224,15 +224,70 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, } /* + * em28xx_is_ac97_ready() + * Checks if ac97 is ready + */ +static int em28xx_is_ac97_ready(struct em28xx *dev) +{ + int ret, i; + + /* Wait up to 50 ms for AC97 command to complete */ + for (i = 0; i < 10; i++, msleep(5)) { + ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); + if (ret < 0) + return ret; + + if (!(ret & 0x01)) + return 0; + } + + em28xx_warn("AC97 command still being executed: not handled properly!\n"); + return -EBUSY; +} + +/* + * em28xx_read_ac97() + * write a 16 bit value to the specified AC97 address (LSB first!) + */ +static int em28xx_read_ac97(struct em28xx *dev, u8 reg) +{ + int ret; + u8 addr = (reg & 0x7f) | 0x80; + u16 val; + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) + return ret; + + ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB, + (u8 *)&val, sizeof(val)); + + if (ret < 0) + return ret; + return le16_to_cpu(val); +} + +/* * em28xx_write_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) +static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) { - int ret, i; + int ret; u8 addr = reg & 0x7f; + __le16 value; + + value = cpu_to_le16(val); + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; - ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2); + ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2); if (ret < 0) return ret; @@ -240,25 +295,36 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) if (ret < 0) return ret; - /* Wait up to 50 ms for AC97 command to complete */ - for (i = 0; i < 10; i++) { - ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); - if (ret < 0) - return ret; + return 0; +} - if (!(ret & 0x01)) - return 0; - msleep(5); +static int set_ac97_em202_input(struct em28xx *dev) +{ + int ret; + u16 enable = 0x0808; /* 12 dB attenuation Left/Right */ + u16 disable = 0x8808; /* bit 15 - mute volumme */ + u16 video, line; + + if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) { + video = enable; + line = disable; + } else { + video = disable; + line = enable; } - em28xx_warn("AC97 command still being executed: not handled properly!\n"); - return 0; + + /* Sets em202 AC97 mixer registers */ + ret = em28xx_write_ac97(dev, AC97_VIDEO_VOL, video); + if (ret < 0) + return ret; + + ret = em28xx_write_ac97(dev, AC97_LINEIN_VOL, line); + + return ret; } static int em28xx_set_audio_source(struct em28xx *dev) { - static char *enable = "\x08\x08"; - static char *disable = "\x08\x88"; - char *video = enable, *line = disable; int ret; u8 input; @@ -280,18 +346,8 @@ static int em28xx_set_audio_source(struct em28xx *dev) case EM28XX_AMUX_VIDEO: input = EM28XX_AUDIO_SRC_TUNER; break; - case EM28XX_AMUX_LINE_IN: + default: input = EM28XX_AUDIO_SRC_LINE; - video = disable; - line = enable; - break; - case EM28XX_AMUX_AC97_VIDEO: - input = EM28XX_AUDIO_SRC_LINE; - break; - case EM28XX_AMUX_AC97_LINE_IN: - input = EM28XX_AUDIO_SRC_LINE; - video = disable; - line = enable; break; } } @@ -301,33 +357,36 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; msleep(5); - /* Sets AC97 mixer registers - This is seems to be needed, even for non-ac97 configs - */ - ret = em28xx_write_ac97(dev, AC97_VIDEO_VOL, video); - if (ret < 0) - return ret; - - ret = em28xx_write_ac97(dev, AC97_LINEIN_VOL, line); + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + break; + case EM28XX_AC97_OTHER: + /* We don't know how to handle this chip. + Let's hope it is close enough to em202 to work + */ + case EM28XX_AC97_EM202: + ret = set_ac97_em202_input(dev); + break; + } - return ret; + return 0; } int em28xx_audio_analog_set(struct em28xx *dev) { int ret; - char s[2] = { 0x00, 0x00 }; u8 xclk = 0x07; - s[0] |= 0x1f - dev->volume; - s[1] |= 0x1f - dev->volume; + if (!dev->audio_mode.has_audio) + return 0; - /* Mute */ - s[1] |= 0x80; - ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, s); + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + /* Mute */ + ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, 0x8000); - if (ret < 0) - return ret; + if (ret < 0) + return ret; + } if (dev->has_12mhz_i2s) xclk |= 0x20; @@ -343,15 +402,113 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Selects the proper audio input */ ret = em28xx_set_audio_source(dev); - /* Unmute device */ - if (!dev->mute) - s[1] &= ~0x80; - ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, s); + /* Sets volume */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + int vol; + + /* LSB: left channel - both channels with the same level */ + vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8); + + /* Mute device, if needed */ + if (dev->mute) + vol |= 0x8000; + + /* Sets volume */ + ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, vol); + } return ret; } EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); +int em28xx_audio_setup(struct em28xx *dev) +{ + int vid1, vid2, feat, cfg; + + if (dev->chip_id == CHIP_ID_EM2874) { + /* Digital only device - don't load any alsa module */ + dev->audio_mode.has_audio = 0; + dev->has_audio_class = 0; + dev->has_alsa_audio = 0; + return 0; + } + + /* If device doesn't support Usb Audio Class, use vendor class */ + if (!dev->has_audio_class) + dev->has_alsa_audio = 1; + + dev->audio_mode.has_audio = 1; + + /* See how this device is configured */ + cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); + if (cfg < 0) + cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */ + else + em28xx_info("Config register raw data: 0x%02x\n", cfg); + + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_3_SAMPRATES) { + em28xx_info("I2S Audio (3 sample rates)\n"); + dev->audio_mode.i2s_3rates = 1; + } + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_5_SAMPRATES) { + em28xx_info("I2S Audio (5 sample rates)\n"); + dev->audio_mode.i2s_5rates = 1; + } + + if (!(cfg & EM28XX_CHIPCFG_AC97)) { + dev->audio_mode.ac97 = EM28XX_NO_AC97; + goto init_audio; + } + + dev->audio_mode.ac97 = EM28XX_AC97_OTHER; + + vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); + if (vid1 < 0) { + /* Device likely doesn't support AC97 */ + em28xx_warn("AC97 chip type couldn't be determined\n"); + goto init_audio; + } + + vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2); + if (vid2 < 0) + goto init_audio; + + dev->audio_mode.ac97_vendor_id1 = vid1; + dev->audio_mode.ac97_vendor_id2 = vid2; + em28xx_warn("AC97 vendor ID = %04x:%04x\n", vid1, vid2); + + feat = em28xx_read_ac97(dev, AC97_RESET); + if (feat < 0) + goto init_audio; + + dev->audio_mode.ac97_feat = feat; + em28xx_warn("AC97 features = 0x%04x\n", feat); + + if ((vid1 == 0xffff) && (vid2 == 0xffff) && (feat == 0x6a90)) + dev->audio_mode.ac97 = EM28XX_AC97_EM202; + +init_audio: + /* Reports detected AC97 processor */ + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + em28xx_info("No AC97 audio processor\n"); + break; + case EM28XX_AC97_EM202: + em28xx_info("Empia 202 AC97 audio processor detected\n"); + break; + case EM28XX_AC97_OTHER: + em28xx_warn("Unknown AC97 audio processor detected!\n"); + break; + default: + break; + } + + return em28xx_audio_analog_set(dev); +} +EXPORT_SYMBOL_GPL(em28xx_audio_setup); + int em28xx_colorlevels_set_default(struct em28xx *dev) { em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */ diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 1e26061116e..6b7d44a7117 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1931,53 +1931,6 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } -int em28xx_supports_audio_extension(struct em28xx *dev) -{ - int rc; - - /* The chip dictates whether we support the Empia analog audio - extension */ - switch (dev->chip_id) { - case CHIP_ID_EM2874: - /* Digital only device - no analog support */ - dev->audio_mode = EM28XX_NO_AUDIO; - return 0; - case CHIP_ID_EM2860: - case CHIP_ID_EM2883: - default: - /* See how this device is configured */ - rc = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); - if (rc & EM28XX_CHIPCFG_VENDOR_AUDIO) { - switch(rc & EM28XX_CHIPCFG_AUDIOMASK) { - case EM28XX_CHIPCFG_AC97: - em28xx_info("AC97 audio (5 sample rates)\n"); - dev->audio_mode = EM28XX_AC97; - break; - case EM28XX_CHIPCFG_I2S_3_SAMPRATES: - em28xx_info("I2S Audio (3 sample rates)\n"); - dev->audio_mode = EM28XX_I2S_3_SAMPLE_RATES; - break; - case EM28XX_CHIPCFG_I2S_5_SAMPRATES: - em28xx_info("I2S Audio (5 sample rates)\n"); - dev->audio_mode = EM28XX_I2S_5_SAMPLE_RATES; - break; - default: - em28xx_info("No audio support detected\n"); - dev->audio_mode = EM28XX_NO_AUDIO; - return 0; - } - } else { - em28xx_info("USB Audio class device\n"); - return 0; - } - /* The em28xx audio extension needs to be loaded */ - return 1; - } - - /* We should never reach this point */ - return 0; -} - static int register_analog_devices(struct em28xx *dev) { int ret; @@ -2080,11 +2033,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_card_setup(dev); /* Configure audio */ - errCode = em28xx_audio_analog_set(dev); + errCode = em28xx_audio_setup(dev); if (errCode < 0) { - em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n", + em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n", __func__, errCode); - return errCode; } /* configure the device */ @@ -2318,17 +2270,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, em28xx_info("Found %s\n", em28xx_boards[dev->model].name); - if (dev->has_audio_class == 0) { - /* We don't have a USB audio class, let's see if we support - ALSA Audio */ - dev->has_alsa_audio = em28xx_supports_audio_extension(dev); - if (dev->has_alsa_audio) - printk(KERN_INFO DRIVER_NAME " supports alsa audio\n"); - } else { - printk(KERN_INFO DRIVER_NAME " has usb audio class\n"); - } - - /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index f47c8d34ec7..3e8e13a55f5 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -256,18 +256,28 @@ enum enum28xx_itype { EM28XX_RADIO, }; -enum em28xx_audio_mode { - EM28XX_NO_AUDIO, - EM28XX_I2S_3_SAMPLE_RATES, - EM28XX_I2S_5_SAMPLE_RATES, - EM28XX_AC97, +enum em28xx_ac97_mode { + EM28XX_NO_AC97 = 0, + EM28XX_AC97_EM202, + EM28XX_AC97_OTHER, +}; + +struct em28xx_audio_mode { + enum em28xx_ac97_mode ac97; + + u16 ac97_feat; + u16 ac97_vendor_id1; + u16 ac97_vendor_id2; + + unsigned int has_audio:1; + + unsigned int i2s_3rates:1; + unsigned int i2s_5rates:1; }; enum em28xx_amux { EM28XX_AMUX_VIDEO, EM28XX_AMUX_LINE_IN, - EM28XX_AMUX_AC97_VIDEO, - EM28XX_AMUX_AC97_LINE_IN, }; struct em28xx_input { @@ -410,7 +420,7 @@ struct em28xx { u32 i2s_speed; /* I2S speed for audio digital stream */ enum em28xx_decoder decoder; - enum em28xx_audio_mode audio_mode; + struct em28xx_audio_mode audio_mode; int tuner_type; /* type of the tuner */ int tuner_addr; /* tuner address */ @@ -531,6 +541,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int len); int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); int em28xx_audio_analog_set(struct em28xx *dev); +int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); |