/* * linux/drivers/sound/pxa-ac97.c -- AC97 interface for the Cotula chip * * Author: Nicolas Pitre * Created: Aug 15, 2001 * Copyright: MontaVista Software Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pxa-audio.h" /* * The codec register read operation requires 3 read cycles on PXA250 in order * to guarrantee that good read data can be returned. * _ _ _ _ *sync: _____| |_addr______| |_data1______| |__data2___| |__data_n__ *SDONE:__ _ _ _______________ * |_addr1____| |__addr2_____| |__addr_n____| * ^ * First read begins * ^ SDONE usually goes true in the latter half of AC link frame * ^ Second read begins, but data from codec hasn't arrived yet! * ^ second read ends, from 1 to 3 frames AFTER frame * in which the address goes out! * ^ Third read begins from one to 3 frames after the * initial frame, data from codec guarranteed to be * available by this time. * ^ read cycle ends. * Note how reads can be pipelined, possibly useful for reading touch panel * control registers or rapid sampling of codec gpio lines. */ static struct completion CAR_completion; static DECLARE_MUTEX(CAR_mutex); static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg) { u16 val = -1; down(&CAR_mutex); if (!(CAR & CAR_CAIP)) { volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); init_completion(&CAR_completion); (void)*reg_addr; wait_for_completion(&CAR_completion); init_completion(&CAR_completion); (void)*reg_addr; // This initiates second read cycle, but codec data isn't here yet... wait_for_completion(&CAR_completion); if (GSR & GSR_RDCS) { GSR |= GSR_RDCS; printk(KERN_CRIT __FUNCTION__": read codec register timeout.\n"); } init_completion(&CAR_completion); val = *reg_addr; // ahh, that's better. But we've just started another cycle... wait_for_completion(&CAR_completion); //this, along with trailing delay, avoids hassle with CAR_CAIP bit udelay(20); //don't come back too soon... } else { printk(KERN_CRIT __FUNCTION__": CAR_CAIP already set\n"); } up(&CAR_mutex); //printk("%s(0x%02x) = 0x%04x\n", __FUNCTION__, reg, val); return val; } static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) { down(&CAR_mutex); if (!(CAR & CAR_CAIP)) { volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); init_completion(&CAR_completion); *reg_addr = val; wait_for_completion(&CAR_completion); } else { printk(KERN_CRIT __FUNCTION__": CAR_CAIP already set\n"); } up(&CAR_mutex); //printk("%s(0x%02x, 0x%04x)\n", __FUNCTION__, reg, val); } static void pxa_ac97_irq(int irq, void *dev_id, struct pt_regs *regs) { if (GSR & (GSR_SDONE|GSR_CDONE)) { GSR = GSR_SDONE|GSR_CDONE; complete(&CAR_completion); } } static struct ac97_codec pxa_ac97_codec = { codec_read: pxa_ac97_read, codec_write: pxa_ac97_write, }; static DECLARE_MUTEX(pxa_ac97_mutex); static int pxa_ac97_refcount; int pxa_ac97_get(struct ac97_codec **codec) { int ret; *codec = NULL; down(&pxa_ac97_mutex); if (!pxa_ac97_refcount) { ret = request_irq(IRQ_AC97, pxa_ac97_irq, 0, "AC97", NULL); if (ret) return ret; CKEN |= CKEN2_AC97; set_GPIO_mode(GPIO31_SYNC_AC97_MD); set_GPIO_mode(GPIO30_SDATA_OUT_AC97_MD); set_GPIO_mode(GPIO28_BITCLK_AC97_MD); set_GPIO_mode(GPIO29_SDATA_IN_AC97_MD); GCR = 0; udelay(10); GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; while (!(GSR & GSR_PCR)) { schedule(); } ret = ac97_probe_codec(&pxa_ac97_codec); if (ret != 1) { free_irq(IRQ_AC97, NULL); GCR = GCR_ACLINK_OFF; CKEN &= ~CKEN2_AC97; return ret; } // need little hack for UCB1400 (should be moved elsewhere) pxa_ac97_write(&pxa_ac97_codec,AC97_EXTENDED_STATUS,1); //pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x1ff7); pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050); pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030); } pxa_ac97_refcount++; up(&pxa_ac97_mutex); *codec = &pxa_ac97_codec; return 0; } void pxa_ac97_put(void) { down(&pxa_ac97_mutex); pxa_ac97_refcount--; if (!pxa_ac97_refcount) { GCR = GCR_ACLINK_OFF; CKEN &= ~CKEN2_AC97; free_irq(IRQ_AC97, NULL); } up(&pxa_ac97_mutex); } EXPORT_SYMBOL(pxa_ac97_get); EXPORT_SYMBOL(pxa_ac97_put); /* * Audio Mixer stuff */ static int mixer_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg); } static struct file_operations mixer_fops = { ioctl: mixer_ioctl, llseek: no_llseek, owner: THIS_MODULE }; /* * AC97 codec ioctls */ static int codec_adc_rate = 48000; static int codec_dac_rate = 48000; static int ac97_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int ret; long val; switch(cmd) { case SNDCTL_DSP_STEREO: ret = get_user(val, (int *) arg); if (ret) return ret; /* FIXME: do we support mono? */ ret = (val == 0) ? -EINVAL : 1; return put_user(ret, (int *) arg); case SNDCTL_DSP_CHANNELS: case SOUND_PCM_READ_CHANNELS: /* FIXME: do we support mono? */ return put_user(2, (long *) arg); case SNDCTL_DSP_SPEED: ret = get_user(val, (long *) arg); if (ret) return ret; if (file->f_mode & FMODE_READ) codec_adc_rate = ac97_set_adc_rate(&pxa_ac97_codec, val); if (file->f_mode & FMODE_WRITE) codec_dac_rate = ac97_set_dac_rate(&pxa_ac97_codec, val); /* fall through */ case SOUND_PCM_READ_RATE: if (file->f_mode & FMODE_READ) val = codec_adc_rate; if (file->f_mode & FMODE_WRITE) val = codec_dac_rate; return put_user(val, (long *) arg); case SNDCTL_DSP_SETFMT: case SNDCTL_DSP_GETFMTS: /* FIXME: can we do other fmts? */ return put_user(AFMT_S16_LE, (long *) arg); default: /* Maybe this is meant for the mixer (As per OSS Docs) */ return mixer_ioctl(inode, file, cmd, arg); } return 0; } /* * Audio stuff */ static audio_stream_t ac97_audio_out = { name: "AC97 audio out", dcmd: DCMD_TXPCDR, drcmr: &DRCMRTXPCDR, dev_addr: __PREG(PCDR), }; static audio_stream_t ac97_audio_in = { name: "AC97 audio in", dcmd: DCMD_RXPCDR, drcmr: &DRCMRRXPCDR, dev_addr: __PREG(PCDR), }; static audio_state_t ac97_audio_state = { output_stream: &ac97_audio_out, input_stream: &ac97_audio_in, client_ioctl: ac97_ioctl, sem: __MUTEX_INITIALIZER(ac97_audio_state.sem), }; static int ac97_audio_open(struct inode *inode, struct file *file) { return pxa_audio_attach(inode, file, &ac97_audio_state); } /* * Missing fields of this structure will be patched with the call * to pxa_audio_attach(). */ static struct file_operations ac97_audio_fops = { open: ac97_audio_open, owner: THIS_MODULE }; static int __init pxa_ac97_init(void) { int ret; struct ac97_codec *dummy; ret = pxa_ac97_get(&dummy); if (ret) return ret; ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); return 0; } static void __exit pxa_ac97_exit(void) { unregister_sound_dsp(ac97_audio_state.dev_dsp); unregister_sound_mixer(pxa_ac97_codec.dev_mixer); pxa_ac97_put(); } module_init(pxa_ac97_init); module_exit(pxa_ac97_exit);