summaryrefslogtreecommitdiffstats
path: root/drivers/sbus/audio/amd7930.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sbus/audio/amd7930.c')
-rw-r--r--drivers/sbus/audio/amd7930.c270
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