diff options
Diffstat (limited to 'drivers/sbus/audio/amd7930.c')
-rw-r--r-- | drivers/sbus/audio/amd7930.c | 270 |
1 files changed, 258 insertions, 12 deletions
diff --git a/drivers/sbus/audio/amd7930.c b/drivers/sbus/audio/amd7930.c index 9b71e0a80..e61d74793 100644 --- a/drivers/sbus/audio/amd7930.c +++ b/drivers/sbus/audio/amd7930.c @@ -102,6 +102,11 @@ #include <asm/audioio.h> #include "amd7930.h" +static __u8 bilinear2mulaw(__u8 data); +static __u8 mulaw2bilinear(__u8 data); +static __u8 linear2mulaw(__u16 data); +static __u16 mulaw2linear(__u8 data); + #if defined (AMD79C30_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff #include "../../isdn/hisax/hisax.h" #include "../../isdn/hisax/isdnl1.h" @@ -146,8 +151,12 @@ struct amd7930_channel { /* Callback routine (and argument) when input is done on */ void (*input_callback)(); void * input_callback_arg; + + int input_format; + int output_format; }; + /* Private information we store for each amd7930 chip. */ struct amd7930_info { struct amd7930_channel D; @@ -173,6 +182,9 @@ struct amd7930_info { int irq; volatile int ints_on; + /* Format type */ + int format_type; + /* Someone to signal when the ISDN LIU state changes */ int liu_state; @@ -489,10 +501,25 @@ static void transceive_Bchannel(struct amd7930_channel *channel, if (channel->output_ptr && channel->output_count > 0) { /* Send the next byte and advance buffer pointer. */ - *io_reg = *(channel->output_ptr); + switch(channel->output_format) { + case AUDIO_ENCODING_ULAW: + case AUDIO_ENCODING_ALAW: + *io_reg = *(channel->output_ptr); + break; + case AUDIO_ENCODING_LINEAR8: + *io_reg = bilinear2mulaw(*(channel->output_ptr)); + break; + case AUDIO_ENCODING_LINEAR: + if (channel->output_count >= 2) { + *io_reg = linear2mulaw(*((__u16*)(channel->output_ptr))); + channel->output_ptr++; + channel->output_count--; + }; + }; channel->output_ptr++; channel->output_count--; + /* Done with the buffer? Notify the midlevel driver. */ if (channel->output_count == 0) { channel->output_ptr = NULL; @@ -510,7 +537,21 @@ static void transceive_Bchannel(struct amd7930_channel *channel, if (channel->input_ptr && channel->input_count > 0) { /* Get the next byte and advance buffer pointer. */ - *(channel->input_ptr) = *io_reg; + switch(channel->input_format) { + case AUDIO_ENCODING_ULAW: + case AUDIO_ENCODING_ALAW: + *(channel->input_ptr) = *io_reg; + break; + case AUDIO_ENCODING_LINEAR8: + *(channel->input_ptr) = mulaw2bilinear(*io_reg); + break; + case AUDIO_ENCODING_LINEAR: + if (channel->input_count >= 2) { + *((__u16*)(channel->input_ptr)) = mulaw2linear(*io_reg); + channel->input_ptr++; + channel->input_count--; + } else *(channel->input_ptr) = 0; + }; channel->input_ptr++; channel->input_count--; @@ -565,6 +606,20 @@ static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs) static int amd7930_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv) { + struct amd7930_info *info = (struct amd7930_info *)drv->private; + + switch(MINOR(inode->i_rdev) & 0xf) { + case SPARCAUDIO_AUDIO_MINOR: + info->format_type = AUDIO_ENCODING_ULAW; + break; + case SPARCAUDIO_DSP_MINOR: + info->format_type = AUDIO_ENCODING_LINEAR8; + break; + case SPARCAUDIO_DSP16_MINOR: + info->format_type = AUDIO_ENCODING_LINEAR; + break; + }; + MOD_INC_USE_COUNT; return 0; } @@ -637,8 +692,9 @@ static void amd7930_start_output(struct sparcaudio_driver *drv, if (info->Baudio) { info->Baudio->output_ptr = buffer; info->Baudio->output_count = count; - info->Baudio->output_callback = (void *) &sparcaudio_output_done; - info->Baudio->output_callback_arg = (void *) drv; + info->Baudio->output_format = info->format_type; + info->Baudio->output_callback = (void *) &sparcaudio_output_done; + info->Baudio->output_callback_arg = (void *) drv; info->Baudio->xmit_idle_char = 0; } } @@ -667,6 +723,7 @@ static void amd7930_start_input(struct sparcaudio_driver *drv, if (info->Baudio) { info->Baudio->input_ptr = buffer; info->Baudio->input_count = count; + info->Baudio->input_format = info->format_type; info->Baudio->input_callback = (void *) &sparcaudio_input_done; info->Baudio->input_callback_arg = (void *) drv; } @@ -700,7 +757,7 @@ static int amd7930_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) static int amd7930_get_formats(struct sparcaudio_driver *drv) { - return (AFMT_MU_LAW | AFMT_A_LAW); + return (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_S16_BE); } static int amd7930_get_output_ports(struct sparcaudio_driver *drv) @@ -850,9 +907,11 @@ static int amd7930_get_input_port(struct sparcaudio_driver *drv) static int amd7930_get_encoding(struct sparcaudio_driver *drv) { struct amd7930_info *info = (struct amd7930_info *)drv->private; - if (info->map.mmr1 & AM_MAP_MMR1_ALAW) + if ((info->map.mmr1 & AM_MAP_MMR1_ALAW) && + (info->format_type == AUDIO_ENCODING_ALAW)) return AUDIO_ENCODING_ALAW; - return AUDIO_ENCODING_ULAW; + + return info->format_type; } static int @@ -861,15 +920,20 @@ amd7930_set_encoding(struct sparcaudio_driver *drv, int value) struct amd7930_info *info = (struct amd7930_info *)drv->private; switch (value) { - case AUDIO_ENCODING_ULAW: - info->map.mmr1 &= ~AM_MAP_MMR1_ALAW; - break; case AUDIO_ENCODING_ALAW: info->map.mmr1 |= AM_MAP_MMR1_ALAW; break; + case AUDIO_ENCODING_LINEAR8: + case AUDIO_ENCODING_LINEAR: + case AUDIO_ENCODING_ULAW: + info->map.mmr1 &= ~AM_MAP_MMR1_ALAW; + break; default: return -EINVAL; - } + }; + + info->format_type = value; + amd7930_update_map(drv); return 0; } @@ -1390,6 +1454,7 @@ static void amd7930_bxmit(int dev, unsigned int chan, Bchan->output_ptr = buffer; Bchan->output_count = count; + Bchan->output_format = AUDIO_ENCODING_ULAW; Bchan->output_callback = (void *) callback; Bchan->output_callback_arg = callback_arg; @@ -1418,6 +1483,7 @@ static void amd7930_brecv(int dev, unsigned int chan, Bchan->input_ptr = buffer; Bchan->input_count = size; + Bchan->input_format = AUDIO_ENCODING_ULAW; Bchan->input_callback = (void *) callback; Bchan->input_callback_arg = callback_arg; @@ -1568,6 +1634,11 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node, info->rgain = 128; info->pgain = 200; info->mgain = 0; + info->format_type = AUDIO_ENCODING_ULAW; + info->Bb.input_format = AUDIO_ENCODING_ULAW; + info->Bb.output_format = AUDIO_ENCODING_ULAW; + info->Bc.input_format = AUDIO_ENCODING_ULAW; + info->Bc.output_format = AUDIO_ENCODING_ULAW; amd7930_update_map(drv); /* Register the amd7930 with the midlevel audio driver. */ @@ -1608,7 +1679,7 @@ static void amd7930_detach(struct sparcaudio_driver *drv) #ifdef MODULE int init_module(void) #else -__initfunc(int amd7930_init(void)) +int __init amd7930_init(void) #endif { struct linux_sbus *bus; @@ -1651,3 +1722,178 @@ void cleanup_module(void) } } #endif + + +/*************************************************************/ +/* Audio format conversion */ +/*************************************************************/ + +/* Translation tables */ + +static unsigned char ulaw[] = { + 3, 7, 11, 15, 19, 23, 27, 31, + 35, 39, 43, 47, 51, 55, 59, 63, + 66, 68, 70, 72, 74, 76, 78, 80, + 82, 84, 86, 88, 90, 92, 94, 96, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, + 113, 114, 114, 115, 115, 116, 116, 117, + 117, 118, 118, 119, 119, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 123, 123, + 123, 123, 124, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 126, 126, 126, 126, + 126, 126, 126, 126, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 253, 249, 245, 241, 237, 233, 229, 225, + 221, 217, 213, 209, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 174, 172, 170, 168, 166, 164, 162, 160, + 158, 157, 156, 155, 154, 153, 152, 151, + 150, 149, 148, 147, 146, 145, 144, 143, + 143, 142, 142, 141, 141, 140, 140, 139, + 139, 138, 138, 137, 137, 136, 136, 135, + 135, 135, 134, 134, 134, 134, 133, 133, + 133, 133, 132, 132, 132, 132, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 130, 130, 130, 130, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128 +}; + +static __u8 mulaw2bilinear(__u8 data) +{ + return ulaw[data]; +} + + +static unsigned char linear[] = { + 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 2, 0, 0, 0, 3, + 0, 0, 0, 4, 0, 0, 0, 5, + 0, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 8, 0, 0, 0, 9, + 0, 0, 0, 10, 0, 0, 0, 11, + 0, 0, 0, 12, 0, 0, 0, 13, + 0, 0, 0, 14, 0, 0, 0, 15, + 0, 0, 16, 0, 17, 0, 18, 0, + 19, 0, 20, 0, 21, 0, 22, 0, + 23, 0, 24, 0, 25, 0, 26, 0, + 27, 0, 28, 0, 29, 0, 30, 0, + 31, 0, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, + 46, 48, 50, 52, 54, 56, 58, 60, + 62, 65, 69, 73, 77, 83, 91, 103, + 255, 231, 219, 211, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 174, 173, 172, 171, 170, 169, 168, 167, + 166, 165, 164, 163, 162, 161, 160, 0, + 159, 0, 158, 0, 157, 0, 156, 0, + 155, 0, 154, 0, 153, 0, 152, 0, + 151, 0, 150, 0, 149, 0, 148, 0, + 147, 0, 146, 0, 145, 0, 144, 0, + 0, 143, 0, 0, 0, 142, 0, 0, + 0, 141, 0, 0, 0, 140, 0, 0, + 0, 139, 0, 0, 0, 138, 0, 0, + 0, 137, 0, 0, 0, 136, 0, 0, + 0, 135, 0, 0, 0, 134, 0, 0, + 0, 133, 0, 0, 0, 132, 0, 0, + 0, 131, 0, 0, 0, 130, 0, 0, + 0, 129, 0, 0, 0, 128, 0, 0 +}; + +static __u8 bilinear2mulaw(__u8 data) +{ + return linear[data]; +} + +static int exp_lut[256] = { + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +#define BIAS 0x84 +#define CLIP 32635 + +#define SWAP_ENDIAN(x) ((x >> 8) | ((x & 0xff) << 8)) + +static __u8 linear2mulaw(__u16 data) +{ + static int sign, exponent, mantissa; + + /* not really sure, if swapping is ok - comment next line to disable it */ + data = SWAP_ENDIAN(data); + + sign = (data >> 8) & 0x80; + if (sign != 0) data = -data; + + if (data > CLIP) data = CLIP; + data += BIAS; + exponent = exp_lut[(data >> 7) & 0xFF]; + mantissa = (data >> (exponent + 3)) & 0x0F; + + return (~(sign | (exponent << 4) | mantissa)); +} + +static __u16 mulaw2linear(__u8 data) +{ + /* this conversion is not working */ + return data; +} + +#if 0 +#define INOUT(x,y) (((x) << 16) | (y)) +static int convert_audio(int in_format, int out_format, __u8* buffer, int count) +{ + static int i,sign,exponent; + static __u16 data; + + if (in_format == out_format) return count; + + switch(INOUT(in_format, out_format)) { + case INOUT(AUDIO_ENCODING_ULAW, AUDIO_ENCODING_LINEAR8): + for (i = 0;i < count; i++) { + buffer[i] = ulaw[buffer[i]]; + }; + break; + case INOUT(AUDIO_ENCODING_ULAW, AUDIO_ENCODING_LINEAR): + break; + case INOUT(AUDIO_ENCODING_LINEAR, AUDIO_ENCODING_ULAW): + /* buffer is two-byte => convert to first */ + for (i = 0; i < count/2; i++) { + data = ((__u16*)buffer)[i]; + sign = (data >> 8) & 0x80; + if (data > CLIP) data = CLIP; + data += BIAS; + exponent = exp_lut[(data >> 7) & 0xFF]; + buffer[i] = ~(sign | (exponent << 4) | + ((data >> (exponent + 3)) & 0x0F)); + }; + break; + case INOUT(AUDIO_ENCODING_LINEAR8, AUDIO_ENCODING_ULAW): + for (i = 0; i < count; i++) { + buffer[i] = linear[buffer[i]]; + }; + break; + default: + return 0; + }; + + return count; +} +#undef INOUT +#endif + +#undef BIAS +#undef CLIP +#undef SWAP_ENDIAN |