Index: linux-2.6.23.17/include/sound/ac97_codec.h =================================================================== --- linux-2.6.23.17.orig/include/sound/ac97_codec.h +++ linux-2.6.23.17/include/sound/ac97_codec.h @@ -281,10 +281,12 @@ /* specific - Analog Devices */ #define AC97_AD_TEST 0x5a /* test register */ #define AC97_AD_TEST2 0x5c /* undocumented test register 2 */ +#define AC97_AD_HPFD_SHIFT 12 /* High Pass Filter Disable */ #define AC97_AD_CODEC_CFG 0x70 /* codec configuration */ #define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */ #define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ #define AC97_AD_MISC 0x76 /* Misc Control Bits */ +#define AC97_AD_VREFD_SHIFT 2 /* V_REFOUT Disable (AD1888) */ /* specific - Cirrus Logic */ #define AC97_CSR_ACMODE 0x5e /* AC Mode Register */ Index: linux-2.6.23.17/sound/pci/ac97/ac97_codec.c =================================================================== --- linux-2.6.23.17.orig/sound/pci/ac97/ac97_codec.c +++ linux-2.6.23.17/sound/pci/ac97/ac97_codec.c @@ -49,7 +49,7 @@ module_param(enable_loopback, bool, 0444 MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); #ifdef CONFIG_SND_AC97_POWER_SAVE -static int power_save; +static int power_save = 1; module_param(power_save, bool, 0644); MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control"); #endif Index: linux-2.6.23.17/sound/pci/ac97/ac97_patch.c =================================================================== --- linux-2.6.23.17.orig/sound/pci/ac97/ac97_patch.c +++ linux-2.6.23.17/sound/pci/ac97/ac97_patch.c @@ -1973,8 +1973,9 @@ static const struct snd_kcontrol_new snd .get = snd_ac97_ad1888_lohpsel_get, .put = snd_ac97_ad1888_lohpsel_put }, - AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1), - AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1), + AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, AC97_AD_VREFD_SHIFT, 1, 1), + AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, + AC97_AD_HPFD_SHIFT, 1, 1), AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, Index: linux-2.6.23.17/sound/pci/cs5535audio/Makefile =================================================================== --- linux-2.6.23.17.orig/sound/pci/cs5535audio/Makefile +++ linux-2.6.23.17/sound/pci/cs5535audio/Makefile @@ -8,5 +8,9 @@ ifeq ($(CONFIG_PM),y) snd-cs5535audio-objs += cs5535audio_pm.o endif +ifdef CONFIG_OLPC +snd-cs5535audio-objs += cs5535audio_olpc.o +endif + # Toplevel Module Dependency obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o Index: linux-2.6.23.17/sound/pci/cs5535audio/cs5535audio.c =================================================================== --- linux-2.6.23.17.orig/sound/pci/cs5535audio/cs5535audio.c +++ linux-2.6.23.17/sound/pci/cs5535audio/cs5535audio.c @@ -145,7 +145,7 @@ static unsigned short snd_cs5535audio_ac return snd_cs5535audio_codec_read(cs5535au, reg); } -static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au) +static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au) { struct snd_card *card = cs5535au->card; struct snd_ac97_bus *pbus; @@ -160,10 +160,14 @@ static int snd_cs5535audio_mixer(struct return err; memset(&ac97, 0, sizeof(ac97)); - ac97.scaps = AC97_SCAP_AUDIO|AC97_SCAP_SKIP_MODEM; + ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM + | AC97_SCAP_POWER_SAVE; ac97.private_data = cs5535au; ac97.pci = cs5535au->pci; + /* olpc_prequirks is dummied out if not olpc */ + olpc_prequirks(card, &ac97); + if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) { snd_printk(KERN_ERR "mixer failed\n"); return err; @@ -171,6 +175,12 @@ static int snd_cs5535audio_mixer(struct snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk); + /* olpc_quirks is dummied out if not olpc */ + if (( err = olpc_quirks(card, cs5535au->ac97)) < 0) { + snd_printk(KERN_ERR "olpc quirks failed\n"); + return err; + } + return 0; } @@ -206,7 +216,6 @@ static void process_bm1_irq(struct cs553 static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id) { u16 acc_irq_stat; - u8 bm_stat; unsigned char count; struct cs5535audio *cs5535au = dev_id; @@ -217,7 +226,7 @@ static irqreturn_t snd_cs5535audio_inter if (!acc_irq_stat) return IRQ_NONE; - for (count = 0; count < 10; count++) { + for (count = 0; count < 4; count++) { if (acc_irq_stat & (1 << count)) { switch (count) { case IRQ_STS: @@ -232,26 +241,9 @@ static irqreturn_t snd_cs5535audio_inter case BM1_IRQ_STS: process_bm1_irq(cs5535au); break; - case BM2_IRQ_STS: - bm_stat = cs_readb(cs5535au, ACC_BM2_STATUS); - break; - case BM3_IRQ_STS: - bm_stat = cs_readb(cs5535au, ACC_BM3_STATUS); - break; - case BM4_IRQ_STS: - bm_stat = cs_readb(cs5535au, ACC_BM4_STATUS); - break; - case BM5_IRQ_STS: - bm_stat = cs_readb(cs5535au, ACC_BM5_STATUS); - break; - case BM6_IRQ_STS: - bm_stat = cs_readb(cs5535au, ACC_BM6_STATUS); - break; - case BM7_IRQ_STS: - bm_stat = cs_readb(cs5535au, ACC_BM7_STATUS); - break; default: - snd_printk(KERN_ERR "Unexpected irq src\n"); + snd_printk(KERN_ERR "Unexpected irq src: " + "0x%x\n", acc_irq_stat); break; } } Index: linux-2.6.23.17/sound/pci/cs5535audio/cs5535audio.h =================================================================== --- linux-2.6.23.17.orig/sound/pci/cs5535audio/cs5535audio.h +++ linux-2.6.23.17/sound/pci/cs5535audio/cs5535audio.h @@ -16,57 +16,28 @@ #define ACC_IRQ_STATUS 0x12 #define ACC_BM0_CMD 0x20 #define ACC_BM1_CMD 0x28 -#define ACC_BM2_CMD 0x30 -#define ACC_BM3_CMD 0x38 -#define ACC_BM4_CMD 0x40 -#define ACC_BM5_CMD 0x48 -#define ACC_BM6_CMD 0x50 -#define ACC_BM7_CMD 0x58 #define ACC_BM0_PRD 0x24 #define ACC_BM1_PRD 0x2C -#define ACC_BM2_PRD 0x34 -#define ACC_BM3_PRD 0x3C -#define ACC_BM4_PRD 0x44 -#define ACC_BM5_PRD 0x4C -#define ACC_BM6_PRD 0x54 -#define ACC_BM7_PRD 0x5C #define ACC_BM0_STATUS 0x21 #define ACC_BM1_STATUS 0x29 -#define ACC_BM2_STATUS 0x31 -#define ACC_BM3_STATUS 0x39 -#define ACC_BM4_STATUS 0x41 -#define ACC_BM5_STATUS 0x49 -#define ACC_BM6_STATUS 0x51 -#define ACC_BM7_STATUS 0x59 #define ACC_BM0_PNTR 0x60 #define ACC_BM1_PNTR 0x64 -#define ACC_BM2_PNTR 0x68 -#define ACC_BM3_PNTR 0x6C -#define ACC_BM4_PNTR 0x70 -#define ACC_BM5_PNTR 0x74 -#define ACC_BM6_PNTR 0x78 -#define ACC_BM7_PNTR 0x7C + /* acc_codec bar0 reg bits */ /* ACC_IRQ_STATUS */ #define IRQ_STS 0 #define WU_IRQ_STS 1 #define BM0_IRQ_STS 2 #define BM1_IRQ_STS 3 -#define BM2_IRQ_STS 4 -#define BM3_IRQ_STS 5 -#define BM4_IRQ_STS 6 -#define BM5_IRQ_STS 7 -#define BM6_IRQ_STS 8 -#define BM7_IRQ_STS 9 /* ACC_BMX_STATUS */ #define EOP (1<<0) #define BM_EOP_ERR (1<<1) /* ACC_BMX_CTL */ -#define BM_CTL_EN 0x00000001 -#define BM_CTL_PAUSE 0x00000011 -#define BM_CTL_DIS 0x00000000 -#define BM_CTL_BYTE_ORD_LE 0x00000000 -#define BM_CTL_BYTE_ORD_BE 0x00000100 +#define BM_CTL_EN 0x01 +#define BM_CTL_PAUSE 0x03 +#define BM_CTL_DIS 0x00 +#define BM_CTL_BYTE_ORD_LE 0x00 +#define BM_CTL_BYTE_ORD_BE 0x04 /* cs5535 specific ac97 codec register defines */ #define CMD_MASK 0xFF00FFFF #define CMD_NEW 0x00010000 @@ -106,8 +77,8 @@ struct cs5535audio_dma { struct snd_pcm_substream *substream; unsigned int buf_addr, buf_bytes; unsigned int period_bytes, periods; - int suspended; u32 saved_prd; + int pcm_open_flag; }; struct cs5535audio { @@ -123,8 +94,21 @@ struct cs5535audio { struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; }; +#ifdef CONFIG_PM int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); int snd_cs5535audio_resume(struct pci_dev *pci); +#endif + +#ifdef CONFIG_OLPC +void olpc_prequirks(struct snd_card *card, struct snd_ac97_template *ac97) __devinit; +int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) __devinit; +int olpc_ai_enable(struct snd_ac97 *ac97, u8 val); +#else +#define olpc_prequirks(arg,arg2) do {} while (0) +#define olpc_quirks(arg,arg2) (0) +#define olpc_ai_enable(a, v) (0) +#endif + int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio); #endif /* __SOUND_CS5535AUDIO_H */ Index: linux-2.6.23.17/sound/pci/cs5535audio/cs5535audio_pcm.c =================================================================== --- linux-2.6.23.17.orig/sound/pci/cs5535audio/cs5535audio_pcm.c +++ linux-2.6.23.17/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -164,6 +164,7 @@ static int cs5535audio_build_dma_packets jmpprd_addr = cpu_to_le32(lastdesc->addr + (sizeof(struct cs5535audio_dma_desc)*periods)); + dma->substream = substream; dma->period_bytes = period_bytes; dma->periods = periods; spin_lock_irq(&cs5535au->reg_lock); @@ -241,6 +242,7 @@ static void cs5535audio_clear_dma_packet { snd_dma_free_pages(&dma->desc_buf); dma->desc_buf.area = NULL; + dma->substream = NULL; } static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream, @@ -260,6 +262,9 @@ static int snd_cs5535audio_hw_params(str err = cs5535audio_build_dma_packets(cs5535au, dma, substream, params_periods(hw_params), params_period_bytes(hw_params)); + if (!err) + dma->pcm_open_flag = 1; + return err; } @@ -268,6 +273,15 @@ static int snd_cs5535audio_hw_free(struc struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream); struct cs5535audio_dma *dma = substream->runtime->private_data; + if (dma->pcm_open_flag) { + if (substream == cs5535au->playback_substream) + snd_ac97_update_power(cs5535au->ac97, + AC97_PCM_FRONT_DAC_RATE, 0); + else + snd_ac97_update_power(cs5535au->ac97, + AC97_PCM_LR_ADC_RATE, 0); + dma->pcm_open_flag = 0; + } cs5535audio_clear_dma_packets(cs5535au, dma, substream); return snd_pcm_lib_free_pages(substream); } @@ -298,14 +312,12 @@ static int snd_cs5535audio_trigger(struc break; case SNDRV_PCM_TRIGGER_RESUME: dma->ops->enable_dma(cs5535au); - dma->suspended = 0; break; case SNDRV_PCM_TRIGGER_STOP: dma->ops->disable_dma(cs5535au); break; case SNDRV_PCM_TRIGGER_SUSPEND: dma->ops->disable_dma(cs5535au); - dma->suspended = 1; break; default: snd_printk(KERN_ERR "unhandled trigger\n"); @@ -344,6 +356,7 @@ static int snd_cs5535audio_capture_open( int err; struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ac97 *ac97 = cs5535au->ac97; runtime->hw = snd_cs5535audio_capture; cs5535au->capture_substream = substream; @@ -352,11 +365,29 @@ static int snd_cs5535audio_capture_open( if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; - return 0; + +#ifdef CONFIG_OLPC + /* Disable Analog Input */ + olpc_ai_enable(ac97, 0); + /* Enable V_ref bias while recording. */ + snd_ac97_update_bits(ac97, AC97_AD_MISC, 1<ac97; + + /* Disable Analog Input */ + olpc_ai_enable(ac97, 0); + /* Disable V_ref bias. */ + snd_ac97_update_bits(ac97, AC97_AD_MISC, 1<pcm); + snd_ac97_suspend(cs5535au->ac97); for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { struct cs5535audio_dma *dma = &cs5535au->dmas[i]; - if (dma && dma->substream && !dma->suspended) + if (dma && dma->substream) dma->saved_prd = dma->ops->read_prd(cs5535au); } - snd_pcm_suspend_all(cs5535au->pcm); - snd_ac97_suspend(cs5535au->ac97); /* save important regs, then disable aclink in hw */ snd_cs5535audio_stop_hardware(cs5535au); + if (pci_save_state(pci)) { + printk(KERN_ERR "cs5535audio: pci_save_state failed!\n"); + return -EIO; + } pci_disable_device(pci); - pci_save_state(pci); pci_set_power_state(pci, pci_choose_state(pci, state)); return 0; } @@ -89,7 +92,12 @@ int snd_cs5535audio_resume(struct pci_de int i; pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); + if (pci_restore_state(pci) < 0) { + printk(KERN_ERR "cs5535audio: pci_restore_state failed, " + "disabling device\n"); + snd_card_disconnect(card); + return -EIO; + } if (pci_enable_device(pci) < 0) { printk(KERN_ERR "cs5535audio: pci_enable_device failed, " "disabling device\n"); @@ -112,17 +120,17 @@ int snd_cs5535audio_resume(struct pci_de if (!timeout) snd_printk(KERN_ERR "Failure getting AC Link ready\n"); - /* we depend on ac97 to perform the codec power up */ - snd_ac97_resume(cs5535au->ac97); /* set up rate regs, dma. actual initiation is done in trig */ for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { struct cs5535audio_dma *dma = &cs5535au->dmas[i]; - if (dma && dma->substream && dma->suspended) { + if (dma && dma->substream) { dma->substream->ops->prepare(dma->substream); dma->ops->setup_prd(cs5535au, dma->saved_prd); } } - + + /* we depend on ac97 to perform the codec power up */ + snd_ac97_resume(cs5535au->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0;