patch-2.4.6 linux/drivers/sound/cs46xx.c
Next file: linux/drivers/sound/cs46xxpm-24.h
Previous file: linux/drivers/sound/cs4281/cs4281m.c
Back to the patch index
Back to the overall index
- Lines: 917
- Date:
Mon Jul 2 13:56:41 2001
- Orig file:
v2.4.5/linux/drivers/sound/cs46xx.c
- Orig date:
Thu May 24 15:14:08 2001
diff -u --recursive --new-file v2.4.5/linux/drivers/sound/cs46xx.c linux/drivers/sound/cs46xx.c
@@ -50,6 +50,7 @@
* 20010409-tw add hercules game theatre XP amp code.
* 20010420-tw cleanup powerdown/up code.
* 20010521-tw eliminate pops, and fixes for powerdown.
+ * 20010525-tw added fixes for thinkpads with powerdown logic.
*
* Status:
* Playback/Capture supported from 8k-48k.
@@ -179,7 +180,7 @@
MODULE_PARM(hercules_egpio_disable, "i");
static unsigned long initdelay=700; /* PM delay in millisecs */
MODULE_PARM(initdelay, "i");
-static unsigned long powerdown=1; /* turn on/off powerdown processing in driver */
+static unsigned long powerdown=-1; /* turn on/off powerdown processing in driver */
MODULE_PARM(powerdown, "i");
#define DMABUF_DEFAULTORDER 3
static unsigned long defaultorder=DMABUF_DEFAULTORDER;
@@ -190,6 +191,14 @@
static int thinkpad;
MODULE_PARM(thinkpad, "i");
+/*
+* set the powerdown module parm to 0 to disable all
+* powerdown. also set thinkpad to 1 to disable powerdown,
+* but also to enable the clkrun functionality.
+*/
+static unsigned cs_powerdown=1;
+static unsigned cs_laptop_wait=1;
+
/* An instance of the 4610 channel */
struct cs_channel
{
@@ -199,7 +208,7 @@
};
#define CS46XX_MAJOR_VERSION "1"
-#define CS46XX_MINOR_VERSION "27"
+#define CS46XX_MINOR_VERSION "28"
#ifdef __ia64__
#define CS46XX_ARCH "64" //architecture key
@@ -284,6 +293,8 @@
int ossmaxfrags;
unsigned subdivision;
} dmabuf;
+ /* Guard against mmap/write/read races */
+ struct semaphore sem;
};
struct cs_card {
@@ -370,8 +381,10 @@
static loff_t cs_llseek(struct file *file, loff_t offset, int origin);
static int cs_hardware_init(struct cs_card *card);
static int cs46xx_powerup(struct cs_card *card, unsigned int type);
-static int cs461x_powerdown(struct cs_card *card, unsigned int type);
+static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag);
static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type);
+static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state);
+static int cs46xx_resume_tbl(struct pci_dev *pcidev);
static inline unsigned ld2(unsigned int x)
{
@@ -688,7 +701,7 @@
{
struct ac97_codec *dev=card->ac97_codec[0];
- CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mute()+ %s\n",
+ CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()+ %s\n",
(state == CS_TRUE) ? "Muting" : "UnMuting") );
if(state == CS_TRUE)
@@ -717,7 +730,7 @@
cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, card->pm.u32AC97_master_volume_mono);
cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, card->pm.u32AC97_pcm_out_volume);
}
- CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mute()-\n"));
+ CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()-\n"));
}
/* set playback sample rate */
@@ -1250,7 +1263,7 @@
dmabuf->SGok = 0;
}
-static int prog_dmabuf(struct cs_state *state)
+static int __prog_dmabuf(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
@@ -1431,6 +1444,17 @@
return 1;
}
+static int prog_dmabuf(struct cs_state *state)
+{
+ int ret;
+
+ down(&state->sem);
+ ret = __prog_dmabuf(state);
+ up(&state->sem);
+
+ return ret;
+}
+
static void cs_clear_tail(struct cs_state *state)
{
}
@@ -2089,11 +2113,12 @@
return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
- if (!dmabuf->ready && (ret = prog_dmabuf(state)))
- return ret;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
- ret = 0;
+
+ down(&state->sem);
+ if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
+ goto out;
add_wait_queue(&state->dmabuf.wait, &wait);
while (count > 0) {
@@ -2101,8 +2126,8 @@
{
schedule();
if (signal_pending(current)) {
- ret = ret ? ret : -ERESTARTSYS;
- break;
+ if(!ret) ret = -ERESTARTSYS;
+ goto out;
}
}
spin_lock_irqsave(&state->card->lock, flags);
@@ -2122,12 +2147,20 @@
start_adc(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- break;
+ goto out;
}
+ up(&state->sem);
schedule();
if (signal_pending(current)) {
- ret = ret ? ret : -ERESTARTSYS;
- break;
+ if(!ret) ret = -ERESTARTSYS;
+ goto out;
+ }
+ down(&state->sem);
+ if (dmabuf->mapped)
+ {
+ if(!ret)
+ ret = -ENXIO;
+ goto out;
}
continue;
}
@@ -2142,7 +2175,7 @@
(void *)((unsigned)dmabuf->rawbuf + swptr), cnt, &copied))
{
if (!ret) ret = -EFAULT;
- break;
+ goto out;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
spin_lock_irqsave(&card->lock, flags);
@@ -2154,6 +2187,8 @@
ret += copied;
start_adc(state);
}
+out:
+ up(&state->sem);
remove_wait_queue(&state->dmabuf.wait, &wait);
set_current_state(TASK_RUNNING);
CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4,
@@ -2183,12 +2218,21 @@
if (ppos != &file->f_pos)
return -ESPIPE;
+
+ down(&state->sem);
if (dmabuf->mapped)
- return -ENXIO;
- if (!dmabuf->ready && (ret = prog_dmabuf(state)))
- return ret;
+ {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
+ goto out;
if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
+ {
+ ret = -EFAULT;
+ goto out;
+ }
add_wait_queue(&state->dmabuf.wait, &wait);
ret = 0;
/*
@@ -2200,8 +2244,8 @@
{
schedule();
if (signal_pending(current)) {
- ret = ret ? ret : -ERESTARTSYS;
- break;
+ if(!ret) ret = -ERESTARTSYS;
+ goto out;
}
}
spin_lock_irqsave(&state->card->lock, flags);
@@ -2234,18 +2278,26 @@
start_dac(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- break;
+ goto out;
}
+ up(&state->sem);
schedule();
if (signal_pending(current)) {
- ret = ret ? ret : -ERESTARTSYS;
- break;
+ if(!ret) ret = -ERESTARTSYS;
+ goto out;
}
+ down(&state->sem);
+ if (dmabuf->mapped)
+ {
+ if(!ret)
+ ret = -ENXIO;
+ goto out;
+ }
continue;
}
if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
if (!ret) ret = -EFAULT;
- return ret;
+ goto out;
}
spin_lock_irqsave(&state->card->lock, flags);
swptr = (swptr + cnt) % dmabuf->dmasize;
@@ -2265,6 +2317,8 @@
ret += cnt;
start_dac(state);
}
+out:
+ up(&state->sem);
remove_wait_queue(&state->dmabuf.wait, &wait);
set_current_state(TASK_RUNNING);
@@ -2352,9 +2406,9 @@
struct cs_card *card = (struct cs_card *)file->private_data;
struct cs_state *state;
struct dmabuf *dmabuf;
- int ret;
+ int ret = 0;
unsigned long size;
-
+
CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=0x%x %s %s\n",
(unsigned)file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "",
vma->vm_flags & VM_READ ? "VM_READ" : "") );
@@ -2393,24 +2447,39 @@
*/
state = card->states[1];
if(!(unsigned)state)
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ goto out;
+ }
+ down(&state->sem);
dmabuf = &state->dmabuf;
if (cs4x_pgoff(vma) != 0)
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ goto out;
+ }
size = vma->vm_end - vma->vm_start;
CS_DBGOUT(CS_PARMS, 2, printk("cs46xx: cs_mmap(): size=%d\n",(unsigned)size) );
if (size > (PAGE_SIZE << dmabuf->buforder))
- return -EINVAL;
+ {
+ ret = -EINVAL;
+ goto out;
+ }
if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),
size, vma->vm_page_prot))
- return -EAGAIN;
+ {
+ ret = -EAGAIN;
+ goto out;
+ }
dmabuf->mapped = 1;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mmap()-\n") );
- return 0;
+out:
+ up(&state->sem);
+ return ret;
}
static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
@@ -3146,6 +3215,7 @@
if (state == NULL)
return -ENOMEM;
memset(state, 0, sizeof(struct cs_state));
+ init_MUTEX(&state->sem);
dmabuf = &state->dmabuf;
dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA);
if(dmabuf->pbuf==NULL)
@@ -3217,6 +3287,7 @@
if (state == NULL)
return -ENOMEM;
memset(state, 0, sizeof(struct cs_state));
+ init_MUTEX(&state->sem);
dmabuf = &state->dmabuf;
dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA);
if(dmabuf->pbuf==NULL)
@@ -3281,7 +3352,6 @@
if((ret = prog_dmabuf(state)))
return ret;
}
-
CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n") );
return 0;
}
@@ -3321,11 +3391,10 @@
state->card->states[state->virt] = NULL;
state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
- if( (tmp = cs461x_powerdown(card, CS_POWER_DAC )) )
+ if( (tmp = cs461x_powerdown(card, CS_POWER_DAC, CS_FALSE )) )
{
CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
"cs46xx: cs_release_mixdev() powerdown DAC failure (0x%x)\n",tmp) );
- return -EIO;
}
/* Now turn off external AMP if needed */
@@ -3355,11 +3424,10 @@
state->card->states[state->virt] = NULL;
state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
- if( (tmp = cs461x_powerdown(card, CS_POWER_ADC )) )
+ if( (tmp = cs461x_powerdown(card, CS_POWER_ADC, CS_FALSE )) )
{
CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
"cs46xx: cs_release_mixdev() powerdown ADC failure (0x%x)\n",tmp) );
- return -EIO;
}
/* Now turn off external AMP if needed */
@@ -3375,7 +3443,7 @@
return 0;
}
-void printpm(struct cs_card *s)
+static void printpm(struct cs_card *s)
{
CS_DBGOUT(CS_PM, 9, printk("pm struct:\n"));
CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n",
@@ -3419,8 +3487,8 @@
void cs46xx_ac97_suspend(struct cs_card *card)
{
int Count,i;
- unsigned int tmp;
struct ac97_codec *dev=card->ac97_codec[0];
+ unsigned int tmp;
CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()+\n"));
@@ -3445,7 +3513,6 @@
* Save the ac97 volume registers as well as the current powerdown state.
* Now, mute the all the outputs (master, headphone, and mono), as well
* as the PCM volume, in preparation for powering down the entire part.
-*/
card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev,
(u8)BA0_AC97_MASTER_VOLUME);
card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev,
@@ -3454,22 +3521,27 @@
(u8)BA0_AC97_MASTER_VOLUME_MONO);
card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev,
(u8)BA0_AC97_PCM_OUT_VOLUME);
-
+*/
+/*
+* mute the outputs
+*/
cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000);
cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000);
cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000);
cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000);
+/*
+* save the registers that cause pops
+*/
card->pm.u32AC97_powerdown = (u32)cs_ac97_get(dev, (u8)AC97_POWER_CONTROL);
card->pm.u32AC97_general_purpose = (u32)cs_ac97_get(dev, (u8)BA0_AC97_GENERAL_PURPOSE);
-
/*
* And power down everything on the AC97 codec.
* well, for now, only power down the DAC/ADC and MIXER VREFON components.
* trouble with removing VREF.
*/
if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
- CS_POWER_MIXVON )) )
+ CS_POWER_MIXVON, CS_TRUE )) )
{
CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO
"cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp) );
@@ -3502,17 +3574,12 @@
*/
cs_ac97_set(dev, (u8)BA0_AC97_GENERAL_PURPOSE,
(u16)card->pm.u32AC97_general_purpose);
-
- cs_ac97_set(dev, (u8)AC97_POWER_CONTROL,
- (u16)card->pm.u32AC97_powerdown);
- mdelay(10);
-
/*
* Now, while the outputs are still muted, restore the state of power
* on the AC97 part.
*/
cs_ac97_set(dev, (u8)BA0_AC97_POWERDOWN, (u16)card->pm.u32AC97_powerdown);
- mdelay(5);
+ mdelay(5 * cs_laptop_wait);
/*
* Restore just the first set of registers, from register number
* 0x02 to the register number that ulHighestRegToRestore specifies.
@@ -3544,7 +3611,7 @@
dmabuf->ready = 0;
resync_dma_ptrs(card->states[1]);
cs_set_divisor(dmabuf);
- if(prog_dmabuf(card->states[1]))
+ if(__prog_dmabuf(card->states[1]))
{
CS_DBGOUT(CS_PM | CS_ERROR, 1,
printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() dac error\n"));
@@ -3558,7 +3625,7 @@
dmabuf->ready = 0;
resync_dma_ptrs(card->states[0]);
cs_set_divisor(dmabuf);
- if(prog_dmabuf(card->states[0]))
+ if(__prog_dmabuf(card->states[0]))
{
CS_DBGOUT(CS_PM | CS_ERROR, 1,
printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() adc error\n"));
@@ -3588,7 +3655,7 @@
static void cs461x_reset(struct cs_card *card);
static void cs461x_proc_stop(struct cs_card *card);
-int cs46xx_suspend(struct cs_card *card)
+static int cs46xx_suspend(struct cs_card *card, u32 state)
{
unsigned int tmp;
CS_DBGOUT(CS_PM | CS_FUNCTION, 4,
@@ -3680,7 +3747,7 @@
return 0;
}
-int cs46xx_resume(struct cs_card *card)
+static int cs46xx_resume(struct cs_card *card)
{
int i;
@@ -3704,7 +3771,7 @@
{
CS_DBGOUT(CS_PM | CS_ERROR, 4, printk(
"cs46xx: cs46xx_resume()- ERROR in cs_hardware_init()\n"));
- mdelay(10);
+ mdelay(10 * cs_laptop_wait);
cs461x_reset(card);
continue;
}
@@ -3789,12 +3856,13 @@
if(!(card->pm.flags & CS46XX_PM_IDLE))
loopcnt = 2000;
else
- loopcnt = 500;
+ loopcnt = 500 * cs_laptop_wait;
+ loopcnt *= cs_laptop_wait;
for (count = 0; count < loopcnt; count++) {
/*
* First, we want to wait for a short time.
*/
- udelay(10);
+ udelay(10 * cs_laptop_wait);
/*
* Now, check to see if the read has completed.
* ACCTL = 460h, DCV should be reset by now and 460h = 17h
@@ -3819,7 +3887,8 @@
if(!(card->pm.flags & CS46XX_PM_IDLE))
loopcnt = 2000;
else
- loopcnt = 100;
+ loopcnt = 1000;
+ loopcnt *= cs_laptop_wait;
for (count = 0; count < loopcnt; count++) {
/*
* Read the AC97 status register.
@@ -3828,16 +3897,16 @@
*/
if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS)
break;
- udelay(10);
+ udelay(10 * cs_laptop_wait);
}
/*
* Make sure we got valid status.
*/
if (!( (tmp=cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) {
- CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING
- "cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x \n", reg, tmp));
- CS_DBGOUT(CS_ERROR, 9, printk(KERN_WARNING "returning 0xffff\n"));
+ CS_DBGOUT(CS_ERROR, 2, printk(KERN_WARNING
+ "cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x 0xffff \n",
+ reg, tmp));
return 0xffff;
}
@@ -3849,7 +3918,7 @@
"cs46xx: cs_ac97_get() reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n",
reg, cs461x_peekBA0(card, BA0_ACSDA),
cs461x_peekBA0(card, BA0_ACCAD)));
- return (cs461x_peekBA0(card, BA0_ACSDA));
+ return(cs461x_peekBA0(card, BA0_ACSDA));
}
static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val)
@@ -3893,7 +3962,7 @@
/*
* First, we want to wait for a short time.
*/
- udelay(10);
+ udelay(10 * cs_laptop_wait);
/*
* Now, check to see if the write has completed.
* ACCTL = 460h, DCV should be reset by now and 460h = 07h
@@ -4032,24 +4101,28 @@
return -ENODEV;
}
match:
- card->active_ctrl(card, -1);
- card->amplifier_ctrl(card, -1);
MOD_DEC_USE_COUNT;
if(!CS_DEC_AND_TEST(&card->mixer_use_cnt))
{
CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4,
printk(KERN_INFO "cs46xx: cs_release_mixdev()- no powerdown, usecnt>0\n"));
+ card->active_ctrl(card, -1);
+ card->amplifier_ctrl(card, -1);
return 0;
}
/*
* ok, no outstanding mixer opens, so powerdown.
*/
- if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON )) )
+ if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON, CS_FALSE )) )
{
CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO
"cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n",tmp) );
+ card->active_ctrl(card, -1);
+ card->amplifier_ctrl(card, -1);
return -EIO;
}
+ card->active_ctrl(card, -1);
+ card->amplifier_ctrl(card, -1);
CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4,
printk(KERN_INFO "cs46xx: cs_release_mixdev()- 0\n"));
return 0;
@@ -4101,7 +4174,7 @@
list_for_each(entry, &cs46xx_devs)
{
card = list_entry(entry, struct cs_card, list);
- cs46xx_suspend(card);
+ cs46xx_suspend(card, 0);
}
}
@@ -4341,14 +4414,14 @@
}
-static int cs461x_powerdown(struct cs_card *card, unsigned int type)
+static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag)
{
int count;
- unsigned int tmp=0;
+ unsigned int tmp=0,muted=0;
CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
"cs46xx: cs461x_powerdown()+ type=0x%x\n",type));
- if(!powerdown)
+ if(!cs_powerdown && !suspendflag)
{
CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO
"cs46xx: cs461x_powerdown() DISABLED exiting\n"));
@@ -4380,8 +4453,6 @@
type &= ~CS_POWER_MIXVON;
type &= ~CS_POWER_MIXVOFF;
- cs_mute(card, CS_TRUE);
-
/*
* Power down indicated areas.
*/
@@ -4396,6 +4467,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp |= CS_AC97_POWER_CONTROL_MIXVOFF;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
/*
@@ -4439,6 +4515,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp |= CS_AC97_POWER_CONTROL_MIXVON;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
/*
@@ -4480,6 +4561,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (tmp & CS_AC97_POWER_CONTROL_ADC_ON)
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp |= CS_AC97_POWER_CONTROL_ADC;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
@@ -4524,6 +4610,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (tmp & CS_AC97_POWER_CONTROL_DAC_ON)
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp |= CS_AC97_POWER_CONTROL_DAC;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
/*
@@ -4557,7 +4648,8 @@
}
}
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
- cs_mute(card, CS_FALSE);
+ if(muted)
+ cs_mute(card, CS_FALSE);
CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
"cs46xx: cs461x_powerdown()- 0 tmp=0x%x\n",tmp));
return 0;
@@ -4566,7 +4658,7 @@
static int cs46xx_powerup(struct cs_card *card, unsigned int type)
{
int count;
- unsigned int tmp=0;
+ unsigned int tmp=0,muted=0;
CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO
"cs46xx: cs46xx_powerup()+ type=0x%x\n",type));
@@ -4578,7 +4670,6 @@
if(type & (CS_POWER_DAC | CS_POWER_ADC))
type |= CS_POWER_MIXVON | CS_POWER_MIXVOFF;
- cs_mute(card, CS_TRUE);
/*
* Power up indicated areas.
*/
@@ -4593,6 +4684,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON))
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp &= ~CS_AC97_POWER_CONTROL_MIXVOFF;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
/*
@@ -4636,6 +4732,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON))
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp &= ~CS_AC97_POWER_CONTROL_MIXVON;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
/*
@@ -4677,6 +4778,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON))
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp &= ~CS_AC97_POWER_CONTROL_ADC;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
@@ -4721,6 +4827,11 @@
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON))
{
+ if(!muted)
+ {
+ cs_mute(card, CS_TRUE);
+ muted=1;
+ }
tmp &= ~CS_AC97_POWER_CONTROL_DAC;
cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
/*
@@ -4754,7 +4865,8 @@
}
}
tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
- cs_mute(card, CS_FALSE);
+ if(muted)
+ cs_mute(card, CS_FALSE);
CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
"cs46xx: cs46xx_powerup()- 0 tmp=0x%x\n",tmp));
return 0;
@@ -4843,7 +4955,7 @@
* generating bit clock (so we don't try to start the PLL without an
* input clock).
*/
- mdelay(5); /* 1 should be enough ?? (and pigs might fly) */
+ mdelay(5 * cs_laptop_wait); /* 1 should be enough ?? (and pigs might fly) */
/*
* Set the serial port timing configuration, so that
@@ -4876,7 +4988,7 @@
/*
* Wait until the PLL has stabilized.
*/
- mdelay(5); /* Again 1 should be enough ?? */
+ mdelay(5 * cs_laptop_wait); /* Again 1 should be enough ?? */
/*
* Turn on clocking of the core so that we can setup the serial ports.
@@ -4903,7 +5015,7 @@
cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
- mdelay(5); /* Shouldnt be needed ?? */
+ mdelay(5 * cs_laptop_wait); /* Shouldnt be needed ?? */
/*
* If we are resuming under 2.2.x then we can not schedule a timeout.
@@ -4930,7 +5042,7 @@
{
for (count = 0; count < 100; count++) {
// First, we want to wait for a short time.
- udelay(25);
+ udelay(25 * cs_laptop_wait);
if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)
break;
@@ -4978,7 +5090,7 @@
{
for (count = 0; count < 100; count++) {
// First, we want to wait for a short time.
- udelay(25);
+ udelay(25 * cs_laptop_wait);
if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
break;
@@ -5068,7 +5180,7 @@
*/
if(card->pm.flags & CS46XX_PM_IDLE)
{
- if(!powerdown)
+ if(!cs_powerdown)
{
if( (tmp = cs46xx_powerup(card, CS_POWER_DAC | CS_POWER_ADC |
CS_POWER_MIXVON )) )
@@ -5081,7 +5193,7 @@
else
{
if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
- CS_POWER_MIXVON )) )
+ CS_POWER_MIXVON, CS_FALSE )) )
{
CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO
"cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) );
@@ -5148,6 +5260,7 @@
CS_DBGOUT(CS_FUNCTION | CS_INIT, 2,
printk(KERN_INFO "cs46xx: probe()+\n"));
+ dma_mask = 0xffffffff; /* this enables playback and recording */
if (pci_enable_device(pci_dev)) {
CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
"cs46xx: pci_enable_device() failed\n"));
@@ -5169,8 +5282,6 @@
"cs46xx: probe() architecture does not support 32bit PCI busmaster DMA\n"));
return -1;
}
- dma_mask = 0xffffffff; /* this enables playback and recording */
-
pci_read_config_word(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor);
pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &ss_card);
@@ -5239,7 +5350,31 @@
printk(KERN_INFO "cs46xx: Activating CLKRUN hack for Thinkpad.\n");
card->active_ctrl = clkrun_hack;
}
-
+/*
+* The thinkpads don't work well without runtime updating on their kernel
+* delay values (or any laptop with variable CPU speeds really).
+* so, just to be safe set the init delay to 2100. Eliminates
+* failures on T21 Thinkpads. remove this code when the udelay
+* and mdelay kernel code is replaced by a pm timer, or the delays
+* work well for battery and/or AC power both.
+*/
+ if(card->active_ctrl == clkrun_hack)
+ {
+ initdelay = 2100;
+ cs_laptop_wait = 5;
+ }
+ if((card->active_ctrl == clkrun_hack) && !(powerdown == 1))
+ {
+/*
+* for some currently unknown reason, powering down the DAC and ADC component
+* blocks on thinkpads causes some funky behavior... distoorrrtion and ac97
+* codec access problems. probably the serial clock becomes unsynced.
+* added code to sync the chips back up, but only helped about 70% the time.
+*/
+ cs_powerdown = 0;
+ }
+ if(powerdown == 0)
+ cs_powerdown = 0;
card->active_ctrl(card, 1);
/* claim our iospace and irq */
@@ -5290,7 +5425,7 @@
unregister_sound_mixer(card->ac97_codec[j]->dev_mixer);
kfree (card->ac97_codec[j]);
}
- mdelay(10);
+ mdelay(10 * cs_laptop_wait);
continue;
}
break;
@@ -5413,7 +5548,7 @@
* them.
*/
if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
- CS_POWER_MIXVON )) )
+ CS_POWER_MIXVON, CS_TRUE )) )
{
CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO
"cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) );
@@ -5530,7 +5665,7 @@
case PM_SUSPEND:
CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
"cs46xx: PM suspend request\n"));
- if(cs46xx_suspend(card))
+ if(cs46xx_suspend(card, 0))
{
CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
"cs46xx: PM suspend request refused\n"));
@@ -5553,21 +5688,22 @@
return 0;
}
-static void cs46xx_suspend_tbl(struct pci_dev *pcidev)
+#if CS46XX_ACPI_SUPPORT
+static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state)
{
struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev);
CS_DBGOUT(CS_PM | CS_FUNCTION, 2,
printk(KERN_INFO "cs46xx: cs46xx_suspend_tbl request\n"));
- cs46xx_suspend(s);
- return;
+ cs46xx_suspend(s, 0);
+ return 0;
}
-static void cs46xx_resume_tbl(struct pci_dev *pcidev)
+static int cs46xx_resume_tbl(struct pci_dev *pcidev)
{
struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev);
CS_DBGOUT(CS_PM | CS_FUNCTION, 2,
printk(KERN_INFO "cs46xx: cs46xx_resume_tbl request\n"));
cs46xx_resume(s);
- return;
+ return 0;
}
-
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)