[parisc-linux-cvs] 2.4.16-pa31, harmony.c changes
Helge Deller
deller@gmx.de
Thu, 3 Jan 2002 00:56:46 +0100
--------------Boundary-00=_M65C5V9A3SIQ7S6WV2NP
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 8bit
On Thursday 03 January 2002 01:14, Helge Deller wrote:
> CVSROOT: /var/cvs
> Module name: linux
> Changes by: deller 02/01/02 17:14:59
>
> Modified files:
> . : Makefile
> drivers/sound : harmony.c
>
> Log message:
> 2.4.16-pa31 (harmony.c changes)
>
> 2001-12-31: Helge Deller <hdeller@gmx.de>
> - if possible use DMA consistent memory
> - many cleanups & fixes
> - more OSS compatibility functions
--------------Boundary-00=_M65C5V9A3SIQ7S6WV2NP
Content-Type: text/plain;
charset="iso-8859-1";
name="diff"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="diff"
Index: Makefile
===================================================================
RCS file: /var/cvs/linux/Makefile,v
retrieving revision 1.235
diff -u -p -r1.235 Makefile
--- Makefile 2001/12/30 03:29:04 1.235
+++ Makefile 2002/01/03 00:14:07
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 16
-EXTRAVERSION = -pa30
+EXTRAVERSION = -pa31
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
Index: drivers/sound/harmony.c
===================================================================
RCS file: /var/cvs/linux/drivers/sound/harmony.c,v
retrieving revision 1.15
diff -u -p -r1.15 harmony.c
--- drivers/sound/harmony.c 2001/12/26 22:42:26 1.15
+++ drivers/sound/harmony.c 2002/01/03 00:14:08
@@ -19,38 +19,6 @@ reported by the inventory code. The HPA
interrupts aren't registered properly.
-About the rotating buffer
--------------------------
-
-I'm going to explain how the rotating buffer thing works because I had a hard
-time figuring it out for myself.
-
-At any given time, harmony uses two different buffers. One is the frame
-that is currently playing, another is the frame that is ready to be played.
-
-Obviously you can't be writing into either of these frames while the chip
-is playing, or you'll hear clicks, pops and out of order frames. One weekend
-of listening to RMS sing The Free Software Song out of order is a good way to
-drive this point home.
-
-So, we use a total of three frames. One is in PLAYING mode, one is is
-READY_TO_PLAY mode, and the third is either LOADED or EMPTY. All buffers
-start up by being EMTPY. When harmony_audio_write loads up two frames,
-interrupts are started and the loop starts. The ISR pops in the first frame,
-marking it as PLAYING. The ISR will get called again, and the second
-frame will be loaded and marked as READY_TO_PLAY. When the first frame is
-done, the ISR will be called again; frame one is marked as EMPTY to reload,
-frame two is marked PLAYING, frame three is marked READY_TO_PLAY.
-
-All the while, harmony_audio_write is looking for frames marked EMPTY, and
-filling them and marking them LOADED.
-
-Yes, this does actually work.
-
-Let me save you an entire Thursday of listening to "orvalds Linus Linux, I
-prounouce as Linus This is" by giving you this rotating buffer code.
-
-
Changes:
--------
@@ -70,13 +38,12 @@ Changes:
- cleanups
2001-03-07: Matthieu Delahaye <delahaym@esiee.fr>
- - added record functionality
+ - added record functionality
-TODO:
------
- - update/cleanup the above documentation
- - fix CHECK_xxx functions -> use newer cache flushing functions
- - test if loading/unloading this driver as module works
+2001-12-31: Helge Deller <hdeller@gmx.de>
+ - if possible use DMA consistent memory
+ - many cleanups & fixes
+ - more OSS compatibility functions
*/
#include <linux/delay.h>
@@ -85,15 +52,17 @@ TODO:
#include <linux/ioport.h>
#include <linux/types.h>
#include <linux/mm.h>
+#include <linux/pci.h>
#include <asm/gsc.h>
#include <asm/io.h>
-#include <asm/pgalloc.h> /* for cache flushing */
+#include <asm/pgalloc.h>
#include "sound_config.h"
-#define DEBUG
+#undef DEBUG
+#define PFX "harmony: "
#ifdef DEBUG
# define DPRINTK printk
@@ -102,17 +71,9 @@ TODO:
#endif
-#define dma_consistent 0 /* XXX fixme */
-#define CHECK_WBACK(addr,len) \
- do { if (!dma_consistent) dma_cache_wback((unsigned long)addr,len); } while (0)
-#define CHECK_INV(addr,len) \
- do { if (!dma_consistent) dma_cache_inv((unsigned long)addr,len); } while(0)
-
-#define CHECK_WBACK_INV(addr,len) \
- do { if (!dma_consistent) dma_cache_wback_inv((unsigned long)addr,len); } while (0)
-
-
+#define MAX_BUFS 10 /* maximum number of rotating buffers */
+#define HARMONY_BUF_SIZE 4096 /* needs to be a multiple of PAGE_SIZE (4096)! */
#define CNTL_C 0x80000000
@@ -152,14 +113,6 @@ TODO:
#define HARMONY_SR_6KHZ 0x17
-#define MAX_BUFS 10 /* maximum number of rotating buffers */
-#define HARMONY_BUF_SIZE 4096
-
-#define HARMONY_BUF_NONE -1
-#define HARMONY_BUF_EMPTY 0
-#define HARMONY_BUF_FILLED 1
-#define HARMONY_BUF_READY_TO_PLAY 2
-#define HARMONY_BUF_PLAYING 3
/* Some magics numbers used to auto-detect file formats */
@@ -198,20 +151,9 @@ TODO:
#define MAX_VOLUME_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT)
/*
- * OSS values
- */
-
-#define HARMONY_MIXER_DEVICES (SOUND_MASK_OGAIN|SOUND_MASK_IGAIN|SOUND_MASK_VOLUME)
-#define HARMONY_RECORDING_DEVICES (SOUND_MASK_MIC | SOUND_MASK_LINE )
-#define HARMONY_OUTPUT_DEVICES (SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_PHONEOUT )
-#define HARMONY_CAPS 1
-#define HARMONY_STEREO_DEVICES (SOUND_MASK_IGAIN | SOUND_MASK_OGAIN)
-
-/*
* Channels Mask in mixer register
*/
-
#define GAIN_TOTAL_SILENCE 0x00F00FFF
#define GAIN_DEFAULT 0x0FF00000
@@ -239,55 +181,94 @@ struct harmony_hpa {
struct harmony_dev {
int irq;
struct harmony_hpa *hpa;
- int frames_so_far;
u32 current_gain;
- u8 data_format;
- u8 sample_rate;
- u8 stereo_select; /* 1 = stereo, 0 = mono */
+ u8 data_format; /* HARMONY_DF_xx_BIT_xxx */
+ u8 sample_rate; /* HARMONY_SR_xx_KHZ */
+ u8 stereo_select; /* HARMONY_SS_MONO or HARMONY_SS_STEREO */
int format_initialized;
+ u32 dac_rate; /* 8000 ... 48000 (Hz) */
int suspended_playing;
int suspended_recording;
int blocked_playing;
int blocked_recording;
- int done;
wait_queue_head_t wq_play, wq_record;
int first_filled_play; /* first buffer containing data (next to play) */
int nb_filled_play;
int first_filled_record;
int nb_filled_record;
- int playing;
- int recording;
int audio_open, mixer_open;
int dsp_unit, mixer_unit;
};
+static struct harmony_dev harmony;
-/* forward declarations */
-static void harmony_mixer_mute_all(void);
-static void harmony_mixer_unmute(void);
-/* Until we have recording working, this is where we're putting the recording data. */
+/* Dynamic sound buffer allocation and DMA memory */
-static struct harmony_dev harmony;
+struct harmony_buffer {
+ unsigned char *addr;
+ dma_addr_t dma_handle;
+ int dma_consistent; /* Zero if pci_alloc_consistent() fails */
+ int len;
+};
-static unsigned char played_buf[MAX_BUFS * HARMONY_BUF_SIZE]
- __attribute__ ((aligned (HARMONY_BUF_SIZE)));
-static unsigned char recorded_buf[MAX_BUFS * HARMONY_BUF_SIZE]
- __attribute__ ((aligned (HARMONY_BUF_SIZE)));
-static char silent[HARMONY_BUF_SIZE]
- __attribute__((aligned(4096)));
-static char graveyard[HARMONY_BUF_SIZE]
- __attribute__((aligned(4096)));
+/* the harmony memory buffers */
+static struct harmony_buffer played_buf, recorded_buf, silent, graveyard;
+#define CHECK_WBACK_INV_OFFSET(b,offset,len) \
+ do { if (!b.dma_consistent) \
+ dma_cache_wback_inv((unsigned long)b.addr+offset,len); \
+ } while (0)
+
+
+static int __init harmony_alloc_buffer(struct harmony_buffer *b,
+ int buffer_count, int prefer_consistent)
+{
+ b->len = buffer_count * HARMONY_BUF_SIZE;
+ if (prefer_consistent)
+ b->addr = pci_alloc_consistent(NULL, b->len, &b->dma_handle);
+ else
+ b->addr = NULL; /* don't use DMA consistent memory */
+ if (b->addr) {
+ b->dma_consistent = 1;
+ DPRINTK(KERN_INFO PFX "consistent memory: 0x%lx, played_buf: 0x%lx\n",
+ (unsigned long)b->dma_handle, (unsigned long)b->addr);
+ } else {
+ b->dma_consistent = 0;
+ b->addr = kmalloc(b->len, GFP_KERNEL);
+ if (!b->addr) {
+ printk(KERN_ERR PFX "couldn't allocate memory\n");
+ return -EBUSY;
+ }
+ b->dma_handle = __pa(b->addr);
+ }
+ return 0;
+}
-static void harmony_wait_CNTL(void)
+static void __exit harmony_free_buffer(struct harmony_buffer *b)
{
+ if (!b->addr)
+ return;
+
+ if (b->dma_consistent)
+ pci_free_consistent(NULL, b->len, b->addr, b->dma_handle);
+ else
+ kfree(b->addr);
+ b->addr = NULL;
+}
+
+
+
+/* Low-Level sound-chip programming */
+
+static void __inline__ harmony_wait_CNTL(void)
+{
/* Wait until we're out of control mode */
while (gsc_readl(&harmony.hpa->cntl) & CNTL_C)
/* wait */ ;
@@ -307,7 +288,7 @@ static void harmony_update_control(void)
/* initialize CNTL */
harmony_wait_CNTL();
- gsc_writel( default_cntl, &harmony.hpa->cntl);
+ gsc_writel(default_cntl, &harmony.hpa->cntl);
harmony_wait_CNTL();
}
@@ -325,10 +306,10 @@ static void harmony_set_rate(u8 data_rat
harmony_update_control();
}
-static int harmony_detect_rate(int freq)
+static int harmony_detect_rate(int *freq)
{
int newrate;
- switch (freq) {
+ switch (*freq) {
case 8000: newrate = HARMONY_SR_8KHZ; break;
case 16000: newrate = HARMONY_SR_16KHZ; break;
case 27428: newrate = HARMONY_SR_27KHZ; break;
@@ -343,7 +324,8 @@ static int harmony_detect_rate(int freq)
case 44100: newrate = HARMONY_SR_44KHZ; break;
case 33075: newrate = HARMONY_SR_33KHZ; break;
case 6615: newrate = HARMONY_SR_6KHZ; break;
- default: newrate = HARMONY_SR_8KHZ;
+ default: newrate = HARMONY_SR_8KHZ;
+ *freq = 8000; break;
}
return newrate;
}
@@ -360,23 +342,20 @@ static void harmony_set_stereo(u8 stereo
harmony_update_control();
}
-static int harmony_disable_interrupts(void)
+static void harmony_disable_interrupts(void)
{
harmony_wait_CNTL();
-
gsc_writel(0, &(harmony.hpa->dstatus));
- return 0;
}
-static int harmony_enable_interrupts(void)
+static void harmony_enable_interrupts(void)
{
harmony_wait_CNTL();
gsc_writel(DSTATUS_IE, &(harmony.hpa->dstatus));
- return 0;
}
/*
- * silence()
+ * harmony_silence()
*
* This subroutine fills in a buffer starting at location start and
* silences for length bytes. This references the current
@@ -384,21 +363,20 @@ static int harmony_enable_interrupts(voi
*
*/
-static int harmony_silence(unsigned char * buffer,int start, int length)
+static void harmony_silence(struct harmony_buffer *buffer, int start, int length)
{
u8 silence_char;
/* Despite what you hear, silence is different in
different audio formats. */
switch (harmony.data_format) {
- case HARMONY_DF_16BIT_LINEAR: silence_char = 0; break;
case HARMONY_DF_8BIT_ULAW: silence_char = 0x55; break;
case HARMONY_DF_8BIT_ALAW: silence_char = 0xff; break;
+ case HARMONY_DF_16BIT_LINEAR: /* fall through */
default: silence_char = 0;
}
- memset(played_buf+start, silence_char, length);
- return 0;
+ memset(buffer->addr+start, silence_char, length);
}
@@ -408,32 +386,23 @@ static int harmony_audio_open(struct ino
return -EBUSY;
harmony.audio_open++;
- harmony.frames_so_far = 0;
- harmony.done = 0;
harmony.format_initialized = 0;
- harmony.suspended_playing = 1;
- harmony.blocked_playing = 0;
- harmony.suspended_recording =1;
- harmony.blocked_recording=0;
- harmony.first_filled_play = 0;
- harmony.nb_filled_play = 0;
- harmony.first_filled_record = 0;
- harmony.nb_filled_record = 0;
+ harmony.suspended_playing = harmony.suspended_recording = 1;
+ harmony.blocked_playing = harmony.blocked_recording = 0;
+ harmony.first_filled_play = harmony.first_filled_record = 0;
+ harmony.nb_filled_play = harmony.nb_filled_record = 0;
init_waitqueue_head(&harmony.wq_play);
init_waitqueue_head(&harmony.wq_record);
/* Start off in a balanced mode. */
-
harmony_set_control(HARMONY_DF_16BIT_LINEAR, HARMONY_SR_44KHZ, HARMONY_SS_STEREO);
harmony_update_control();
/* Clear out all the buffers and flush to cache */
- harmony_silence(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS);
- CHECK_WBACK_INV(played_buf, HARMONY_BUF_SIZE*MAX_BUFS);
+ harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS);
+ CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS);
-// harmony_mixer_mute_all();
-
harmony_wait_CNTL();
return 0;
@@ -448,16 +417,14 @@ static int harmony_audio_release(struct
if (!harmony.audio_open)
return -EBUSY;
- harmony.done = 1;
harmony.audio_open--;
+
return 0;
}
-static loff_t harmony_audio_llseek(struct file *file, loff_t offset, int whence)
-{
- DPRINTK(KERN_ERR "%s: invalid llseek\n", __FUNCTION__);
- return -ENODEV;
-}
+/*
+ * Read recorded data off the audio device.
+ */
static ssize_t harmony_audio_read(struct file *file,
char *buffer,
@@ -473,33 +440,32 @@ static ssize_t harmony_audio_read(struct
harmony_wait_CNTL();
/* Figure out which buffer to fill in */
-
- if(harmony.nb_filled_record<=2) {
+ if (harmony.nb_filled_record <= 2) {
harmony.blocked_recording = 1;
- if(harmony.suspended_recording) {
- harmony.suspended_recording=0;
+ if (harmony.suspended_recording) {
+ harmony.suspended_recording = 0;
harmony_enable_interrupts();
}
- interruptible_sleep_on(&(harmony.wq_record));
- harmony.blocked_recording=0;
+ interruptible_sleep_on(&harmony.wq_record);
+ harmony.blocked_recording = 0;
}
- if(harmony.nb_filled_record<2) return -EBUSY;
+ if (harmony.nb_filled_record < 2)
+ return -EBUSY;
+
buf_to_read = harmony.first_filled_record;
- /* Figure out the size of the frame */
-
/* Copy the page to an aligned buffer */
copy_to_user(buffer+count,
- recorded_buf+(HARMONY_BUF_SIZE*buf_to_read),
+ recorded_buf.addr+(HARMONY_BUF_SIZE*buf_to_read),
HARMONY_BUF_SIZE);
harmony.nb_filled_record--;
harmony.first_filled_record++;
- harmony.first_filled_record%=MAX_BUFS;
+ harmony.first_filled_record %= MAX_BUFS;
- count += 4096;
+ count += HARMONY_BUF_SIZE;
}
return count;
}
@@ -508,7 +474,7 @@ static ssize_t harmony_audio_read(struct
/* Here is the place where we try to recognize file format.
- Sun/NeXT .au files began with the string .snd
+ Sun/NeXT .au files begin with the string .snd
At offset 12 is specified the encoding.
At offset 16 is specified speed rate
At Offset 20 is specified the numbers of voices
@@ -522,7 +488,7 @@ static ssize_t harmony_audio_read(struct
#define test_rate(tested,real_value,harmony_value) if ((tested)<=(real_value))\
-static void harmony_format_auto_detect(const char * buffer, int block_size)
+static void harmony_format_auto_detect(const char *buffer, int block_size)
{
u8 file_header[24];
u32 start_string;
@@ -554,7 +520,6 @@ static void harmony_format_auto_detect(c
harmony_set_control(HARMONY_DF_16BIT_LINEAR,
HARMONY_SR_44KHZ, HARMONY_SS_STEREO);
return;
-
}
switch (nb_voices) {
case HARMONY_MAGIC_MONO:
@@ -567,7 +532,8 @@ static void harmony_format_auto_detect(c
harmony.stereo_select = HARMONY_SS_MONO;
break;
}
- harmony_set_rate(harmony_detect_rate(speed));
+ harmony_set_rate(harmony_detect_rate(&speed));
+ harmony.dac_rate = speed;
return;
}
}
@@ -595,13 +561,13 @@ static ssize_t harmony_audio_write(struc
harmony_wait_CNTL();
/* Figure out which buffer to fill in */
-
- if(harmony.nb_filled_play+2 >= MAX_BUFS) {
+ if (harmony.nb_filled_play+2 >= MAX_BUFS) {
harmony.blocked_playing = 1;
- interruptible_sleep_on(&(harmony.wq_play));
- harmony.blocked_playing=0;
+ interruptible_sleep_on(&harmony.wq_play);
+ harmony.blocked_playing = 0;
}
- if(harmony.nb_filled_play+2 >= MAX_BUFS) return -EBUSY;
+ if (harmony.nb_filled_play+2 >= MAX_BUFS)
+ return -EBUSY;
buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play) % MAX_BUFS;
@@ -613,24 +579,23 @@ static ssize_t harmony_audio_write(struc
frame_size = total_count - count;
/* Clear out the buffer, since there we'll only be
overlaying part of the old buffer with the new one */
- harmony_silence(played_buf, HARMONY_BUF_SIZE*buf_to_fill,
- HARMONY_BUF_SIZE);
+ harmony_silence(&played_buf,
+ HARMONY_BUF_SIZE*buf_to_fill+frame_size,
+ HARMONY_BUF_SIZE-frame_size);
}
/* Copy the page to an aligned buffer */
- copy_from_user(played_buf+(HARMONY_BUF_SIZE*buf_to_fill),
+ copy_from_user(played_buf.addr + (HARMONY_BUF_SIZE*buf_to_fill),
buffer+count, frame_size);
+ CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill),
+ frame_size);
harmony.nb_filled_play++;
-
- CHECK_WBACK_INV(played_buf, HARMONY_BUF_SIZE*MAX_BUFS);
- harmony.done = 0;
count += frame_size;
- if (harmony.suspended_playing && (harmony.nb_filled_play>=4)) {
+ if (harmony.suspended_playing && (harmony.nb_filled_play>=4))
harmony_enable_interrupts();
- }
}
return count;
@@ -639,48 +604,84 @@ static ssize_t harmony_audio_write(struc
static unsigned int harmony_audio_poll(struct file *file,
struct poll_table_struct *wait)
{
- DPRINTK(KERN_ERR "%s: invalid poll\n", __FUNCTION__);
- return -ENODEV;
-}
+ unsigned int mask = 0;
+
+ if (file->f_mode & FMODE_READ) {
+ if (!harmony.suspended_recording)
+ poll_wait(file, &harmony.wq_record, wait);
+ if (harmony.nb_filled_record)
+ mask |= POLLIN | POLLRDNORM;
+ }
-#define DBGEV printk
-#define DBGX printk
-#define DBGP
+ if (file->f_mode & FMODE_WRITE) {
+ if (!harmony.suspended_playing)
+ poll_wait(file, &harmony.wq_play, wait);
+ if (harmony.nb_filled_play)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ return mask;
+}
+
static int harmony_audio_ioctl(struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
- int ival;
- u32 new_format;
+ int ival, new_format;
switch (cmd) {
- case OSS_GETVERSION: /* _SIOR ('M', 118, int) */
- ival = SOUND_VERSION;
- return put_user(ival, (int *) arg);
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, (int *) arg);
- case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */
- ival = DSP_CAP_DUPLEX | DSP_CAP_BATCH ;
+ case SNDCTL_DSP_GETCAPS:
+ ival = DSP_CAP_DUPLEX | DSP_CAP_BATCH;
return put_user(ival, (int *) arg);
- case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */
- ival = (AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW );
+ case SNDCTL_DSP_GETFMTS:
+ ival = (AFMT_S16_BE | AFMT_U16_BE | AFMT_MU_LAW | AFMT_A_LAW );
return put_user(ival, (int *) arg);
- break;
+
+ case SNDCTL_DSP_SETFMT:
+ if (get_user(ival, (int *) arg))
+ return -EFAULT;
+ if (ival != AFMT_QUERY) {
+ switch (ival) {
+ case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break;
+ case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break;
+ case AFMT_S16_BE: /* fall through */
+ case AFMT_S16_LE: /* fall through, this is wrong but some don't use _NE */
+ case AFMT_U16_BE: new_format = HARMONY_DF_16BIT_LINEAR; break;
+ case AFMT_U16_LE: /* fall through, not supported */
+ default: {
+ printk(KERN_WARNING PFX "can't set unsupported sound format (%d)\n",
+ ival);
+ return -EINVAL;
+ }
+ }
+ harmony_set_format(new_format);
+ } else {
+ switch (harmony.data_format) {
+ case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break;
+ case HARMONY_DF_8BIT_ALAW: ival = AFMT_A_LAW; break;
+ case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break;
+ default: ival = 0;
+ }
+ }
+ return put_user(ival, (int *) arg);
- case SOUND_PCM_READ_RATE: /* _SIOR ('P', 2, int) */
- /* I have no idea what this should be. */
- ival = 0;
+ case SOUND_PCM_READ_RATE:
+ ival = harmony.dac_rate;
return put_user(ival, (int *) arg);
case SNDCTL_DSP_SPEED:
if (get_user(ival, (int *) arg))
return -EFAULT;
- harmony_set_rate(harmony_detect_rate(ival));
+ harmony_set_rate(harmony_detect_rate(&ival));
+ harmony.dac_rate = ival;
return put_user(ival, (int*) arg);
- case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */
+ case SNDCTL_DSP_STEREO:
if (get_user(ival, (int *) arg))
return -EFAULT;
if (ival != 0 && ival != 1)
@@ -688,59 +689,33 @@ static int harmony_audio_ioctl(struct in
harmony_set_stereo(ival);
return put_user(ival, (int *) arg);
- case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */
+ case SNDCTL_DSP_GETBLKSIZE:
ival = HARMONY_BUF_SIZE;
return put_user(ival, (int *) arg);
- case SNDCTL_DSP_RESET:
- return 0;
-
- case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- if (ival != AFMT_QUERY) {
- switch (ival) {
- case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break;
- case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break;
- case AFMT_S16_LE: /* fall through */
- case AFMT_S16_BE: /* fall through, not sure. */
- case AFMT_U16_LE: /* fall through */
- case AFMT_U16_BE: new_format = HARMONY_DF_16BIT_LINEAR; break;
- default: {
- DPRINTK("%s: Invalid sound format %d\n",
- __FUNCTION__, ival);
- return -EINVAL;
- }
- }
- harmony_set_format(new_format);
- } else {
- switch (harmony.data_format) {
- case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break;
- case HARMONY_DF_8BIT_ALAW: ival = AFMT_A_LAW; break;
- case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break;
- default: ival = 0;
- }
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+
+ case SNDCTL_DSP_RESET:
+ if (!harmony.suspended_recording) {
+ /* TODO: stop_recording() */
}
- return put_user(ival, (int *) arg);
+ return 0;
- case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */
+ case SNDCTL_DSP_SETFRAGMENT:
if (get_user(ival, (int *)arg))
return -EFAULT;
if ((ival & 0xffff)*((ival>>16)&0xffff) > (MAX_BUFS*HARMONY_BUF_SIZE))
return -EINVAL;
+ /* TODO */
return 0;
-
- default:
- return -EINVAL;
}
return -EINVAL;
}
-static int harmony_audio_mmap(struct file *file, struct vm_area_struct *vma)
-{
- DPRINTK(KERN_ERR "%s: invalid mmap\n", __FUNCTION__);
- return -ENODEV;
-}
+
+/* harmony_interrupt() - main harmony interruption service routine */
static void harmony_interrupt(int irq, void *dev, struct pt_regs *regs)
{
@@ -761,51 +736,49 @@ static void harmony_interrupt(int irq, v
if (dstatus & DSTATUS_PN) {
if (!harmony.nb_filled_play) {
harmony.suspended_playing = 1;
- harmony_silence(silent, 0,HARMONY_BUF_SIZE);
- gsc_writel(__pa(silent), &hpa->pnxtadd);
+ gsc_writel((unsigned long)silent.dma_handle, &hpa->pnxtadd);
- if(!harmony.suspended_recording) {
+ if (!harmony.suspended_recording)
harmony_enable_interrupts();
- }
} else {
- harmony.suspended_playing=0;
- gsc_writel(__pa(played_buf+(HARMONY_BUF_SIZE*harmony.first_filled_play)),
+ harmony.suspended_playing = 0;
+ gsc_writel((unsigned long)played_buf.dma_handle +
+ (HARMONY_BUF_SIZE*harmony.first_filled_play),
&hpa->pnxtadd);
harmony.first_filled_play++;
harmony.first_filled_play %= MAX_BUFS;
harmony.nb_filled_play--;
harmony_enable_interrupts();
- }
- if (harmony.blocked_playing) {
- wake_up_interruptible(&(harmony.wq_play));
}
+
+ if (harmony.blocked_playing)
+ wake_up_interruptible(&harmony.wq_play);
}
/* Check if we're being asked to fill in a recording buffer */
if (dstatus & DSTATUS_RN) {
if((harmony.nb_filled_record+2>=MAX_BUFS) || harmony.suspended_recording)
{
- harmony.nb_filled_record=0;
- harmony.first_filled_record=0;
+ harmony.nb_filled_record = 0;
+ harmony.first_filled_record = 0;
harmony.suspended_recording = 1;
- gsc_writel(__pa(graveyard), &hpa->rnxtadd);
- if(!harmony.suspended_playing)
+ gsc_writel((unsigned long)graveyard.dma_handle, &hpa->rnxtadd);
+ if (!harmony.suspended_playing)
harmony_enable_interrupts();
} else {
int buf_to_fill;
- buf_to_fill=(harmony.first_filled_record+harmony.nb_filled_record)%MAX_BUFS;
- CHECK_WBACK_INV(recorded_buf+(HARMONY_BUF_SIZE*buf_to_fill), HARMONY_BUF_SIZE);
- gsc_writel(__pa(recorded_buf+(HARMONY_BUF_SIZE*(buf_to_fill))),&hpa->rnxtadd);
+ buf_to_fill = (harmony.first_filled_record+harmony.nb_filled_record) % MAX_BUFS;
+ CHECK_WBACK_INV_OFFSET(recorded_buf, HARMONY_BUF_SIZE*buf_to_fill, HARMONY_BUF_SIZE);
+ gsc_writel((unsigned long)recorded_buf.dma_handle +
+ HARMONY_BUF_SIZE*buf_to_fill,
+ &hpa->rnxtadd);
harmony.nb_filled_record++;
harmony_enable_interrupts();
}
- if (harmony.blocked_recording &&
- harmony.nb_filled_record>3) {
-
- wake_up_interruptible(&(harmony.wq_record));
- }
+ if (harmony.blocked_recording && harmony.nb_filled_record>3)
+ wake_up_interruptible(&harmony.wq_record);
}
}
@@ -813,12 +786,11 @@ static void harmony_interrupt(int irq, v
static struct file_operations harmony_audio_fops = {
owner: THIS_MODULE,
- llseek: harmony_audio_llseek,
+ llseek: no_llseek,
read: harmony_audio_read,
write: harmony_audio_write,
poll: harmony_audio_poll,
ioctl: harmony_audio_ioctl,
- mmap: harmony_audio_mmap,
open: harmony_audio_open,
release:harmony_audio_release,
};
@@ -826,22 +798,28 @@ static struct file_operations harmony_au
static int harmony_audio_init(void)
{
/* Request that IRQ */
- request_irq(harmony.irq, harmony_interrupt, 0 ,"harmony", &harmony);
+ if (request_irq(harmony.irq, harmony_interrupt, 0 ,"harmony", &harmony)) {
+ printk(KERN_ERR PFX "Error requesting irq %d.\n", harmony.irq);
+ return -EFAULT;
+ }
harmony.dsp_unit = register_sound_dsp(&harmony_audio_fops, -1);
if (harmony.dsp_unit < 0) {
- printk(KERN_ERR "%s: Error registering dsp\n", __FUNCTION__);
+ printk(KERN_ERR PFX "Error registering dsp\n");
free_irq(harmony.irq, &harmony);
return -EFAULT;
}
-
/* Clear the buffers so you don't end up with crap in the buffers. */
- harmony_silence(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS);
+ harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS);
/* Make sure this makes it to cache */
- CHECK_WBACK_INV(played_buf, HARMONY_BUF_SIZE*MAX_BUFS);
+ CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS);
+ /* Clear out the silent buffer and flush to cache */
+ harmony_silence(&silent, 0, HARMONY_BUF_SIZE);
+ CHECK_WBACK_INV_OFFSET(silent, 0, HARMONY_BUF_SIZE);
+
/* Wait around until we're out of control mode */
harmony_wait_CNTL();
@@ -860,18 +838,6 @@ static void harmony_mixer_set_gain(void)
harmony_wait_CNTL();
}
-static void harmony_mixer_mute_all(void)
-{
- harmony_wait_CNTL();
- gsc_writel(GAIN_TOTAL_SILENCE, &harmony.hpa->gainctl);
- harmony_wait_CNTL();
-}
-
-static void harmony_mixer_unmute(void)
-{
- harmony_mixer_set_gain();
-}
-
/*
* Read gain of selected channel.
* The OSS rate is from 0 (silent) to 100 -> need some conversions
@@ -934,7 +900,8 @@ static int harmony_mixer_set_level(int c
left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL);
new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL);
new_left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL);
- harmony.current_gain = (harmony.current_gain & ~(GAIN_LO_MASK | GAIN_RO_MASK)) | (left_level << GAIN_LO_SHIFT) | (right_level << GAIN_RO_SHIFT);
+ harmony.current_gain = (harmony.current_gain & ~(GAIN_LO_MASK | GAIN_RO_MASK))
+ | (left_level << GAIN_LO_SHIFT) | (right_level << GAIN_RO_SHIFT);
harmony_mixer_set_gain();
return (new_right_level << 8) + new_left_level;
@@ -943,7 +910,8 @@ static int harmony_mixer_set_level(int c
left_level = to_harmony_level(left_level, MAX_INPUT_LEVEL);
new_right_level = to_oss_level(right_level, MAX_INPUT_LEVEL);
new_left_level = to_oss_level(left_level, MAX_INPUT_LEVEL);
- harmony.current_gain = (harmony.current_gain & ~(GAIN_LI_MASK | GAIN_RI_MASK)) | (left_level << GAIN_LI_SHIFT) | (right_level << GAIN_RI_SHIFT);
+ harmony.current_gain = (harmony.current_gain & ~(GAIN_LI_MASK | GAIN_RI_MASK))
+ | (left_level << GAIN_LI_SHIFT) | (right_level << GAIN_RI_SHIFT);
harmony_mixer_set_gain();
return (new_right_level << 8) + new_left_level;
@@ -1004,9 +972,7 @@ static int harmony_mixer_set_recmask(int
static int harmony_mixer_get_outmask(void)
{
- int outmask;
-
- outmask = 0;
+ int outmask = 0;
if (harmony.current_gain & GAIN_HE_MASK) outmask |=SOUND_MASK_PHONEOUT;
if (harmony.current_gain & GAIN_LE_MASK) outmask |=SOUND_MASK_LINE;
@@ -1047,62 +1013,79 @@ static int harmony_mixer_ioctl(struct in
int val;
int ret;
- if (((cmd >> 8) & 0xff) == 'M') {
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- if (get_user(val, (int *)arg))
- return -EFAULT;
-
- switch (cmd & 0xff) {
- case SOUND_MIXER_RECSRC:
- ret = harmony_mixer_set_recmask(val);
- break;
-
- case SOUND_MIXER_OUTSRC:
- ret = harmony_mixer_set_outmask(val);
- break;
-
- default:
- ret = harmony_mixer_set_level(cmd & 0xff, val);
- }
- } else {
- switch (cmd & 0xff) {
-
- case SOUND_MIXER_RECSRC:
- ret = harmony_mixer_get_recmask();
- break;
-
- case SOUND_MIXER_OUTSRC:
- ret = harmony_mixer_get_outmask();
- break;
-
- case SOUND_MIXER_DEVMASK:
- ret = HARMONY_MIXER_DEVICES;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- ret = HARMONY_STEREO_DEVICES;
- break;
-
- case SOUND_MIXER_RECMASK:
- ret = HARMONY_RECORDING_DEVICES;
- break;
-
- case SOUND_MIXER_OUTMASK:
- ret = HARMONY_OUTPUT_DEVICES;
- break;
-
- case SOUND_MIXER_CAPS:
- ret = HARMONY_CAPS;
- break;
+ if (cmd == SOUND_MIXER_INFO) {
+ mixer_info info;
+ memset(&info, 0, sizeof(info));
+ strncpy(info.id, "harmony", sizeof(info.id)-1);
+ strncpy(info.name, "Harmony audio", sizeof(info.name)-1);
+ info.modify_counter = 1; /* ? */
+ if (copy_to_user((void *)arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (cmd == OSS_GETVERSION)
+ return put_user(SOUND_VERSION, (int *)arg);
+
+ /* read */
+ val = 0;
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+
+ switch (cmd) {
+ case MIXER_READ(SOUND_MIXER_CAPS):
+ ret = SOUND_CAP_EXCL_INPUT;
+ break;
+ case MIXER_READ(SOUND_MIXER_STEREODEVS):
+ ret = SOUND_MASK_IGAIN | SOUND_MASK_OGAIN;
+ break;
+
+ case MIXER_READ(SOUND_MIXER_RECMASK):
+ ret = SOUND_MASK_MIC | SOUND_MASK_LINE;
+ break;
+ case MIXER_READ(SOUND_MIXER_DEVMASK):
+ ret = SOUND_MASK_OGAIN | SOUND_MASK_IGAIN |
+ SOUND_MASK_VOLUME;
+ break;
+ case MIXER_READ(SOUND_MIXER_OUTMASK):
+ ret = SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
+ SOUND_MASK_PHONEOUT;
+ break;
+
+ case MIXER_WRITE(SOUND_MIXER_RECSRC):
+ ret = harmony_mixer_set_recmask(val);
+ break;
+ case MIXER_READ(SOUND_MIXER_RECSRC):
+ ret = harmony_mixer_get_recmask();
+ break;
- default:
- ret = harmony_mixer_get_level(cmd & 0xff);
- break;
- }
- }
- return put_user(ret, (int *)arg);
- } else
+ case MIXER_WRITE(SOUND_MIXER_OUTSRC):
+ ret = harmony_mixer_set_outmask(val);
+ break;
+ case MIXER_READ(SOUND_MIXER_OUTSRC):
+ ret = harmony_mixer_get_outmask();
+ break;
+
+ case MIXER_WRITE(SOUND_MIXER_OGAIN):
+ case MIXER_WRITE(SOUND_MIXER_IGAIN):
+ case MIXER_WRITE(SOUND_MIXER_VOLUME):
+ ret = harmony_mixer_set_level(cmd & 0xff, val);
+ break;
+
+ case MIXER_READ(SOUND_MIXER_OGAIN):
+ case MIXER_READ(SOUND_MIXER_IGAIN):
+ case MIXER_READ(SOUND_MIXER_VOLUME):
+ ret = harmony_mixer_get_level(cmd & 0xff);
+ break;
+
+ default:
return -EINVAL;
+ }
+
+ if (put_user(ret, (int *)arg))
+ return -EFAULT;
+ return 0;
}
@@ -1124,9 +1107,10 @@ static int harmony_mixer_release(struct
static struct file_operations harmony_mixer_fops = {
owner: THIS_MODULE,
+ llseek: no_llseek,
open: harmony_mixer_open,
- ioctl: harmony_mixer_ioctl,
release: harmony_mixer_release,
+ ioctl: harmony_mixer_ioctl,
};
@@ -1150,7 +1134,7 @@ static int __init harmony_mixer_init(voi
/* Register the device file operations */
harmony.mixer_unit = register_sound_mixer(&harmony_mixer_fops, -1);
if (harmony.mixer_unit < 0) {
- printk(KERN_WARNING "Harmony: Error Registering Mixer Driver\n");
+ printk(KERN_WARNING PFX "Error Registering Mixer Driver\n");
return -EFAULT;
}
@@ -1174,24 +1158,29 @@ harmony_driver_callback(struct parisc_de
u32 cntl;
int ret;
+ if (harmony.hpa) {
+ /* We only support one Harmony at this time */
+ printk(KERN_ERR PFX "driver already registered\n");
+ return -EBUSY;
+ }
+
/* Set the HPA of harmony */
harmony.hpa = (struct harmony_hpa *)dev->hpa;
harmony.irq = dev->irq;
if (!harmony.irq) {
- printk(KERN_ERR "%s: problem getting irq\n", __FUNCTION__);
+ printk(KERN_ERR PFX "no irq found\n");
return -ENODEV;
}
/* Grab the ID and revision from the device */
id = gsc_readb(&harmony.hpa->id);
if ((id | 1) != 0x15) {
- printk(KERN_WARNING "%s: wrong id %02x\n",
- __FUNCTION__, id);
+ printk(KERN_WARNING PFX "wrong harmony id 0x%02x\n", id);
return -EBUSY;
}
cntl = gsc_readl(&harmony.hpa->cntl);
- rev = (cntl>>20) & 0x3f; /* 0xff ?? */
+ rev = (cntl>>20) & 0xff;
printk(KERN_INFO "Lasi Harmony Audio rev. %i at 0x%lx, using IRQ %i\n",
rev, dev->hpa, harmony.irq);
@@ -1199,8 +1188,20 @@ harmony_driver_callback(struct parisc_de
/* Make sure the control bit isn't set, although I don't think it
ever is. */
if (cntl & CNTL_C) {
- printk(KERN_WARNING "%s: CNTL busy\n",
- __FUNCTION__);
+ printk(KERN_WARNING PFX "CNTL busy\n");
+ return -EBUSY;
+ }
+
+ /* Initialize the memory buffers,
+ * graveyard and silent buffers don't really need consistent memory */
+ if (harmony_alloc_buffer(&played_buf, MAX_BUFS, 1) ||
+ harmony_alloc_buffer(&recorded_buf, MAX_BUFS, 1) ||
+ harmony_alloc_buffer(&graveyard, 1, 0) ||
+ harmony_alloc_buffer(&silent, 1, 0)) {
+ harmony_free_buffer(&played_buf);
+ harmony_free_buffer(&recorded_buf);
+ harmony_free_buffer(&graveyard);
+ harmony_free_buffer(&silent);
return -EBUSY;
}
@@ -1240,6 +1241,10 @@ static void __exit cleanup_harmony(void)
unregister_sound_mixer(harmony.mixer_unit);
unregister_sound_dsp(harmony.dsp_unit);
release_mem_region((unsigned long)harmony.hpa, 13);
+ harmony_free_buffer(&played_buf);
+ harmony_free_buffer(&recorded_buf);
+ harmony_free_buffer(&graveyard);
+ harmony_free_buffer(&silent);
unregister_parisc_driver(&harmony_driver);
}
--------------Boundary-00=_M65C5V9A3SIQ7S6WV2NP--