aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorPavel Hofman <dustin@seznam.cz>2007-12-03 12:44:28 +0100
committerJaroslav Kysela <perex@perex.cz>2008-01-31 17:29:30 +0100
commit6632d64b0b596b9588b607806ac6d36c8c2c9696 (patch)
tree0e8126af6c2c47e2b981c7ab7d8cf29aaaee8360 /sound/pci
parentacec30ffffe1eee07e3202cff03e7ca8350d250f (diff)
downloadkernel_goldelico_gta04-6632d64b0b596b9588b607806ac6d36c8c2c9696.zip
kernel_goldelico_gta04-6632d64b0b596b9588b607806ac6d36c8c2c9696.tar.gz
kernel_goldelico_gta04-6632d64b0b596b9588b607806ac6d36c8c2c9696.tar.bz2
[ALSA] switching rate in STAC9460 codec of Prodigy192
* support for switching rate in STAC9460 - using set_rate_val of the akm infrastructure * listing all STAC9460 registers in proc * disabling mpu401 device for Prodigy192 - otherwise the currently flawed mpu401 code hangs kernel when opening the midi device * removing old unused commented-out code Signed-off-by: Pavel Hofman <dustin@seznam.cz> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/ice1712/ice1712.h2
-rw-r--r--sound/pci/ice1712/prodigy192.c271
2 files changed, 118 insertions, 155 deletions
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 3c3cac3..4dc576a 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -399,6 +399,8 @@ struct snd_ice1712 {
} juli;
struct {
struct ak4114 *ak4114;
+ /* rate change needs atomic mute/unmute of all dacs*/
+ struct mutex mute_mutex;
} prodigy192;
struct {
struct {
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 4b21d5c..6d81a1c 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -81,6 +81,24 @@ static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
/*
* DAC mute control
*/
+
+/*
+ * idx = STAC9460 volume register number, mute: 0 = mute, 1 = unmute
+ */
+static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx,
+ unsigned char mute)
+{
+ unsigned char new, old;
+ int change;
+ old = stac9460_get(ice, idx);
+ new = (~mute << 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change)
+ /*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/
+ stac9460_put(ice, idx, new);
+ return change;
+}
+
#define stac9460_dac_mute_info snd_ctl_boolean_mono_info
static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -101,20 +119,18 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- unsigned char new, old;
- int idx;
- int change;
+ int idx, change;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
- old = stac9460_get(ice, idx);
- new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
- change = (new != old);
- if (change)
- stac9460_put(ice, idx, new);
-
+ /* due to possible conflicts with stac9460_set_rate_val, mutexing */
+ mutex_lock(&ice->spec.prodigy192.mute_mutex);
+ /*printk("Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
+ ucontrol->value.integer.value[0]);*/
+ change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
+ mutex_unlock(&ice->spec.prodigy192.mute_mutex);
return change;
}
@@ -162,6 +178,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
+ ovol = (0x7f - nvol) | (tmp & 0x80);
+ /*printk("DAC Volume: reg 0x%02x: 0x%02x\n", idx, ovol);*/
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
}
return change;
@@ -251,121 +269,6 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return change;
}
-#if 0
-/*
- * Headphone Amplifier
- */
-static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)
-{
- unsigned int tmp, tmp2;
-
- tmp2 = tmp = snd_ice1712_gpio_read(ice);
- if (enable)
- tmp |= AUREON_HP_SEL;
- else
- tmp &= ~ AUREON_HP_SEL;
- if (tmp != tmp2) {
- snd_ice1712_gpio_write(ice, tmp);
- return 1;
- }
- return 0;
-}
-
-static int aureon_get_headphone_amp(struct snd_ice1712 *ice)
-{
- unsigned int tmp = snd_ice1712_gpio_read(ice);
-
- return ( tmp & AUREON_HP_SEL )!= 0;
-}
-
-#define aureon_bool_info snd_ctl_boolean_mono_info
-
-static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
- return 0;
-}
-
-
-static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
- return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
-}
-
-/*
- * Deemphasis
- */
-static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
- return 0;
-}
-
-static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- int temp, temp2;
- temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
- if (ucontrol->value.integer.value[0])
- temp |= 0xf;
- else
- temp &= ~0xf;
- if (temp != temp2) {
- wm_put(ice, WM_DAC_CTRL2, temp);
- return 1;
- }
- return 0;
-}
-
-/*
- * ADC Oversampling
- */
-static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[2] = { "128x", "64x" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
- return 0;
-}
-
-static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- int temp, temp2;
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
- temp2 = temp = wm_get(ice, WM_MASTER);
-
- if (ucontrol->value.enumerated.item[0])
- temp |= 0x8;
- else
- temp &= ~0x8;
-
- if (temp != temp2) {
- wm_put(ice, WM_MASTER, temp);
- return 1;
- }
- return 0;
-}
-#endif
static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -407,6 +310,56 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
return change;
}
+/*
+ * Handler for setting correct codec rate - called when rate change is detected
+ */
+static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
+{
+ unsigned char old, new;
+ int idx;
+ unsigned char changed[7];
+ struct snd_ice1712 *ice = ak->private_data[0];
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+ else if (rate <= 48000)
+ new = 0x08; /* 256x, base rate mode */
+ else if (rate <= 96000)
+ new = 0x11; /* 256x, mid rate mode */
+ else
+ new = 0x12; /* 128x, high rate mode */
+ old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
+ if (old == new)
+ return;
+ /* change detected, setting master clock, muting first */
+ /* due to possible conflicts with mute controls - mutexing */
+ mutex_lock(&ice->spec.prodigy192.mute_mutex);
+ /* we have to remember current mute status for each DAC */
+ for (idx = 0; idx < 7 ; ++idx)
+ changed[idx] = stac9460_dac_mute(ice,
+ STAC946X_MASTER_VOLUME + idx, 0);
+ /*printk("Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+ stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
+ udelay(10);
+ /* unmuting - only originally unmuted dacs -
+ * i.e. those changed when muting */
+ for (idx = 0; idx < 7 ; ++idx) {
+ if (changed[idx])
+ stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1);
+ }
+ mutex_unlock(&ice->spec.prodigy192.mute_mutex);
+}
+
+/* using akm infrastructure for setting rate of the codec */
+static struct snd_akm4xxx akmlike_stac9460 __devinitdata = {
+ .type = NON_AKM, /* special value */
+ .num_adcs = 6, /* not used in any way, just for completeness */
+ .num_dacs = 2,
+ .ops = {
+ .set_rate_val = stac9460_set_rate_val
+ }
+};
+
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
@@ -483,39 +436,8 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
.put = stac9460_mic_sw_put,
},
-#if 0
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Route",
- .info = wm_adc_mux_info,
- .get = wm_adc_mux_get,
- .put = wm_adc_mux_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Headphone Amplifier Switch",
- .info = aureon_bool_info,
- .get = aureon_hpamp_get,
- .put = aureon_hpamp_put
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DAC Deemphasis Switch",
- .info = aureon_bool_info,
- .get = aureon_deemp_get,
- .put = aureon_deemp_put
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "ADC Oversampling",
- .info = aureon_oversampling_info,
- .get = aureon_oversampling_get,
- .put = aureon_oversampling_put
- },
-#endif
};
-
/* AK4114 - ICE1724 connections on Prodigy192 + MI/ODI/O */
/* CDTO (pin 32) -- GPIO11 pin 86
* CDTI (pin 33) -- GPIO10 pin 77
@@ -720,6 +642,27 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
ice, &ice->spec.prodigy192.ak4114);
}
+static void stac9460_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ int reg, val;
+ /* registers 0x0 - 0x14 */
+ for (reg = 0; reg <= 0x15; reg++) {
+ val = stac9460_get(ice, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+
+static void stac9460_proc_init(struct snd_ice1712 *ice)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry))
+ snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read);
+}
+
+
static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
@@ -746,6 +689,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
if (err < 0)
return err;
}
+ stac9460_proc_init(ice);
return 0;
}
@@ -778,6 +722,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
{
static const unsigned short stac_inits_prodigy[] = {
STAC946X_RESET, 0,
+ STAC946X_MASTER_CLOCKING, 0x11,
/* STAC946X_MASTER_VOLUME, 0,
STAC946X_LF_VOLUME, 0,
STAC946X_RF_VOLUME, 0,
@@ -789,6 +734,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
};
const unsigned short *p;
int err = 0;
+ struct snd_akm4xxx *ak;
/* prodigy 192 */
ice->num_total_dacs = 6;
@@ -799,6 +745,15 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
p = stac_inits_prodigy;
for (; *p != (unsigned short)-1; p += 2)
stac9460_put(ice, p[0], p[1]);
+ /* reusing the akm codecs infrastructure,
+ * for setting rate on stac9460 */
+ ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ if (!ak)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+ err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice);
+ if (err < 0)
+ return err;
/* MI/ODI/O add on card with AK4114 */
if (prodigy192_miodio_exists(ice)) {
@@ -812,6 +767,8 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
if (err < 0)
return err;
+ mutex_init(&ice->spec.prodigy192.mute_mutex);
+
return 0;
}
@@ -854,6 +811,10 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
.build_controls = prodigy192_add_controls,
.eeprom_size = sizeof(prodigy71_eeprom),
.eeprom_data = prodigy71_eeprom,
+ /* the current MPU401 code loops infinitely
+ * when opening midi device
+ */
+ .no_mpu401 = 1,
},
{ } /* terminator */
};