Alsa-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: James Courtier-Dutton <James@superbug.demon.co.uk>
To: Takashi Iwai <tiwai@suse.de>
Cc: alsa-devel@lists.sourceforge.net
Subject: Re: [PATCH] Audigy LS SPDIF output.
Date: Tue, 22 Jun 2004 20:17:15 +0100	[thread overview]
Message-ID: <40D885BB.80502@superbug.demon.co.uk> (raw)
In-Reply-To: <s5hsmcnlia7.wl@alsa2.suse.de>

[-- Attachment #1: Type: text/plain, Size: 601 bytes --]

Takashi Iwai wrote:
> At Tue, 22 Jun 2004 16:23:21 +0100,
> James Courtier-Dutton wrote:
> 
>>I have uploaded new versions of:
>>audigyls.c
>>AudigyLS.conf
>>
>>http://www.superbug.demon.co.uk/alsa
> 
> 
> Could you please send as patches to CVS version?
> 
> 
> thanks,
> 
> Takashi
> 
> 

This new patch, implements full SPDIF Stereo PCM and also AC3/DTS output.
These new versions implement AC3/DTS output where the others did not.

One can use a standard stereo mini-jack cable to two RCA plugs cable.
Full explanation in the audigyls.c source code, as comments.

See attachements.

Cheers
James


[-- Attachment #2: alsa-lib.cvs.diff --]
[-- Type: text/x-patch, Size: 91 bytes --]

cvs -q -f -z3 -d ":pserver:anonymous:@cvs.sourceforge.net:/cvsroot/alsa" diff -u alsa-lib


[-- Attachment #3: alsa-driver.diff.txt --]
[-- Type: text/plain, Size: 14371 bytes --]

Index: alsa-driver/pci/emu10k1/audigyls.c
===================================================================
RCS file: /cvsroot/alsa/alsa-driver/pci/emu10k1/audigyls.c,v
retrieving revision 1.1
diff -u -r1.1 audigyls.c
--- alsa-driver/pci/emu10k1/audigyls.c	18 Jun 2004 14:21:20 -0000	1.1
+++ alsa-driver/pci/emu10k1/audigyls.c	22 Jun 2004 18:05:46 -0000
@@ -6,6 +6,9 @@
  *    Front, Rear and Center/LFE.
  *    Surround40 and Surround51.
  *    Capture from MIC input.
+ *    SPDIF digital playback of PCM stereo and AC3/DTS works.
+ *    (One can use a standard stereo mini-jack to two RCA plugs cable.
+ *     Plug one of the RCA plugs into the Coax input of the external decoder/receiver.)
  *
  *  BUGS:
  *    --
@@ -13,7 +16,6 @@
  *  TODO:
  *    Need to add a way to select capture source.
  *    4 Capture channels, only one implemented so far.
- *    SPDIF playback not implemented.
  *    Need to find out what the AC97 chip actually does.
  *    Other rates apart from 48khz not implemented.
  *    MIDI
@@ -128,7 +130,27 @@
 #define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
 #define PLAYBACk_LAST_SAMPLE    0x20            /* The sample currently being played */
 #define BASIC_INTERRUPT         0x40            /* Used by both playback and capture interrupt handler */
-#define SPCS0			0x41		/* SPDIF output Channel Status 0 register default=0x02108004, non-audio=0x02108006	*/
+/* The Digital out jack is shared with the Center/LFE Analogue output. 
+ * The jack has 4 poles. I will call 1 - Tip, 2 - Next to 1, 3 - Next to 2, 4 - Next to 3
+ * For Analogue: 1 -> Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground
+ * For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground.
+ * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red.
+ * So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card.
+ */
+/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS
+ * The Rear SPDIF can be used for Stereo PCM and also AC3/DTS
+ * The Center/LFE SPDIF cannot be used for AC3/DTS, but can be used for Stereo PCM.
+ * Summary: For ALSA we use the Rear channel for SPDIF Digital AC3/DTS output
+ */
+/* A standard 2 pole mono mini-jack to RCA plug can be used for SPDIF Stereo PCM output from the Front channel.
+ * A standard 3 pole stereo mini-jack to 2 RCA plugs can be used for SPDIF AC3/DTS and Stereo PCM output utilising the Rear channel and just one of the RCA plugs. 
+ */
+
+
+#define SPCS0			0x41		/* SPDIF output Channel Status 0 register. For Rear. default=0x02108004, non-audio=0x02108006	*/
+#define SPCS1			0x42		/* SPDIF output Channel Status 1 register. For Front */
+#define SPCS2			0x43		/* SPDIF output Channel Status 2 register. For Center/LFE */
+#define SPCS3			0x44		/* SPDIF output Channel Status 3 register. Unknown */
 #define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
 #define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
 #define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
@@ -237,10 +259,10 @@
 	audigyls_voice_t voices[4];
 	audigyls_channel_t channels[4];
 	audigyls_channel_t capture_channel;
+	u32 spdif_bits[4];             /* s/pdif out setup */
 
 	struct snd_dma_device dma_dev;
 	struct snd_dma_buffer buffer;
-        snd_kcontrol_t *ctl_front_volume;
 };
 
 #define audigyls_t_magic        0xa15a4501
@@ -1068,35 +1090,45 @@
 	 *  AN                = 0     (Audio data)
 	 *  P                 = 0     (Consumer)
 	 */
-#if 0
 	snd_audigyls_ptr_write(chip, SPCS0, 0,
-			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
-			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
-			       SPCS_GENERATIONSTATUS | 0x00001200 |
-			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+				chip->spdif_bits[0] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	/* Only SPCS1 has been tested */
 	snd_audigyls_ptr_write(chip, SPCS1, 0,
-			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
-			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
-			       SPCS_GENERATIONSTATUS | 0x00001200 |
-			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+				chip->spdif_bits[1] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
 	snd_audigyls_ptr_write(chip, SPCS2, 0,
-			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
-			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
-			       SPCS_GENERATIONSTATUS | 0x00001200 |
-			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
-#endif
-/* Select analogue output */
+				chip->spdif_bits[2] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_audigyls_ptr_write(chip, SPCS3, 0,
+				chip->spdif_bits[3] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+	/* Select analogue output */
         //snd_audigyls_ptr_write(chip, 0x41, 0, 0x70f); // ???
         //snd_audigyls_ptr_write(chip, 0x65, 0, 0x1000);
 
         /* Write 0x8000 to AC97_REC_GAIN to mute it. */
         outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
         outw(0x8000, chip->port + AC97DATA);
-
+#if 0
 	snd_audigyls_ptr_write(chip, SPCS0, 0, 0x2108006);
 	snd_audigyls_ptr_write(chip, 0x42, 0, 0x2108006);
 	snd_audigyls_ptr_write(chip, 0x43, 0, 0x2108006);
 	snd_audigyls_ptr_write(chip, 0x44, 0, 0x2108006);
+#endif
 
 	snd_audigyls_ptr_write(chip, 0x72, 0, 0xf0f003f);
 
@@ -1206,6 +1238,21 @@
 	}
 }
 
+static void snd_audigyls_proc_reg_write(snd_info_entry_t *entry, 
+				       snd_info_buffer_t * buffer)
+{
+	audigyls_t *emu = snd_magic_cast(audigyls_t, entry->private_data, return);
+        char line[64];
+        unsigned int reg, channel_id , val;
+        while (!snd_info_get_line(buffer, line, sizeof(line))) {
+                if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+                        continue;
+                if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+                        snd_audigyls_ptr_write(emu, reg, channel_id, val);
+        }
+}
+
+
 static int __devinit snd_audigyls_proc_init(audigyls_t * emu)
 {
 	snd_info_entry_t *entry;
@@ -1216,13 +1263,132 @@
 		snd_info_set_text_ops(entry, emu, 1024, snd_audigyls_proc_reg_read16);
 	if(! snd_card_proc_new(emu->card, "audigyls_reg8", &entry))
 		snd_info_set_text_ops(entry, emu, 1024, snd_audigyls_proc_reg_read8);
-	if(! snd_card_proc_new(emu->card, "audigyls_regs1", &entry))
+	if(! snd_card_proc_new(emu->card, "audigyls_regs1", &entry)) {
 		snd_info_set_text_ops(entry, emu, 1024, snd_audigyls_proc_reg_read1);
-	if(! snd_card_proc_new(emu->card, "audigyls_regs2", &entry))
+		entry->c.text.write_size = 64;
+		entry->c.text.write = snd_audigyls_proc_reg_write;
+//		entry->private_data = emu;
+	}
+	if(! snd_card_proc_new(emu->card, "audigyls_regs2", &entry)) 
 		snd_info_set_text_ops(entry, emu, 1024, snd_audigyls_proc_reg_read2);
 	return 0;
 }
 
+static int snd_audigyls_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_audigyls_shared_spdif_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	audigyls_t *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = snd_audigyls_ptr_read(emu, SPDIF_SELECT, 0);
+        return 0;
+}
+
+static int snd_audigyls_shared_spdif_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	audigyls_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int reg, val;
+	int change = 0;
+
+	reg = snd_audigyls_ptr_read(emu, SPDIF_SELECT, 0);
+	val = ucontrol->value.integer.value[0] ;
+	/* FIXME: Check whether we want ON == SPDIF on, or OFF == SPDIF on */
+	change = ((reg == 0xf00) != val);
+	if (change) {
+		reg ^= 0xf00;
+		snd_audigyls_ptr_write(emu, SPDIF_SELECT, 0, reg);
+	}
+        return change;
+}
+
+static snd_kcontrol_new_t snd_audigyls_shared_spdif __devinitdata =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Analog/Digital Output Jack",
+	.info =		snd_audigyls_shared_spdif_info,
+	.get =		snd_audigyls_shared_spdif_get,
+	.put =		snd_audigyls_shared_spdif_put
+};
+
+static int snd_audigyls_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_audigyls_spdif_get(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+	audigyls_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+        return 0;
+}
+
+static int snd_audigyls_spdif_get_mask(snd_kcontrol_t * kcontrol,
+				      snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+        return 0;
+}
+
+static int snd_audigyls_spdif_put(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+	audigyls_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	int change;
+	unsigned int val;
+
+	val = (ucontrol->value.iec958.status[0] << 0) |
+	      (ucontrol->value.iec958.status[1] << 8) |
+	      (ucontrol->value.iec958.status[2] << 16) |
+	      (ucontrol->value.iec958.status[3] << 24);
+	change = val != emu->spdif_bits[idx];
+	if (change) {
+		snd_audigyls_ptr_write(emu, SPCS0 + idx, 0, val);
+		emu->spdif_bits[idx] = val;
+	}
+        return change;
+}
+
+static snd_kcontrol_new_t snd_audigyls_spdif_mask_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	.count =	4,
+        .info =         snd_audigyls_spdif_info,
+        .get =          snd_audigyls_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_audigyls_spdif_control =
+{
+        .iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.count =	4,
+        .info =         snd_audigyls_spdif_info,
+        .get =          snd_audigyls_spdif_get,
+        .put =          snd_audigyls_spdif_put
+};
+
 static int snd_audigyls_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo, int channel_id)
 {
         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1361,28 +1527,75 @@
         .put =          snd_audigyls_volume_put_rear
 };
 
+static int remove_ctl(snd_card_t *card, const char *name)
+{
+	snd_ctl_elem_id_t id;
+	memset(&id, 0, sizeof(id));
+	strcpy(id.name, name);
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_remove_id(card, &id);
+}
+
+static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name)
+{
+	snd_ctl_elem_id_t sid;
+	memset(&sid, 0, sizeof(sid));
+	/* FIXME: strcpy is bad. */
+	strcpy(sid.name, name);
+	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_find_id(card, &sid);
+}
+
+#if 0
+static int rename_ctl(snd_card_t *card, const char *src, const char *dst)
+{
+	snd_kcontrol_t *kctl = ctl_find(card, src);
+	if (kctl) {
+		strcpy(kctl->id.name, dst);
+		return 0;
+	}
+	return -ENOENT;
+}
+#endif
+
 static int __devinit snd_audigyls_mixer(audigyls_t *emu)
 {
         int err;
         snd_kcontrol_t *kctl;
         snd_card_t *card = emu->card;
-        if ((kctl = emu->ctl_front_volume = snd_ctl_new1(&snd_audigyls_volume_control_front, emu)) == NULL)
+        if ((kctl = snd_ctl_new1(&snd_audigyls_volume_control_front, emu)) == NULL)
                 return -ENOMEM;
         if ((err = snd_ctl_add(card, kctl)))
                 return err;
-        if ((kctl = emu->ctl_front_volume = snd_ctl_new1(&snd_audigyls_volume_control_rear, emu)) == NULL)
+        if ((kctl = snd_ctl_new1(&snd_audigyls_volume_control_rear, emu)) == NULL)
                 return -ENOMEM;
         if ((err = snd_ctl_add(card, kctl)))
                 return err;
-        if ((kctl = emu->ctl_front_volume = snd_ctl_new1(&snd_audigyls_volume_control_center_lfe, emu)) == NULL)
+        if ((kctl = snd_ctl_new1(&snd_audigyls_volume_control_center_lfe, emu)) == NULL)
                 return -ENOMEM;
         if ((err = snd_ctl_add(card, kctl)))
                 return err;
-        if ((kctl = emu->ctl_front_volume = snd_ctl_new1(&snd_audigyls_volume_control_unknown, emu)) == NULL)
+        if ((kctl = snd_ctl_new1(&snd_audigyls_volume_control_unknown, emu)) == NULL)
                 return -ENOMEM;
         if ((err = snd_ctl_add(card, kctl)))
                 return err;
-
+	if ((kctl = snd_ctl_new1(&snd_audigyls_spdif_mask_control, emu)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	if ((kctl = snd_ctl_new1(&snd_audigyls_shared_spdif, emu)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	if ((kctl = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) {
+		/* already defined by ac97, remove it */
+		/* FIXME: or do we need both controls? */
+		remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT));
+	}
+	if ((kctl = snd_ctl_new1(&snd_audigyls_spdif_control, emu)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
         return 0;
 }
 

  reply	other threads:[~2004-06-22 19:17 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-06-21 18:53 Audigy LS SPDIF output James Courtier-Dutton
2004-06-21 20:34 ` Eric Klein
2004-06-21 22:08   ` James Courtier-Dutton
2004-06-22  4:01     ` James Courtier-Dutton
2004-06-22 15:23       ` [PATCH] " James Courtier-Dutton
2004-06-22 15:33         ` Takashi Iwai
2004-06-22 19:17           ` James Courtier-Dutton [this message]
2004-06-23 17:22             ` Takashi Iwai
2004-06-23 18:37               ` James Courtier-Dutton
2004-06-23 18:09                 ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=40D885BB.80502@superbug.demon.co.uk \
    --to=james@superbug.demon.co.uk \
    --cc=alsa-devel@lists.sourceforge.net \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox