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", ®, &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;
}
next prev parent 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