Commit 5ce15909 authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

[PATCH] PA-RISC sound updates

PA-RISC sound updates:

 - Do a DAC/ADC reset for sampling rate changes in ad1889 (Randolph Chung)
 - Set the ad1889 interrupt configuration properly (Randolph Chung)
 - Fix dependency for the OSS Harmony driver (Thibaut Varene)
 - Forward port Stuart Brady's 2.4 Harmony driver patches (Thibaut Varene)
   - Fix sample skipping (Stuart Brady)
   - Prevent harmony_silence being called wrongly (Stuart Brady)
   - Fix crash caused by buf_to_fill becoming -1 (Stuart Brady)
   - Improve naming of mixer channels (Stuart Brady)
   - Implement SNDCTL_DSP_CHANNELS ioctl (Stuart Brady)
   - Improve toggling the recording source (Stuart Brady)
   - Sanity check MIXER_WRITE volume levels (Stuart Brady)
   - Fix MIXER_READ right_level return (Stuart Brady)
   - Reject AFMT_S16_LE format (Stuart Brady)
 - Fail OSS Harmony initialisation if no irq (Helge Deller)
 - Fix typos in ALSA Harmony (Andy Walker, Grant Grundler, Stuart Brady)
parent b5ac4f2c
...@@ -162,7 +162,10 @@ config SOUND_ICH ...@@ -162,7 +162,10 @@ config SOUND_ICH
config SOUND_HARMONY config SOUND_HARMONY
tristate "PA Harmony audio driver" tristate "PA Harmony audio driver"
depends on GSC_LASI && SOUND depends on GSC_LASI && SOUND_PRIME!=n
help
Say 'Y' or 'M' to include support for Harmony soundchip
on HP 712, 715/new and many other GSC based machines.
config SOUND_SONICVIBES config SOUND_SONICVIBES
tristate "S3 SonicVibes" tristate "S3 SonicVibes"
......
/* /*
* Copyright 2001 Randolph Chung <tausq@debian.org> * Copyright 2001-2004 Randolph Chung <tausq@debian.org>
* *
* Analog Devices 1889 PCI audio driver (AD1819 AC97-compatible codec) * Analog Devices 1889 PCI audio driver (AD1819 AC97-compatible codec)
* *
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#define AD1889_WRITEL(dev,reg,val) writel((val), dev->regbase + reg) #define AD1889_WRITEL(dev,reg,val) writel((val), dev->regbase + reg)
//now 100ms //now 100ms
/* #define WAIT_10MS() schedule_timeout(HZ/10) */
#define WAIT_10MS() do { int __i; for (__i = 0; __i < 100; __i++) udelay(1000); } while(0) #define WAIT_10MS() do { int __i; for (__i = 0; __i < 100; __i++) udelay(1000); } while(0)
/* currently only support a single device */ /* currently only support a single device */
...@@ -69,25 +70,43 @@ static ad1889_dev_t *ad1889_dev = NULL; ...@@ -69,25 +70,43 @@ static ad1889_dev_t *ad1889_dev = NULL;
/************************* helper routines ***************************** */ /************************* helper routines ***************************** */
static inline void ad1889_set_wav_rate(ad1889_dev_t *dev, int rate) static inline void ad1889_set_wav_rate(ad1889_dev_t *dev, int rate)
{ {
struct ac97_codec *ac97_codec = dev->ac97_codec;
DBG("Setting WAV rate to %d\n", rate);
dev->state[AD_WAV_STATE].dmabuf.rate = rate; dev->state[AD_WAV_STATE].dmabuf.rate = rate;
AD1889_WRITEW(dev, AD_DSWAS, rate); AD1889_WRITEW(dev, AD_DSWAS, rate);
/* Cycle the DAC to enable the new rate */
ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0x0200);
WAIT_10MS();
ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0);
} }
static inline void ad1889_set_adc_rate(ad1889_dev_t *dev, int rate) static inline void ad1889_set_adc_rate(ad1889_dev_t *dev, int rate)
{ {
struct ac97_codec *ac97_codec = dev->ac97_codec;
DBG("Setting ADC rate to %d\n", rate);
dev->state[AD_ADC_STATE].dmabuf.rate = rate; dev->state[AD_ADC_STATE].dmabuf.rate = rate;
AD1889_WRITEW(dev, AD_DSRES, rate); AD1889_WRITEW(dev, AD_DSRES, rate);
/* Cycle the ADC to enable the new rate */
ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0x0100);
WAIT_10MS();
ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0);
} }
static inline void ad1889_set_wav_fmt(ad1889_dev_t *dev, int fmt) static inline void ad1889_set_wav_fmt(ad1889_dev_t *dev, int fmt)
{ {
u16 tmp; u16 tmp;
DBG("Setting WAV format to 0x%x\n", fmt);
tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); tmp = AD1889_READW(ad1889_dev, AD_DSWSMC);
if (fmt == AFMT_S16_LE) { if (fmt & AFMT_S16_LE) {
//tmp |= 0x0100; /* set WA16 */ //tmp |= 0x0100; /* set WA16 */
tmp |= 0x0300; /* set WA16 stereo */ tmp |= 0x0300; /* set WA16 stereo */
} else if (fmt == AFMT_U8) { } else if (fmt & AFMT_U8) {
tmp &= ~0x0100; /* clear WA16 */ tmp &= ~0x0100; /* clear WA16 */
} }
AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp);
...@@ -97,10 +116,12 @@ static inline void ad1889_set_adc_fmt(ad1889_dev_t *dev, int fmt) ...@@ -97,10 +116,12 @@ static inline void ad1889_set_adc_fmt(ad1889_dev_t *dev, int fmt)
{ {
u16 tmp; u16 tmp;
DBG("Setting ADC format to 0x%x\n", fmt);
tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); tmp = AD1889_READW(ad1889_dev, AD_DSRAMC);
if (fmt == AFMT_S16_LE) { if (fmt & AFMT_S16_LE) {
tmp |= 0x0100; /* set WA16 */ tmp |= 0x0100; /* set WA16 */
} else if (fmt == AFMT_U8) { } else if (fmt & AFMT_U8) {
tmp &= ~0x0100; /* clear WA16 */ tmp &= ~0x0100; /* clear WA16 */
} }
AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp);
...@@ -133,6 +154,9 @@ static void ad1889_start_wav(ad1889_state_t *state) ...@@ -133,6 +154,9 @@ static void ad1889_start_wav(ad1889_state_t *state)
dmabuf->dma_len = cnt; dmabuf->dma_len = cnt;
dmabuf->ready = 1; dmabuf->ready = 1;
DBG("Starting playback at 0x%p for %ld bytes\n", dmabuf->rawbuf +
dmabuf->rd_ptr, dmabuf->dma_len);
/* load up the current register set */ /* load up the current register set */
AD1889_WRITEL(ad1889_dev, AD_DMAWAVCC, cnt); AD1889_WRITEL(ad1889_dev, AD_DMAWAVCC, cnt);
AD1889_WRITEL(ad1889_dev, AD_DMAWAVICC, cnt); AD1889_WRITEL(ad1889_dev, AD_DMAWAVICC, cnt);
...@@ -243,7 +267,7 @@ static ad1889_dev_t *ad1889_alloc_dev(struct pci_dev *pci) ...@@ -243,7 +267,7 @@ static ad1889_dev_t *ad1889_alloc_dev(struct pci_dev *pci)
dmabuf->dma_handle = 0; dmabuf->dma_handle = 0;
dmabuf->rd_ptr = dmabuf->wr_ptr = dmabuf->dma_len = 0UL; dmabuf->rd_ptr = dmabuf->wr_ptr = dmabuf->dma_len = 0UL;
dmabuf->ready = 0; dmabuf->ready = 0;
dmabuf->rate = 44100; dmabuf->rate = 48000;
} }
return dev; return dev;
...@@ -472,7 +496,6 @@ static ssize_t ad1889_write(struct file *file, const char __user *buffer, size_t ...@@ -472,7 +496,6 @@ static ssize_t ad1889_write(struct file *file, const char __user *buffer, size_t
long cnt = count; long cnt = count;
unsigned long flags; unsigned long flags;
for (;;) { for (;;) {
long used_bytes; long used_bytes;
long timeout; /* max time for DMA in jiffies */ long timeout; /* max time for DMA in jiffies */
...@@ -498,17 +521,11 @@ static ssize_t ad1889_write(struct file *file, const char __user *buffer, size_t ...@@ -498,17 +521,11 @@ static ssize_t ad1889_write(struct file *file, const char __user *buffer, size_t
} }
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (!schedule_timeout(timeout + 1)) schedule_timeout(timeout + 1);
printk(KERN_WARNING "AD1889 timeout(%ld) r/w %lx/%lx len %lx\n",
timeout+1,
dmabuf->rd_ptr, dmabuf->wr_ptr,
dmabuf->dma_len);
if (signal_pending(current)) { if (signal_pending(current)) {
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
goto err2; goto err2;
} }
} }
/* watch out for wrapping around static buffer */ /* watch out for wrapping around static buffer */
...@@ -616,6 +633,8 @@ static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -616,6 +633,8 @@ static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd
audio_buf_info abinfo; audio_buf_info abinfo;
int __user *p = (int __user *)arg; int __user *p = (int __user *)arg;
DBG("ad1889_ioctl cmd 0x%x arg %lu\n", cmd, arg);
switch (cmd) switch (cmd)
{ {
case OSS_GETVERSION: case OSS_GETVERSION:
...@@ -674,11 +693,15 @@ static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -674,11 +693,15 @@ static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd
if (get_user(val, p)) if (get_user(val, p))
return -EFAULT; return -EFAULT;
if (val == 0) {
if (file->f_mode & FMODE_READ) if (file->f_mode & FMODE_READ)
ad1889_set_adc_fmt(dev, val); ad1889_set_adc_fmt(dev, val);
if (file->f_mode & FMODE_WRITE) if (file->f_mode & FMODE_WRITE)
ad1889_set_wav_fmt(dev, val); ad1889_set_wav_fmt(dev, val);
} else {
val = AFMT_S16_LE | AFMT_U8;
}
return put_user(val, p); return put_user(val, p);
...@@ -758,7 +781,7 @@ static int ad1889_open(struct inode *inode, struct file *file) ...@@ -758,7 +781,7 @@ static int ad1889_open(struct inode *inode, struct file *file)
file->private_data = ad1889_dev; file->private_data = ad1889_dev;
ad1889_set_wav_rate(ad1889_dev, 44100); ad1889_set_wav_rate(ad1889_dev, 48000);
ad1889_set_wav_fmt(ad1889_dev, AFMT_S16_LE); ad1889_set_wav_fmt(ad1889_dev, AFMT_S16_LE);
AD1889_WRITEW(ad1889_dev, AD_DSWADA, 0x0404); /* attenuation */ AD1889_WRITEW(ad1889_dev, AD_DSWADA, 0x0404); /* attenuation */
return nonseekable_open(inode, file); return nonseekable_open(inode, file);
...@@ -938,7 +961,6 @@ static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -938,7 +961,6 @@ static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs)
ad1889_stop_wav(&dev->state[AD_WAV_STATE]); /* clean up */ ad1889_stop_wav(&dev->state[AD_WAV_STATE]); /* clean up */
ad1889_start_wav(&dev->state[AD_WAV_STATE]); /* start new */ ad1889_start_wav(&dev->state[AD_WAV_STATE]); /* start new */
} }
} }
if ((stat & 0x2) && dev->state[AD_ADC_STATE].dmabuf.ready) { /* ADCI */ if ((stat & 0x2) && dev->state[AD_ADC_STATE].dmabuf.ready) { /* ADCI */
...@@ -952,18 +974,19 @@ static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -952,18 +974,19 @@ static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void ad1889_initcfg(ad1889_dev_t *dev) static void ad1889_initcfg(ad1889_dev_t *dev)
{ {
u16 tmp; u16 tmp16;
u32 tmp32;
/* make sure the interrupt bits are setup the way we want */ /* make sure the interrupt bits are setup the way we want */
tmp = AD1889_READW(dev, AD_DMAWAVCTRL); tmp32 = AD1889_READL(dev, AD_DMAWAVCTRL);
tmp &= ~0x00ff; /* flat dma, no sg, mask out the intr bits */ tmp32 &= ~0xff; /* flat dma, no sg, mask out the intr bits */
tmp |= 0x0004; /* intr on count, loop */ tmp32 |= 0x6; /* intr on count, loop */
AD1889_WRITEW(dev, AD_DMAWAVCTRL, tmp); AD1889_WRITEL(dev, AD_DMAWAVCTRL, tmp32);
/* unmute... */ /* unmute... */
tmp = AD1889_READW(dev, AD_DSWADA); tmp16 = AD1889_READW(dev, AD_DSWADA);
tmp &= ~0x8080; tmp16 &= ~0x8080;
AD1889_WRITEW(dev, AD_DSWADA, tmp); AD1889_WRITEW(dev, AD_DSWADA, tmp16);
} }
static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
......
...@@ -34,9 +34,9 @@ ...@@ -34,9 +34,9 @@
#define AD_DMAWAVICC 0x98 /* WAV interrupt current count */ #define AD_DMAWAVICC 0x98 /* WAV interrupt current count */
#define AD_DMAWAVIBC 0x9c /* WAV interrupt base count */ #define AD_DMAWAVIBC 0x9c /* WAV interrupt base count */
#define AD_DMARESCTRL 0xa0 /* RES PCI control/status */ #define AD_DMARESCTRL 0xa0 /* RES PCI control/status */
#define AD_DMAADCCTRL 0xa8 /* RES PCI control/status */ #define AD_DMAADCCTRL 0xa8 /* ADC PCI control/status */
#define AD_DMASYNCTRL 0xb0 /* RES PCI control/status */ #define AD_DMASYNCTRL 0xb0 /* SYN PCI control/status */
#define AD_DMAWAVCTRL 0xb8 /* RES PCI control/status */ #define AD_DMAWAVCTRL 0xb8 /* WAV PCI control/status */
#define AD_DMADISR 0xc0 /* PCI DMA intr status */ #define AD_DMADISR 0xc0 /* PCI DMA intr status */
#define AD_DMACHSS 0xc4 /* PCI DMA channel stop status */ #define AD_DMACHSS 0xc4 /* PCI DMA channel stop status */
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
Copyright 2000-2003 (c) Helge Deller <deller@gmx.de> Copyright 2000-2003 (c) Helge Deller <deller@gmx.de>
Copyright 2001 (c) Matthieu Delahaye <delahaym@esiee.fr> Copyright 2001 (c) Matthieu Delahaye <delahaym@esiee.fr>
Copyright 2001 (c) Jean-Christophe Vaugeois <vaugeoij@esiee.fr> Copyright 2001 (c) Jean-Christophe Vaugeois <vaugeoij@esiee.fr>
Copyright 2004 (c) Stuart Brady <sdbrady@ntlworld.com>
TODO: TODO:
...@@ -126,7 +127,15 @@ ...@@ -126,7 +127,15 @@
#define MAX_OUTPUT_LEVEL (GAIN_RO_MASK >> GAIN_RO_SHIFT) #define MAX_OUTPUT_LEVEL (GAIN_RO_MASK >> GAIN_RO_SHIFT)
#define MAX_INPUT_LEVEL (GAIN_RI_MASK >> GAIN_RI_SHIFT) #define MAX_INPUT_LEVEL (GAIN_RI_MASK >> GAIN_RI_SHIFT)
#define MAX_VOLUME_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT) #define MAX_MONITOR_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT)
#define MIXER_INTERNAL SOUND_MIXER_LINE1
#define MIXER_LINEOUT SOUND_MIXER_LINE2
#define MIXER_HEADPHONES SOUND_MIXER_LINE3
#define MASK_INTERNAL SOUND_MASK_LINE1
#define MASK_LINEOUT SOUND_MASK_LINE2
#define MASK_HEADPHONES SOUND_MASK_LINE3
/* /*
* Channels Mask in mixer register * Channels Mask in mixer register
...@@ -543,6 +552,7 @@ static ssize_t harmony_audio_write(struct file *file, ...@@ -543,6 +552,7 @@ static ssize_t harmony_audio_write(struct file *file,
int count = 0; int count = 0;
int frame_size; int frame_size;
int buf_to_fill; int buf_to_fill;
int fresh_buffer;
if (!harmony.format_initialized) { if (!harmony.format_initialized) {
if (harmony_format_auto_detect(buffer, total_count)) if (harmony_format_auto_detect(buffer, total_count))
...@@ -564,12 +574,16 @@ static ssize_t harmony_audio_write(struct file *file, ...@@ -564,12 +574,16 @@ static ssize_t harmony_audio_write(struct file *file,
buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play); buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play);
if (harmony.play_offset) if (harmony.play_offset) {
buf_to_fill--; buf_to_fill--;
buf_to_fill += MAX_BUFS;
}
buf_to_fill %= MAX_BUFS; buf_to_fill %= MAX_BUFS;
fresh_buffer = (harmony.play_offset == 0);
/* Figure out the size of the frame */ /* Figure out the size of the frame */
if ((total_count-count) > HARMONY_BUF_SIZE - harmony.play_offset) { if ((total_count-count) >= HARMONY_BUF_SIZE - harmony.play_offset) {
frame_size = HARMONY_BUF_SIZE - harmony.play_offset; frame_size = HARMONY_BUF_SIZE - harmony.play_offset;
} else { } else {
frame_size = total_count - count; frame_size = total_count - count;
...@@ -587,7 +601,7 @@ static ssize_t harmony_audio_write(struct file *file, ...@@ -587,7 +601,7 @@ static ssize_t harmony_audio_write(struct file *file,
CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill + harmony.play_offset), CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill + harmony.play_offset),
frame_size); frame_size);
if (!harmony.play_offset) if (fresh_buffer)
harmony.nb_filled_play++; harmony.nb_filled_play++;
count += frame_size; count += frame_size;
...@@ -650,18 +664,17 @@ static int harmony_audio_ioctl(struct inode *inode, ...@@ -650,18 +664,17 @@ static int harmony_audio_ioctl(struct inode *inode,
switch (ival) { switch (ival) {
case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break; case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break;
case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break; case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break;
case AFMT_S16_LE: /* fall through, but not really supported */ case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR; break;
case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR;
ival = AFMT_S16_BE;
break;
default: { default: {
DPRINTK(KERN_WARNING PFX DPRINTK(KERN_WARNING PFX
"unsupported sound format 0x%04x requested.\n", "unsupported sound format 0x%04x requested.\n",
ival); ival);
return -EINVAL; ival = AFMT_S16_BE;
return put_user(ival, (int *) arg);
} }
} }
harmony_set_format(new_format); harmony_set_format(new_format);
return 0;
} else { } else {
switch (harmony.data_format) { switch (harmony.data_format) {
case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break; case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break;
...@@ -669,8 +682,8 @@ static int harmony_audio_ioctl(struct inode *inode, ...@@ -669,8 +682,8 @@ static int harmony_audio_ioctl(struct inode *inode,
case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break; case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break;
default: ival = 0; default: ival = 0;
} }
}
return put_user(ival, (int *) arg); return put_user(ival, (int *) arg);
}
case SOUND_PCM_READ_RATE: case SOUND_PCM_READ_RATE:
ival = harmony.dac_rate; ival = harmony.dac_rate;
...@@ -689,7 +702,17 @@ static int harmony_audio_ioctl(struct inode *inode, ...@@ -689,7 +702,17 @@ static int harmony_audio_ioctl(struct inode *inode,
if (ival != 0 && ival != 1) if (ival != 0 && ival != 1)
return -EINVAL; return -EINVAL;
harmony_set_stereo(ival); harmony_set_stereo(ival);
return 0;
case SNDCTL_DSP_CHANNELS:
if (get_user(ival, (int *) arg))
return -EFAULT;
if (ival != 1 && ival != 2) {
ival = harmony.stereo_select == HARMONY_SS_MONO ? 1 : 2;
return put_user(ival, (int *) arg); return put_user(ival, (int *) arg);
}
harmony_set_stereo(ival-1);
return 0;
case SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE:
ival = HARMONY_BUF_SIZE; ival = HARMONY_BUF_SIZE;
...@@ -887,7 +910,7 @@ static int harmony_mixer_get_level(int channel) ...@@ -887,7 +910,7 @@ static int harmony_mixer_get_level(int channel)
int right_level; int right_level;
switch (channel) { switch (channel) {
case SOUND_MIXER_OGAIN: case SOUND_MIXER_VOLUME:
left_level = (harmony.current_gain & GAIN_LO_MASK) >> GAIN_LO_SHIFT; left_level = (harmony.current_gain & GAIN_LO_MASK) >> GAIN_LO_SHIFT;
right_level = (harmony.current_gain & GAIN_RO_MASK) >> GAIN_RO_SHIFT; right_level = (harmony.current_gain & GAIN_RO_MASK) >> GAIN_RO_SHIFT;
left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL);
...@@ -901,10 +924,10 @@ static int harmony_mixer_get_level(int channel) ...@@ -901,10 +924,10 @@ static int harmony_mixer_get_level(int channel)
right_level= to_oss_level(right_level, MAX_INPUT_LEVEL); right_level= to_oss_level(right_level, MAX_INPUT_LEVEL);
return (right_level << 8)+left_level; return (right_level << 8)+left_level;
case SOUND_MIXER_VOLUME: case SOUND_MIXER_MONITOR:
left_level = (harmony.current_gain & GAIN_MA_MASK) >> GAIN_MA_SHIFT; left_level = (harmony.current_gain & GAIN_MA_MASK) >> GAIN_MA_SHIFT;
left_level = to_oss_level(MAX_VOLUME_LEVEL-left_level, MAX_VOLUME_LEVEL); left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL);
return left_level; return (left_level << 8)+left_level;
} }
return -EINVAL; return -EINVAL;
} }
...@@ -926,9 +949,11 @@ static int harmony_mixer_set_level(int channel, int value) ...@@ -926,9 +949,11 @@ static int harmony_mixer_set_level(int channel, int value)
right_level = (value & 0x0000ff00) >> 8; right_level = (value & 0x0000ff00) >> 8;
left_level = value & 0x000000ff; left_level = value & 0x000000ff;
if (right_level > 100) right_level = 100;
if (left_level > 100) left_level = 100;
switch (channel) { switch (channel) {
case SOUND_MIXER_OGAIN: case SOUND_MIXER_VOLUME:
right_level = to_harmony_level(100-right_level, MAX_OUTPUT_LEVEL); right_level = to_harmony_level(100-right_level, MAX_OUTPUT_LEVEL);
left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL); left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL);
new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL);
...@@ -948,12 +973,12 @@ static int harmony_mixer_set_level(int channel, int value) ...@@ -948,12 +973,12 @@ static int harmony_mixer_set_level(int channel, int value)
harmony_mixer_set_gain(); harmony_mixer_set_gain();
return (new_right_level << 8) + new_left_level; return (new_right_level << 8) + new_left_level;
case SOUND_MIXER_VOLUME: case SOUND_MIXER_MONITOR:
left_level = to_harmony_level(100-left_level, MAX_VOLUME_LEVEL); left_level = to_harmony_level(100-left_level, MAX_MONITOR_LEVEL);
new_left_level = to_oss_level(MAX_VOLUME_LEVEL-left_level, MAX_VOLUME_LEVEL); new_left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL);
harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK)| (left_level << GAIN_MA_SHIFT); harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK) | (left_level << GAIN_MA_SHIFT);
harmony_mixer_set_gain(); harmony_mixer_set_gain();
return new_left_level; return (new_left_level << 8) + new_left_level;
} }
return -EINVAL; return -EINVAL;
...@@ -986,8 +1011,12 @@ static int harmony_mixer_set_recmask(int recmask) ...@@ -986,8 +1011,12 @@ static int harmony_mixer_set_recmask(int recmask)
{ {
int new_input_line; int new_input_line;
int new_input_mask; int new_input_mask;
int current_input_line;
if ((recmask & SOUND_MASK_LINE)) { current_input_line = (harmony.current_gain & GAIN_IS_MASK)
>> GAIN_IS_SHIFT;
if ((current_input_line && ((recmask & SOUND_MASK_LINE) || !(recmask & SOUND_MASK_MIC))) ||
(!current_input_line && ((recmask & SOUND_MASK_LINE) && !(recmask & SOUND_MASK_MIC)))) {
new_input_line = 0; new_input_line = 0;
new_input_mask = SOUND_MASK_LINE; new_input_mask = SOUND_MASK_LINE;
} else { } else {
...@@ -1009,9 +1038,9 @@ static int harmony_mixer_get_outmask(void) ...@@ -1009,9 +1038,9 @@ static int harmony_mixer_get_outmask(void)
{ {
int outmask = 0; int outmask = 0;
if (harmony.current_gain & GAIN_HE_MASK) outmask |=SOUND_MASK_PHONEOUT; if (harmony.current_gain & GAIN_SE_MASK) outmask |= MASK_INTERNAL;
if (harmony.current_gain & GAIN_LE_MASK) outmask |=SOUND_MASK_LINE; if (harmony.current_gain & GAIN_LE_MASK) outmask |= MASK_LINEOUT;
if (harmony.current_gain & GAIN_SE_MASK) outmask |=SOUND_MASK_SPEAKER; if (harmony.current_gain & GAIN_HE_MASK) outmask |= MASK_HEADPHONES;
return outmask; return outmask;
} }
...@@ -1019,24 +1048,24 @@ static int harmony_mixer_get_outmask(void) ...@@ -1019,24 +1048,24 @@ static int harmony_mixer_get_outmask(void)
static int harmony_mixer_set_outmask(int outmask) static int harmony_mixer_set_outmask(int outmask)
{ {
if (outmask & SOUND_MASK_PHONEOUT) if (outmask & MASK_INTERNAL)
harmony.current_gain |= GAIN_HE_MASK; harmony.current_gain |= GAIN_SE_MASK;
else else
harmony.current_gain &= ~GAIN_HE_MASK; harmony.current_gain &= ~GAIN_SE_MASK;
if (outmask & SOUND_MASK_LINE) if (outmask & MASK_LINEOUT)
harmony.current_gain |= GAIN_LE_MASK; harmony.current_gain |= GAIN_LE_MASK;
else else
harmony.current_gain &= ~GAIN_LE_MASK; harmony.current_gain &= ~GAIN_LE_MASK;
if (outmask & SOUND_MASK_SPEAKER) if (outmask & MASK_HEADPHONES)
harmony.current_gain |= GAIN_SE_MASK; harmony.current_gain |= GAIN_HE_MASK;
else else
harmony.current_gain &= ~GAIN_SE_MASK; harmony.current_gain &= ~GAIN_HE_MASK;
harmony_mixer_set_gain(); harmony_mixer_set_gain();
return (outmask & (SOUND_MASK_PHONEOUT | SOUND_MASK_LINE | SOUND_MASK_SPEAKER)); return (outmask & (MASK_INTERNAL | MASK_LINEOUT | MASK_HEADPHONES));
} }
/* /*
...@@ -1074,19 +1103,19 @@ static int harmony_mixer_ioctl(struct inode * inode, struct file * file, ...@@ -1074,19 +1103,19 @@ static int harmony_mixer_ioctl(struct inode * inode, struct file * file,
ret = SOUND_CAP_EXCL_INPUT; ret = SOUND_CAP_EXCL_INPUT;
break; break;
case MIXER_READ(SOUND_MIXER_STEREODEVS): case MIXER_READ(SOUND_MIXER_STEREODEVS):
ret = SOUND_MASK_IGAIN | SOUND_MASK_OGAIN; ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN;
break; break;
case MIXER_READ(SOUND_MIXER_RECMASK): case MIXER_READ(SOUND_MIXER_RECMASK):
ret = SOUND_MASK_MIC | SOUND_MASK_LINE; ret = SOUND_MASK_MIC | SOUND_MASK_LINE;
break; break;
case MIXER_READ(SOUND_MIXER_DEVMASK): case MIXER_READ(SOUND_MIXER_DEVMASK):
ret = SOUND_MASK_OGAIN | SOUND_MASK_IGAIN | ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN |
SOUND_MASK_VOLUME; SOUND_MASK_MONITOR;
break; break;
case MIXER_READ(SOUND_MIXER_OUTMASK): case MIXER_READ(SOUND_MIXER_OUTMASK):
ret = SOUND_MASK_SPEAKER | SOUND_MASK_LINE | ret = MASK_INTERNAL | MASK_LINEOUT |
SOUND_MASK_PHONEOUT; MASK_HEADPHONES;
break; break;
case MIXER_WRITE(SOUND_MIXER_RECSRC): case MIXER_WRITE(SOUND_MIXER_RECSRC):
...@@ -1103,15 +1132,15 @@ static int harmony_mixer_ioctl(struct inode * inode, struct file * file, ...@@ -1103,15 +1132,15 @@ static int harmony_mixer_ioctl(struct inode * inode, struct file * file,
ret = harmony_mixer_get_outmask(); ret = harmony_mixer_get_outmask();
break; break;
case MIXER_WRITE(SOUND_MIXER_OGAIN):
case MIXER_WRITE(SOUND_MIXER_IGAIN):
case MIXER_WRITE(SOUND_MIXER_VOLUME): case MIXER_WRITE(SOUND_MIXER_VOLUME):
case MIXER_WRITE(SOUND_MIXER_IGAIN):
case MIXER_WRITE(SOUND_MIXER_MONITOR):
ret = harmony_mixer_set_level(cmd & 0xff, val); ret = harmony_mixer_set_level(cmd & 0xff, val);
break; break;
case MIXER_READ(SOUND_MIXER_OGAIN):
case MIXER_READ(SOUND_MIXER_IGAIN):
case MIXER_READ(SOUND_MIXER_VOLUME): case MIXER_READ(SOUND_MIXER_VOLUME):
case MIXER_READ(SOUND_MIXER_IGAIN):
case MIXER_READ(SOUND_MIXER_MONITOR):
ret = harmony_mixer_get_level(cmd & 0xff); ret = harmony_mixer_get_level(cmd & 0xff);
break; break;
...@@ -1201,16 +1230,15 @@ harmony_driver_probe(struct parisc_device *dev) ...@@ -1201,16 +1230,15 @@ harmony_driver_probe(struct parisc_device *dev)
return -EBUSY; return -EBUSY;
} }
harmony.dev = dev; if (!dev->irq) {
/* Set the HPA of harmony */
harmony.hpa = (struct harmony_hpa *)dev->hpa;
if (!harmony.dev->irq) {
printk(KERN_ERR PFX "no irq found\n"); printk(KERN_ERR PFX "no irq found\n");
return -ENODEV; return -ENODEV;
} }
/* Set the HPA of harmony */
harmony.hpa = (struct harmony_hpa *)dev->hpa;
harmony.dev = dev;
/* Grab the ID and revision from the device */ /* Grab the ID and revision from the device */
id = gsc_readb(&harmony.hpa->id); id = gsc_readb(&harmony.hpa->id);
if ((id | 1) != 0x15) { if ((id | 1) != 0x15) {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Harmony is found in HP 712s, 715/new and many other GSC based machines. * Harmony is found in HP 712s, 715/new and many other GSC based machines.
* On older 715 machines you'll find the technically identical chip * On older 715 machines you'll find the technically identical chip
* called 'Vivace'. Both Harmony and Vicace are supported by this driver. * called 'Vivace'. Both Harmony and Vivace are supported by this driver.
* *
* this ALSA driver is based on OSS driver by: * this ALSA driver is based on OSS driver by:
* Copyright 2000 (c) Linuxcare Canada, Alex deVries <alex@linuxcare.com> * Copyright 2000 (c) Linuxcare Canada, Alex deVries <alex@linuxcare.com>
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
* to be recorded is put in RNXTADD. There is 2 read-only registers, PCURADD and * to be recorded is put in RNXTADD. There is 2 read-only registers, PCURADD and
* RCURADD that provides adress of current page. * RCURADD that provides adress of current page.
* *
* Harmony has no way to controll full duplex or half duplex mode. It means * Harmony has no way to control full duplex or half duplex mode. It means
* that we always need to provide adresses of playback and capture data, even * that we always need to provide adresses of playback and capture data, even
* when this is not needed. That's why we statically alloc one graveyard * when this is not needed. That's why we statically alloc one graveyard
* buffer (to put recorded data in play-only mode) and a silence buffer. * buffer (to put recorded data in play-only mode) and a silence buffer.
...@@ -556,7 +556,7 @@ static int snd_card_harmony_playback_prepare(snd_pcm_substream_t * substream) ...@@ -556,7 +556,7 @@ static int snd_card_harmony_playback_prepare(snd_pcm_substream_t * substream)
harmony->sample_rate = snd_card_harmony_rate_bits(runtime->rate); harmony->sample_rate = snd_card_harmony_rate_bits(runtime->rate);
/* data format */ /* data format */
harmony->data_format = snd_harmony_set_data_format(haromny, runtime->format); harmony->data_format = snd_harmony_set_data_format(harmony, runtime->format);
/* number of channels */ /* number of channels */
if (runtime->channels == 2) if (runtime->channels == 2)
...@@ -587,7 +587,7 @@ static int snd_card_harmony_capture_prepare(snd_pcm_substream_t * substream) ...@@ -587,7 +587,7 @@ static int snd_card_harmony_capture_prepare(snd_pcm_substream_t * substream)
harmony->sample_rate = snd_card_harmony_rate_bits(runtime->rate); harmony->sample_rate = snd_card_harmony_rate_bits(runtime->rate);
/* data format */ /* data format */
harmony->data_format = snd_harmony_set_data_format(haromny, runtime->format); harmony->data_format = snd_harmony_set_data_format(harmony, runtime->format);
/* number of channels */ /* number of channels */
if (runtime->channels == 1) if (runtime->channels == 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment