summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c253
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c63
-rw-r--r--drivers/media/video/em28xx/em28xx.h27
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 4de1b2d..2f3257e 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 1e26061..6b7d44a 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 f47c8d3..3e8e13a 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);
OpenPOWER on IntegriCloud