Jelajahi Sumber

sound: virtuoso: add PCM1796 oversampling control

Add a control to increase the oversampling factor to 128x on cards with
PCM1796 or PCM1792A DACs.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Clemens Ladisch 15 tahun lalu
induk
melakukan
973dca93a3
1 mengubah file dengan 157 tambahan dan 25 penghapusan
  1. 157 25
      sound/pci/oxygen/xonar_pcm179x.c

+ 157 - 25
sound/pci/oxygen/xonar_pcm179x.c

@@ -173,8 +173,11 @@ struct xonar_pcm179x {
 	struct xonar_generic generic;
 	unsigned int dacs;
 	u8 pcm1796_regs[4][5];
+	unsigned int current_rate;
+	bool os_128;
 	bool hp_active;
 	s8 hp_gain_offset;
+	bool has_cs2000;
 	u8 cs2000_fun_cfg_1;
 };
 
@@ -277,6 +280,7 @@ static void pcm1796_init(struct oxygen *chip)
 		PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
 	data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64;
 	pcm1796_registers_init(chip);
+	data->current_rate = 48000;
 }
 
 static void xonar_d2_init(struct oxygen *chip)
@@ -401,6 +405,7 @@ static void xonar_st_init(struct oxygen *chip)
 {
 	struct xonar_pcm179x *data = chip->model_data;
 
+	data->has_cs2000 = 1;
 	data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
 
 	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
@@ -486,18 +491,57 @@ static void xonar_st_resume(struct oxygen *chip)
 	xonar_stx_resume(chip);
 }
 
-static void set_pcm1796_params(struct oxygen *chip,
-			       struct snd_pcm_hw_params *params)
+static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	if (rate <= 32000)
+		return OXYGEN_I2S_MCLK_512;
+	else if (rate <= 48000 && data->os_128)
+		return OXYGEN_I2S_MCLK_512;
+	else if (rate <= 96000)
+		return OXYGEN_I2S_MCLK_256;
+	else
+		return OXYGEN_I2S_MCLK_128;
+}
+
+static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip,
+					 unsigned int channel,
+					 struct snd_pcm_hw_params *params)
+{
+	if (channel == PCM_MULTICH)
+		return mclk_from_rate(chip, params_rate(params));
+	else
+		return oxygen_default_i2s_mclk(chip, channel, params);
+}
+
+static void update_pcm1796_oversampling(struct oxygen *chip)
 {
 	struct xonar_pcm179x *data = chip->model_data;
 	unsigned int i;
 	u8 reg;
 
-	reg = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
+	if (data->current_rate <= 32000)
+		reg = PCM1796_OS_128;
+	else if (data->current_rate <= 48000 && data->os_128)
+		reg = PCM1796_OS_128;
+	else if (data->current_rate <= 96000 || data->os_128)
+		reg = PCM1796_OS_64;
+	else
+		reg = PCM1796_OS_32;
 	for (i = 0; i < data->dacs; ++i)
 		pcm1796_write_cached(chip, i, 20, reg);
 }
 
+static void set_pcm1796_params(struct oxygen *chip,
+			       struct snd_pcm_hw_params *params)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	data->current_rate = params_rate(params);
+	update_pcm1796_oversampling(chip);
+}
+
 static void update_pcm1796_volume(struct oxygen *chip)
 {
 	struct xonar_pcm179x *data = chip->model_data;
@@ -526,26 +570,44 @@ static void update_pcm1796_mute(struct oxygen *chip)
 		pcm1796_write_cached(chip, i, 18, value);
 }
 
-static void set_cs2000_params(struct oxygen *chip,
-			      struct snd_pcm_hw_params *params)
+static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)
 {
-	/* XXX Why is the I2S A MCLK half the actual I2S multich MCLK? */
-	static const u8 rate_mclks[] = {
-		[OXYGEN_RATE_32000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_128,
-		[OXYGEN_RATE_44100] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128,
-		[OXYGEN_RATE_48000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128,
-		[OXYGEN_RATE_64000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256,
-		[OXYGEN_RATE_88200] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256,
-		[OXYGEN_RATE_96000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256,
-		[OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256,
-		[OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256,
-	};
-	unsigned int rate_index;
+	struct xonar_pcm179x *data = chip->model_data;
 	u8 rate_mclk, reg;
 
-	rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT)
-		& OXYGEN_I2S_RATE_MASK;
-	rate_mclk = rate_mclks[rate_index];
+	switch (rate) {
+		/* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
+	case 32000:
+		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 44100:
+		if (data->os_128)
+			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+		else
+			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128;
+		break;
+	default: /* 48000 */
+		if (data->os_128)
+			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+		else
+			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128;
+		break;
+	case 64000:
+		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 88200:
+		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 96000:
+		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 176400:
+		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 192000:
+		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+		break;
+	}
 	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
 			      OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
 	if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128)
@@ -558,7 +620,7 @@ static void set_cs2000_params(struct oxygen *chip,
 static void set_st_params(struct oxygen *chip,
 			  struct snd_pcm_hw_params *params)
 {
-	set_cs2000_params(chip, params);
+	update_cs2000_rate(chip, params_rate(params));
 	set_pcm1796_params(chip, params);
 }
 
@@ -580,6 +642,59 @@ static const struct snd_kcontrol_new alt_switch = {
 	.private_value = GPIO_D2_ALT,
 };
 
+static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = { "64x", "128x" };
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 2;
+	if (info->value.enumerated.item >= 2)
+		info->value.enumerated.item = 1;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int os_128_get(struct snd_kcontrol *ctl,
+		      struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+
+	value->value.enumerated.item[0] = data->os_128;
+	return 0;
+}
+
+static int os_128_put(struct snd_kcontrol *ctl,
+		      struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	changed = value->value.enumerated.item[0] != data->os_128;
+	if (changed) {
+		data->os_128 = value->value.enumerated.item[0];
+		if (data->has_cs2000)
+			update_cs2000_rate(chip, data->current_rate);
+		oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
+				      mclk_from_rate(chip, data->current_rate),
+				      OXYGEN_I2S_MCLK_MASK);
+		update_pcm1796_oversampling(chip);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new os_128_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Oversampling Playback Enum",
+	.info = os_128_info,
+	.get = os_128_get,
+	.put = os_128_put,
+};
+
 static int st_output_switch_info(struct snd_kcontrol *ctl,
 				 struct snd_ctl_elem_info *info)
 {
@@ -745,7 +860,20 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template)
 
 static int xonar_d2_mixer_init(struct oxygen *chip)
 {
-	return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
+	int err;
+
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int xonar_hdav_mixer_init(struct oxygen *chip)
+{
+	return snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
 }
 
 static int xonar_st_mixer_init(struct oxygen *chip)
@@ -759,6 +887,9 @@ static int xonar_st_mixer_init(struct oxygen *chip)
 		if (err < 0)
 			return err;
 	}
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
+	if (err < 0)
+		return err;
 	return 0;
 }
 
@@ -771,7 +902,7 @@ static const struct oxygen_model model_xonar_d2 = {
 	.cleanup = xonar_d2_cleanup,
 	.suspend = xonar_d2_suspend,
 	.resume = xonar_d2_resume,
-	.get_i2s_mclk = oxygen_default_i2s_mclk,
+	.get_i2s_mclk = get_pcm1796_i2s_mclk,
 	.set_dac_params = set_pcm1796_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.update_dac_volume = update_pcm1796_volume,
@@ -798,11 +929,12 @@ static const struct oxygen_model model_xonar_hdav = {
 	.longname = "Asus Virtuoso 200",
 	.chip = "AV200",
 	.init = xonar_hdav_init,
+	.mixer_init = xonar_hdav_mixer_init,
 	.cleanup = xonar_hdav_cleanup,
 	.suspend = xonar_hdav_suspend,
 	.resume = xonar_hdav_resume,
 	.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
-	.get_i2s_mclk = oxygen_default_i2s_mclk,
+	.get_i2s_mclk = get_pcm1796_i2s_mclk,
 	.set_dac_params = set_hdav_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.update_dac_volume = update_pcm1796_volume,
@@ -833,7 +965,7 @@ static const struct oxygen_model model_xonar_st = {
 	.cleanup = xonar_st_cleanup,
 	.suspend = xonar_st_suspend,
 	.resume = xonar_st_resume,
-	.get_i2s_mclk = oxygen_default_i2s_mclk,
+	.get_i2s_mclk = get_pcm1796_i2s_mclk,
 	.set_dac_params = set_st_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.update_dac_volume = update_pcm1796_volume,