patch-2.3.16 linux/drivers/sbus/audio/amd7930.c

Next file: linux/drivers/sbus/audio/audio.c
Previous file: linux/drivers/pnp/isapnp_proc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.15/linux/drivers/sbus/audio/amd7930.c linux/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 @@
 	/* 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 @@
 	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 @@
 	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 @@
 	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 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 @@
 	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 @@
 	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_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_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 @@
   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 @@
 
 		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 @@
 
 		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 @@
 	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 @@
 #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 @@
 	}
 }
 #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

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)