patch-2.4.10 linux/drivers/sound/i810_audio.c
Next file: linux/drivers/sound/ite8172.c
Previous file: linux/drivers/sound/gus_wave.c
Back to the patch index
Back to the overall index
- Lines: 987
- Date:
Sun Sep 9 10:52:35 2001
- Orig file:
v2.4.9/linux/drivers/sound/i810_audio.c
- Orig date:
Mon Aug 27 12:41:45 2001
diff -u --recursive --new-file v2.4.9/linux/drivers/sound/i810_audio.c linux/drivers/sound/i810_audio.c
@@ -106,6 +106,7 @@
static int ftsodell=0;
static int strict_clocking=0;
static unsigned int clocking=48000;
+static int spdif_locked=0;
//#define DEBUG
//#define DEBUG2
@@ -118,6 +119,11 @@
#define I810_FMT_STEREO 2
#define I810_FMT_MASK 3
+#define SPDIF_ON 0x0004
+#define SURR_ON 0x0010
+#define CENTER_LFE_ON 0x0020
+#define VOL_MUTED 0x8000
+
/* the 810's array of pointers to data buffers */
struct sg_item {
@@ -191,7 +197,7 @@
#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
-#define DRIVER_VERSION "0.03"
+#define DRIVER_VERSION "0.04"
/* magic numbers to protect our data structures */
#define I810_CARD_MAGIC 0x5072696E /* "Prin" */
@@ -325,6 +331,8 @@
struct i810_state *states[NR_HW_CH];
u16 ac97_features;
+ u16 ac97_status;
+ u16 channels;
/* hardware resources */
unsigned long iobase;
@@ -401,11 +409,164 @@
card->channel[channel].used=0;
}
+static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate )
+{
+ unsigned long id = 0L;
+
+ id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16);
+ id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff;
+#ifdef DEBUG
+ printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id);
+#endif
+ switch ( id ) {
+ case 0x41445361: /* AD1886 */
+ if (rate == 48000) {
+ return 1;
+ }
+ break;
+ default: /* all other codecs, until we know otherwiae */
+ if (rate == 48000 || rate == 44100 || rate == 32000) {
+ return 1;
+ }
+ break;
+ }
+ return (0);
+}
+
+/* i810_set_spdif_output
+ *
+ * Configure the S/PDIF output transmitter. When we turn on
+ * S/PDIF, we turn off the analog output. This may not be
+ * the right thing to do.
+ *
+ * Assumptions:
+ * The DSP sample rate must already be set to a supported
+ * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort.
+ */
+static void i810_set_spdif_output(struct i810_state *state, int slots, int rate)
+{
+ int vol;
+ int aud_reg;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
+
+ if(!(state->card->ac97_features & 4)) {
+#ifdef DEBUG
+ printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
+#endif
+ state->card->ac97_status &= ~SPDIF_ON;
+ } else {
+ if ( slots == -1 ) { /* Turn off S/PDIF */
+ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+ i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+
+ /* If the volume wasn't muted before we turned on S/PDIF, unmute it */
+ if ( !(state->card->ac97_status & VOL_MUTED) ) {
+ aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO);
+ i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED));
+ }
+ state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON);
+ return;
+ }
+
+ vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO);
+ state->card->ac97_status = vol & VOL_MUTED;
+
+ /* Set S/PDIF transmitter sample rate */
+ aud_reg = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
+ switch ( rate ) {
+ case 32000:
+ aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
+ break;
+ case 44100:
+ aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
+ break;
+ case 48000:
+ aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
+ break;
+ default:
+#ifdef DEBUG
+ printk(KERN_WARNING "i810_audio: %d sample rate not supported by S/PDIF.\n", rate);
+#endif
+ /* turn off S/PDIF */
+ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+ i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+ state->card->ac97_status &= ~SPDIF_ON;
+ return;
+ }
+
+ i810_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg);
+
+ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+ aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_VRA | AC97_EA_SPDIF;
+ i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);
+ state->card->ac97_status |= SPDIF_ON;
+
+ /* Check to make sure the configuration is valid */
+ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+ if ( ! (aud_reg & 0x0400) ) {
+#ifdef DEBUG
+ printk(KERN_WARNING "i810_audio: S/PDIF transmitter configuration not valid (0x%04x).\n", aud_reg);
+#endif
+
+ /* turn off S/PDIF */
+ i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+ state->card->ac97_status &= ~SPDIF_ON;
+ return;
+ }
+ /* Mute the analog output */
+ /* Should this only mute the PCM volume??? */
+ i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED));
+ }
+}
+
+/* i810_set_dac_channels
+ *
+ * Configure the codec's multi-channel DACs
+ *
+ * The logic is backwards. Setting the bit to 1 turns off the DAC.
+ *
+ * What about the ICH? We currently configure it using the
+ * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC,
+ * does that imply that we want the ICH set to support
+ * these channels?
+ *
+ * TODO:
+ * vailidate that the codec really supports these DACs
+ * before turning them on.
+ */
+static void i810_set_dac_channels(struct i810_state *state, int channel)
+{
+ int aud_reg;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
+
+ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+ aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK;
+ state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON);
+
+ switch ( channel ) {
+ case 2: /* always enabled */
+ break;
+ case 4:
+ aud_reg &= ~AC97_EA_PRJ;
+ state->card->ac97_status |= SURR_ON;
+ break;
+ case 6:
+ aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK);
+ state->card->ac97_status |= SURR_ON | CENTER_LFE_ON;
+ break;
+ default:
+ break;
+ }
+ i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);
+
+}
+
+
/* set playback sample rate */
static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate)
{
struct dmabuf *dmabuf = &state->dmabuf;
- u32 dacp, new_rate;
+ u32 new_rate;
struct ac97_codec *codec=state->card->ac97_codec[0];
if(!(state->card->ac97_features&0x0001))
@@ -429,20 +590,11 @@
dmabuf->rate = (rate * 48000)/clocking;
}
- if(rate != i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE))
- {
- /* Power down the DAC */
- dacp=i810_ac97_get(codec, AC97_POWER_CONTROL);
- i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0200);
- /* Load the rate and read the effective rate */
- i810_ac97_set(codec, AC97_PCM_FRONT_DAC_RATE, rate);
- new_rate=i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE);
- /* Power it back up */
- i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);
- if(new_rate != rate) {
- dmabuf->rate = (new_rate * 48000)/clocking;
- rate = new_rate;
- }
+ new_rate = ac97_set_dac_rate(codec, rate);
+
+ if(new_rate != rate) {
+ dmabuf->rate = (new_rate * 48000)/clocking;
+ rate = new_rate;
}
#ifdef DEBUG
printk("i810_audio: called i810_set_dac_rate : rate = %d/%d\n", dmabuf->rate, rate);
@@ -454,7 +606,7 @@
static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate)
{
struct dmabuf *dmabuf = &state->dmabuf;
- u32 dacp, new_rate;
+ u32 new_rate;
struct ac97_codec *codec=state->card->ac97_codec[0];
if(!(state->card->ac97_features&0x0001))
@@ -478,21 +630,12 @@
rate = 8000;
dmabuf->rate = (rate * 48000)/clocking;
}
+
+ new_rate = ac97_set_adc_rate(codec, rate);
- if(rate != i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE))
- {
- /* Power down the ADC */
- dacp=i810_ac97_get(codec, AC97_POWER_CONTROL);
- i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0100);
- /* Load the rate and read the effective rate */
- i810_ac97_set(codec, AC97_PCM_LR_DAC_RATE, rate);
- new_rate=i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE);
- /* Power it back up */
- i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);
- if(new_rate != rate) {
- dmabuf->rate = (new_rate * 48000)/clocking;
- rate = new_rate;
- }
+ if(new_rate != rate) {
+ dmabuf->rate = (new_rate * 48000)/clocking;
+ rate = new_rate;
}
#ifdef DEBUG
printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate);
@@ -985,8 +1128,10 @@
dmabuf = &state->dmabuf;
if(dmabuf->enable & DAC_RUNNING)
c=dmabuf->write_channel;
- else
+ else if(dmabuf->enable & ADC_RUNNING)
c=dmabuf->read_channel;
+ else /* This can occur going from R/W to close */
+ continue;
port+=c->port;
@@ -1375,7 +1520,9 @@
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
- int val, mapped, ret;
+ unsigned int i_glob_cnt;
+ int val = 0, mapped, ret;
+ struct ac97_codec *codec = state->card->ac97_codec[0];
mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
((file->f_mode & FMODE_READ) && dmabuf->mapped);
@@ -1428,11 +1575,31 @@
return -EFAULT;
if (val >= 0) {
if (file->f_mode & FMODE_WRITE) {
- stop_dac(state);
- dmabuf->ready = 0;
- spin_lock_irqsave(&state->card->lock, flags);
- i810_set_dac_rate(state, val);
- spin_unlock_irqrestore(&state->card->lock, flags);
+ if ( (state->card->ac97_status & SPDIF_ON) ) { /* S/PDIF Enabled */
+ /* AD1886 only supports 48000, need to check that */
+ if ( i810_valid_spdif_rate ( codec, val ) ) {
+ /* Set DAC rate */
+ i810_set_spdif_output ( state, -1, 0 );
+ stop_dac(state);
+ dmabuf->ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ i810_set_dac_rate(state, val);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ /* Set S/PDIF transmitter rate. */
+ i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, val );
+ if ( ! (state->card->ac97_status & SPDIF_ON) ) {
+ val = dmabuf->rate;
+ }
+ } else { /* Not a valid rate for S/PDIF, ignore it */
+ val = dmabuf->rate;
+ }
+ } else {
+ stop_dac(state);
+ dmabuf->ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ i810_set_dac_rate(state, val);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
@@ -1450,18 +1617,14 @@
#endif
if (get_user(val, (int *)arg))
return -EFAULT;
- if(val==0) {
- return -EINVAL;
- } else {
- ret = 1;
- }
+
if (dmabuf->enable & DAC_RUNNING) {
stop_dac(state);
}
if (dmabuf->enable & ADC_RUNNING) {
stop_adc(state);
}
- return put_user(ret, (int *)arg);
+ return put_user(1, (int *)arg);
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
@@ -1487,13 +1650,82 @@
#ifdef DEBUG
printk("SNDCTL_DSP_SETFMT\n");
#endif
- return put_user(AFMT_S16_LE, (int *)arg);
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+
+ switch ( val ) {
+ case AFMT_S16_LE:
+ break;
+ case AFMT_QUERY:
+ default:
+ val = AFMT_S16_LE;
+ break;
+ }
+ return put_user(val, (int *)arg);
case SNDCTL_DSP_CHANNELS:
#ifdef DEBUG
printk("SNDCTL_DSP_CHANNELS\n");
#endif
- return put_user(2, (int *)arg);
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+
+ if (val > 0) {
+ if (dmabuf->enable & DAC_RUNNING) {
+ stop_dac(state);
+ }
+ if (dmabuf->enable & ADC_RUNNING) {
+ stop_adc(state);
+ }
+ } else {
+ return put_user(state->card->channels, (int *)arg);
+ }
+
+ /* ICH and ICH0 only support 2 channels */
+ if ( state->card->pci_id == 0x2415 || state->card->pci_id == 0x2425 )
+ return put_user(2, (int *)arg);
+
+ /* Multi-channel support was added with ICH2. Bits in */
+ /* Global Status and Global Control register are now */
+ /* used to indicate this. */
+
+ i_glob_cnt = inl(state->card->iobase + GLOB_CNT);
+
+ /* Current # of channels enabled */
+ if ( i_glob_cnt & 0x0100000 )
+ ret = 4;
+ else if ( i_glob_cnt & 0x0200000 )
+ ret = 6;
+ else
+ ret = 2;
+
+ switch ( val ) {
+ case 2: /* 2 channels is always supported */
+ outl(state->card->iobase + GLOB_CNT, (i_glob_cnt & 0xcfffff));
+ /* Do we need to change mixer settings???? */
+ break;
+ case 4: /* Supported on some chipsets, better check first */
+ if ( state->card->channels >= 4 ) {
+ outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0100000));
+ /* Do we need to change mixer settings??? */
+ } else {
+ val = ret;
+ }
+ break;
+ case 6: /* Supported on some chipsets, better check first */
+ if ( state->card->channels >= 6 ) {
+ outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0200000));
+ /* Do we need to change mixer settings??? */
+ } else {
+ val = ret;
+ }
+ break;
+ default: /* nothing else is ever supported by the chipset */
+ val = ret;
+ break;
+ }
+
+ return put_user(val, (int *)arg);
case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */
/* we update the swptr to the end of the last sg segment then return */
@@ -1740,6 +1972,148 @@
#endif
return put_user(AFMT_S16_LE, (int *)arg);
+ case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_SETSPDIF\n");
+#endif
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+
+ /* Check to make sure the codec supports S/PDIF transmitter */
+
+ if((state->card->ac97_features & 4)) {
+ /* mask out the transmitter speed bits so the user can't set them */
+ val &= ~0x3000;
+
+ /* Add the current transmitter speed bits to the passed value */
+ ret = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
+ val |= (ret & 0x3000);
+
+ i810_ac97_set(codec, AC97_SPDIF_CONTROL, val);
+ if(i810_ac97_get(codec, AC97_SPDIF_CONTROL) != val ) {
+ printk(KERN_ERR "i810_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val);
+ return -EFAULT;
+ }
+ }
+#ifdef DEBUG
+ else
+ printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
+#endif
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETSPDIF\n");
+#endif
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+
+ /* Check to make sure the codec supports S/PDIF transmitter */
+
+ if(!(state->card->ac97_features & 4)) {
+#ifdef DEBUG
+ printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
+#endif
+ val = 0;
+ } else {
+ val = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
+ }
+ //return put_user((val & 0xcfff), (int *)arg);
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_GETCHANNELMASK:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_GETCHANNELMASK\n");
+#endif
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+
+ /* Based on AC'97 DAC support, not ICH hardware */
+ val = DSP_BIND_FRONT;
+ if ( state->card->ac97_features & 0x0004 )
+ val |= DSP_BIND_SPDIF;
+
+ if ( state->card->ac97_features & 0x0080 )
+ val |= DSP_BIND_SURR;
+ if ( state->card->ac97_features & 0x0140 )
+ val |= DSP_BIND_CENTER_LFE;
+
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_BIND_CHANNEL:
+#ifdef DEBUG
+ printk("SNDCTL_DSP_BIND_CHANNEL\n");
+#endif
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+ if ( val == DSP_BIND_QUERY ) {
+ val = DSP_BIND_FRONT; /* Always report this as being enabled */
+ if ( state->card->ac97_status & SPDIF_ON )
+ val |= DSP_BIND_SPDIF;
+ else {
+ if ( state->card->ac97_status & SURR_ON )
+ val |= DSP_BIND_SURR;
+ if ( state->card->ac97_status & CENTER_LFE_ON )
+ val |= DSP_BIND_CENTER_LFE;
+ }
+ } else { /* Not a query, set it */
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ if ( dmabuf->enable == DAC_RUNNING ) {
+ stop_dac(state);
+ }
+ if ( val & DSP_BIND_SPDIF ) { /* Turn on SPDIF */
+ /* Ok, this should probably define what slots
+ * to use. For now, we'll only set it to the
+ * defaults:
+ *
+ * non multichannel codec maps to slots 3&4
+ * 2 channel codec maps to slots 7&8
+ * 4 channel codec maps to slots 6&9
+ * 6 channel codec maps to slots 10&11
+ *
+ * there should be some way for the app to
+ * select the slot assignment.
+ */
+
+ i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, dmabuf->rate );
+ if ( !(state->card->ac97_status & SPDIF_ON) )
+ val &= ~DSP_BIND_SPDIF;
+ } else {
+ int mask;
+ int channels;
+
+ /* Turn off S/PDIF if it was on */
+ if ( state->card->ac97_status & SPDIF_ON )
+ i810_set_spdif_output ( state, -1, 0 );
+
+ mask = val & (DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE);
+ switch (mask) {
+ case DSP_BIND_FRONT:
+ channels = 2;
+ break;
+ case DSP_BIND_FRONT|DSP_BIND_SURR:
+ channels = 4;
+ break;
+ case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE:
+ channels = 6;
+ break;
+ default:
+ val = DSP_BIND_FRONT;
+ channels = 2;
+ break;
+ }
+ i810_set_dac_channels ( state, channels );
+
+ /* check that they really got turned on */
+ if ( !state->card->ac97_status & SURR_ON )
+ val &= ~DSP_BIND_SURR;
+ if ( !state->card->ac97_status & CENTER_LFE_ON )
+ val &= ~DSP_BIND_CENTER_LFE;
+ }
+ }
+ return put_user(val, (int *)arg);
+
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
@@ -1805,7 +2179,15 @@
card->states[i] = NULL;;
return -EBUSY;
}
- i810_set_dac_rate(state, 8000);
+ /* Initialize to 8kHz? What if we don't support 8kHz? */
+ /* Let's change this to check for S/PDIF stuff */
+
+ if ( spdif_locked ) {
+ i810_set_dac_rate(state, spdif_locked);
+ i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked);
+ } else {
+ i810_set_dac_rate(state, 8000);
+ }
dmabuf->trigger |= PCM_ENABLE_OUTPUT;
}
@@ -1875,20 +2257,23 @@
{
struct i810_card *card = dev->private_data;
int count = 100;
+ u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));
while(count-- && (inb(card->iobase + CAS) & 1))
udelay(1);
- return inw(card->ac97base + (reg&0x7f));
+
+ return inw(card->ac97base + reg_set);
}
static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data)
{
struct i810_card *card = dev->private_data;
int count = 100;
+ u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));
while(count-- && (inb(card->iobase + CAS) & 1))
udelay(1);
- outw(data, card->ac97base + (reg&0x7f));
+ outw(data, card->ac97base + reg_set);
}
@@ -1929,7 +2314,7 @@
static int __init i810_ac97_init(struct i810_card *card)
{
int num_ac97 = 0;
- int ready_2nd = 0;
+ int total_channels = 0;
struct ac97_codec *codec;
u16 eid;
int i=0;
@@ -1961,10 +2346,38 @@
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ/5);
+
+ /* Number of channels supported */
+ /* What about the codec? Just because the ICH supports */
+ /* multiple channels doesn't mean the codec does. */
+ /* we'll have to modify this in the codec section below */
+ /* to reflect what the codec has. */
+ /* ICH and ICH0 only support 2 channels so don't bother */
+ /* to check.... */
+
+ card->channels = 2;
+ reg = inl(card->iobase + GLOB_STA);
+ if ( reg & 0x0200000 )
+ card->channels = 6;
+ else if ( reg & 0x0100000 )
+ card->channels = 4;
+ printk("i810_audio: Audio Controller supports %d channels.\n", card->channels);
inw(card->ac97base);
for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+
+ /* The ICH programmer's reference says you should */
+ /* check the ready status before probing. So we chk */
+ /* What do we do if it's not ready? Wait and try */
+ /* again, or abort? */
+ reg = inl(card->iobase + GLOB_STA);
+ if (!(reg & (0x100 << num_ac97))) {
+ if(num_ac97 == 0)
+ printk(KERN_ERR "i810_audio: Primary codec not ready.\n");
+ break; /* I think this works, if not ready stop */
+ }
+
if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(codec, 0, sizeof(struct ac97_codec));
@@ -1992,6 +2405,9 @@
schedule_timeout(HZ/20);
}
+ /* Store state information about S/PDIF transmitter */
+ card->ac97_status = 0;
+
/* Don't attempt to get eid until powerup is complete */
eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
@@ -2023,6 +2439,63 @@
}
}
+ /* Determine how many channels the codec(s) support */
+ /* - The primary codec always supports 2 */
+ /* - If the codec supports AMAP, surround DACs will */
+ /* automaticlly get assigned to slots. */
+ /* * Check for surround DACs and increment if */
+ /* found. */
+ /* - Else check if the codec is revision 2.2 */
+ /* * If surround DACs exist, assign them to slots */
+ /* and increment channel count. */
+
+ /* All of this only applies to ICH2 and above. ICH */
+ /* and ICH0 only support 2 channels. ICH2 will only */
+ /* support multiple codecs in a "split audio" config. */
+ /* as described above. */
+
+ /* TODO: Remove all the debugging messages! */
+
+ if((eid & 0xc000) == 0) /* primary codec */
+ total_channels += 2;
+
+ if(eid & 0x200) { /* GOOD, AMAP support */
+ if (eid & 0x0080) /* L/R Surround channels */
+ total_channels += 2;
+ if (eid & 0x0140) /* LFE and Center channels */
+ total_channels += 2;
+ printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", num_ac97, total_channels);
+ } else if (eid & 0x0400) { /* this only works on 2.2 compliant codecs */
+ eid &= 0xffcf;
+ if((eid & 0xc000) != 0) {
+ switch ( total_channels ) {
+ case 2:
+ /* Set dsa1, dsa0 to 01 */
+ eid |= 0x0010;
+ break;
+ case 4:
+ /* Set dsa1, dsa0 to 10 */
+ eid |= 0x0020;
+ break;
+ case 6:
+ /* Set dsa1, dsa0 to 11 */
+ eid |= 0x0030;
+ break;
+ }
+ total_channels += 2;
+ }
+ i810_ac97_set(codec, AC97_EXTENDED_ID, eid);
+ eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
+ printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", num_ac97, eid);
+ if (eid & 0x0080) /* L/R Surround channels */
+ total_channels += 2;
+ if (eid & 0x0140) /* LFE and Center channels */
+ total_channels += 2;
+ printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", num_ac97, total_channels);
+ } else {
+ printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", num_ac97, total_channels);
+ }
+
if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
printk(KERN_ERR "i810_audio: couldn't register mixer!\n");
kfree(codec);
@@ -2030,14 +2503,83 @@
}
card->ac97_codec[num_ac97] = codec;
-
- /* if there is no secondary codec at all, don't probe any more */
- if (!ready_2nd)
- return num_ac97+1;
}
+
+ /* pick the minimum of channels supported by ICHx or codec(s) */
+ card->channels = (card->channels > total_channels)?total_channels:card->channels;
+
return num_ac97;
}
+static void __init i810_configure_clocking (void)
+{
+ struct i810_card *card;
+ struct i810_state *state;
+ struct dmabuf *dmabuf;
+ unsigned int i, offset, new_offset;
+ unsigned long flags;
+
+ card = devs;
+ /* We could try to set the clocking for multiple cards, but can you even have
+ * more than one i810 in a machine? Besides, clocking is global, so unless
+ * someone actually thinks more than one i810 in a machine is possible and
+ * decides to rewrite that little bit, setting the rate for more than one card
+ * is a waste of time.
+ */
+ if(card != NULL) {
+ state = card->states[0] = (struct i810_state *)
+ kmalloc(sizeof(struct i810_state), GFP_KERNEL);
+ if (state == NULL)
+ return;
+ memset(state, 0, sizeof(struct i810_state));
+ dmabuf = &state->dmabuf;
+
+ dmabuf->write_channel = card->alloc_pcm_channel(card);
+ state->virt = 0;
+ state->card = card;
+ state->magic = I810_STATE_MAGIC;
+ init_waitqueue_head(&dmabuf->wait);
+ init_MUTEX(&state->open_sem);
+ dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT;
+ dmabuf->trigger = PCM_ENABLE_OUTPUT;
+ i810_set_dac_rate(state, 48000);
+ if(prog_dmabuf(state, 0) != 0) {
+ goto config_out_nodmabuf;
+ }
+ if(dmabuf->dmasize < 16384) {
+ goto config_out;
+ }
+ dmabuf->count = dmabuf->dmasize;
+ outb(31,card->iobase+dmabuf->write_channel->port+OFF_LVI);
+ save_flags(flags);
+ cli();
+ start_dac(state);
+ offset = i810_get_dma_addr(state, 0);
+ mdelay(50);
+ new_offset = i810_get_dma_addr(state, 0);
+ stop_dac(state);
+ outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR);
+ restore_flags(flags);
+ i = new_offset - offset;
+#ifdef DEBUG
+ printk("i810_audio: %d bytes in 50 milliseconds\n", i);
+#endif
+ if(i == 0)
+ goto config_out;
+ i = i / 4 * 20;
+ if (i > 48500 || i < 47500) {
+ clocking = clocking * clocking / i;
+ printk("i810_audio: setting clocking to %d\n", clocking);
+ }
+config_out:
+ dealloc_dmabuf(state);
+config_out_nodmabuf:
+ state->card->free_pcm_channel(state->card,state->dmabuf.write_channel->num);
+ kfree(state);
+ card->states[0] = NULL;
+ }
+}
+
/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered
until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
@@ -2102,27 +2644,37 @@
kfree(card);
return -ENODEV;
}
- /* register /dev/dsp */
- if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) {
- printk(KERN_ERR "i810_audio: couldn't register DSP device!\n");
+
+ /* initialize AC97 codec and register /dev/mixer */
+ if (i810_ac97_init(card) <= 0) {
release_region(card->iobase, 64);
release_region(card->ac97base, 256);
free_irq(card->irq, card);
kfree(card);
return -ENODEV;
}
+ pci_dev->driver_data = card;
+ if(clocking == 48000) {
+ i810_configure_clocking();
+ }
- /* initialize AC97 codec and register /dev/mixer */
- if (i810_ac97_init(card) <= 0) {
- unregister_sound_dsp(card->dev_audio);
+ /* register /dev/dsp */
+ if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) {
+ int i;
+ printk(KERN_ERR "i810_audio: couldn't register DSP device!\n");
release_region(card->iobase, 64);
release_region(card->ac97base, 256);
free_irq(card->irq, card);
+ for (i = 0; i < NR_AC97; i++)
+ if (card->ac97_codec[i] != NULL) {
+ unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+ kfree (card->ac97_codec[i]);
+ }
kfree(card);
return -ENODEV;
}
- pci_dev->driver_data = card;
+
return 0;
}
@@ -2137,7 +2689,7 @@
/* unregister audio devices */
for (i = 0; i < NR_AC97; i++)
- if (devs->ac97_codec[i] != NULL) {
+ if (card->ac97_codec[i] != NULL) {
unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
kfree (card->ac97_codec[i]);
}
@@ -2151,6 +2703,7 @@
MODULE_PARM(ftsodell, "i");
MODULE_PARM(clocking, "i");
MODULE_PARM(strict_clocking, "i");
+MODULE_PARM(spdif_locked, "i");
#define I810_MODULE_NAME "intel810_audio"
@@ -2161,74 +2714,6 @@
remove: i810_remove,
};
-static void __init i810_configure_clocking (void)
-{
- struct i810_card *card;
- struct i810_state *state;
- struct dmabuf *dmabuf;
- unsigned int i, offset, new_offset;
- unsigned long flags;
-
- card = devs;
- /* We could try to set the clocking for multiple cards, but can you even have
- * more than one i810 in a machine? Besides, clocking is global, so unless
- * someone actually thinks more than one i810 in a machine is possible and
- * decides to rewrite that little bit, setting the rate for more than one card
- * is a waste of time.
- */
- if(card != NULL) {
- state = card->states[0] = (struct i810_state *)
- kmalloc(sizeof(struct i810_state), GFP_KERNEL);
- if (state == NULL)
- return;
- memset(state, 0, sizeof(struct i810_state));
- dmabuf = &state->dmabuf;
-
- dmabuf->write_channel = card->alloc_pcm_channel(card);
- state->virt = 0;
- state->card = card;
- state->magic = I810_STATE_MAGIC;
- init_waitqueue_head(&dmabuf->wait);
- init_MUTEX(&state->open_sem);
- dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT;
- dmabuf->trigger = PCM_ENABLE_OUTPUT;
- i810_set_dac_rate(state, 48000);
- if(prog_dmabuf(state, 0) != 0) {
- goto config_out_nodmabuf;
- }
- if(dmabuf->dmasize < 16384) {
- goto config_out;
- }
- dmabuf->count = dmabuf->dmasize;
- outb(31,card->iobase+dmabuf->write_channel->port+OFF_LVI);
- save_flags(flags);
- cli();
- start_dac(state);
- offset = i810_get_dma_addr(state, 0);
- mdelay(50);
- new_offset = i810_get_dma_addr(state, 0);
- stop_dac(state);
- outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR);
- restore_flags(flags);
- i = new_offset - offset;
-#ifdef DEBUG
- printk("i810_audio: %d bytes in 50 milliseconds\n", i);
-#endif
- if(i == 0)
- goto config_out;
- i = i / 4 * 20;
- if (i > 48500 || i < 47500) {
- clocking = clocking * clocking / i;
- printk("i810_audio: setting clocking to %d\n", clocking);
- }
-config_out:
- dealloc_dmabuf(state);
-config_out_nodmabuf:
- state->card->free_pcm_channel(state->card,state->dmabuf.write_channel->num);
- kfree(state);
- card->states[0] = NULL;
- }
-}
static int __init i810_init_module (void)
{
@@ -2248,6 +2733,15 @@
if(clocking == 48000) {
i810_configure_clocking();
}
+ if(spdif_locked > 0 ) {
+ if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) {
+ printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked);
+ } else {
+ printk("i810_audio: S/PDIF can only be locked to 32000, 441000, or 48000Hz.\n");
+ spdif_locked = 0;
+ }
+ }
+
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)