123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- /*
- * linux/sound/oss/dmasound/dmasound_q40.c
- *
- * Q40 DMA Sound Driver
- *
- * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
- * prior to 28/01/2001
- *
- * 28/01/2001 [0.1] Iain Sandoe
- * - added versioning
- * - put in and populated the hardware_afmts field.
- * [0.2] - put in SNDCTL_DSP_GETCAPS value.
- * [0.3] - put in default hard/soft settings.
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/slab.h>
- #include <linux/soundcard.h>
- #include <linux/interrupt.h>
- #include <asm/uaccess.h>
- #include <asm/q40ints.h>
- #include <asm/q40_master.h>
- #include "dmasound.h"
- #define DMASOUND_Q40_REVISION 0
- #define DMASOUND_Q40_EDITION 3
- static int expand_bal; /* Balance factor for expanding (not volume!) */
- static int expand_data; /* Data for expanding */
- /*** Low level stuff *********************************************************/
- static void *Q40Alloc(unsigned int size, gfp_t flags);
- static void Q40Free(void *, unsigned int);
- static int Q40IrqInit(void);
- #ifdef MODULE
- static void Q40IrqCleanUp(void);
- #endif
- static void Q40Silence(void);
- static void Q40Init(void);
- static int Q40SetFormat(int format);
- static int Q40SetVolume(int volume);
- static void Q40PlayNextFrame(int index);
- static void Q40Play(void);
- static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
- static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
- static void Q40Interrupt(void);
- /*** Mid level stuff *********************************************************/
- /* userCount, frameUsed, frameLeft == byte counts */
- static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
- ssize_t count, used;
- u_char *p = (u_char *) &frame[*frameUsed];
- used = count = min_t(size_t, userCount, frameLeft);
- if (copy_from_user(p,userPtr,count))
- return -EFAULT;
- while (count > 0) {
- *p = table[*p]+128;
- p++;
- count--;
- }
- *frameUsed += used ;
- return used;
- }
- static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- ssize_t count, used;
- u_char *p = (u_char *) &frame[*frameUsed];
- used = count = min_t(size_t, userCount, frameLeft);
- if (copy_from_user(p,userPtr,count))
- return -EFAULT;
- while (count > 0) {
- *p = *p + 128;
- p++;
- count--;
- }
- *frameUsed += used;
- return used;
- }
- static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- ssize_t count, used;
- u_char *p = (u_char *) &frame[*frameUsed];
- used = count = min_t(size_t, userCount, frameLeft);
- if (copy_from_user(p,userPtr,count))
- return -EFAULT;
- *frameUsed += used;
- return used;
- }
- /* a bit too complicated to optimise right now ..*/
- static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- unsigned char *table = (unsigned char *)
- (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
- unsigned int data = expand_data;
- u_char *p = (u_char *) &frame[*frameUsed];
- int bal = expand_bal;
- int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
- int utotal, ftotal;
- ftotal = frameLeft;
- utotal = userCount;
- while (frameLeft) {
- u_char c;
- if (bal < 0) {
- if (userCount == 0)
- break;
- if (get_user(c, userPtr++))
- return -EFAULT;
- data = table[c];
- data += 0x80;
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- expand_bal = bal;
- expand_data = data;
- *frameUsed += (ftotal - frameLeft);
- utotal -= userCount;
- return utotal;
- }
- static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- u_char *p = (u_char *) &frame[*frameUsed];
- unsigned int data = expand_data;
- int bal = expand_bal;
- int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
- int utotal, ftotal;
- ftotal = frameLeft;
- utotal = userCount;
- while (frameLeft) {
- u_char c;
- if (bal < 0) {
- if (userCount == 0)
- break;
- if (get_user(c, userPtr++))
- return -EFAULT;
- data = c ;
- data += 0x80;
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- expand_bal = bal;
- expand_data = data;
- *frameUsed += (ftotal - frameLeft);
- utotal -= userCount;
- return utotal;
- }
- static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- u_char *p = (u_char *) &frame[*frameUsed];
- unsigned int data = expand_data;
- int bal = expand_bal;
- int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
- int utotal, ftotal;
- ftotal = frameLeft;
- utotal = userCount;
- while (frameLeft) {
- u_char c;
- if (bal < 0) {
- if (userCount == 0)
- break;
- if (get_user(c, userPtr++))
- return -EFAULT;
- data = c ;
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- expand_bal = bal;
- expand_data = data;
- *frameUsed += (ftotal - frameLeft) ;
- utotal -= userCount;
- return utotal;
- }
- /* compressing versions */
- static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- unsigned char *table = (unsigned char *)
- (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
- unsigned int data = expand_data;
- u_char *p = (u_char *) &frame[*frameUsed];
- int bal = expand_bal;
- int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
- int utotal, ftotal;
-
- ftotal = frameLeft;
- utotal = userCount;
- while (frameLeft) {
- u_char c;
- while(bal<0) {
- if (userCount == 0)
- goto lout;
- if (!(bal<(-hSpeed))) {
- if (get_user(c, userPtr))
- return -EFAULT;
- data = 0x80 + table[c];
- }
- userPtr++;
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- lout:
- expand_bal = bal;
- expand_data = data;
- *frameUsed += (ftotal - frameLeft);
- utotal -= userCount;
- return utotal;
- }
- static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- u_char *p = (u_char *) &frame[*frameUsed];
- unsigned int data = expand_data;
- int bal = expand_bal;
- int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
- int utotal, ftotal;
- ftotal = frameLeft;
- utotal = userCount;
- while (frameLeft) {
- u_char c;
- while (bal < 0) {
- if (userCount == 0)
- goto lout;
- if (!(bal<(-hSpeed))) {
- if (get_user(c, userPtr))
- return -EFAULT;
- data = c + 0x80;
- }
- userPtr++;
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- lout:
- expand_bal = bal;
- expand_data = data;
- *frameUsed += (ftotal - frameLeft);
- utotal -= userCount;
- return utotal;
- }
- static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
- u_char frame[], ssize_t *frameUsed,
- ssize_t frameLeft)
- {
- u_char *p = (u_char *) &frame[*frameUsed];
- unsigned int data = expand_data;
- int bal = expand_bal;
- int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
- int utotal, ftotal;
- ftotal = frameLeft;
- utotal = userCount;
- while (frameLeft) {
- u_char c;
- while (bal < 0) {
- if (userCount == 0)
- goto lout;
- if (!(bal<(-hSpeed))) {
- if (get_user(c, userPtr))
- return -EFAULT;
- data = c ;
- }
- userPtr++;
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- lout:
- expand_bal = bal;
- expand_data = data;
- *frameUsed += (ftotal - frameLeft) ;
- utotal -= userCount;
- return utotal;
- }
- static TRANS transQ40Normal = {
- q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
- };
- static TRANS transQ40Expanding = {
- q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
- };
- static TRANS transQ40Compressing = {
- q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
- };
- /*** Low level stuff *********************************************************/
- static void *Q40Alloc(unsigned int size, gfp_t flags)
- {
- return kmalloc(size, flags); /* change to vmalloc */
- }
- static void Q40Free(void *ptr, unsigned int size)
- {
- kfree(ptr);
- }
- static int __init Q40IrqInit(void)
- {
- /* Register interrupt handler. */
- if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
- "DMA sound", Q40Interrupt))
- return 0;
- return(1);
- }
- #ifdef MODULE
- static void Q40IrqCleanUp(void)
- {
- master_outb(0,SAMPLE_ENABLE_REG);
- free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
- }
- #endif /* MODULE */
- static void Q40Silence(void)
- {
- master_outb(0,SAMPLE_ENABLE_REG);
- *DAC_LEFT=*DAC_RIGHT=127;
- }
- static char *q40_pp;
- static unsigned int q40_sc;
- static void Q40PlayNextFrame(int index)
- {
- u_char *start;
- u_long size;
- u_char speed;
- int error;
- /* used by Q40Play() if all doubts whether there really is something
- * to be played are already wiped out.
- */
- start = write_sq.buffers[write_sq.front];
- size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
- q40_pp=start;
- q40_sc=size;
- write_sq.front = (write_sq.front+1) % write_sq.max_count;
- write_sq.active++;
- speed=(dmasound.hard.speed==10000 ? 0 : 1);
- master_outb( 0,SAMPLE_ENABLE_REG);
- free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
- if (dmasound.soft.stereo)
- error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
- "Q40 sound", Q40Interrupt);
- else
- error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
- "Q40 sound", Q40Interrupt);
- if (error && printk_ratelimit())
- pr_err("Couldn't register sound interrupt\n");
- master_outb( speed, SAMPLE_RATE_REG);
- master_outb( 1,SAMPLE_CLEAR_REG);
- master_outb( 1,SAMPLE_ENABLE_REG);
- }
- static void Q40Play(void)
- {
- unsigned long flags;
- if (write_sq.active || write_sq.count<=0 ) {
- /* There's already a frame loaded */
- return;
- }
- /* nothing in the queue */
- if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
- /* hmmm, the only existing frame is not
- * yet filled and we're not syncing?
- */
- return;
- }
- spin_lock_irqsave(&dmasound.lock, flags);
- Q40PlayNextFrame(1);
- spin_unlock_irqrestore(&dmasound.lock, flags);
- }
- static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
- {
- spin_lock(&dmasound.lock);
- if (q40_sc>1){
- *DAC_LEFT=*q40_pp++;
- *DAC_RIGHT=*q40_pp++;
- q40_sc -=2;
- master_outb(1,SAMPLE_CLEAR_REG);
- }else Q40Interrupt();
- spin_unlock(&dmasound.lock);
- return IRQ_HANDLED;
- }
- static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
- {
- spin_lock(&dmasound.lock);
- if (q40_sc>0){
- *DAC_LEFT=*q40_pp;
- *DAC_RIGHT=*q40_pp++;
- q40_sc --;
- master_outb(1,SAMPLE_CLEAR_REG);
- }else Q40Interrupt();
- spin_unlock(&dmasound.lock);
- return IRQ_HANDLED;
- }
- static void Q40Interrupt(void)
- {
- if (!write_sq.active) {
- /* playing was interrupted and sq_reset() has already cleared
- * the sq variables, so better don't do anything here.
- */
- WAKE_UP(write_sq.sync_queue);
- master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
- goto exit;
- } else write_sq.active=0;
- write_sq.count--;
- Q40Play();
- if (q40_sc<2)
- { /* there was nothing to play, disable irq */
- master_outb(0,SAMPLE_ENABLE_REG);
- *DAC_LEFT=*DAC_RIGHT=127;
- }
- WAKE_UP(write_sq.action_queue);
- exit:
- master_outb(1,SAMPLE_CLEAR_REG);
- }
- static void Q40Init(void)
- {
- int i, idx;
- const int freq[] = {10000, 20000};
- /* search a frequency that fits into the allowed error range */
- idx = -1;
- for (i = 0; i < 2; i++)
- if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
- idx = i;
- dmasound.hard = dmasound.soft;
- /*sound.hard.stereo=1;*/ /* no longer true */
- dmasound.hard.size=8;
- if (idx > -1) {
- dmasound.soft.speed = freq[idx];
- dmasound.trans_write = &transQ40Normal;
- } else
- dmasound.trans_write = &transQ40Expanding;
- Q40Silence();
- if (dmasound.hard.speed > 20200) {
- /* squeeze the sound, we do that */
- dmasound.hard.speed = 20000;
- dmasound.trans_write = &transQ40Compressing;
- } else if (dmasound.hard.speed > 10000) {
- dmasound.hard.speed = 20000;
- } else {
- dmasound.hard.speed = 10000;
- }
- expand_bal = -dmasound.soft.speed;
- }
- static int Q40SetFormat(int format)
- {
- /* Q40 sound supports only 8bit modes */
- switch (format) {
- case AFMT_QUERY:
- return(dmasound.soft.format);
- case AFMT_MU_LAW:
- case AFMT_A_LAW:
- case AFMT_S8:
- case AFMT_U8:
- break;
- default:
- format = AFMT_S8;
- }
- dmasound.soft.format = format;
- dmasound.soft.size = 8;
- if (dmasound.minDev == SND_DEV_DSP) {
- dmasound.dsp.format = format;
- dmasound.dsp.size = 8;
- }
- Q40Init();
- return(format);
- }
- static int Q40SetVolume(int volume)
- {
- return 0;
- }
- /*** Machine definitions *****************************************************/
- static SETTINGS def_hard = {
- .format = AFMT_U8,
- .stereo = 0,
- .size = 8,
- .speed = 10000
- } ;
- static SETTINGS def_soft = {
- .format = AFMT_U8,
- .stereo = 0,
- .size = 8,
- .speed = 8000
- } ;
- static MACHINE machQ40 = {
- .name = "Q40",
- .name2 = "Q40",
- .owner = THIS_MODULE,
- .dma_alloc = Q40Alloc,
- .dma_free = Q40Free,
- .irqinit = Q40IrqInit,
- #ifdef MODULE
- .irqcleanup = Q40IrqCleanUp,
- #endif /* MODULE */
- .init = Q40Init,
- .silence = Q40Silence,
- .setFormat = Q40SetFormat,
- .setVolume = Q40SetVolume,
- .play = Q40Play,
- .min_dsp_speed = 10000,
- .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
- .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
- .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
- };
- /*** Config & Setup **********************************************************/
- static int __init dmasound_q40_init(void)
- {
- if (MACH_IS_Q40) {
- dmasound.mach = machQ40;
- dmasound.mach.default_hard = def_hard ;
- dmasound.mach.default_soft = def_soft ;
- return dmasound_init();
- } else
- return -ENODEV;
- }
- static void __exit dmasound_q40_cleanup(void)
- {
- dmasound_deinit();
- }
- module_init(dmasound_q40_init);
- module_exit(dmasound_q40_cleanup);
- MODULE_DESCRIPTION("Q40/Q60 sound driver");
- MODULE_LICENSE("GPL");
|