660 likes | 810 Views
14. AC 97 디바이스 드라이버. 목차. 14.1 Sound 용 Codec chip architecture 14.1.1 CS4202 14.1.2 AC’97 Controller Unit 14.2 Sound Device Driver 의 원리 14.3 Audio 구동 13.3.1 Mixer 13.3.2 madplayer 14.4 OSS(Open Sound System) Interface 를 이용한 Sound Programming. 14.1.1 CS4202.
E N D
목차 14.1 Sound 용 Codec chip architecture 14.1.1 CS4202 14.1.2 AC’97 Controller Unit 14.2 Sound Device Driver의 원리 14.3 Audio 구동 13.3.1 Mixer 13.3.2 madplayer 14.4 OSS(Open Sound System) Interface를 이용한 Sound Programming www.huins.com
14.1.1 CS4202 www.huins.com
14.1.2 AC’97 Controller Unit • AC-Link www.huins.com
14.1.2 AC’97 Controller Unit • Audio Frame www.huins.com
14.1.2 AC’97 Controller Unit • AC-link Audio Output Frame(SDATA_OUT) A new audio output frame begins with a low-to-high SYNC transition synchronous to BITCLK’s rising edge. BITCLK’s falling edge immediately follows and AC’97 samples SYNC’s assertion. BITCLK’s falling edge marks the instance that AC-link’s sides are each aware that a new audio frame has started. On BITCLK’s next rising edge, the ACUNIT transitions SDATA_OUT into the slot 0’s first bit position (valid frame bit). Each new bit position is presented to AC-link on a BITCLK rising edge and then sampled by AC’97 on the following BITCLK falling edge. This sequence ensures that data transitions and subsequent sample points for both incoming and outgoing data streams are time aligned. www.huins.com
14.1.2 AC’97 Controller Unit • Start of Audio Output Frame www.huins.com
14.1.2 AC’97 Controller Unit • AC-link Audio Input Frame (SDATA_IN) www.huins.com
14.1.2 AC’97 Controller Unit • Start of an Audio Input Frame www.huins.com
14.1.2 AC’97 Controller Unit • Feature List The processor ACUNIT supports the following AC’97 features: • Independent channels for Stereo PCM In, Stereo PCM out, modem-out, modem-in and mono mic-in. • All of the above channels support only 16-bit samples in hardware. Samples less than 16 bits are supported through software. www.huins.com
14.1.2 AC’97 Controller Unit www.huins.com
14.1.2 AC’97 Controller Unit • Signal Configuration Steps 1. Configure SYNC and SDATA_OUT as outputs. 2. Configure BITCLK, SDATA_IN_0, and SDATA_IN_1 as inputs. 3. nACRESET is a dedicated output. It remains asserted on power-up. Complete these steps to deassert nACRESET: a. Configure the other AC’97 signals as previously described. b. In the Global Control Register(GCR), Set the GCR[COLD_RST]bit. www.huins.com
14.1.2 AC’97 Controller Unit • Signal Configuration Steps(Cont’) www.huins.com
14.1.2 AC’97 Controller Unit • Waking up the AC-link www.huins.com
14.1.2 AC’97 Controller Unit • Waking up the AC-link(Cont’) • To wake up the AC-link, a CODEC drives SDATA_IN to a logic high level. The rising edge triggers the Resume Interrupt if that CODEC’s resume enable bit is set to a one. • The CPU then wakes up the CODEC using the cold or warm reset sequence. The ACUNIT uses a warm reset to wake up the primary CODEC. The CODEC detects a warm reset when SYNC is driven high for a minimum of one microsecond and the BITCLK is absent. • The CODEC must wait until it samples SYNC low before it can start BITCLK. The CODEC that signaled the wake event must keep its SDATA_IN high until it detects that a warm reset has been completed. The CODEC can then transition its SDATA_IN low. www.huins.com
14.1.2 AC’97 Controller Unit • Codec Access Register www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 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); waitingForMask=GSR_SDONE; init_completion(&CAR_completion); //start read access across the ac97 link (void)*reg_addr; wait_for_completion(&CAR_completion); 0x40500200 : primary audio codec 1 << 18 done = 0 wait = null www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) • Each CODEC has up to sixty-four 16-bit registers that are addressable internal to the CODEC at half-word boundaries(16-bit boundaries). Because the processor only supports internal register accesses at word boundaries (32-bit boundaries), software must select the one of the following formulas to translate a 7-bit CODEC address into a 32-bit processor address: • Processor physical address for a Primary Audio CODEC = 0x4050-0200 + Shift_Left_Once(Internal 7-bit CODEC Register Address) • Processor physical address for a Secondary Audio CODEC = 0x4050-0300 + Shift_Left_Once(Internal 7-bit CODEC Register Address) • Processor physical address for a Primary Modem CODEC = 0x4050-0400 + Shift_Left_Once(Internal 7-bit CODEC Register Address) • Processor physical address for a Secondary Modem CODEC = 0x4050-0500 + Shift_Left_Once(Internal 7-bit CODEC Register Address) www.huins.com
14.2 Sound Device Driver의 원리 • Global Status Register(GSR) www.huins.com
14.2 Sound Device Driver의 원리 cleared by software writing a ‘1’ to this location • pxa-ac97.c source 분석 (Cont’) if (GSR & GSR_RDCS) { GSR |= GSR_RDCS; printk(KERN_CRIT __FUNCTION__": read codec register timeout.\n"); } init_completion(&CAR_completion); val = *reg_addr; wait_for_completion(&CAR_completion); } else { printk(KERN_CRIT __FUNCTION__": CAR_CAIP already set\n"); } up(&CAR_mutex); return val; } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) 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); waitingForMask=GSR_CDONE; 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); } 0x40500200 : primary audio codec 1 << 19 www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) static void pxa_ac97_irq(int irq, void *dev_id, struct pt_regs *regs) { int gsr = GSR; GSR = gsr & (GSR_SDONE|GSR_CDONE); if (gsr & waitingForMask) { complete(&CAR_completion); } } 0x4050001C GSCI = 1 www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) 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; www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) 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; www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) 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(); } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) ret = ac97_probe_codec(&pxa_ac97_codec); if (ret != 1) { free_irq(IRQ_AC97, NULL); GCR = GCR_ACLINK_OFF; CKEN &= ~CKEN2_AC97; return ret; } pxa_ac97_write(&pxa_ac97_codec,AC97_EXTENDED_STATUS,1); 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; } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) 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); www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) static int ac97_audio_open(struct inode *inode, struct file *file) { return pxa_audio_attach(inode, file, &ac97_audio_state); } static struct file_operations ac97_audio_fops = { open: ac97_audio_open, owner: THIS_MODULE }; www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) 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; } Minor number를 자동으로 잡아준다. www.huins.com
14.2 Sound Device Driver의 원리 • pxa-ac97.c source 분석 (Cont’) 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); Insmod실행 시 호출 : 모듈을 커널에 등록 rmmod시 호출 www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석 void pxa_audio_clear_buf(audio_stream_t * s) { DECLARE_WAITQUEUE(wait, current); int frag; if (!s->buffers) return; /* Ensure DMA isn't running */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&s->stop_wq, &wait); DCSR(s->dma_ch) = DCSR_STOPIRQEN; schedule(); remove_wait_queue(&s->stop_wq, &wait); Set to true whe thread is in WaitForSingleObject Audio buffer array Stop interrupt enbale(read/write) dma chnnel number www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) /* free DMA buffers */ for (frag = 0; frag < s->nbfrags; frag++) { audio_buf_t *b = &s->buffers[frag]; if (!b->master) continue; consistent_free(b->data, b->master, b->dma_desc->dsadr); } /* free descriptor ring */ if (s->buffers->dma_desc) consistent_free(s->buffers->dma_desc, s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE, s->dma_desc_phys); /* free buffer structure array */ kfree(s->buffers); s->buffers = NULL; } Buffer를 clear 한다는 메시지와 함께 buffer를 free한다. www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) static int audio_set_fragments(audio_stream_t *s, int val) { if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN) return -EBUSY; if (s->buffers) audio_clear_buf(s); s->nbfrags = (val >> 16) & 0x7FFF; val &= 0xffff; if (val < 5) val = 5; if (val > 15) val = 15; Device or resource is busy 19 Number of fragments www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) s->fragsize = 1 << val; if (s->nbfrags < 2) s->nbfrags = 2; if (s->nbfrags * s->fragsize > 256 * 1024) s->nbfrags = 256 * 1024 / s->fragsize; if (audio_setup_buf(s)) return -ENOMEM; return val|(s->nbfrags << 16); } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) static int audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) { const char *buffer0 = buffer; audio_state_t *state = (audio_state_t *)file->private_data; audio_stream_t *s = state->output_stream; int chunksize, ret = 0; if (ppos != &file->f_pos) return -ESPIPE; if (s->mapped) return -ENXIO; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; Illegal seek No such device or address Out of memory www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_frag]; /* Grab a fragment */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&s->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&s->sem)) break; } /* Feed the current buffer */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; 00004 Try again : 35 www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) if (copy_from_user(b->data + b->offset, buffer, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize; buffer += chunksize; count -= chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } /*activate DMA on current buffer*/ b->offset = 0; b->dma_desc->ddadr &= ~DDADR_STOP; if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { DDADR(s->dma_ch) = b->dma_desc->ddadr; DCSR(s->dma_ch) = DCSR_RUN; } Bad address Unlock this fragment’s checkpoint descriptor and kick DMA if it is idle. Using checkpoint descriptors allows for control operations without the need for stopping the DMA channel if it is already running. www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) /* move the index to the next fragment */ if (++s->usr_frag >= s->nbfrags) s->usr_frag = 0; } if ((buffer - buffer0)) ret = buffer - buffer0; return ret; } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) static int audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) { char *buffer0 = buffer; audio_state_t *state = file->private_data; audio_stream_t *s = state->input_stream; int chunksize, ret = 0; if (ppos != &file->f_pos) return -ESPIPE; if (s->mapped) return -ENXIO; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_frag]; /* prime DMA */ if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { DDADR(s->dma_ch) = s->buffers[s->dma_frag].dma_desc->ddadr; DCSR(s->dma_ch) = DCSR_RUN; } /* Wait for a buffer to become full */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&s->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&s->sem)) break; } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) /* Grab data from current buffer */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; if (copy_to_user(buffer, b->data + b->offset, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize; buffer += chunksize; count -= chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) b->offset = 0; b->dma_desc->ddadr &= ~DDADR_STOP; /* move the index to the next fragment */ if (++s->usr_frag >= s->nbfrags) s->usr_frag = 0; } if ((buffer - buffer0)) ret = buffer - buffer0; return ret; } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) static int audio_release(struct inode *inode, struct file *file) { audio_state_t *state = file->private_data; down(&state->sem); if (file->f_mode & FMODE_READ) { audio_clear_buf(state->input_stream); *state->input_stream->drcmr = 0; pxa_free_dma(state->input_stream->dma_ch); state->rd_ref = 0; } 1 DMA request channel to use DMA channel number www.huins.com
14.2 Sound Device Driver의 원리 2 • pxa-audio.c source 분석(cont’d) if (file->f_mode & FMODE_WRITE) { audio_sync(file); audio_clear_buf(state->output_stream); *state->output_stream->drcmr = 0; pxa_free_dma(state->output_stream->dma_ch); state->wr_ref = 0; } up(&state->sem); return 0; } www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) int pxa_audio_attach(struct inode *inode, struct file *file, audio_state_t *state) { audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; int err; down(&state->sem); /* access control */ err = -ENODEV; if ((file->f_mode & FMODE_WRITE) && !os) goto out; if ((file->f_mode & FMODE_READ) && !is) goto out; No such device www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) err = -EBUSY; if ((file->f_mode & FMODE_WRITE) && state->wr_ref) goto out; if ((file->f_mode & FMODE_READ) && state->rd_ref) goto out; /* request DMA channels */ if (file->f_mode & FMODE_WRITE) { err = pxa_request_dma(os->name, DMA_PRIO_LOW, audio_dma_irq, os); if (err < 0) goto out; os->dma_ch = err; } 8 www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) if (file->f_mode & FMODE_READ) { err = pxa_request_dma(is->name, DMA_PRIO_LOW, audio_dma_irq, is); if (err < 0) { if (file->f_mode & FMODE_WRITE) { *os->drcmr = 0; pxa_free_dma(os->dma_ch); } goto out; } is->dma_ch = err; } file->private_data = state; file->f_op->release = audio_release; file->f_op->write = audio_write; file->f_op->read = audio_read; file->f_op->mmap = audio_mmap; file->f_op->poll = audio_poll; file->f_op->ioctl = audio_ioctl; file->f_op->llseek = no_llseek; www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) if ((file->f_mode & FMODE_WRITE)) { state->wr_ref = 1; os->fragsize = AUDIO_FRAGSIZE_DEFAULT; os->nbfrags = AUDIO_NBFRAGS_DEFAULT; os->output = 1; os->mapped = 0; init_waitqueue_head(&os->frag_wq); init_waitqueue_head(&os->stop_wq); *os->drcmr = os->dma_ch | DRCMR_MAPVLD; } if (file->f_mode & FMODE_READ) { state->rd_ref = 1; is->fragsize = AUDIO_FRAGSIZE_DEFAULT; is->nbfrags = AUDIO_NBFRAGS_DEFAULT; is->output = 0; is->mapped = 0; init_waitqueue_head(&is->frag_wq); init_waitqueue_head(&is->stop_wq); *is->drcmr = is->dma_ch | DRCMR_MAPVLD; } 8192 8 www.huins.com
14.2 Sound Device Driver의 원리 • pxa-audio.c source 분석(cont’d) err = 0; out: up(&state->sem); return err; } EXPORT_SYMBOL(pxa_audio_attach); EXPORT_SYMBOL(pxa_audio_clear_buf); www.huins.com
14.3 Audio 구동 • mixer • /dev 디렉토리에 가면 mixer가 있음을 확인할 수 있다. www.huins.com