patch-2.3.11 linux/drivers/sound/dmasound.c
Next file: linux/drivers/sound/es1370.c
Previous file: linux/drivers/sound/awacs_defs.h
Back to the patch index
Back to the overall index
- Lines: 1193
- Date:
Mon Jul 12 16:21:25 1999
- Orig file:
v2.3.10/linux/drivers/sound/dmasound.c
- Orig date:
Mon Jun 7 12:12:22 1999
diff -u --recursive --new-file v2.3.10/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
@@ -131,7 +131,7 @@
static int irq_installed = 0;
#endif /* MODULE */
static char **sound_buffers = NULL;
-
+static char **sound_read_buffers = NULL;
#ifdef CONFIG_ATARI
extern void atari_microwire_cmd(int cmd);
@@ -184,6 +184,9 @@
static void *awacs_tx_cmd_space;
static volatile struct dbdma_cmd *awacs_tx_cmds;
+static void *awacs_rx_cmd_space;
+static volatile struct dbdma_cmd *awacs_rx_cmds;
+
/*
* Cached values of AWACS registers (we can't read them).
* Except on the burgundy. XXX
@@ -239,6 +242,7 @@
static int beep_volume = BEEP_VOLUME;
static int beep_playing = 0;
+static int awacs_beep_state = 0;
static short *beep_buf;
static volatile struct dbdma_cmd *beep_dbdma_cmd;
static void (*orig_mksound)(unsigned int, unsigned int);
@@ -276,10 +280,15 @@
#define MIN_BUFSIZE 4
#define MAX_BUFSIZE 128 /* Limit for Amiga */
-static int catchRadius = 0, numBufs = 4, bufSize = 32;
+static int catchRadius = 0;
+static int numBufs = 4, bufSize = 32;
+static int numReadBufs = 4, readbufSize = 32;
+
MODULE_PARM(catchRadius, "i");
MODULE_PARM(numBufs, "i");
MODULE_PARM(bufSize, "i");
+MODULE_PARM(numreadBufs, "i");
+MODULE_PARM(readbufSize, "i");
#define arraysize(x) (sizeof(x)/sizeof(*(x)))
#define min(x, y) ((x) < (y) ? (x) : (y))
@@ -630,6 +639,12 @@
static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft);
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft);
#endif /* CONFIG_PPC */
/*** Machine definitions *****************************************************/
@@ -681,6 +696,9 @@
SETTINGS soft; /* software settings */
SETTINGS dsp; /* /dev/dsp default settings */
TRANS *trans; /* supported translations */
+#if defined(CONFIG_PPC)
+ TRANS *read_trans; /* supported translations */
+#endif
int volume_left; /* volume (range is machine dependent) */
int volume_right;
int bass; /* tone (range is machine dependent) */
@@ -746,9 +764,11 @@
static void PMacSilence(void);
static void PMacInit(void);
static void PMacPlay(void);
+static void PMacRecord(void);
static int PMacSetFormat(int format);
static int PMacSetVolume(int volume);
static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs);
+static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs);
static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs);
static void awacs_write(int val);
static int awacs_get_volume(int reg, int lshift);
@@ -776,6 +796,10 @@
size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
+static ssize_t sound_copy_translate_read(const u_char *userPtr,
+ size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft);
/*
@@ -809,8 +833,8 @@
* Amiga: Bit 0 is set: a frame is loaded
* Bit 1 is set: a frame is playing
*/
- int playing;
- wait_queue_head_t write_queue, open_queue, sync_queue;
+ int active;
+ wait_queue_head_t action_queue, open_queue, sync_queue;
int open_mode;
int busy, syncing;
#ifdef CONFIG_ATARI
@@ -822,6 +846,7 @@
};
static struct sound_queue sq;
+static struct sound_queue read_sq;
#define sq_block_address(i) (sq.buffers[i])
#define SIGNAL_RECEIVED (signal_pending(current))
@@ -2172,6 +2197,135 @@
return stereo? utotal * 4: utotal * 2;
}
+static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ ssize_t count, used;
+ short *p = (short *) &frame[*frameUsed];
+ int val, stereo = sound.soft.stereo;
+
+ frameLeft >>= 2;
+ if (stereo)
+ userCount >>= 1;
+ used = count = min(userCount, frameLeft);
+ while (count > 0) {
+ u_char data;
+
+ val = *p++;
+ data = val >> 8;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ if (stereo) {
+ val = *p;
+ data = val >> 8;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ }
+ p++;
+ count--;
+ }
+ *frameUsed += used * 4;
+ return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ ssize_t count, used;
+ short *p = (short *) &frame[*frameUsed];
+ int val, stereo = sound.soft.stereo;
+
+ frameLeft >>= 2;
+ if (stereo)
+ userCount >>= 1;
+ used = count = min(userCount, frameLeft);
+ while (count > 0) {
+ u_char data;
+
+ val = *p++;
+ data = (val >> 8) ^ 0x80;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ if (stereo) {
+ val = *p;
+ data = (val >> 8) ^ 0x80;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ }
+ p++;
+ count--;
+ }
+ *frameUsed += used * 4;
+ return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ ssize_t count, used;
+ int stereo = sound.soft.stereo;
+ short *fp = (short *) &frame[*frameUsed];
+
+ frameLeft >>= 2;
+ userCount >>= (stereo? 2: 1);
+ used = count = min(userCount, frameLeft);
+ if (!stereo) {
+ short *up = (short *) userPtr;
+ while (count > 0) {
+ short data;
+ data = *fp;
+ if (put_user(data, up++))
+ return -EFAULT;
+ fp+=2;
+ count--;
+ }
+ } else {
+ if (copy_to_user((u_char *)userPtr, fp, count * 4))
+ return -EFAULT;
+ }
+ *frameUsed += used * 4;
+ return stereo? used * 4: used * 2;
+}
+
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ ssize_t count, used;
+ int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+ int stereo = sound.soft.stereo;
+ short *fp = (short *) &frame[*frameUsed];
+ short *up = (short *) userPtr;
+
+ frameLeft >>= 2;
+ userCount >>= (stereo? 2: 1);
+ used = count = min(userCount, frameLeft);
+ while (count > 0) {
+ int data;
+
+ data = *fp++;
+ data ^= mask;
+ if (put_user(data, up++))
+ return -EFAULT;
+ if (stereo) {
+ data = *fp;
+ data ^= mask;
+ if (put_user(data, up++))
+ return -EFAULT;
+ }
+ fp++;
+ count--;
+ }
+ *frameUsed += used * 4;
+ return stereo? used * 4: used * 2;
+}
+
+
#endif /* CONFIG_PPC */
@@ -2212,6 +2366,11 @@
pmac_ctx_law, pmac_ctx_law, pmac_ctx_s8, pmac_ctx_u8,
pmac_ctx_s16, pmac_ctx_u16, pmac_ctx_s16, pmac_ctx_u16
};
+
+static TRANS transAwacsNormalRead = {
+ NULL, NULL, pmac_ct_s8_read, pmac_ct_u8_read,
+ pmac_ct_s16_read, pmac_ct_u16_read, pmac_ct_s16_read, pmac_ct_u16_read
+};
#endif /* CONFIG_PPC */
/*** Low level stuff *********************************************************/
@@ -2574,18 +2733,18 @@
/* Since only an even number of samples per frame can
be played, we might lose one byte here. (TO DO) */
sq.front = (sq.front+1) % sq.max_count;
- sq.playing++;
+ sq.active++;
tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
}
static void AtaPlay(void)
{
- /* ++TeSche: Note that sq.playing is no longer just a flag but holds
+ /* ++TeSche: Note that sq.active is no longer just a flag but holds
* the number of frames the DMA is currently programmed for instead,
* may be 0, 1 (currently being played) or 2 (pre-programmed).
*
- * Changes done to sq.count and sq.playing are a bit more subtle again
+ * Changes done to sq.count and sq.active are a bit more subtle again
* so now I must admit I also prefer disabling the irq here rather
* than considering all possible situations. But the point is that
* disabling the irq doesn't have any bad influence on this version of
@@ -2596,13 +2755,13 @@
*/
atari_disable_irq(IRQ_MFP_TIMA);
- if (sq.playing == 2 || /* DMA is 'full' */
+ if (sq.active == 2 || /* DMA is 'full' */
sq.count <= 0) { /* nothing to do */
atari_enable_irq(IRQ_MFP_TIMA);
return;
}
- if (sq.playing == 0) {
+ if (sq.active == 0) {
/* looks like there's nothing 'in' the DMA yet, so try
* to put two frames into it (at least one is available).
*/
@@ -2650,7 +2809,7 @@
#if 0
/* ++TeSche: if you should want to test this... */
static int cnt = 0;
- if (sq.playing == 2)
+ if (sq.active == 2)
if (++cnt == 10) {
/* simulate losing an interrupt */
cnt = 0;
@@ -2667,7 +2826,7 @@
return;
}
- if (!sq.playing) {
+ if (!sq.active) {
/* playing was interrupted and sq_reset() has already cleared
* the sq variables, so better don't do anything here.
*/
@@ -2683,29 +2842,29 @@
* as soon as the irq gets through.
*/
sq.count--;
- sq.playing--;
+ sq.active--;
- if (!sq.playing) {
+ if (!sq.active) {
tt_dmasnd.ctrl = DMASND_CTRL_OFF;
sq.ignore_int = 1;
}
- WAKE_UP(sq.write_queue);
+ WAKE_UP(sq.action_queue);
/* At least one block of the queue is free now
so wake up a writing process blocked because
of a full queue. */
- if ((sq.playing != 1) || (sq.count != 1))
+ if ((sq.active != 1) || (sq.count != 1))
/* We must be a bit carefully here: sq.count indicates the
* number of buffers used and not the number of frames to
- * be played. If sq.count==1 and sq.playing==1 that means
+ * be played. If sq.count==1 and sq.active==1 that means
* the only remaining frame was already programmed earlier
* (and is currently running) so we mustn't call AtaPlay()
* here, otherwise we'll play one frame too much.
*/
AtaPlay();
- if (!sq.playing) WAKE_UP(sq.sync_queue);
+ if (!sq.active) WAKE_UP(sq.sync_queue);
/* We are not playing after AtaPlay(), so there
is nothing to play any more. Wake up a process
waiting for audio output to drain. */
@@ -2902,7 +3061,7 @@
custom.dmacon = AMI_AUDIO_8;
}
sq.front = (sq.front+1) % sq.max_count;
- sq.playing |= AMI_PLAY_LOADED;
+ sq.active |= AMI_PLAY_LOADED;
}
@@ -2912,13 +3071,13 @@
custom.intena = IF_AUD0;
- if (sq.playing & AMI_PLAY_LOADED) {
+ if (sq.active & AMI_PLAY_LOADED) {
/* There's already a frame loaded */
custom.intena = IF_SETCLR | IF_AUD0;
return;
}
- if (sq.playing & AMI_PLAY_PLAYING)
+ if (sq.active & AMI_PLAY_PLAYING)
/* Increase threshold: frame 1 is already being played */
minframes = 2;
@@ -2946,7 +3105,7 @@
{
int minframes = 1;
- if (!sq.playing) {
+ if (!sq.active) {
/* Playing was interrupted and sq_reset() has already cleared
* the sq variables, so better don't do anything here.
*/
@@ -2954,20 +3113,20 @@
return;
}
- if (sq.playing & AMI_PLAY_PLAYING) {
+ if (sq.active & AMI_PLAY_PLAYING) {
/* We've just finished a frame */
sq.count--;
- WAKE_UP(sq.write_queue);
+ WAKE_UP(sq.action_queue);
}
- if (sq.playing & AMI_PLAY_LOADED)
+ if (sq.active & AMI_PLAY_LOADED)
/* Increase threshold: frame 1 is already being played */
minframes = 2;
/* Shift the flags */
- sq.playing = (sq.playing<<1) & AMI_PLAY_MASK;
+ sq.active = (sq.active<<1) & AMI_PLAY_MASK;
- if (!sq.playing)
+ if (!sq.active)
/* No frame is playing, disable audio DMA */
custom.dmacon = AMI_AUDIO_OFF;
@@ -2975,7 +3134,7 @@
/* Try to play the next frame */
AmiPlay();
- if (!sq.playing)
+ if (!sq.active)
/* Nothing to play anymore.
Wake up a process waiting for audio output to drain. */
WAKE_UP(sq.sync_queue);
@@ -3001,7 +3160,8 @@
static int __init PMacIrqInit(void)
{
if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0)
- || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0))
+ || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)
+ || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0))
return 0;
return 1;
}
@@ -3015,7 +3175,10 @@
out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
free_irq(awacs_irq, pmac_awacs_intr);
free_irq(awacs_tx_irq, pmac_awacs_tx_intr);
+ free_irq(awacs_rx_irq, pmac_awacs_rx_intr);
kfree(awacs_tx_cmd_space);
+ if (awacs_rx_cmd_space)
+ kfree(awacs_rx_cmd_space);
if (beep_buf)
kfree(beep_buf);
kd_mksound = orig_mksound;
@@ -3065,10 +3228,10 @@
sound.trans = &transAwacsNormal;
else
sound.trans = &transAwacsExpand;
+ sound.read_trans = &transAwacsNormalRead;
sound.hard.speed = awacs_freqs[i];
awacs_rate_index = i;
- PMacSilence();
/* XXX disable error interrupt on burgundy for now */
out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11
| (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
@@ -3076,6 +3239,18 @@
awacs_write(awacs_reg[1] | MASK_ADDR1);
out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE);
+ /* We really want to execute a DMA stop command, after the AWACS
+ * is initialized.
+ * For reasons I don't understand, it stops the hissing noise
+ * common to many PowerBook G3 systems (like mine :-). Maybe it
+ * is just the AWACS control register change......
+ */
+ out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
+ st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
+ out_le32(&awacs->control, (in_le32(&awacs->control) & ~0x1f00));
+ out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+ out_le32(&awacs_txdma->control, RUN | (RUN << 16));
+
sound.bal = -sound.soft.speed;
}
@@ -3163,20 +3338,23 @@
unsigned long flags;
save_flags(flags); cli();
- if (beep_playing) {
+ if (awacs_beep_state) {
/* sound takes precedence over beeps */
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
out_le32(&awacs->control,
(in_le32(&awacs->control) & ~0x1f00)
- || (awacs_rate_index << 8));
+ | (awacs_rate_index << 8));
out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE);
+ out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count])));
+
beep_playing = 0;
+ awacs_beep_state = 0;
}
- i = sq.front + sq.playing;
+ i = sq.front + sq.active;
if (i >= sq.max_count)
i -= sq.max_count;
- while (sq.playing < 2 && sq.playing < sq.count) {
- count = (sq.count == sq.playing + 1)? sq.rear_size: sq.block_size;
+ while (sq.active < 2 && sq.active < sq.count) {
+ count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size;
if (count < sq.block_size && !sq.syncing)
/* last block not yet filled, and we're not syncing. */
break;
@@ -3187,14 +3365,33 @@
i = 0;
out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP);
out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
- if (sq.playing == 0)
+ if (sq.active == 0)
out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
- ++sq.playing;
+ ++sq.active;
}
restore_flags(flags);
}
+
+static void PMacRecord(void)
+{
+ unsigned long flags;
+
+ if (read_sq.active)
+ return;
+
+ save_flags(flags); cli();
+
+ /* This is all we have to do......Just start it up.
+ */
+ out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
+ read_sq.active = 1;
+
+ restore_flags(flags);
+}
+
+
static void
pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs)
{
@@ -3202,26 +3399,78 @@
int stat;
volatile struct dbdma_cmd *cp;
- while (sq.playing > 0) {
+ while (sq.active > 0) {
cp = &awacs_tx_cmds[i];
stat = ld_le16(&cp->xfer_status);
if ((stat & ACTIVE) == 0)
break; /* this frame is still going */
--sq.count;
- --sq.playing;
+ --sq.active;
if (++i >= sq.max_count)
i = 0;
}
if (i != sq.front)
- WAKE_UP(sq.write_queue);
+ WAKE_UP(sq.action_queue);
sq.front = i;
PMacPlay();
- if (!sq.playing)
+ if (!sq.active)
WAKE_UP(sq.sync_queue);
}
+
+static void
+pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs)
+{
+
+ /* For some reason on my PowerBook G3, I get one interrupt
+ * when the interrupt vector is installed (like something is
+ * pending). This happens before the dbdma is initialize by
+ * us, so I just check the command pointer and if it is zero,
+ * just blow it off.
+ */
+ if (in_le32(&awacs_rxdma->cmdptr) == 0)
+ return;
+
+ /* We also want to blow 'em off when shutting down.
+ */
+ if (read_sq.active == 0)
+ return;
+
+ /* Check multiple buffers in case we were held off from
+ * interrupt processing for a long time. Geeze, I really hope
+ * this doesn't happen.
+ */
+ while (awacs_rx_cmds[read_sq.rear].xfer_status) {
+
+ /* Clear status and move on to next buffer.
+ */
+ awacs_rx_cmds[read_sq.rear].xfer_status = 0;
+ read_sq.rear++;
+
+ /* Wrap the buffer ring.
+ */
+ if (read_sq.rear >= read_sq.max_active)
+ read_sq.rear = 0;
+
+ /* If we have caught up to the front buffer, bump it.
+ * This will cause weird (but not fatal) results if the
+ * read loop is currently using this buffer. The user is
+ * behind in this case anyway, so weird things are going
+ * to happen.
+ */
+ if (read_sq.rear == read_sq.front) {
+ read_sq.front++;
+ if (read_sq.front >= read_sq.max_active)
+ read_sq.front = 0;
+ }
+ }
+
+ WAKE_UP(read_sq.action_queue);
+}
+
+
static void
pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs)
{
@@ -3294,7 +3543,7 @@
beep_timer.expires = jiffies + ticks;
add_timer(&beep_timer);
}
- if (beep_playing || sq.playing || beep_buf == NULL) {
+ if (beep_playing || sq.active || beep_buf == NULL) {
restore_flags(flags);
return; /* too hard, sorry :-( */
}
@@ -3324,6 +3573,7 @@
st_le16(&beep_dbdma_cmd->xfer_status, 0);
st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd));
st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf));
+ awacs_beep_state = 1;
save_flags(flags); cli();
if (beep_playing) { /* i.e. haven't been terminated already */
@@ -3781,6 +4031,45 @@
return 0;
}
+static ssize_t sound_copy_translate_read(const u_char *userPtr,
+ size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL;
+
+ switch (sound.soft.format) {
+ case AFMT_MU_LAW:
+ ct_func = sound.read_trans->ct_ulaw;
+ break;
+ case AFMT_A_LAW:
+ ct_func = sound.read_trans->ct_alaw;
+ break;
+ case AFMT_S8:
+ ct_func = sound.read_trans->ct_s8;
+ break;
+ case AFMT_U8:
+ ct_func = sound.read_trans->ct_u8;
+ break;
+ case AFMT_S16_BE:
+ ct_func = sound.read_trans->ct_s16be;
+ break;
+ case AFMT_U16_BE:
+ ct_func = sound.read_trans->ct_u16be;
+ break;
+ case AFMT_S16_LE:
+ ct_func = sound.read_trans->ct_s16le;
+ break;
+ case AFMT_U16_LE:
+ ct_func = sound.read_trans->ct_u16le;
+ break;
+ }
+ if (ct_func)
+ return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+ else
+ return 0;
+}
+
/*
* /dev/mixer abstraction
@@ -3999,7 +4288,8 @@
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
| SOUND_MASK_LINE | SOUND_MASK_MIC
| SOUND_MASK_CD | SOUND_MASK_RECLEV
- | SOUND_MASK_ALTPCM;
+ | SOUND_MASK_ALTPCM
+ | SOUND_MASK_MONITOR;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_RECMASK:
data = SOUND_MASK_LINE | SOUND_MASK_MIC
@@ -4013,20 +4303,27 @@
data |= SOUND_MASK_MIC;
if (awacs_reg[0] & MASK_MUX_CD)
data |= SOUND_MASK_CD;
+ if (awacs_reg[1] & MASK_LOOPTHRU)
+ data |= SOUND_MASK_MONITOR;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_RECSRC:
IOCTL_IN(arg, data);
data &= (SOUND_MASK_LINE
- | SOUND_MASK_MIC | SOUND_MASK_CD);
+ | SOUND_MASK_MIC | SOUND_MASK_CD
+ | SOUND_MASK_MONITOR);
awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
| MASK_MUX_AUDIN);
+ awacs_reg[1] &= ~MASK_LOOPTHRU;
if (data & SOUND_MASK_LINE)
awacs_reg[0] |= MASK_MUX_AUDIN;
if (data & SOUND_MASK_MIC)
awacs_reg[0] |= MASK_MUX_MIC;
if (data & SOUND_MASK_CD)
awacs_reg[0] |= MASK_MUX_CD;
+ if (data & SOUND_MASK_MONITOR)
+ awacs_reg[1] |= MASK_LOOPTHRU;
awacs_write(awacs_reg[0] | MASK_ADDR0);
+ awacs_write(awacs_reg[1] | MASK_ADDR1);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_STEREODEVS:
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
@@ -4347,7 +4644,61 @@
}
-static void sq_setup(int numBufs, int bufSize, char **buffers)
+static int sq_allocate_read_buffers(void)
+{
+ int i;
+ int j;
+
+ if (sound_read_buffers)
+ return 0;
+ sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL);
+ if (!sound_read_buffers)
+ return -ENOMEM;
+ for (i = 0; i < numBufs; i++) {
+ sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10,
+ GFP_KERNEL);
+ if (!sound_read_buffers[i]) {
+ while (i--)
+ sound.mach.dma_free (sound_read_buffers[i],
+ readbufSize << 10);
+ kfree (sound_read_buffers);
+ sound_read_buffers = 0;
+ return -ENOMEM;
+ }
+ /* XXXX debugging code */
+ for (j=0; j<readbufSize; j++) {
+ sound_read_buffers[i][j] = 0xef;
+ }
+ }
+ return 0;
+}
+
+static void sq_release_read_buffers(void)
+{
+ int i;
+ volatile struct dbdma_cmd *cp;
+
+
+ if (sound_read_buffers) {
+
+#if CONFIG_PPC
+ cp = awacs_rx_cmds;
+ for (i = 0; i < numReadBufs; i++,cp++) {
+ st_le16(&cp->command, DBDMA_STOP);
+ }
+ /* We should probably wait for the thing to stop before we
+ release the memory */
+#endif
+ for (i = 0; i < numBufs; i++)
+ sound.mach.dma_free (sound_read_buffers[i],
+ bufSize << 10);
+ kfree (sound_read_buffers);
+ sound_read_buffers = 0;
+ }
+}
+
+
+static void sq_setup(int numBufs, int bufSize, char **write_buffers)
{
#ifdef CONFIG_PPC
int i;
@@ -4357,12 +4708,12 @@
sq.max_count = numBufs;
sq.max_active = numBufs;
sq.block_size = bufSize;
- sq.buffers = buffers;
+ sq.buffers = write_buffers;
sq.front = sq.count = 0;
sq.rear = -1;
sq.syncing = 0;
- sq.playing = 0;
+ sq.active = 0;
#ifdef CONFIG_ATARI
sq.ignore_int = 0;
@@ -4375,7 +4726,7 @@
cp = awacs_tx_cmds;
memset((void *) cp, 0, (numBufs + 1) * sizeof(struct dbdma_cmd));
for (i = 0; i < numBufs; ++i, ++cp) {
- st_le32(&cp->phy_addr, virt_to_bus(buffers[i]));
+ st_le32(&cp->phy_addr, virt_to_bus(write_buffers[i]));
}
st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds));
@@ -4384,6 +4735,59 @@
#endif /* CONFIG_PPC */
}
+static void read_sq_setup(int numBufs, int bufSize, char **read_buffers)
+{
+#ifdef CONFIG_PPC
+ int i;
+ volatile struct dbdma_cmd *cp;
+#endif /* CONFIG_PPC */
+
+ read_sq.max_count = numBufs;
+ read_sq.max_active = numBufs;
+ read_sq.block_size = bufSize;
+ read_sq.buffers = read_buffers;
+
+ read_sq.front = read_sq.count = 0;
+ read_sq.rear = 0;
+ read_sq.rear_size = 0;
+ read_sq.syncing = 0;
+ read_sq.active = 0;
+
+#ifdef CONFIG_ATARI
+ read_sq.ignore_int = 0;
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+ read_sq.block_size_half = read_sq.block_size>>1;
+ read_sq.block_size_quarter = read_sq.block_size_half>>1;
+#endif /* CONFIG_AMIGA */
+#ifdef CONFIG_PPC
+ cp = awacs_rx_cmds;
+ memset((void *) cp, 0, (numBufs + 1) * sizeof(struct dbdma_cmd));
+
+ /* Set dma buffers up in a loop */
+ for (i = 0; i < numBufs; i++,cp++) {
+ st_le32(&cp->phy_addr, virt_to_bus(read_buffers[i]));
+ st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS);
+ st_le16(&cp->req_count, read_sq.block_size);
+ st_le16(&cp->xfer_status, 0);
+ }
+
+ /* The next two lines make the thing loop around.
+ */
+ st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
+ st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds));
+
+ /* Don't start until the first read is done.
+ * This will also abort any operations in progress if the DMA
+ * happens to be running (and it shouldn't).
+ */
+ out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+ out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));
+
+#endif /* CONFIG_PPC */
+}
+
+
static void sq_play(void)
{
(*sound.mach.play)();
@@ -4428,7 +4832,7 @@
sq_play();
if (NON_BLOCKING(sq.open_mode))
return uWritten > 0 ? uWritten : -EAGAIN;
- SLEEP(sq.write_queue, ONE_SECOND);
+ SLEEP(sq.action_queue, ONE_SECOND);
if (SIGNAL_RECEIVED)
return uWritten > 0 ? uWritten : -EINTR;
}
@@ -4462,29 +4866,117 @@
}
+/***********/
+
+/* Here is how the values are used for reading.
+ * The value 'active' simply indicates the DMA is running. This is
+ * done so the driver semantics are DMA starts when the first read is
+ * posted. The value 'front' indicates the buffer we should next
+ * send to the user. The value 'rear' indicates the buffer the DMA is
+ * currently filling. When 'front' == 'rear' the buffer "ring" is
+ * empty (we always have an empty available). The 'rear_size' is used
+ * to track partial offsets into the current buffer. Right now, I just keep
+ * the DMA running. If the reader can't keep up, the interrupt tosses
+ * the oldest buffer. We could also shut down the DMA in this case.
+ */
+static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,
+ loff_t *ppos)
+{
+
+ ssize_t uRead, bLeft, bUsed, uUsed;
+
+ if (uLeft == 0)
+ return 0;
+
+ if (!read_sq.active)
+ PMacRecord(); /* Kick off the record process. */
+
+ uRead = 0;
+
+ /* Move what the user requests, depending upon other options.
+ */
+ while (uLeft > 0) {
+
+ /* When front == rear, the DMA is not done yet.
+ */
+ while (read_sq.front == read_sq.rear) {
+ if (NON_BLOCKING(read_sq.open_mode)) {
+ return uRead > 0 ? uRead : -EAGAIN;
+ }
+ SLEEP(read_sq.action_queue, ONE_SECOND);
+ if (SIGNAL_RECEIVED)
+ return uRead > 0 ? uRead : -EINTR;
+ }
+
+ /* The amount we move is either what is left in the
+ * current buffer or what the user wants.
+ */
+ bLeft = read_sq.block_size - read_sq.rear_size;
+ bUsed = read_sq.rear_size;
+ uUsed = sound_copy_translate_read(dst, uLeft,
+ read_sq.buffers[read_sq.front], &bUsed, bLeft);
+ if (uUsed <= 0)
+ return uUsed;
+ dst += uUsed;
+ uRead += uUsed;
+ uLeft -= uUsed;
+ read_sq.rear_size += bUsed;
+ if (read_sq.rear_size >= read_sq.block_size) {
+ read_sq.rear_size = 0;
+ read_sq.front++;
+ if (read_sq.front >= read_sq.max_active)
+ read_sq.front = 0;
+ }
+ }
+ return uRead;
+}
+
static int sq_open(struct inode *inode, struct file *file)
{
int rc = 0;
MOD_INC_USE_COUNT;
- if (sq.busy) {
- rc = -EBUSY;
- if (NON_BLOCKING(file->f_flags))
- goto err_out;
- rc = -EINTR;
- while (sq.busy) {
- SLEEP(sq.open_queue, ONE_SECOND);
- if (SIGNAL_RECEIVED)
+ if (file->f_mode & FMODE_WRITE) {
+ if (sq.busy) {
+ rc = -EBUSY;
+ if (NON_BLOCKING(file->f_flags))
goto err_out;
+ rc = -EINTR;
+ while (sq.busy) {
+ SLEEP(sq.open_queue, ONE_SECOND);
+ if (SIGNAL_RECEIVED)
+ goto err_out;
+ }
}
- rc = 0;
+ sq.busy = 1; /* Let's play spot-the-race-condition */
+
+ if (sq_allocate_buffers()) goto err_out_nobusy;
+
+ sq_setup(numBufs, bufSize<<10,sound_buffers);
+ sq.open_mode = file->f_mode;
}
- sq.busy = 1;
- rc = sq_allocate_buffers();
- if (rc)
- goto err_out_nobusy;
- sq_setup(numBufs, bufSize << 10, sound_buffers);
- sq.open_mode = file->f_flags;
+
+
+ if (file->f_mode & FMODE_READ) {
+ if (read_sq.busy) {
+ rc = -EBUSY;
+ if (NON_BLOCKING(file->f_flags))
+ goto err_out;
+ rc = -EINTR;
+ while (read_sq.busy) {
+ SLEEP(read_sq.open_queue, ONE_SECOND);
+ if (SIGNAL_RECEIVED)
+ goto err_out;
+ }
+ rc = 0;
+ }
+ read_sq.busy = 1;
+ if (sq_allocate_read_buffers()) goto err_out_nobusy;
+
+ read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers);
+ read_sq.open_mode = file->f_mode;
+ }
+
#ifdef CONFIG_ATARI
sq.ignore_int = 1;
#endif /* CONFIG_ATARI */
@@ -4497,10 +4989,25 @@
sound_set_stereo(0);
sound_set_format(AFMT_MU_LAW);
}
+
+#if 0
+ if (file->f_mode == FMODE_READ) {
+ /* Start dma'ing straight away */
+ PMacRecord();
+ }
+#endif
+
return 0;
+
err_out_nobusy:
- sq.busy = 0;
- WAKE_UP(sq.open_queue);
+ if (file->f_mode & FMODE_WRITE) {
+ sq.busy = 0;
+ WAKE_UP(sq.open_queue);
+ }
+ if (file->f_mode & FMODE_READ) {
+ read_sq.busy = 0;
+ WAKE_UP(read_sq.open_queue);
+ }
err_out:
MOD_DEC_USE_COUNT;
return rc;
@@ -4510,7 +5017,7 @@
static void sq_reset(void)
{
sound_silence();
- sq.playing = 0;
+ sq.active = 0;
sq.count = 0;
sq.front = (sq.rear+1) % sq.max_count;
}
@@ -4523,7 +5030,7 @@
sq.syncing = 1;
sq_play(); /* there may be an incomplete frame waiting */
- while (sq.playing) {
+ while (sq.active) {
SLEEP(sq.sync_queue, ONE_SECOND);
if (SIGNAL_RECEIVED) {
/* While waiting for audio output to drain, an
@@ -4548,11 +5055,23 @@
sound.soft = sound.dsp;
sound.hard = sound.dsp;
sound_silence();
+
+ sq_release_read_buffers();
sq_release_buffers();
MOD_DEC_USE_COUNT;
- sq.busy = 0;
- WAKE_UP(sq.open_queue);
+ /* There is probably a DOS atack here. They change the mode flag. */
+ /* XXX add check here */
+ if (file->f_mode & FMODE_READ) {
+ read_sq.busy = 0;
+ WAKE_UP(read_sq.open_queue);
+ }
+
+ if (file->f_mode & FMODE_WRITE) {
+ sq.busy = 0;
+ WAKE_UP(sq.open_queue);
+ }
+
/* Wake up a process waiting for the queue being released.
* Note: There may be several processes waiting for a call
* to open() returning. */
@@ -4624,7 +5143,7 @@
case SNDCTL_DSP_SUBDIVIDE:
break;
case SNDCTL_DSP_SETFRAGMENT:
- if (sq.count || sq.playing || sq.syncing)
+ if (sq.count || sq.active || sq.syncing)
return -EINVAL;
IOCTL_IN(arg, size);
nbufs = size >> 16;
@@ -4654,7 +5173,7 @@
static struct file_operations sq_fops =
{
sound_lseek,
- NULL, /* sq_read */
+ sq_read, /* sq_read */
sq_write,
NULL, /* sq_readdir */
NULL, /* sq_poll */
@@ -4675,10 +5194,16 @@
if (sq_unit < 0)
return;
- init_waitqueue_head(&sq.write_queue);
+ init_waitqueue_head(&sq.action_queue);
init_waitqueue_head(&sq.open_queue);
init_waitqueue_head(&sq.sync_queue);
+
+ init_waitqueue_head(&read_sq.action_queue);
+ init_waitqueue_head(&read_sq.open_queue);
+ init_waitqueue_head(&read_sq.sync_queue);
+
sq.busy = 0;
+ read_sq.busy = 0;
/* whatever you like as startup mode for /dev/dsp,
* (/dev/audio hasn't got a startup mode). note that
@@ -4726,7 +5251,7 @@
static int state_open(struct inode *inode, struct file *file)
{
- char *buffer = state.buf, *mach = "";
+ char *buffer = state.buf, *mach = "", awacs_buf[50];
int len = 0;
if (state.busy)
@@ -4750,7 +5275,8 @@
#endif /* CONFIG_AMIGA */
#ifdef CONFIG_PPC
case DMASND_AWACS:
- sprintf(mach, "PowerMac (AWACS rev %d) ", awacs_revision);
+ sprintf(awacs_buf, "PowerMac (AWACS rev %d) ", awacs_revision);
+ mach = awacs_buf;
break;
#endif /* CONFIG_PPC */
}
@@ -4821,8 +5347,8 @@
sq.block_size, sq.max_count, sq.max_active);
len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count,
sq.rear_size);
- len += sprintf(buffer+len, "\tsq.playing = %d sq.syncing = %d\n",
- sq.playing, sq.syncing);
+ len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n",
+ sq.active, sq.syncing);
state.len = len;
return 0;
}
@@ -4951,15 +5477,18 @@
int vol;
sound.mach = machPMac;
has_sound = 1;
+
awacs = (volatile struct awacs_regs *)
ioremap(np->addrs[0].address, 0x80);
awacs_txdma = (volatile struct dbdma_regs *)
ioremap(np->addrs[1].address, 0x100);
awacs_rxdma = (volatile struct dbdma_regs *)
ioremap(np->addrs[2].address, 0x100);
+
awacs_irq = np->intrs[0].line;
awacs_tx_irq = np->intrs[1].line;
awacs_rx_irq = np->intrs[2].line;
+
awacs_tx_cmd_space = kmalloc((numBufs + 4) * sizeof(struct dbdma_cmd),
GFP_KERNEL);
if (awacs_tx_cmd_space == NULL) {
@@ -4968,6 +5497,18 @@
}
awacs_tx_cmds = (volatile struct dbdma_cmd *)
DBDMA_ALIGN(awacs_tx_cmd_space);
+
+
+ awacs_rx_cmd_space = kmalloc((numReadBufs + 4) * sizeof(struct dbdma_cmd),
+ GFP_KERNEL);
+ if (awacs_rx_cmd_space == NULL) {
+ printk("DMA sound driver: No memory for input");
+ }
+ awacs_rx_cmds = (volatile struct dbdma_cmd *)
+ DBDMA_ALIGN(awacs_rx_cmd_space);
+
+
+
awacs_reg[0] = MASK_MUX_CD;
awacs_reg[1] = MASK_LOOPTHRU | MASK_PAROUT;
/* get default volume from nvram */
@@ -5083,6 +5624,7 @@
sound.mach.irqcleanup();
}
+ sq_release_read_buffers();
sq_release_buffers();
if (mixer_unit >= 0)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)