blob: 6b89584ab665a50256c7243f0890f7803ed6db62 [file] [log] [blame]
/*
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
*
* 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.
*
* Peter Shepherd
*/
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/sound.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/bitops.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/ac97_codec.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include <asm/io.h>
#include <asm/arc_ac97.h>
/* --------------------------------------------------------------------- */
#undef OSS_DOCUMENTED_MIXER_SEMANTICS
//#define ARC_AC97_DEBUG
//#define ARC_AC97_VERBOSE_DEBUG
#define ARC_AC97_MODULE_NAME "ARC_AC97"
#define PFX ARC_AC97_MODULE_NAME
#define mem_map_reserve(p) set_bit(PG_reserved, &((p)->flags))
#define mem_map_unreserve(p) clear_bit(PG_reserved, &((p)->flags))
#ifdef ARC_AC97_DEBUG
#define dbg(fn, format, arg...) printk(KERN_DEBUG "%s : " format "\n" , fn, arg)
#define DBG_TRACE(arg...) printk(arg)
#else
#define dbg(format, arg...) do {} while (0)
#define DBG_TRACE(arg...)
#endif
#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
/* misc stuff */
#define POLL_COUNT 0x5000
#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
/* Boot options */
static int vra = 1; // 0 = no VRA, 1 = use VRA if codec supports it
//MODULE_PARM(vra, "i");
//MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
/* Controller */
static volatile ARC_AC97_if* controller= (ARC_AC97_if *) AC97_CONTROLLER_BASE;
/* --------------------------------------------------------------------- */
static unsigned long tmpmask; /* OK, being dodgy here - assuming only one instance of the interface */
static unsigned long flags;
// Debugging counts
static int written = 0;
static int output = 0;
static int input = 0;
static int queued = 0;
// Set if the controller has the version 2 features;- ability to query queue lengths, extra status, etc.
static int extended_controller;
// Sample format conversion scratchpad
static unsigned char tmpbuf[TMPBUF_SIZE];
#define SAMPLE_SIZE 2
struct arc_ac97_state {
/* soundcore stuff */
int dev_audio;
/* Active FIFO interrupts */
unsigned long intrs;
#ifdef ARC_AC97_DEBUG
/* debug /proc entry */
struct proc_dir_entry *ps;
struct proc_dir_entry *ac97_ps;
#endif /* ARC_AC97_DEBUG */
struct ac97_codec *codec;
unsigned int codec_base_caps;// AC'97 reg 00h, "Reset Register"
unsigned int codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID"
int no_vra; // do not use VRA
spinlock_t lock;
struct semaphore open_sem;
mode_t open_mode;
wait_queue_head_t open_wait;
struct device_buf {
struct semaphore sem;
unsigned int is_play; // 0 -> capture, 1 -> play
unsigned int sample_rate; // Hz
unsigned int sample_size; // 8 or 16
int num_channels; // 1 = mono, 2 = stereo, 4, 6
int bytes_per_sample; // DMA bytes per audio sample frame
unsigned int fifo_depth;
unsigned int transfer_size;
unsigned int bufsize;
unsigned int readpos;
unsigned int writepos;
unsigned short* buffer;
unsigned int error; // over/underrun
wait_queue_head_t wait;
unsigned int latency;
unsigned int threshold; // Number of samples required before playback starts
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
unsigned stopped:1;
unsigned int subdivision;
unsigned int sample_count;
int interrupted;
int packed;
} play, capture;
} arc_ac97_state;
/* --------------------------------------------------------------------- */
static void arc_ac97_delay(int msec)
{
DBG_TRACE("%s\n",__FUNCTION__);
__udelay(msec * 1000); // FIXME - want this to be better behaved, revert to schedule() loop
}
/* --------------------------------------------------------------------- */
/* The buffers used are linear only - neither read nor write offset
ever wraps round. This has two benefits; firstly, we only have to set
a lock on the buffer when we perform this shuffle; and secondly, it
simplifies the buffer handling code, as we never have to handle
wrap-round conditions and can safely assume that the read offset is
always <= write offset. The price of this arrangement is that we
frequently have to shuffle the contents of the buffer down in order
to maintain sufficient write space. This is done as infrequently as
possible, and only ever in user context. This routine must be called
with the buffer lock set, but - as said above - this is the only point
in the driver where we need to protect user and kernel code from
each other. Only problem is that this won't mmap out easily. */
static inline void pack_audio_buffer(struct device_buf *db)
{
if (db->readpos)
{
db->interrupted = 0;
db->packed = 1;
memcpy(db->buffer,(db->buffer + db->readpos),((db->writepos-db->readpos)*SAMPLE_SIZE));
db->writepos -= db->readpos;
db->readpos = 0;
if (db->interrupted != 0) {
printk("@@@\n");
db->interrupted = 0;
}
}
DBG_TRACE("%s readpos %d writepos %d\n",__FUNCTION__, db->readpos, db->writepos);
}
/* --------------------------------------------------------------------- */
static inline int audio_buffer_usage(struct device_buf *db)
{
return (db->writepos - db->readpos)*SAMPLE_SIZE;
}
/* --------------------------------------------------------------------- */
static inline int audio_buffer_free_space(struct device_buf *db)
{
return (db->bufsize - (db->writepos - db->readpos)*SAMPLE_SIZE);
}
/* --------------------------------------------------------------------- */
static inline int audio_buffer_writeable_space(struct device_buf *db)
{
return (db->bufsize/SAMPLE_SIZE - db->writepos);
}
/* --------------------------------------------------------------------- */
static inline int audio_buffer_readable_space(struct device_buf *db)
{
return (db->writepos - db->readpos);
}
/* --------------------------------------------------------------------- */
static void reset_audio_buffer(struct device_buf *db)
{
db->writepos = 0;
db->readpos = 0;
db->sample_count = 0;
}
/* --------------------------------------------------------------------- */
#define READPTR(db) (db->buffer + db->readpos)
#define WRITEPTR(db) (db->buffer + db->writepos)
/* --------------------------------------------------------------------- */
static inline u8 S16_TO_U8(s16 ch)
{
DBG_TRACE("%s\n",__FUNCTION__);
return (u8) (ch >> 8) + 0x80;
}
/* --------------------------------------------------------------------- */
static inline s16 U8_TO_S16(u8 ch)
{
DBG_TRACE("%s\n",__FUNCTION__);
return (s16) (ch - 0x80) << 8;
}
/* --------------------------------------------------------------------- */
static int translate_from_user(struct device_buf *db,
const char* userbuf,
int count)
{
unsigned char* thissample;
short outval;
unsigned short *bufptr;
int bytes;
// Should be no need to translate 16 bit samples, assuming endianness is already sorted out
if (db->sample_size == 16)
{
db->interrupted = 0;
count *= 2;
bytes = copy_from_user((&db->buffer[db->writepos]),userbuf,count);
count -= bytes; // copy_from_user returns no of bytes _not_ copied
count /= 2;
db->writepos += count;
if (db->interrupted != 0) {
printk("~~~\n");
db->interrupted = 0;
}
}
// Convert 8 bit samples
else if (db->sample_size == 8)
{
if (count > TMPBUF_SIZE) count = TMPBUF_SIZE;
if ((bytes = copy_from_user(tmpbuf,userbuf,count)) != 0) {
dbg(__FUNCTION__, ": fault copying %d samples",count);
return -EFAULT;
}
thissample = (unsigned char*) tmpbuf;
count -= bytes;
bufptr = &(db->buffer[db->writepos]);
for (bytes = 0 ; bytes < count; bytes++)
{
outval = *thissample;
outval -= 0x0080;
*bufptr = (outval << 8);
bufptr++;
thissample++;
db->writepos++;
}
db->writepos+=bytes;
}
else
count = 0;
DBG_TRACE("%s, translating %d samples of %d bit;s writepos %d\n", __FUNCTION__, count,
db->sample_size, db->writepos);
return count; // return number of samples copied
}
/* --------------------------------------------------------------------- */
static int translate_to_user(struct device_buf *db,
char* userbuf,
int count)
{
unsigned short* thissample;
unsigned char* thischar;
int bytes;
DBG_TRACE("%s, translating %d samples 0f %d bits userbuf 0x%x\n",__FUNCTION__,count,db->sample_size,userbuf);
// Should be no need to translate 16 bit samples, assuming endianness is already sorted out
if (db->sample_size == 16)
{
count *= 2;
bytes = copy_to_user(userbuf,(db->buffer+db->readpos),count);
count -= bytes;
count /= 2;
db->readpos += count;
}
// Convert 8 bit samples
else if (db->sample_size == 8)
{
thissample = (unsigned short*) tmpbuf;
thischar = (unsigned char*) db->buffer + db->readpos;
if (count > TMPBUF_SIZE) printk("Bollocks\n");
for (bytes = 0 ; bytes < count; bytes++)
{
*thissample = S16_TO_U8(*thischar);
thissample++;
thischar++;
db->readpos++;
}
if ((bytes = copy_to_user(userbuf,tmpbuf,count)) != 0) {
dbg(__FUNCTION__, ": fault copying %d samples",count);
return -EFAULT;
}
count -= bytes; // copy_from_user returns no. of bytes _not_ copied
}
else
count = 0;
DBG_TRACE("%s, translated %d samples of %d bits\n",__FUNCTION__,count,db->sample_size);
return count; // return number of samples copied
}
/* --------------------------------------------------------------------- */
static u16 rdcodec(struct ac97_codec *codec, u8 addr)
{
u32 cmd;
u16 data;
int failcount, retries;
DBG_TRACE("%s\n",__FUNCTION__);
// spin_lock_irqsave(&s->lock, flags);
tmpmask = controller->intr_mask;
controller->intr_mask &= ~STATUS_INTR;
// controller->intr_mask |= STATUS_INTR;
//controller->intr_mask = -1;
cmd = addr;
cmd <<= 16;
cmd |= CMD_READ;
retries = 0;
do {
failcount = 0;
controller->cmd = cmd;
do {
arc_ac97_delay(5);
cmd = controller->intr_id;
failcount++;
}
while ((! (cmd & STATUS_INTR)) && (failcount < 50));
retries++;
}
while ((! (cmd & STATUS_INTR)) && (retries < 5));
if (retries == 5) printk("Giving up on AC97 Codec read after 5 tries\n");
cmd = controller->status;
data = cmd & 0xffff;
controller->intr_mask = tmpmask;
// controller->intr_ctrl = INTR_ENABLE;
// spin_unlock_irqrestore(&s->lock, flags);
return data;
}
/* --------------------------------------------------------------------- */
static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
{
u32 cmd;
DBG_TRACE("%s\n",__FUNCTION__);
// spin_lock_irqsave(&s->lock, flags);
tmpmask = controller->intr_mask;
controller->intr_mask &= ~STATUS_INTR;
cmd = addr;
cmd <<= 16;
cmd &= 0xff0000;
cmd |= CMD_WRITE;
cmd |= data;
controller->cmd = cmd;
arc_ac97_delay(5);
controller->intr_mask = tmpmask;
// spin_unlock_irqrestore(&s->lock, flags);
}
/* --------------------------------------------------------------------- */
static void waitcodec(struct ac97_codec *codec)
{
u16 temp;
/* codec_wait is used to wait for a ready state after
an AC97C_RESET. */
tmpmask = controller->intr_mask;
controller->intr_mask &= ~STATUS_INTR;
// get AC'97 powerdown control/status register
temp = rdcodec(codec, AC97_POWER_CONTROL);
if (temp & 0x7f00) {
// Power on
wrcodec(codec, AC97_POWER_CONTROL, 0);
arc_ac97_delay(100);
// Reread
temp = rdcodec(codec, AC97_POWER_CONTROL);
}
// Check if Codec REF,ANL,DAC,ADC ready
if ((temp & 0x7f0f) != 0x000f)
err("codec reg 26 status (0x%x) not ready!!", temp);
controller->intr_mask = tmpmask;
}
/* --------------------------------------------------------------------- */
/* stop the ADC before calling */
static void set_capture_rate(struct arc_ac97_state *s, unsigned rate)
{
struct device_buf *capture = &s->capture;
struct device_buf *play = &s->play;
unsigned capture_rate, play_rate;
u16 ac97_extstat;
DBG_TRACE("%s\n",__FUNCTION__);
ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
rate = rate > 48000 ? 48000 : rate; // Shouldn't this be decided by the codec?
// enable VRA
wrcodec(s->codec, AC97_EXTENDED_STATUS,
ac97_extstat | AC97_EXTSTAT_VRA);
// now write the sample rate
wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
// read it back for actual supported rate
capture_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
#ifdef ARC_AC97_VERBOSE_DEBUG
dbg(__FUNCTION__, ": set to %d Hz", capture_rate);
#endif
// some codec's don't allow unequal DAC and ADC rates, in which case
// writing one rate reg actually changes both.
play_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
if (play->num_channels > 2)
wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, play_rate);
if (play->num_channels > 4)
wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, play_rate);
capture->sample_rate = capture_rate;
play->sample_rate = play_rate;
}
/* --------------------------------------------------------------------- */
/* stop the DAC before calling */
static void set_play_rate(struct arc_ac97_state *s, unsigned rate)
{
struct device_buf *play = &s->play;
struct device_buf *capture = &s->capture;
unsigned capture_rate, play_rate;
u16 ac97_extstat;
DBG_TRACE("%s new rate will be %d\n",__FUNCTION__,rate);
ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
rate = rate > 48000 ? 48000 : rate;
// enable VRA
wrcodec(s->codec, AC97_EXTENDED_STATUS,
ac97_extstat | AC97_EXTSTAT_VRA);
// now write the sample rate
wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
// I don't support different sample rates for multichannel,
// so make these channels the same.
if (play->num_channels > 2)
wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
if (play->num_channels > 4)
wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
// read it back for actual supported rate
play_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
#ifdef ARC_AC97_VERBOSE_DEBUG
dbg(__FUNCTION__, ": set to %d Hz", play_rate);
#endif
DBG_TRACE("%s confirming play rate set to %d\n",__FUNCTION__,play_rate);
// some codec's don't allow unequal DAC and ADC rates, in which case
// writing one rate reg actually changes both.
capture_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
play->sample_rate = play_rate;
capture->sample_rate = capture_rate;
}
/* --------------------------------------------------------------------- */
static void stop_play(struct arc_ac97_state *s)
{
struct device_buf *db = &s->play;
DBG_TRACE("%s\n",__FUNCTION__);
DBG_TRACE("%s written %d output %d queued %d\n",__FUNCTION__,written,output,queued);
if (db->stopped) {
return;
}
// spin_lock_irqsave(&s->lock, flags);
s->intrs &= ~PLAY_INTRS;
controller->intr_mask = s->intrs;
controller->intr_ctrl = INTR_CLEAR;
arc_ac97_delay(10);
controller->intr_ctrl = 0;
db->stopped = 1;
// spin_unlock_irqrestore(&s->lock, flags);
}
/* --------------------------------------------------------------------- */
static void stop_capture(struct arc_ac97_state *s)
{
struct device_buf *db = &s->capture;
DBG_TRACE("%s\n",__FUNCTION__);
if (db->stopped)
return;
// spin_lock_irqsave(&s->lock, flags);
s->intrs &= ~CAPTURE_INTRS;
controller->intr_mask = s->intrs;
controller->intr_ctrl = INTR_CLEAR;
arc_ac97_delay(10);
controller->intr_ctrl = 0;
db->stopped = 1;
// spin_unlock_irqrestore(&s->lock, flags);
}
/* --------------------------------------------------------------------- */
static unsigned long set_xmit_slots(int num_channels)
{
unsigned long intmask = 0;
DBG_TRACE("%s\n",__FUNCTION__);
switch (num_channels) {
case 1: // mono
case 2: // stereo, slots 3,4
controller->point1 = 3;
controller->point2 = 4;
controller->point3 = 0;
intmask = PLAY1_INTR;
controller->intr_mask = intmask;
break;
case 4: // stereo with surround, slots 3,4,7,8
case 6: // stereo with surround and center/LFE, slots 3,4,6,7,8,9
default:
/* Only 3 slots available, so can't do. */
break;
}
return intmask;
}
/* --------------------------------------------------------------------- */
static unsigned long set_recv_slots(int num_channels)
{
// Slot mapping set up by set_xmit_slots
// FIXME no capture interrupts just yet.
DBG_TRACE("%s\n",__FUNCTION__);
return 0;
}
/* --------------------------------------------------------------------- */
// Blast a set of samples into one of the FIFOs - FIXME use outw?
static inline int c_write_mono_to_controller(volatile unsigned long* play1, volatile unsigned long* play2, unsigned short *data, int limit) {
int i = limit;
while (i--) {
*play1 = *data;
*play2 = *data;
data++;
}
return limit;
}
static inline int c_write_stereo_to_controller(volatile unsigned long* play1, volatile unsigned long* play2, unsigned short *data, int limit) {
int i = limit;
while (i--) {
*play1 = *data;
data++;
*play2 = *data;
data++;
}
return limit;
}
static inline int c_read_mono_from_controller(volatile unsigned long* play1, volatile unsigned long* play2, unsigned short *data, int limit) {
int i = limit;
while (i--) {
*data = *play1;
*data = *play2;
data++;
}
return limit;
}
static inline int c_read_stereo_from_controller(volatile unsigned long* play1, volatile unsigned long* play2, unsigned short *data, int limit) {
int i = limit;
while (i--) {
*data = *play1;
data++;
*data = *play2;
data++;
}
return limit;
}
/* --------------------------------------------------------------------- */
static void fill_fifo(struct device_buf *db)
{
unsigned short* dataptr;
int limit = 0, samples;
db->interrupted = 1;
db->packed = 0;
#ifdef ARC_EXTENDED_AC97
if (extended_controller)
limit = (db->fifo_depth - controller->play_count1)*db->num_channels;
else
#endif
limit = (db->transfer_size*db->num_channels);
if (audio_buffer_readable_space(db) < limit)
limit = audio_buffer_readable_space(db);
if (limit & 1) printk("Arse");
// get the per-channel sample count
samples = limit/db->num_channels;
dataptr = &db->buffer[db->readpos];
if (db->num_channels == 2) {
c_write_stereo_to_controller(&(controller->play_data1),&(controller->play_data2),dataptr,samples);
} else {
c_write_mono_to_controller(&(controller->play_data1),&(controller->play_data2),dataptr,samples);
}
// update counts - limit is the total number of samples (so a stereo sample pair counts as 2)
output += limit;
db->readpos += limit;
db->sample_count += samples;
if (db->packed) { printk("!!!!!!\n"); db->packed = 0; }
if (db->readpos >= db->writepos) printk("Overrun %d %d \n",db->readpos,db->writepos);
DBG_TRACE("%s readpos = %x\n",__FUNCTION__, db->readpos);
}
/* --------------------------------------------------------------------- */
static void drain_fifo(struct device_buf *db)
{
unsigned short* dataptr;
int limit, samples;
DBG_TRACE("%s\n",__FUNCTION__);
if (audio_buffer_writeable_space(db) > 0)
{
// work out number of samples to be transferred
limit = (audio_buffer_writeable_space(db) > (db->transfer_size*db->num_channels))
? (db->transfer_size*db->num_channels) : audio_buffer_writeable_space(db);
// get the per-channel sample count
samples = limit / db->num_channels;
dataptr = WRITEPTR(db);
if (db->num_channels == 2) {
c_read_stereo_from_controller(&(controller->play_data1),&(controller->play_data2),dataptr,samples);
} else {
c_read_mono_from_controller(&(controller->play_data1),&(controller->play_data2),dataptr,samples);
}
// update counts - limit is the total number of samples (so a stereo sample pair counts as 2)
input += limit;
db->writepos += limit;
db->sample_count += samples;
}
}
/* --------------------------------------------------------------------- */
static void start_play(struct arc_ac97_state *s)
{
int i;
struct device_buf *db = &s->play;
DBG_TRACE("%s\n",__FUNCTION__);
if (!db->stopped)
return;
// spin_lock_irqsave(&s->lock, flags);
if (!audio_buffer_readable_space(db))
return;
controller->intr_ctrl = 0;
s->intrs = set_xmit_slots(db->num_channels);
controller->intr_mask = s->intrs;
for (i = 0; i < FIFO_LEN/(FRAGSIZE/2); i++) // FIXME read status from controller
{
fill_fifo(db);
}
controller->intr_ctrl = INTR_ENABLE;
db->stopped = 0;
// spin_unlock_irqrestore(&s->lock, flags);
}
/* --------------------------------------------------------------------- */
static void start_capture(struct arc_ac97_state *s)
{
struct device_buf *db = &s->capture;
DBG_TRACE("%s\n",__FUNCTION__);
if (!db->stopped)
return;
// spin_lock_irqsave(&s->lock, flags);
set_recv_slots(db->num_channels);
controller->intr_mask = s->intrs;
// do_read(db);
db->stopped = 0;
// spin_unlock_irqrestore(&s->lock, flags);
}
/* --------------------------------------------------------------------- */
static void release_dmabuf(struct arc_ac97_state *s, struct device_buf *db)
{
struct page *page, *pend;
DBG_TRACE("%s\n",__FUNCTION__);
if (db->buffer) {
pend = virt_to_page(db->buffer + db->bufsize);
for (page = virt_to_page(db->buffer); page <= pend; page++)
ClearPageReserved(virt_to_page(page));
free_pages((unsigned long)db->buffer,get_order(db->bufsize));
}
db->mapped = db->ready = 0;
db->bufsize = 0;
db->buffer = 0;
}
/* --------------------------------------------------------------------- */
static int prog_dmabuf(struct arc_ac97_state *s, struct device_buf *db, char* ident)
{
struct page *page, *pend;
DBG_TRACE("%s %d\n",__FUNCTION__);
if (db->buffer)
release_dmabuf(s,db);
db->buffer = (unsigned short *) __get_free_pages(GFP_KERNEL,get_order((db->bufsize)));
if (!db->buffer)
{
return -ENOMEM;
}
pend = virt_to_page(db->buffer + db->bufsize);
for (page = virt_to_page(db->buffer); page <= pend; page++)
SetPageReserved(virt_to_page(page));
db->threshold = START_THRESHOLD * db->sample_rate;
if ((db->threshold > db->bufsize/4) || (db->threshold == 0))
db->threshold = db->bufsize/4;
db->ready = 1;
printk(KERN_INFO "%s buffer at 0x%08x; size %d, threshold %d\n",
ident, (unsigned int) db->buffer, db->bufsize, db->threshold);
return 0;
}
/* --------------------------------------------------------------------- */
extern inline int prog_dmabuf_capture(struct arc_ac97_state *s)
{
DBG_TRACE("%s\n",__FUNCTION__);
stop_capture(s);
return prog_dmabuf(s, &s->capture, "Capture");
}
/* --------------------------------------------------------------------- */
extern inline int prog_dmabuf_play(struct arc_ac97_state *s)
{
DBG_TRACE("%s\n",__FUNCTION__);
stop_play(s);
return prog_dmabuf(s, &s->play, "Playback");
}
/* --------------------------------------------------------------------- */
/* hold spinlock for the following */
static void play_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct arc_ac97_state *s = (struct arc_ac97_state *) dev_id;
struct device_buf *play = &s->play;
DBG_TRACE("%s\n",__FUNCTION__);
if (s->play.stopped) return;
spin_lock_irqsave(&s->lock, flags);
if (audio_buffer_readable_space(play) > 0)
fill_fifo(play);
else
{
printk("! - %d -",audio_buffer_readable_space(play));
stop_play(s);
}
spin_unlock_irqrestore(&s->lock, flags);
/* wake up anybody listening */
if (waitqueue_active(&play->wait))
wake_up_interruptible(&(play->wait));
}
/* --------------------------------------------------------------------- */
static void capture_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct arc_ac97_state *s = (struct arc_ac97_state *) dev_id;
struct device_buf *capture = &s->capture;
DBG_TRACE("%s\n",__FUNCTION__);
spin_lock(&s->lock);
if (audio_buffer_writeable_space(capture) >= capture->transfer_size)
drain_fifo(capture);
else
stop_capture(s);
/* wake up anybody listening */
if (waitqueue_active(&capture->wait))
wake_up_interruptible(&(capture->wait));
spin_unlock(&s->lock);
}
/* --------------------------------------------------------------------- */
static void status_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct arc_ac97_state *s = (struct arc_ac97_state *) dev_id;
DBG_TRACE("%s\n",__FUNCTION__);
spin_lock(&s->lock);
DBG_TRACE("%s\n",__FUNCTION__);
// FIXME fill in
spin_unlock(&s->lock);
}
/* --------------------------------------------------------------------- */
static irqreturn_t arc_ac97_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long int_id;
int_id = controller->intr_id;
controller->intr_ctrl = INTR_CLEAR;
DBG_TRACE("Interrupt ID reg = 0x%x Interrupt mask reg = 0x%x\n", int_id, controller->intr_mask);
if (int_id & PLAY_INTRS)
play_interrupt(irq, dev_id, regs);
if (int_id & CAPTURE_INTRS)
capture_interrupt(irq, dev_id, regs);
if (int_id & STATUS_INTR)
status_interrupt(irq, dev_id, regs);
controller->intr_ctrl = INTR_ENABLE;
return IRQ_HANDLED;
}
/* --------------------------------------------------------------------- */
static loff_t arc_ac97_llseek(struct file *file, loff_t offset, int origin)
{
DBG_TRACE("%s\n",__FUNCTION__);
return -ESPIPE;
}
/* --------------------------------------------------------------------- */
static int arc_ac97_open_mixdev(struct inode *inode, struct file *file)
{
DBG_TRACE("%s\n",__FUNCTION__);
file->private_data = &arc_ac97_state;
return 0;
}
/* --------------------------------------------------------------------- */
static int arc_ac97_release_mixdev(struct inode *inode, struct file *file)
{
DBG_TRACE("%s\n",__FUNCTION__);
return 0;
}
/* --------------------------------------------------------------------- */
static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
unsigned long arg)
{
DBG_TRACE("%s\n",__FUNCTION__);
return codec->mixer_ioctl(codec, cmd, arg);
}
/* --------------------------------------------------------------------- */
static int arc_ac97_ioctl_mixdev(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct arc_ac97_state *s = (struct arc_ac97_state *)file->private_data;
struct ac97_codec *codec = s->codec;
DBG_TRACE("%s\n",__FUNCTION__);
return mixdev_ioctl(codec, cmd, arg);
}
/* --------------------------------------------------------------------- */
static /*const */ struct file_operations arc_ac97_mixer_fops =
{
owner:THIS_MODULE,
llseek:arc_ac97_llseek,
ioctl:arc_ac97_ioctl_mixdev,
open:arc_ac97_open_mixdev,
release:arc_ac97_release_mixdev,
};
/* --------------------------------------------------------------------- */
static int drain_play(struct arc_ac97_state *s, int nonblock)
{
int count, tmo;
if (s->play.mapped || !s->play.ready || s->play.stopped)
return 0;
count = audio_buffer_readable_space(&(s->play));
DBG_TRACE("%s %d to drain; readpos %d writepos %d\n", __FUNCTION__, count,
s->play.readpos, s->play.writepos);
for (;;) {
// spin_lock_irqsave(&s->lock, flags);
// spin_unlock_irqrestore(&s->lock, flags);
if (count <= 0)
break;
if (signal_pending(current))
break;
if (nonblock)
return -EBUSY;
tmo = 1000 * count / (s->no_vra ?
48000 : s->play.sample_rate);
tmo++;
arc_ac97_delay(tmo);
}
s->intrs &= ~PLAY_INTRS;
controller->intr_mask = s->intrs;
if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
/* --------------------------------------------------------------------- */
static ssize_t arc_ac97_read(struct file *file, char *buffer,
size_t count, loff_t *ppos)
{
struct arc_ac97_state *s = (struct arc_ac97_state *)file->private_data;
struct device_buf *db = &s->capture;
ssize_t ret = 0;
int cnt, origcnt, limit;
DBG_TRACE("%s count is %d\n",__FUNCTION__, count);
origcnt = count;
if (ppos != &file->f_pos)
return -ESPIPE;
if (db->mapped)
return -ENXIO;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
__set_current_state(TASK_INTERRUPTIBLE);
while (count > 0) {
limit = count/db->bytes_per_sample;
if (audio_buffer_readable_space(db) < limit) {
if (file->f_flags & O_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
goto out;
}
wait_event_interruptible((db->wait), (audio_buffer_readable_space(db) >= limit));
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
goto out;
}
}
down(&db->sem);
//limit = audio_buffer_readable_space(db) > count/db->bytes_per_sample ? count/db->bytes_per_sample : audio_buffer_readable_space(db);
cnt = translate_to_user(db, buffer, limit);
up(&db->sem);
cnt *= db->bytes_per_sample;
if (audio_buffer_writeable_space(db) <= db->bufsize/8) {
spin_lock_irqsave(&s->lock, flags);
pack_audio_buffer(db);
spin_unlock_irqrestore(&s->lock, flags);
}
if (cnt < 0) {
// printk("****** Error, cnt is %d\n",cnt);
ret = -EFAULT;
goto out;
}
count -= cnt;
ret += cnt;
} // while (count > 0)
out:
set_current_state(TASK_RUNNING);
return ret;
}
/* --------------------------------------------------------------------- */
static ssize_t arc_ac97_write(struct file *file, const char *buffer,
size_t count, loff_t * ppos)
{
struct arc_ac97_state *s = (struct arc_ac97_state *)file->private_data;
struct device_buf *db = &s->play;
ssize_t ret = 0;
int cnt, origcnt, limit;
DBG_TRACE("%s \n",__FUNCTION__);
// take_snap(SNAP_AUDIO_EV1, 0, NULL);
origcnt = count;
//if (ppos != &file->f_pos)
// return -ESPIPE;
if (db->mapped)
{
return -ENXIO;
}
if (!access_ok(VERIFY_READ, buffer, count))
{
return -EFAULT;
}
__set_current_state(TASK_INTERRUPTIBLE);
// Try and do the write in one go, avoiding locking the buffer unless necessary
while (count > 0) {
limit = count/db->bytes_per_sample;
while (audio_buffer_free_space(db) < count)
{
if (file->f_flags & O_NONBLOCK)
{
if (!ret)
ret = -EAGAIN;
goto out;
}
// take_snap(SNAP_AUDIO_EV2, 0, NULL);
wait_event_interruptible(db->wait, (audio_buffer_free_space(db) >= count));
// take_snap(SNAP_AUDIO_EV3, 0, NULL);
if (signal_pending(current))
{
if (!ret)
ret = -ERESTARTSYS;
goto out;
}
}
if (audio_buffer_writeable_space(db) <= limit)
{
// take_snap(SNAP_AUDIO_EV4, 0, NULL);
spin_lock_irqsave(&s->lock, flags);
pack_audio_buffer(db);
spin_unlock_irqrestore(&s->lock, flags);
// take_snap(SNAP_AUDIO_EV5, 0, NULL);
}
// down(&db->sem);
// take_snap(SNAP_AUDIO_EV6, 0, NULL);
spin_lock_irqsave(&s->lock, flags);
cnt = translate_from_user(db, buffer, limit);
spin_unlock_irqrestore(&s->lock, flags);
// take_snap(SNAP_AUDIO_EV7, 0, NULL);
// up(&db->sem);
if (cnt < 0)
{
ret = -EFAULT;
goto out;
}
written += cnt;
cnt *= db->bytes_per_sample;
count -= cnt;
ret += cnt;
if (db->stopped && (audio_buffer_readable_space(db) > db->threshold))
{
// take_snap(SNAP_AUDIO_EV8, 0, NULL);
start_play(s);
// take_snap(SNAP_AUDIO_EV9, 0, NULL);
}
} // while (count > 0)
if (ret != origcnt) printk("%s : %d of %d bytes written %d output readpos %d writepos %d\n",
__FUNCTION__,ret,origcnt,output,db->readpos,db->writepos);
if (db->writepos > db->bufsize) printk("Buffer overrun\n");
out:
set_current_state(TASK_RUNNING);
// take_snap(SNAP_AUDIO_EV10, 0, NULL);
// printk("%s : %d bytes written %d output readpos %d writepos %d\n",__FUNCTION__,ret,output,db->readpos,db->writepos);
return ret;
}
/* --------------------------------------------------------------------- */
/* No kernel lock - we have our own spinlock */
static unsigned int arc_ac97_poll(struct file *file,
struct poll_table_struct *wait)
{
struct arc_ac97_state *s = (struct arc_ac97_state *)file->private_data;
unsigned int mask = 0;
DBG_TRACE("%s\n",__FUNCTION__);
if (file->f_mode & FMODE_WRITE) {
if (!s->play.ready)
return 0;
poll_wait(file, &s->play.wait, wait);
}
if (file->f_mode & FMODE_READ) {
if (!s->capture.ready)
return 0;
poll_wait(file, &s->capture.wait, wait);
}
// spin_lock_irqsave(&s->lock, flags);
if (file->f_mode & FMODE_READ) {
if (audio_buffer_readable_space(&(s->capture)))
mask |= POLLIN | POLLRDNORM;
}
if (file->f_mode & FMODE_WRITE) {
if (audio_buffer_writeable_space(&(s->play)))
mask |= POLLOUT | POLLWRNORM;
}
// spin_unlock_irqrestore(&s->lock, flags);
return mask;
}
/* --------------------------------------------------------------------- */
static int arc_ac97_mmap(struct file *file, struct vm_area_struct *vma)
{
struct arc_ac97_state *s = (struct arc_ac97_state *)file->private_data;
struct device_buf *db;
unsigned long size;
int ret = 0;
DBG_TRACE("%s\n",__FUNCTION__);
dbg(__FUNCTION__,"vma at 0x%x",vma);
// lock_kernel();
// down(&s->sem);
if (vma->vm_flags & VM_WRITE)
db = &s->play;
else if (vma->vm_flags & VM_READ)
db = &s->capture;
else
{
ret = -EINVAL;
goto out;
}
if (vma->vm_pgoff != 0)
{
ret = -EINVAL;
goto out;
}
size = vma->vm_end - vma->vm_start;
if (size > db->bufsize)
{
ret = -EINVAL;
goto out;
}
if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(db->buffer) >> PAGE_SHIFT,
size, vma->vm_page_prot)) {
ret = -EAGAIN;
goto out;
}
vma->vm_flags &= ~VM_IO;
db->mapped = 1;
out:
// up(&s->sem);
// unlock_kernel();
return ret;
}
/* --------------------------------------------------------------------- */
#ifdef ARC_AC97_VERBOSE_DEBUG
static struct ioctl_str_t {
unsigned int cmd;
const char *str;
} ioctl_str[] = {
{SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
{SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
{SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
{SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
{SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
{SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
{SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
{SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
{SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
{SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
/* {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, */
/* {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, */
{SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
{SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
{SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
{SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
{SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
{SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
{SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
{SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
/* {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, */
/* {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, */
{SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
{SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
{SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
{SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
{SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
{SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
{SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
{OSS_GETVERSION, "OSS_GETVERSION"},
{SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
{SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
{SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
{SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"},
{ARC_AC97_GET_SAMPLE_COUNT, "ARC_AC97_GET_SAMPLE_COUNT"},
{ARC_AC97_RESET_SAMPLE_COUNT, "ARC_AC97_RESET_SAMPLE_COUNT"}
};
#endif
/* --------------------------------------------------------------------- */
static int arc_ac97_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct arc_ac97_state *s = (struct arc_ac97_state *)file->private_data;
audio_buf_info abinfo;
int count;
int val, mapped;
//take_snap(SNAP_AUDIO_EV11, 0, NULL);
DBG_TRACE("%s - %d (0x%x)\n",__FUNCTION__,cmd,cmd);
mapped = ((file->f_mode & FMODE_WRITE) && s->play.mapped) ||
((file->f_mode & FMODE_READ) && s->capture.mapped);
#ifdef ARC_AC97_VERBOSE_DEBUG
for (count=0; count<(sizeof(ioctl_str)/sizeof(struct ioctl_str_t)); count++) {
if (ioctl_str[count].cmd == cmd)
break;
}
if (count < sizeof(ioctl_str) / sizeof(struct ioctl_str_t))
dbg(__FUNCTION__,"ioctl %s, arg=0x%lx", ioctl_str[count].str, arg);
else
dbg(__FUNCTION__,"ioctl 0x%x unknown, arg=0x%lx", cmd, arg);
#endif
switch (cmd) {
case OSS_GETVERSION:
return put_user(SOUND_VERSION, (int *) arg);
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE) {
printk("Draining in SNDCTL_DSP_SYNC\n");
return drain_play(s, file->f_flags & O_NONBLOCK); }
return 0;
case SNDCTL_DSP_SETDUPLEX:
return 0;
case SNDCTL_DSP_GETCAPS:
return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
case SNDCTL_DSP_RESET:
printk("Resetting\n");
if (file->f_mode & FMODE_WRITE) {
stop_play(s);
synchronize_irq();
// reset_audio_buffer(&s->play);
}
if (file->f_mode & FMODE_READ) {
stop_capture(s);
synchronize_irq();
// reset_audio_buffer(&s->capture);
}
return 0;
case SNDCTL_DSP_SPEED:
if (get_user(val, (int *) arg))
return -EFAULT;
if (val >= 0) {
if (file->f_mode & FMODE_READ) {
stop_capture(s);
set_capture_rate(s, val);
}
if (file->f_mode & FMODE_WRITE) {
stop_play(s);
set_play_rate(s, val);
}
/* if (s->open_mode & FMODE_READ) */
/* if ((ret = prog_dmabuf_capture(s))) */
/* return ret; */
/* if (s->open_mode & FMODE_WRITE) */
/* if ((ret = prog_dmabuf_play(s))) */
/* return ret; */
}
return put_user((file->f_mode & FMODE_READ) ?
s->capture.sample_rate :
s->play.sample_rate,
(int *)arg);
case SNDCTL_DSP_STEREO:
if (get_user(val, (int *) arg))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
stop_capture(s);
s->capture.num_channels = val ? 2 : 1;
/* if ((ret = prog_dmabuf_capture(s))) */
/* return ret; */
}
if (file->f_mode & FMODE_WRITE) {
stop_play(s);
s->play.num_channels = (val ? 2 : 1);
DBG_TRACE("Number of channels %d\n",s->play.num_channels);
if (s->codec_ext_caps & AC97_EXT_DACS) {
// disable surround and center/lfe in AC'97
u16 ext_stat = rdcodec(s->codec,
AC97_EXTENDED_STATUS);
wrcodec(s->codec, AC97_EXTENDED_STATUS,
ext_stat | (AC97_EXTSTAT_PRI |
AC97_EXTSTAT_PRJ |
AC97_EXTSTAT_PRK));
}
/* if ((ret = prog_dmabuf_play(s))) */
/* return ret; */
}
return 0;
case SNDCTL_DSP_CHANNELS:
if (get_user(val, (int *) arg))
return -EFAULT;
if (val != 0) {
if (file->f_mode & FMODE_READ) {
if (val < 0 || val > 2)
return -EINVAL;
stop_capture(s);
s->capture.num_channels = val;
/* if ((ret = prog_dmabuf_capture(s))) */
/* return ret; */
}
if (file->f_mode & FMODE_WRITE) {
switch (val) {
case 1:
case 2:
break;
case 3:
case 5:
return -EINVAL;
case 4:
if (!(s->codec_ext_caps & AC97_EXTID_SDAC))
return -EINVAL;
break;
case 6:
if ((s->codec_ext_caps & AC97_EXT_DACS) != AC97_EXT_DACS)
return -EINVAL;
break;
default:
return -EINVAL;
}
stop_play(s);
if (val <= 2 &&
(s->codec_ext_caps & AC97_EXT_DACS)) {
// disable surround and center/lfe
// channels in AC'97
u16 ext_stat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
wrcodec(s->codec, AC97_EXTENDED_STATUS,
ext_stat | (AC97_EXTSTAT_PRI | AC97_EXTSTAT_PRJ | AC97_EXTSTAT_PRK));
} else if (val >= 4) {
// enable surround, center/lfe
// channels in AC'97
u16 ext_stat =
rdcodec(s->codec,
AC97_EXTENDED_STATUS);
ext_stat &= ~AC97_EXTSTAT_PRJ;
if (val == 6)
ext_stat &= ~(AC97_EXTSTAT_PRI | AC97_EXTSTAT_PRK);
wrcodec(s->codec, AC97_EXTENDED_STATUS, ext_stat);
}
s->play.num_channels = val;
/* if ((ret = prog_dmabuf_play(s))) */
/* return ret; */
}
}
return put_user(val, (int *) arg);
case SNDCTL_DSP_GETFMTS: /* Returns a mask */
return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);
case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
if (get_user(val, (int *) arg))
return -EFAULT;
if (val != AFMT_QUERY) {
if (file->f_mode & FMODE_READ) {
stop_capture(s);
if (val == AFMT_S16_LE)
s->capture.sample_size = 16;
else {
val = AFMT_U8;
s->capture.sample_size = 8;
}
/* if ((ret = prog_dmabuf_capture(s))) */
/* return ret; */
}
if (file->f_mode & FMODE_WRITE) {
stop_play(s);
if (val == AFMT_S16_LE)
s->play.sample_size = 16;
else {
val = AFMT_U8;
s->play.sample_size = 8;
}
/* if ((ret = prog_dmabuf_play(s))) */
/* return ret; */
}
} else {
if (file->f_mode & FMODE_READ)
val = (s->capture.sample_size == 16) ? AFMT_S16_LE : AFMT_U8;
else
val = (s->play.sample_size == 16) ? AFMT_S16_LE : AFMT_U8;
}
return put_user(val, (int *) arg);
case SNDCTL_DSP_POST:
return 0;
case SNDCTL_DSP_GETTRIGGER:
val = 0;
// spin_lock_irqsave(&s->lock, flags);
if (file->f_mode & FMODE_READ && !s->capture.stopped)
val |= PCM_ENABLE_INPUT;
if (file->f_mode & FMODE_WRITE && !s->play.stopped)
val |= PCM_ENABLE_OUTPUT;
// spin_unlock_irqrestore(&s->lock, flags);
return put_user(val, (int *) arg);
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, (int *) arg))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT)
start_capture(s);
else
stop_capture(s);
}
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT)
start_play(s);
else
stop_play(s);
}
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
abinfo.fragsize = s->play.transfer_size * 8;
abinfo.fragstotal = s->play.bufsize/abinfo.fragsize;
abinfo.fragments = audio_buffer_free_space(&(s->play))/abinfo.fragsize;
abinfo.fragments--;
abinfo.bytes = audio_buffer_free_space(&(s->play));
//take_snap(SNAP_AUDIO_EV12, 0, NULL);
return copy_to_user((void *) arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
abinfo.fragsize = s->capture.transfer_size * 8;
abinfo.fragstotal = s->capture.bufsize/abinfo.fragsize;
abinfo.fragments = audio_buffer_usage(&(s->capture))/abinfo.fragsize;
abinfo.fragments--;
abinfo.bytes = audio_buffer_usage(&(s->capture));
return copy_to_user((void *) arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETODELAY:
// printk("SNDCTL_DSP_GETODELAY\n");
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
// spin_lock_irqsave(&s->lock, flags);
count = audio_buffer_readable_space(&(s->play));
// spin_unlock_irqrestore(&s->lock, flags);
count *= s->play.bytes_per_sample;
if (count < 0)
count = 0;
return put_user(count, (int *) arg);
/* case SNDCTL_DSP_GETIPTR: */
/* if (!(file->f_mode & FMODE_READ)) */
/* return -EINVAL; */
/* spin_lock_irqsave(&s->lock, flags); */
/* cinfo.bytes = s->capture.total_bytes; */
/* count = s->capture.count; */
/* if (!s->capture.stopped) { */
/* diff = dma_count_done(&s->capture); */
/* count += diff; */
/* cinfo.bytes += diff; */
/* cinfo.ptr = virt_to_phys(s->capture.nextIn) + diff - */
/* s->capture.dmaaddr; */
/* } else */
/* cinfo.ptr = virt_to_phys(s->capture.nextIn) - */
/* s->capture.dmaaddr; */
/* if (s->capture.mapped) */
/* s->capture.count &= (s->capture.dma_fragsize-1); */
/* spin_unlock_irqrestore(&s->lock, flags); */
/* if (count < 0) */
/* count = 0; */
/* cinfo.blocks = count >> s->capture.fragshift; */
/* return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); */
/* case SNDCTL_DSP_GETOPTR: */
/* if (!(file->f_mode & FMODE_READ)) */
/* return -EINVAL; */
/* spin_lock_irqsave(&s->lock, flags); */
/* cinfo.bytes = s->play.total_bytes; */
/* count = s->play.count; */
/* if (!s->play.stopped) { */
/* diff = dma_count_done(&s->play); */
/* count -= diff; */
/* cinfo.bytes += diff; */
/* cinfo.ptr = virt_to_phys(s->play.nextOut) + diff - */
/* s->play.dmaaddr; */
/* } else */
/* cinfo.ptr = virt_to_phys(s->play.nextOut) - */
/* s->play.dmaaddr; */
/* if (s->play.mapped) */
/* s->play.count &= (s->play.dma_fragsize-1); */
/* spin_unlock_irqrestore(&s->lock, flags); */
/* if (count < 0) */
/* count = 0; */
/* cinfo.blocks = count >> s->play.fragshift; */
/* return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); */
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE)
return put_user(s->play.transfer_size*8, (int *) arg);
else
return put_user(s->capture.transfer_size*8, (int *) arg);
/* case SNDCTL_DSP_SETFRAGMENT: */
/* if (get_user(val, (int *) arg)) */
/* return -EFAULT; */
/* if (file->f_mode & FMODE_READ) { */
/* stop_capture(s); */
/* s->capture.ossfragshift = val & 0xffff; */
/* s->capture.ossmaxfrags = (val >> 16) & 0xffff; */
/* if (s->capture.ossfragshift < 4) */
/* s->capture.ossfragshift = 4; */
/* if (s->capture.ossfragshift > 15) */
/* s->capture.ossfragshift = 15; */
/* if (s->capture.ossmaxfrags < 4) */
/* s->capture.ossmaxfrags = 4; */
/* if ((ret = prog_dmabuf_capture(s))) */
/* return ret; */
/* } */
/* if (file->f_mode & FMODE_WRITE) { */
/* stop_play(s); */
/* s->play.ossfragshift = val & 0xffff; */
/* s->play.ossmaxfrags = (val >> 16) & 0xffff; */
/* if (s->play.ossfragshift < 4) */
/* s->play.ossfragshift = 4; */
/* if (s->play.ossfragshift > 15) */
/* s->play.ossfragshift = 15; */
/* if (s->play.ossmaxfrags < 4) */
/* s->play.ossmaxfrags = 4; */
/* if ((ret = prog_dmabuf_play(s))) */
/* return ret; */
/* } */
/* return 0; */
/* case SNDCTL_DSP_SUBDIVIDE: */
/* if ((file->f_mode & FMODE_READ && s->capture.subdivision) || */
/* (file->f_mode & FMODE_WRITE && s->play.subdivision)) */
/* return -EINVAL; */
/* if (get_user(val, (int *) arg)) */
/* return -EFAULT; */
/* if (val != 1 && val != 2 && val != 4) */
/* return -EINVAL; */
/* if (file->f_mode & FMODE_READ) { */
/* stop_capture(s); */
/* s->capture.subdivision = val; */
/* if ((ret = prog_dmabuf_capture(s))) */
/* return ret; */
/* } */
/* if (file->f_mode & FMODE_WRITE) { */
/* stop_play(s); */
/* s->play.subdivision = val; */
/* if ((ret = prog_dmabuf_play(s))) */
/* return ret; */
/* } */
/* return 0; */
case SOUND_PCM_READ_RATE:
return put_user((file->f_mode & FMODE_READ) ?
s->capture.sample_rate :
s->play.sample_rate,
(int *)arg);
case SOUND_PCM_READ_CHANNELS:
if (file->f_mode & FMODE_READ)
return put_user(s->capture.num_channels, (int *)arg);
else
return put_user(s->play.num_channels, (int *)arg);
case SOUND_PCM_READ_BITS:
if (file->f_mode & FMODE_READ)
return put_user(s->capture.sample_size, (int *)arg);
else
return put_user(s->play.sample_size, (int *)arg);
case ARC_AC97_GET_SAMPLE_COUNT:
if (file->f_mode & FMODE_READ)
return put_user(s->capture.sample_count, (int *)arg);
else
return put_user(s->play.sample_count, (int *)arg);
case ARC_AC97_RESET_SAMPLE_COUNT:
if (file->f_mode & FMODE_READ)
s->capture.sample_count = 0;
else
s->play.sample_count = 0;
return 0;
case SOUND_PCM_WRITE_FILTER:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_READ_FILTER:
return -EINVAL;
}
return mixdev_ioctl(s->codec, cmd, arg);
}
/* --------------------------------------------------------------------- */
static int arc_ac97_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
DECLARE_WAITQUEUE(wait, current);
struct arc_ac97_state *s = &arc_ac97_state;
written = 0;
queued = 0;
output = 0;
DBG_TRACE("%s\n",__FUNCTION__);
#ifdef ARC_AC97_VERBOSE_DEBUG
if (file->f_flags & O_NONBLOCK)
dbg(__FUNCTION__, ": non-blocking, file at 0x%x",file);
else
dbg(__FUNCTION__ ,": blocking, file at 0x%x",file);
#endif
file->private_data = s;
/* wait for device to become free */
down(&s->open_sem);
while (s->open_mode & file->f_mode) {
if (file->f_flags & O_NONBLOCK) {
up(&s->open_sem);
DBG_TRACE("Sound device busy\n");
return -EBUSY;
}
add_wait_queue(&s->open_wait, &wait);
__set_current_state(TASK_INTERRUPTIBLE);
up(&s->open_sem);
schedule();
remove_wait_queue(&s->open_wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
down(&s->open_sem);
}
stop_play(s);
stop_capture(s);
reset_audio_buffer(&(s->play));
reset_audio_buffer(&(s->capture));
if (file->f_mode & FMODE_READ) {
s->capture.num_channels = 1;
s->capture.sample_size = 8;
set_capture_rate(s, 8000);
if ((minor & 0xf) == SND_DEV_DSP16)
s->capture.sample_size = 16;
}
if (file->f_mode & FMODE_WRITE) {
s->play.num_channels = 1;
s->play.sample_size = 8;
set_play_rate(s, 8000);
if ((minor & 0xf) == SND_DEV_DSP16)
s->play.sample_size = 16;
}
/* if (file->f_mode & FMODE_READ) { */
/* if ((ret = prog_dmabuf_capture(s))) */
/* return ret; */
/* } */
/* if (file->f_mode & FMODE_WRITE) { */
/* if ((ret = prog_dmabuf_play(s))) */
/* return ret; */
/* } */
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
init_MUTEX(&s->play.sem);
init_MUTEX(&s->capture.sem);
return 0;
}
/* --------------------------------------------------------------------- */
static int arc_ac97_release(struct inode *inode, struct file *file)
{
struct arc_ac97_state *s = (struct arc_ac97_state *)file->private_data;
DBG_TRACE("%s written %d output %d queued %d\n",__FUNCTION__,written,output,queued);
// lock_kernel();
if (file->f_mode & FMODE_WRITE) {
// unlock_kernel();
drain_play(s, file->f_flags & O_NONBLOCK);
// lock_kernel();
}
down(&s->open_sem);
if (file->f_mode & FMODE_WRITE) {
stop_play(s);
}
if (file->f_mode & FMODE_READ) {
stop_capture(s);
}
s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
controller->intr_ctrl = 0;
up(&s->open_sem);
wake_up(&s->open_wait);
reset_audio_buffer(&(s->play));
reset_audio_buffer(&(s->capture));
// unlock_kernel();
return 0;
}
/* --------------------------------------------------------------------- */
static /*const */ struct file_operations arc_ac97_audio_fops = {
owner: THIS_MODULE,
llseek: arc_ac97_llseek,
read: arc_ac97_read,
write: arc_ac97_write,
poll: arc_ac97_poll,
ioctl: arc_ac97_ioctl,
mmap: arc_ac97_mmap,
open: arc_ac97_open,
release: arc_ac97_release,
};
/* --------------------------------------------------------------------- */
/* --------------------------------------------------------------------- */
/*
* for debugging purposes, we'll create a proc device that dumps the
* CODEC chipstate
*/
#ifdef ARC_AC97_DEBUG
static int proc_arc_ac97_dump(char *buf, char **start, off_t fpos,
int length, int *eof, void *data)
{
struct arc_ac97_state *s = &arc_ac97_state;
int cnt, len = 0;
DBG_TRACE("%s\n",__FUNCTION__);
/* print out header */
len += sprintf(buf + len, "\n\t\tARC_AC97 Audio Debug\n\n");
/* Add something to look at the controller state */
// FIXME right here
/* print out CODEC state */
len += sprintf(buf + len, "\nAC97 CODEC registers\n");
len += sprintf(buf + len, "----------------------\n");
for (cnt = 0; cnt <= 0x7e; cnt += 2)
len += sprintf(buf + len, "reg %02x = %04x\n",
cnt, rdcodec(s->codec, cnt));
if (fpos >= len) {
*start = buf;
*eof = 1;
return 0;
}
*start = buf + fpos;
if ((len -= fpos) > length)
return length;
*eof = 1;
return len;
}
#endif /* ARC_AC97_DEBUG */
/* --------------------------------------------------------------------- */
static int __init arc_ac97_probe(struct platform_device *dev)
{
struct arc_ac97_state *s = &arc_ac97_state;
int val;
#ifdef ARC_AC97_DEBUG
char proc_str[80];
#endif
DBG_TRACE("%s\n",__FUNCTION__);
memset(s, 0, sizeof(struct arc_ac97_state));
init_waitqueue_head(&(s->capture.wait));
init_waitqueue_head(&(s->play.wait));
init_waitqueue_head(&(s->open_wait));
init_MUTEX(&s->open_sem);
spin_lock_init(&s->lock);
s->play.transfer_size = TRANSFER_SIZE;
s->capture.transfer_size = TRANSFER_SIZE;
s->play.latency = DEFAULT_LATENCY;
s->capture.latency = DEFAULT_LATENCY;
s->play.stopped = 1;
s->capture.stopped = 1;
s->play.bytes_per_sample = 2;
s->capture.bytes_per_sample = 2;
s->play.fifo_depth = 4096; // FIXME
s->capture.fifo_depth = 4096;
s->capture.bufsize = BUFSIZE;
s->play.bufsize = BUFSIZE;
s->capture.buffer = 0;
s->play.buffer = 0;
reset_audio_buffer(&(s->play));
reset_audio_buffer(&(s->capture));
prog_dmabuf_capture(s);
prog_dmabuf_play(s);
s->codec = ac97_alloc_codec();
if(s->codec == NULL)
{
err("Out of memory");
return -1;
}
s->codec->private_data = s;
s->codec->id = 0;
s->codec->codec_read = rdcodec;
s->codec->codec_write = wrcodec;
s->codec->codec_wait = waitcodec;
/* Register interrupt, set up interrupt mask. */
s->intrs = PLAY1_INTR;
controller->intr_ctrl = 2;
arc_ac97_delay(10);
controller->intr_ctrl = 0;
controller->intr_mask = s->intrs;
/* Define channel mappings */
controller->point1 = 3;
controller->point2 = 4;
controller->point3 = 0;
controller->play_trig1 = TRIGGER_LEVEL;
controller->play_trig2 = TRIGGER_LEVEL;
// controller->play_trig2 = (64 - TRANSFER_SIZE);
controller->play_trig3 = 0;
info("controller version %d",ID_REVISION(controller->ID));
extended_controller = (ID_REVISION(controller->ID) > 1);
request_irq(AC97_INTERRUPT, (irq_handler_t)arc_ac97_interrupt, 0 , "ARC AC97", (void *)s);
/* register devices */
if ((s->dev_audio = register_sound_dsp(&arc_ac97_audio_fops, -1)) < 0)
goto err_dev1;
if ((s->codec->dev_mixer =
register_sound_mixer(&arc_ac97_mixer_fops, -1)) < 0)
goto err_dev2;
#ifdef ARC_AC97_DEBUG
/* intialize the debug proc device */
s->ps = create_proc_read_entry(ARC_AC97_MODULE_NAME, 0, NULL,
proc_arc_ac97_dump, NULL);
#endif /* ARC_AC97_DEBUG */
/* FIXME may need to force a reset of the controller here */
/* codec init */
if (!ac97_probe_codec(s->codec))
goto err_dev3;
s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
info("AC'97 Base/Extended ID = %04x/%04x",
s->codec_base_caps, s->codec_ext_caps);
s->codec->supported_mixers |= SOUND_MASK_ALTPCM;
/*
* Now set AUX_OUT's default volume.
*/
val = 0x4343;
mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_ALTPCM,
(unsigned long) &val);
if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
// codec does not support VRA
s->no_vra = 1;
} else if (!vra) {
// Boot option says disable VRA
u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
wrcodec(s->codec, AC97_EXTENDED_STATUS,
ac97_extstat & ~AC97_EXTSTAT_VRA);
s->no_vra = 1;
}
if (s->no_vra)
info("no VRA, interpolating and decimating");
/* set mic to be the recording source */
val = SOUND_MASK_MIC;
mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
(unsigned long) &val);
#ifdef ARC_AC97_DEBUG
sprintf(proc_str, "driver/%s/%d/ac97", ARC_AC97_MODULE_NAME,
s->codec->id);
s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL,
ac97_read_proc, &s->codec);
#endif
return 0;
err_dev3:
unregister_sound_mixer(s->codec->dev_mixer);
err_dev2:
unregister_sound_dsp(s->dev_audio);
err_dev1:
ac97_release_codec(s->codec);
return -1;
}
/* --------------------------------------------------------------------- */
static int arc_ac97_remove(struct platform_device *dev)
{
struct arc_ac97_state *s = &arc_ac97_state;
DBG_TRACE("%s\n",__FUNCTION__);
if (!s)
return -1;
#ifdef ARC_AC97_DEBUG
if (s->ps)
remove_proc_entry(ARC_AC97_MODULE_NAME, NULL);
#endif /* ARC_AC97_DEBUG */
free_irq(AC97_INTERRUPT, 0);
unregister_sound_dsp(s->dev_audio);
unregister_sound_mixer(s->codec->dev_mixer);
ac97_release_codec(s->codec);
return 0;
}
#ifndef MODULE
static int arc_ac97_setup(char *options)
{
char *this_opt;
if (!options || !*options)
return 0;
while((this_opt=strsep(&options, ",")) != NULL)
{
if (!strncmp(this_opt, "vra", 3)) {
vra = 1;
}
}
return 1;
}
__setup("arc_ac97_audio=", arc_ac97_setup);
#endif /* MODULE */
static struct platform_driver arc_ac97_driver = {
.driver = {
.name = "arc_ac97",
},
.probe = arc_ac97_probe,
.remove = arc_ac97_remove,
};
static struct platform_device *arc_ac97_device;
static int __init arc_ac97_init(void)
{
int ret;
DBG_TRACE("ARC AC97 controller\n");
info("Built " __TIME__ " on " __DATE__);
ret = platform_driver_register(&arc_ac97_driver);
if (!ret) {
arc_ac97_device = platform_device_register_simple ("arc_ac97", 0, NULL, 0);
ret = (int)platform_get_drvdata (arc_ac97_device);
}
return ret;
}
static void __exit arc_ac97_exit(void)
{
info("unloading");
platform_device_unregister (arc_ac97_device);
platform_driver_unregister (&arc_ac97_driver);
}
/* --------------------------------------------------------------------- */
module_init(arc_ac97_init);
module_exit(arc_ac97_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pete Shepherd of ARC fame (Ported to 2.6.19 kernel by KT)");
MODULE_DESCRIPTION("ARC AC97 Audio Driver");