diff options
Diffstat (limited to 'target/linux/olpc/patches/110-olpc_sound.patch')
-rw-r--r-- | target/linux/olpc/patches/110-olpc_sound.patch | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/target/linux/olpc/patches/110-olpc_sound.patch b/target/linux/olpc/patches/110-olpc_sound.patch new file mode 100644 index 0000000000..421ab283bc --- /dev/null +++ b/target/linux/olpc/patches/110-olpc_sound.patch @@ -0,0 +1,414 @@ +diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h +index 246ac23..874a2c8 100644 +--- a/include/sound/ac97_codec.h ++++ b/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 */ +diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c +index bbed644..090e852 100644 +--- a/sound/pci/ac97/ac97_codec.c ++++ b/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 +diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c +index 581ebba..06637f7 100644 +--- a/sound/pci/ac97/ac97_patch.c ++++ b/sound/pci/ac97/ac97_patch.c +@@ -1973,8 +1973,9 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = { + .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, +diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile +index ad947b4..3866c4d 100644 +--- a/sound/pci/cs5535audio/Makefile ++++ b/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 +diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c +index b8e75ef..389d9da 100644 +--- a/sound/pci/cs5535audio/cs5535audio.c ++++ b/sound/pci/cs5535audio/cs5535audio.c +@@ -145,7 +145,7 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97, + 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 cs5535audio *cs5535au) + 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 cs5535audio *cs5535au) + + 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 cs5535audio *cs5535au) + 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_interrupt(int irq, void *dev_id) + + 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_interrupt(int irq, void *dev_id) + 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; + } + } +diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h +index 4fd1f31..ff82f10 100644 +--- a/sound/pci/cs5535audio/cs5535audio.h ++++ b/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 */ +diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c +index 5450a9e..d23f8ea 100644 +--- a/sound/pci/cs5535audio/cs5535audio_pcm.c ++++ b/sound/pci/cs5535audio/cs5535audio_pcm.c +@@ -164,6 +164,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au, + 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_packets(struct cs5535audio *cs5535au, + { + 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(struct snd_pcm_substream *substream, + 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(struct snd_pcm_substream *substream) + 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(struct snd_pcm_substream *substream, int cmd) + 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(struct snd_pcm_substream *substream) + 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(struct snd_pcm_substream *substream) + 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_AD_VREFD_SHIFT, 0); ++#endif ++ return err; + } + + static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream) + { ++#ifdef CONFIG_OLPC ++ struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream); ++ struct snd_ac97 *ac97 = cs5535au->ac97; ++ ++ /* Disable Analog Input */ ++ olpc_ai_enable(ac97, 0); ++ /* Disable V_ref bias. */ ++ snd_ac97_update_bits(ac97, AC97_AD_MISC, 1<<AC97_AD_VREFD_SHIFT, ++ 1<<AC97_AD_VREFD_SHIFT); ++#endif ++ + return 0; + } + +diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c +index 3e4d198..838708f 100644 +--- a/sound/pci/cs5535audio/cs5535audio_pm.c ++++ b/sound/pci/cs5535audio/cs5535audio_pm.c +@@ -64,18 +64,21 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state) + int i; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); ++ snd_pcm_suspend_all(cs5535au->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_dev *pci) + 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_dev *pci) + 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; |