Merge branch 'topic/pcxhr' into for-linus
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 7f95459..80e064a 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1334,6 +1334,40 @@
 	snd_iprintf(buffer, "\n");
 }
 
+static void pcxhr_proc_gpio_read(struct snd_info_entry *entry,
+				 struct snd_info_buffer *buffer)
+{
+	struct snd_pcxhr *chip = entry->private_data;
+	struct pcxhr_mgr *mgr = chip->mgr;
+	/* commands available when embedded DSP is running */
+	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
+		/* gpio ports on stereo boards only available */
+		int value = 0;
+		hr222_read_gpio(mgr, 1, &value);	/* GPI */
+		snd_iprintf(buffer, "GPI: 0x%x\n", value);
+		hr222_read_gpio(mgr, 0, &value);	/* GP0 */
+		snd_iprintf(buffer, "GPO: 0x%x\n", value);
+	} else
+		snd_iprintf(buffer, "no firmware loaded\n");
+	snd_iprintf(buffer, "\n");
+}
+static void pcxhr_proc_gpo_write(struct snd_info_entry *entry,
+				 struct snd_info_buffer *buffer)
+{
+	struct snd_pcxhr *chip = entry->private_data;
+	struct pcxhr_mgr *mgr = chip->mgr;
+	char line[64];
+	int value;
+	/* commands available when embedded DSP is running */
+	if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)))
+		return;
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		if (sscanf(line, "GPO: 0x%x", &value) != 1)
+			continue;
+		hr222_write_gpo(mgr, value);	/* GP0 */
+	}
+}
+
 static void __devinit pcxhr_proc_init(struct snd_pcxhr *chip)
 {
 	struct snd_info_entry *entry;
@@ -1342,6 +1376,13 @@
 		snd_info_set_text_ops(entry, chip, pcxhr_proc_info);
 	if (! snd_card_proc_new(chip->card, "sync", &entry))
 		snd_info_set_text_ops(entry, chip, pcxhr_proc_sync);
+	/* gpio available on stereo sound cards only */
+	if (chip->mgr->is_hr_stereo &&
+	    !snd_card_proc_new(chip->card, "gpio", &entry)) {
+		snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read);
+		entry->c.text.write = pcxhr_proc_gpo_write;
+		entry->mode |= S_IWUSR;
+	}
 }
 /* end of proc interface */
 
diff --git a/sound/pci/pcxhr/pcxhr.h b/sound/pci/pcxhr/pcxhr.h
index 69d87de..bda776c 100644
--- a/sound/pci/pcxhr/pcxhr.h
+++ b/sound/pci/pcxhr/pcxhr.h
@@ -27,8 +27,8 @@
 #include <linux/mutex.h>
 #include <sound/pcm.h>
 
-#define PCXHR_DRIVER_VERSION		0x000905	/* 0.9.5 */
-#define PCXHR_DRIVER_VERSION_STRING	"0.9.5"		/* 0.9.5 */
+#define PCXHR_DRIVER_VERSION		0x000906	/* 0.9.6 */
+#define PCXHR_DRIVER_VERSION_STRING	"0.9.6"		/* 0.9.6 */
 
 
 #define PCXHR_MAX_CARDS		6
@@ -124,6 +124,7 @@
 
 	unsigned char xlx_cfg;		/* copy of PCXHR_XLX_CFG register */
 	unsigned char xlx_selmic;	/* copy of PCXHR_XLX_SELMIC register */
+	unsigned char dsp_reset;	/* copy of PCXHR_DSP_RESET register */
 };
 
 
diff --git a/sound/pci/pcxhr/pcxhr_core.h b/sound/pci/pcxhr/pcxhr_core.h
index bbbd66d..be01737 100644
--- a/sound/pci/pcxhr/pcxhr_core.h
+++ b/sound/pci/pcxhr/pcxhr_core.h
@@ -1,7 +1,7 @@
 /*
  * Driver for Digigram pcxhr compatible soundcards
  *
- * low level interface with interrupt ans message handling
+ * low level interface with interrupt and message handling
  *
  * Copyright (c) 2004 by Digigram <alsa@digigram.com>
  *
diff --git a/sound/pci/pcxhr/pcxhr_mix22.c b/sound/pci/pcxhr/pcxhr_mix22.c
index ff01912..1cb82c0 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.c
+++ b/sound/pci/pcxhr/pcxhr_mix22.c
@@ -53,6 +53,8 @@
 #define PCXHR_DSP_RESET_DSP	0x01
 #define PCXHR_DSP_RESET_MUTE	0x02
 #define PCXHR_DSP_RESET_CODEC	0x08
+#define PCXHR_DSP_RESET_GPO_OFFSET	5
+#define PCXHR_DSP_RESET_GPO_MASK	0x60
 
 /* values for PCHR_XLX_CFG register */
 #define PCXHR_CFG_SYNCDSP_MASK		0x80
@@ -81,6 +83,8 @@
 /* values for PCHR_XLX_STATUS register - READ */
 #define PCXHR_STAT_SRC_LOCK		0x01
 #define PCXHR_STAT_LEVEL_IN		0x02
+#define PCXHR_STAT_GPI_OFFSET		2
+#define PCXHR_STAT_GPI_MASK		0x0C
 #define PCXHR_STAT_MIC_CAPS		0x10
 /* values for PCHR_XLX_STATUS register - WRITE */
 #define PCXHR_STAT_FREQ_SYNC_MASK	0x01
@@ -291,10 +295,11 @@
 	PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
 		    PCXHR_DSP_RESET_DSP);
 	msleep(5);
-	PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
-		    PCXHR_DSP_RESET_DSP  |
-		    PCXHR_DSP_RESET_MUTE |
-		    PCXHR_DSP_RESET_CODEC);
+	mgr->dsp_reset = PCXHR_DSP_RESET_DSP  |
+			 PCXHR_DSP_RESET_MUTE |
+			 PCXHR_DSP_RESET_CODEC;
+	PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, mgr->dsp_reset);
+	/* hr222_write_gpo(mgr, 0); does the same */
 	msleep(5);
 
 	/* config AKM */
@@ -496,6 +501,33 @@
 }
 
 
+int hr222_read_gpio(struct pcxhr_mgr *mgr, int is_gpi, int *value)
+{
+	if (is_gpi) {
+		unsigned char reg = PCXHR_INPB(mgr, PCXHR_XLX_STATUS);
+		*value = (int)(reg & PCXHR_STAT_GPI_MASK) >>
+			      PCXHR_STAT_GPI_OFFSET;
+	} else {
+		*value = (int)(mgr->dsp_reset & PCXHR_DSP_RESET_GPO_MASK) >>
+			 PCXHR_DSP_RESET_GPO_OFFSET;
+	}
+	return 0;
+}
+
+
+int hr222_write_gpo(struct pcxhr_mgr *mgr, int value)
+{
+	unsigned char reg = mgr->dsp_reset & ~PCXHR_DSP_RESET_GPO_MASK;
+
+	reg |= (unsigned char)(value << PCXHR_DSP_RESET_GPO_OFFSET) &
+	       PCXHR_DSP_RESET_GPO_MASK;
+
+	PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, reg);
+	mgr->dsp_reset = reg;
+	return 0;
+}
+
+
 int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
 				    int is_capture, int channel)
 {
diff --git a/sound/pci/pcxhr/pcxhr_mix22.h b/sound/pci/pcxhr/pcxhr_mix22.h
index 6b318b2..5a37a00 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.h
+++ b/sound/pci/pcxhr/pcxhr_mix22.h
@@ -32,6 +32,9 @@
 			     enum pcxhr_clock_type clock_type,
 			     int *sample_rate);
 
+int hr222_read_gpio(struct pcxhr_mgr *mgr, int is_gpi, int *value);
+int hr222_write_gpo(struct pcxhr_mgr *mgr, int value);
+
 #define HR222_LINE_PLAYBACK_LEVEL_MIN		0	/* -25.5 dB */
 #define HR222_LINE_PLAYBACK_ZERO_LEVEL		51	/* 0.0 dB */
 #define HR222_LINE_PLAYBACK_LEVEL_MAX		99	/* +24.0 dB */
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index 2436e37..fec0493 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -789,11 +789,15 @@
 	if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) {
 		mutex_lock(&mgr->setup_mutex);
 		mgr->use_clock_type = ucontrol->value.enumerated.item[0];
-		if (mgr->use_clock_type)
+		rate = 0;
+		if (mgr->use_clock_type != PCXHR_CLOCK_TYPE_INTERNAL) {
 			pcxhr_get_external_clock(mgr, mgr->use_clock_type,
 						 &rate);
-		else
+		} else {
 			rate = mgr->sample_rate;
+			if (!rate)
+				rate = 48000;
+		}
 		if (rate) {
 			pcxhr_set_clock(mgr, rate);
 			if (mgr->sample_rate)