📄 vwsnd.c
字号:
/* 4. Put the conversion resources into standby. */ ad1843_write_bits(lith, &ad1843_PDNI, 0); later = jiffies + HZ / 2; /* roughly half a second */ DBGDO(shut_up++); while (ad1843_read_bits(lith, &ad1843_PDNO)) { if (jiffies > later) { printk(KERN_ERR "vwsnd audio: AD1843 won't power up\n"); return -EIO; } schedule(); } DBGDO(shut_up--); /* 5. Power up the clock generators and enable clock output pins. */ ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1); /* 6. Configure conversion resources while they are in standby. */ /* DAC1 uses clock 1 as source, ADC uses clock 2. Always. */ ad1843_write_multi(lith, 3, &ad1843_DA1C, 1, &ad1843_ADLC, 2, &ad1843_ADRC, 2); /* 7. Enable conversion resources. */ ad1843_write_bits(lith, &ad1843_ADTLK, 1); ad1843_write_multi(lith, 5, &ad1843_ANAEN, 1, &ad1843_AAMEN, 1, &ad1843_DA1EN, 1, &ad1843_ADLEN, 1, &ad1843_ADREN, 1); /* 8. Configure conversion resources while they are enabled. */ ad1843_write_bits(lith, &ad1843_DA1C, 1); /* Unmute all channels. */ ad1843_set_outsrc(lith, (SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)); ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0); /* Set default recording source to Line In and set * mic gain to +20 dB. */ ad1843_set_recsrc(lith, SOUND_MASK_LINE); ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1); /* Set Speaker Out level to +/- 4V and unmute it. */ ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0); return 0;}/*****************************************************************************//* PCM I/O */#define READ_INTR_MASK (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW)#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW)typedef enum vwsnd_port_swstate { /* software state */ SW_OFF, SW_INITIAL, SW_RUN, SW_DRAIN,} vwsnd_port_swstate_t;typedef enum vwsnd_port_hwstate { /* hardware state */ HW_STOPPED, HW_RUNNING,} vwsnd_port_hwstate_t;/* * These flags are read by ISR, but only written at baseline. */typedef enum vwsnd_port_flags { DISABLED = 1 << 0, ERFLOWN = 1 << 1, /* overflown or underflown */ HW_BUSY = 1 << 2,} vwsnd_port_flags_t;/* * vwsnd_port is the per-port data structure. Each device has two * ports, one for input and one for output. * * Locking: * * port->lock protects: hwstate, flags, swb_[iu]_avail. * * devc->io_sema protects: swstate, sw_*, swb_[iu]_idx. * * everything else is only written by open/release or * pcm_{setup,shutdown}(), which are serialized by a * combination of devc->open_sema and devc->io_sema. */typedef struct vwsnd_port { spinlock_t lock; wait_queue_head_t queue; vwsnd_port_swstate_t swstate; vwsnd_port_hwstate_t hwstate; vwsnd_port_flags_t flags; int sw_channels; int sw_samplefmt; int sw_framerate; int sample_size; int frame_size; unsigned int zero_word; /* zero for the sample format */ int sw_fragshift; int sw_fragcount; int sw_subdivshift; unsigned int hw_fragshift; unsigned int hw_fragsize; unsigned int hw_fragcount; int hwbuf_size; unsigned long hwbuf_paddr; unsigned long hwbuf_vaddr; caddr_t hwbuf; /* hwbuf == hwbuf_vaddr */ int hwbuf_max; /* max bytes to preload */ caddr_t swbuf; unsigned int swbuf_size; /* size in bytes */ unsigned int swb_u_idx; /* index of next user byte */ unsigned int swb_i_idx; /* index of next intr byte */ unsigned int swb_u_avail; /* # bytes avail to user */ unsigned int swb_i_avail; /* # bytes avail to intr */ dma_chan_t chan; /* Accounting */ int byte_count; int frag_count; int MSC_offset;} vwsnd_port_t;/* vwsnd_dev is the per-device data structure. */typedef struct vwsnd_dev { struct vwsnd_dev *next_dev; int audio_minor; /* minor number of audio device */ int mixer_minor; /* minor number of mixer device */ struct semaphore open_sema; struct semaphore io_sema; struct semaphore mix_sema; mode_t open_mode; wait_queue_head_t open_wait; lithium_t lith; vwsnd_port_t rport; vwsnd_port_t wport;} vwsnd_dev_t;static vwsnd_dev_t *vwsnd_dev_list; /* linked list of all devices */static atomic_t vwsnd_use_count = ATOMIC_INIT(0);# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count))# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count))# define IN_USE (atomic_read(&vwsnd_use_count) != 0)/* * Lithium can only DMA multiples of 32 bytes. Its DMA buffer may * be up to 8 Kb. This driver always uses 8 Kb. * * Memory bug workaround -- I'm not sure what's going on here, but * somehow pcm_copy_out() was triggering segv's going on to the next * page of the hw buffer. So, I make the hw buffer one size bigger * than we actually use. That way, the following page is allocated * and mapped, and no error. I suspect that something is broken * in Cobalt, but haven't really investigated. HBO is the actual * size of the buffer, and HWBUF_ORDER is what we allocate. */#define HWBUF_SHIFT 13#define HWBUF_SIZE (1 << HWBUF_SHIFT)# define HBO (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0)# define HWBUF_ORDER (HBO + 1) /* next size bigger */#define MIN_SPEED 4000#define MAX_SPEED 49000#define MIN_FRAGSHIFT (DMACHUNK_SHIFT + 1)#define MAX_FRAGSHIFT (PAGE_SHIFT)#define MIN_FRAGSIZE (1 << MIN_FRAGSHIFT)#define MAX_FRAGSIZE (1 << MAX_FRAGSHIFT)#define MIN_FRAGCOUNT(fragsize) 3#define MAX_FRAGCOUNT(fragsize) (32 * PAGE_SIZE / (fragsize))#define DEFAULT_FRAGSHIFT 12#define DEFAULT_FRAGCOUNT 16#define DEFAULT_SUBDIVSHIFT 0/* * The software buffer (swbuf) is a ring buffer shared between user * level and interrupt level. Each level owns some of the bytes in * the buffer, and may give bytes away by calling swb_inc_{u,i}(). * User level calls _u for user, and interrupt level calls _i for * interrupt. * * port->swb_{u,i}_avail is the number of bytes available to that level. * * port->swb_{u,i}_idx is the index of the first available byte in the * buffer. * * Each level calls swb_inc_{u,i}() to atomically increment its index, * recalculate the number of bytes available for both sides, and * return the number of bytes available. Since each side can only * give away bytes, the other side can only increase the number of * bytes available to this side. Each side updates its own index * variable, swb_{u,i}_idx, so no lock is needed to read it. * * To query the number of bytes available, call swb_inc_{u,i} with an * increment of zero. */static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc){ if (inc) { port->swb_u_idx += inc; port->swb_u_idx %= port->swbuf_size; port->swb_u_avail -= inc; port->swb_i_avail += inc; } return port->swb_u_avail;}static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc){ unsigned long flags; unsigned int ret; spin_lock_irqsave(&port->lock, flags); { ret = __swb_inc_u(port, inc); } spin_unlock_irqrestore(&port->lock, flags); return ret;}static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc){ if (inc) { port->swb_i_idx += inc; port->swb_i_idx %= port->swbuf_size; port->swb_i_avail -= inc; port->swb_u_avail += inc; } return port->swb_i_avail;}static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc){ unsigned long flags; unsigned int ret; spin_lock_irqsave(&port->lock, flags); { ret = __swb_inc_i(port, inc); } spin_unlock_irqrestore(&port->lock, flags); return ret;}/* * pcm_setup - this routine initializes all port state after * mode-setting ioctls have been done, but before the first I/O is * done. * * Locking: called with devc->io_sema held. * * Returns 0 on success, -errno on failure. */static int pcm_setup(vwsnd_dev_t *devc, vwsnd_port_t *rport, vwsnd_port_t *wport){ vwsnd_port_t *aport = rport ? rport : wport; int sample_size; unsigned int zero_word; DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); ASSERT(aport != NULL); if (aport->swbuf != NULL) return 0; switch (aport->sw_samplefmt) { case AFMT_MU_LAW: sample_size = 1; zero_word = 0xFFFFFFFF ^ 0x80808080; break; case AFMT_A_LAW: sample_size = 1; zero_word = 0xD5D5D5D5 ^ 0x80808080; break; case AFMT_U8: sample_size = 1; zero_word = 0x80808080; break; case AFMT_S8: sample_size = 1; zero_word = 0x00000000; break; case AFMT_S16_LE: sample_size = 2; zero_word = 0x00000000; break; default: sample_size = 0; /* prevent compiler warning */ zero_word = 0; ASSERT(0); } aport->sample_size = sample_size; aport->zero_word = zero_word; aport->frame_size = aport->sw_channels * aport->sample_size; aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift; aport->hw_fragsize = 1 << aport->hw_fragshift; aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift; ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE); ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE); ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize)); ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize)); if (rport) { int hwfrags, swfrags; rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; hwfrags = rport->hwbuf_max >> aport->hw_fragshift; swfrags = aport->hw_fragcount - hwfrags; if (swfrags < 2) swfrags = 2; rport->swbuf_size = swfrags * aport->hw_fragsize; DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); DBGPV("read hwbuf_max = %d, swbuf_size = %d\n", rport->hwbuf_max, rport->swbuf_size); } if (wport) { int hwfrags, swfrags; int total_bytes = aport->hw_fragcount * aport->hw_fragsize; wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; if (wport->hwbuf_max > total_bytes) wport->hwbuf_max = total_bytes; hwfrags = wport->hwbuf_max >> aport->hw_fragshift; DBGPV("hwfrags = %d\n", hwfrags); swfrags = aport->hw_fragcount - hwfrags; if (swfrags < 2) swfrags = 2; wport->swbuf_size = swfrags * aport->hw_fragsize; DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); DBGPV("write hwbuf_max = %d, swbuf_size = %d\n", wport->hwbuf_max, wport->swbuf_size); } aport->swb_u_idx = 0; aport->swb_i_idx = 0; aport->byte_count = 0; /* * Is this a Cobalt bug? We need to make this buffer extend * one page further than we actually use -- somehow memcpy * causes an exceptoin otherwise. I suspect there's a bug in * Cobalt (or somewhere) where it's generating a fault on a * speculative load or something. Obviously, I haven't taken * the time to track it down. */ aport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); if (!aport->swbuf) return -ENOMEM; if (rport && wport) { ASSERT(aport == rport); ASSERT(wport->swbuf == NULL); /* One extra page - see comment above. */ wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); if (!wport->swbuf) { vfree(aport->swbuf); aport->swbuf = NULL; return -ENOMEM; } wport->sample_size = rport->sample_size; wport->zero_word = rport->zero_word; wport->frame_size = rport->frame_size; wport->hw_fragshift = rport->hw_fragshift; wport->hw_fragsize = rport->hw_fragsize; wport->hw_fragcount = rport->hw_fragcount; wport->swbuf_size = rport->swbuf_size; wport->hwbuf_max = rport->hwbuf_max; wport->swb_u_idx = rport->swb_u_idx; wport->swb_i_idx = rport->swb_i_idx; wport->byte_count = rport->byte_count; } if (rport) { rport->swb_u_avail = 0; rport->swb_i_avail = rport->swbuf_size; rport->swstate = SW_RUN; li_setup_dma(&rport->chan, &li_comm1, &devc->lith, rport->hwbuf_paddr, HWBUF_SHIFT, rport->hw_fragshift, rport->sw_channels, rport->sample_size); ad1843_setup_adc(&devc->lith, rport->sw_framerate, rport->sw_samplefmt, rport->sw_channels); li_enable_interrupts(&devc->lith, READ_INTR_MASK); if (!(rport->flags & DISABLED)) { ustmsc_t ustmsc; rport->hwstate = HW_RUNNING; li_activate_dma(&rport->chan); li_read_USTMSC(&rport->chan, &ustmsc); rport->MSC_offset = ustmsc.msc; } } if (wport) { if (wport->hwbuf_max > wport->swbuf_size) wport->hwbuf_max = wport->swbuf_size; wport->flags &= ~ERFLOWN; wport->swb_u_avail = wport->swbuf_size; wport->swb_i_avail = 0; wport->swstate = SW_RUN; li_setup_dma(&wport->chan, &li_comm2, &devc->lith, wport->hwbuf_paddr, HWBUF_SHIFT, wport->hw_fragshift, wport->sw_channels,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -