diff options
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
| -rw-r--r-- | sound/pci/hda/hda_codec.c | 68 | 
1 files changed, 46 insertions, 22 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c7df01b72ca..af989f660cc 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -44,6 +44,7 @@ struct hda_vendor_id {  /* codec vendor labels */  static struct hda_vendor_id hda_vendor_ids[] = {  	{ 0x1002, "ATI" }, +	{ 0x1013, "Cirrus Logic" },  	{ 0x1057, "Motorola" },  	{ 0x1095, "Silicon Image" },  	{ 0x10de, "Nvidia" }, @@ -150,7 +151,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,  {  	u32 val; -	val = (u32)(codec->addr & 0x0f) << 28; +	if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) || +	    (verb & ~0xfff) || (parm & ~0xffff)) { +		printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n", +		       codec->addr, direct, nid, verb, parm); +		return ~0; +	} + +	val = (u32)codec->addr << 28;  	val |= (u32)direct << 27;  	val |= (u32)nid << 20;  	val |= verb << 8; @@ -167,6 +175,9 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,  	struct hda_bus *bus = codec->bus;  	int err; +	if (cmd == ~0) +		return -1; +  	if (res)  		*res = -1;   again: @@ -291,11 +302,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,  	unsigned int parm;  	int i, conn_len, conns;  	unsigned int shift, num_elems, mask; +	unsigned int wcaps;  	hda_nid_t prev_nid;  	if (snd_BUG_ON(!conn_list || max_conns <= 0))  		return -EINVAL; +	wcaps = get_wcaps(codec, nid); +	if (!(wcaps & AC_WCAP_CONN_LIST) && +	    get_wcaps_type(wcaps) != AC_WID_VOL_KNB) { +		snd_printk(KERN_WARNING "hda_codec: " +			   "connection list not available for 0x%x\n", nid); +		return -EINVAL; +	} +  	parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);  	if (parm & AC_CLIST_LONG) {  		/* long form */ @@ -316,6 +336,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,  		/* single connection */  		parm = snd_hda_codec_read(codec, nid, 0,  					  AC_VERB_GET_CONNECT_LIST, 0); +		if (parm == -1 && codec->bus->rirb_error) +			return -EIO;  		conn_list[0] = parm & mask;  		return 1;  	} @@ -327,9 +349,12 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,  		int range_val;  		hda_nid_t val, n; -		if (i % num_elems == 0) +		if (i % num_elems == 0) {  			parm = snd_hda_codec_read(codec, nid, 0,  						  AC_VERB_GET_CONNECT_LIST, i); +			if (parm == -1 && codec->bus->rirb_error) +				return -EIO; +		}  		range_val = !!(parm & (1 << (shift-1))); /* ranges */  		val = parm & mask;  		if (val == 0) { @@ -727,8 +752,7 @@ static int read_pin_defaults(struct hda_codec *codec)  	for (i = 0; i < codec->num_nodes; i++, nid++) {  		struct hda_pincfg *pin;  		unsigned int wcaps = get_wcaps(codec, nid); -		unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> -				AC_WCAP_TYPE_SHIFT; +		unsigned int wid_type = get_wcaps_type(wcaps);  		if (wid_type != AC_WID_PIN)  			continue;  		pin = snd_array_new(&codec->init_pins); @@ -891,7 +915,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,   * Returns 0 if successful, or a negative error code.   */  int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, -				    int do_init, struct hda_codec **codecp) +				    struct hda_codec **codecp)  {  	struct hda_codec *codec;  	char component[31]; @@ -984,11 +1008,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr  			    codec->afg ? codec->afg : codec->mfg,  			    AC_PWRST_D0); -	if (do_init) { -		err = snd_hda_codec_configure(codec); -		if (err < 0) -			goto error; -	}  	snd_hda_codec_proc_new(codec);  	snd_hda_create_hwdep(codec); @@ -1042,6 +1061,7 @@ int snd_hda_codec_configure(struct hda_codec *codec)  		err = init_unsol_queue(codec->bus);  	return err;  } +EXPORT_SYMBOL_HDA(snd_hda_codec_configure);  /**   * snd_hda_codec_setup_stream - set up the codec for streaming @@ -2356,16 +2376,20 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,  	hda_nid_t nid;  	int i; -	snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, +	/* this delay seems necessary to avoid click noise at power-down */ +	if (power_state == AC_PWRST_D3) +		msleep(100); +	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,  			    power_state); -	msleep(10); /* partial workaround for "azx_get_response timeout" */ +	/* partial workaround for "azx_get_response timeout" */ +	if (power_state == AC_PWRST_D0) +		msleep(10);  	nid = codec->start_nid;  	for (i = 0; i < codec->num_nodes; i++, nid++) {  		unsigned int wcaps = get_wcaps(codec, nid);  		if (wcaps & AC_WCAP_POWER) { -			unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> -				AC_WCAP_TYPE_SHIFT; +			unsigned int wid_type = get_wcaps_type(wcaps);  			if (power_state == AC_PWRST_D3 &&  			    wid_type == AC_WID_PIN) {  				unsigned int pincap; @@ -2573,7 +2597,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,  	case 20:  	case 24:  	case 32: -		if (maxbps >= 32) +		if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)  			val |= 0x40;  		else if (maxbps >= 24)  			val |= 0x30; @@ -2700,11 +2724,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,  					bps = 20;  			}  		} -		else if (streams == AC_SUPFMT_FLOAT32) { -			/* should be exclusive */ +		if (streams & AC_SUPFMT_FLOAT32) {  			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; -			bps = 32; -		} else if (streams == AC_SUPFMT_AC3) { +			if (!bps) +				bps = 32; +		} +		if (streams == AC_SUPFMT_AC3) {  			/* should be exclusive */  			/* temporary hack: we have still no proper support  			 * for the direct AC3 stream... @@ -3102,7 +3127,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,  	tbl = q;  	if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE  		char tmp[10];  		const char *model = NULL;  		if (models) @@ -3655,8 +3680,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,  	end_nid = codec->start_nid + codec->num_nodes;  	for (nid = codec->start_nid; nid < end_nid; nid++) {  		unsigned int wid_caps = get_wcaps(codec, nid); -		unsigned int wid_type = -			(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +		unsigned int wid_type = get_wcaps_type(wid_caps);  		unsigned int def_conf;  		short assoc, loc;  |