[alsa-devel] [Fwd: Re: Code relsease to alsa.]

Takashi Iwai tiwai at suse.de
Tue Jan 22 15:55:17 CET 2008


At Mon, 21 Jan 2008 13:48:20 -0500,
Marc Boucher wrote:
> 
> 
> Hi Takashi,
> 
> Here's the sign-off:
> 
> Signed-off-by: Marc Boucher <marc at linuxant.com>

Thanks!

> Do you have any specific comments about the code itself?

I have now applied some parts of your patch to HG tree, namely,

- laptop model fixes for Cxt5045
- clean up of Cxt5047 verbs
- add missing input elements for cxt5047 test model
- a workaround in power-state change
- afg and mfg fields in codec preset
- ratelimit to timeout messages

I modified the above a bit, mainly for fixing coding issues.
So, be careful to rebase your patch again.

The rest, the addition of cxt5051 and the HSF modem are pending.

I checked the patch and found some problems in cxt5051 code:

- the ADC stream may be unswitched even if you plug/unplug the docking
  station when the PCM is being opened
- basically the capture switch has no role
- judging the difference of capture prepare/cleanup via the number of
  adc

The patch below is my revised version with some cleanups.
Can someone test with the top of the very latest HG tree (at least
changeset 5753)?

About HSF modem, let me clarify.  The HSF modem patch can't be applied
as is to the kernel tree.  The purpose of this patch is exactly to
bind with a binary-only object.  And, as everybody knows, binary-only
objects are refused in the kernel upstream in general, so there is 
no reason to apply it.

Oh, I also didn't apply the addition of wallclock and lpos.  These are
basically fine to add but there is no user except for HSF.  The lpos
callback can be replaced with the normal PCM position read, I guess.

The exit callback is also not applied.  Why is this needed?  Doesn't
it work if you call from free callback?


Takashi

diff -r 7cebcd53be49 Documentation/ALSA-Configuration.txt
--- a/Documentation/ALSA-Configuration.txt	Tue Jan 22 15:18:08 2008 +0100
+++ b/Documentation/ALSA-Configuration.txt	Tue Jan 22 15:19:14 2008 +0100
@@ -960,6 +960,9 @@ Prior to version 0.9.0rc4 options had a 
 	  test		for testing/debugging purpose, almost all controls
 			can be adjusted.  Appearing only when compiled with
 			$CONFIG_SND_DEBUG=y
+
+	Conexant 5051
+	  laptop	Basic Laptop config 
 
 	STAC9200
 	  ref		Reference board
diff -r 7cebcd53be49 pci/hda/patch_conexant.c
--- a/pci/hda/patch_conexant.c	Tue Jan 22 15:18:08 2008 +0100
+++ b/pci/hda/patch_conexant.c	Tue Jan 22 15:19:14 2008 +0100
@@ -63,6 +63,11 @@ struct conexant_spec {
 	unsigned int num_adc_nids;
 	hda_nid_t *adc_nids;
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
+
+	unsigned int cur_adc_idx;
+	hda_nid_t cur_adc;
+	unsigned int cur_adc_stream_tag;
+	unsigned int cur_adc_format;
 
 	/* capture source */
 	const struct hda_input_mux *input_mux;
@@ -217,6 +222,41 @@ static struct hda_pcm_stream conexant_pc
 	/* NID is set in alc_build_pcms */
 };
 
+static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      unsigned int stream_tag,
+				      unsigned int format,
+				      struct snd_pcm_substream *substream)
+{
+	struct conexant_spec *spec = codec->spec;
+	spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+	spec->cur_adc_stream_tag = stream_tag;
+	spec->cur_adc_format = format;
+	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+	return 0;
+}
+
+static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      struct snd_pcm_substream *substream)
+{
+	struct conexant_spec *spec = codec->spec;
+	snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+	spec->cur_adc = 0;
+	return 0;
+}
+
+static struct hda_pcm_stream cx5051_pcm_analog_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0, /* fill later */
+	.ops = {
+		.prepare = cx5051_capture_pcm_prepare,
+		.cleanup = cx5051_capture_pcm_cleanup
+	},
+};
+
 static int conexant_build_pcms(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
@@ -231,7 +271,12 @@ static int conexant_build_pcms(struct hd
 		spec->multiout.max_channels;
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
 		spec->multiout.dac_nids[0];
-	info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture;
+	if (codec->vendor_id == 0x14f15051)
+		info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+			cx5051_pcm_analog_capture;
+	else
+		info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+			conexant_pcm_analog_capture;
 	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
 
@@ -1421,10 +1466,325 @@ static int patch_cxt5047(struct hda_code
 	return 0;
 }
 
+/* Conexant 5051 specific */
+static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
+static hda_nid_t cxt5051_capsrc_nids[2] = { 0x14, 0x15 };
+#define CXT5051_SPDIF_OUT	0x1C
+#define CXT5051_PORTB_EVENT	0x38
+#define CXT5051_PORTC_EVENT	0x39
+
+static struct hda_channel_mode cxt5051_modes[1] = {
+	{ 2, NULL },
+};
+
+static struct hda_input_mux cxt5051_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "IntMic", 0x1 },
+		{ "ExtMic", 0x2 },
+	}
+};
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	static struct hda_verb spk_on[] = {
+		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+		{}
+	};
+	static struct hda_verb spk_off[] = {
+		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		{}
+	};
+	static struct hda_verb hp_on[] = {
+		{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+		{}
+	};
+	static struct hda_verb hp_off[] = {
+		{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		{}
+	};
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct conexant_spec *spec = codec->spec;
+
+	if (!cxt_eapd_put(kcontrol, ucontrol))
+		return 0;
+
+	/* toggle internal speakers mute depending of presence of
+	 * the headphone jack
+	 */
+	if (!spec->hp_present && spec->cur_eapd)
+		snd_hda_sequence_write(codec, spk_on);
+	else
+		snd_hda_sequence_write(codec, spk_off);
+
+	if (spec->cur_eapd)
+		snd_hda_sequence_write(codec, hp_on);
+	else
+		snd_hda_sequence_write(codec, hp_off);
+
+	return 1;
+}
+
+/* bind volumes of both NID 0x10 and 0x11 */
+static int cxt5051_hp_master_vol_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0,
+					  HDA_AMP_VOLMASK, valp[0]);
+	change |= snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0,
+					   HDA_AMP_VOLMASK, valp[1]);
+	return change;
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5051_portb_automic(struct hda_codec *codec)
+{
+	static struct hda_verb mic_jack_on[] = {
+		{0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
+		{}
+	};
+	static struct hda_verb mic_jack_off[] = {
+		{0x14, AC_VERB_SET_CONNECT_SEL, 0x0},
+		{}
+	};
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x17, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+	if (present)
+		snd_hda_sequence_write(codec, mic_jack_on);
+	else
+		snd_hda_sequence_write(codec, mic_jack_off);
+}
+
+static void cxt5051_portc_automic(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	unsigned int present;
+	hda_nid_t new_adc;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	if (present)
+		spec->cur_adc_idx = 1;
+	else
+		spec->cur_adc_idx = 0;
+	new_adc = spec->adc_nids[spec->cur_adc_idx];
+	if (spec->cur_adc && spec->cur_adc != new_adc) {
+		snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+		spec->cur_adc = new_adc;
+		snd_hda_codec_setup_stream(codec, new_adc,
+					   spec->cur_adc_stream_tag, 0,
+					   spec->cur_adc_format);
+	}
+}
+
+/* mute internal speaker if HP is plugged */
+static void cxt5051_hp_automute(struct hda_codec *codec)
+{
+	static struct hda_verb spk_on[] = {
+		{0x1A, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+		{}
+	};
+	static struct hda_verb spk_off[] = {
+		{0x1A, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		{}
+	};
+
+	struct conexant_spec *spec = codec->spec;
+	unsigned int bits;
+
+	spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+
+	bits = (spec->hp_present || !spec->cur_eapd) ? 0x80 : 0; 
+	if (bits)
+		snd_hda_sequence_write(codec, spk_off);
+	else
+		snd_hda_sequence_write(codec, spk_on);
+
+	printk(KERN_DEBUG"cxt5051_hp_automute bits =%x\n",bits);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5051_hp_unsol_event(struct hda_codec *codec,
+				   unsigned int res)
+{
+	switch (res >> 26) {
+	case CONEXANT_HP_EVENT:
+		cxt5051_hp_automute(codec);
+		break;
+	case CXT5051_PORTB_EVENT:
+		cxt5051_portb_automic(codec);
+		break;
+	case CXT5051_PORTC_EVENT:
+		cxt5051_portc_automic(codec);
+		break;
+	}
+}
+
+static struct snd_kcontrol_new cxt5051_mixers[] = {
+	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.info = snd_hda_mixer_amp_volume_info,
+		.get = snd_hda_mixer_amp_volume_get,
+		.put = cxt5051_hp_master_vol_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT),
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = cxt_eapd_info,
+		.get = cxt_eapd_get,
+		.put = cxt5051_hp_master_sw_put,
+		.private_value = 0x1a,
+	},
+
+	{}
+};
+
+static struct hda_verb cxt5051_init_verbs[] = {
+	/* Line in, Mic */
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+	/* SPK  */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* HP, Amp  */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* DAC1 */	
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Record selector: Int mic */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+	/* SPDIF route: PCM */
+	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* EAPD */
+	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 
+	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
+	{ } /* end */
+};
+
+
+/* For HP's configuration always use portB as Internal and External Mic */
+static struct hda_verb cxt5051_mic_wid17_init_verbs[] = {
+	/* pin sensing on HP jack */
+	{0x14, AC_VERB_SET_CONNECT_SEL,0x1},
+	{ } /* end */
+};
+
+
+/* initialize jack-sensing, too */
+static int cxt5051_init(struct hda_codec *codec)
+{
+	conexant_init(codec);
+	if (codec->patch_ops.unsol_event) {
+		cxt5051_hp_automute(codec);
+		cxt5051_portb_automic(codec);
+		cxt5051_portc_automic(codec);
+	}
+	return 0;
+}
+
+
+enum {
+	CXT5051_LAPTOP,	 /* Laptops w/ EAPD support */
+	CXT5051_MODELS
+};
+
+static const char *cxt5051_models[CXT5051_MODELS] = {
+	[CXT5051_LAPTOP]	= "laptop",
+};
+
+static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+		      CXT5051_LAPTOP),
+	{}
+};
+
+static int patch_cxt5051(struct hda_codec *codec)
+{
+	struct conexant_spec *spec;
+	int board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (!spec)
+		return -ENOMEM;
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
+	spec->multiout.dac_nids = cxt5051_dac_nids;
+	spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
+	spec->num_adc_nids = 2;
+	spec->adc_nids = cxt5051_adc_nids;
+	spec->capsrc_nids = cxt5051_capsrc_nids;
+	spec->input_mux = &cxt5051_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = cxt5051_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = cxt5051_init_verbs;
+	spec->spdif_route = 0;
+	spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
+	spec->channel_mode = cxt5051_modes;
+	spec->cur_adc = 0;
+	spec->cur_adc_idx = 0;
+
+	codec->patch_ops = conexant_patch_ops;
+
+	board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
+						  cxt5051_models,
+						  cxt5051_cfg_tbl);
+	switch (board_config) {
+	default:
+	case CXT5051_LAPTOP:
+		codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+		spec->input_mux = &cxt5051_capture_source;
+		spec->num_init_verbs = 2;
+		spec->init_verbs[1] = cxt5051_mic_wid17_init_verbs;
+		spec->mixers[0] = cxt5051_mixers;
+		codec->patch_ops.init = cxt5051_init;
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ */
+
 struct hda_codec_preset snd_hda_preset_conexant[] = {
 	{ .id = 0x14f15045, .name = "CX20549 (Venice)",
 	  .patch = patch_cxt5045 },
 	{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
 	  .patch = patch_cxt5047 },
+	{ .id = 0x14f15051, .name = "CX20561 (Hermosa)",
+	  .patch = patch_cxt5051 },
 	{} /* terminator */
 };


More information about the Alsa-devel mailing list