Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42dc2378 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'topic/hda-next' into topic/hda

parents da74ae3e 74aeaabc
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -644,6 +644,7 @@ struct input_absinfo {
#define SW_RADIO		SW_RFKILL_ALL	/* deprecated */
#define SW_RADIO		SW_RFKILL_ALL	/* deprecated */
#define SW_MICROPHONE_INSERT	0x04  /* set = inserted */
#define SW_MICROPHONE_INSERT	0x04  /* set = inserted */
#define SW_DOCK			0x05  /* set = plugged into dock */
#define SW_DOCK			0x05  /* set = plugged into dock */
#define SW_LINEOUT_INSERT	0x06  /* set = inserted */
#define SW_MAX			0x0f
#define SW_MAX			0x0f
#define SW_CNT			(SW_MAX+1)
#define SW_CNT			(SW_MAX+1)


+1 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ enum snd_jack_types {
	SND_JACK_HEADPHONE	= 0x0001,
	SND_JACK_HEADPHONE	= 0x0001,
	SND_JACK_MICROPHONE	= 0x0002,
	SND_JACK_MICROPHONE	= 0x0002,
	SND_JACK_HEADSET	= SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
	SND_JACK_HEADSET	= SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
	SND_JACK_LINEOUT	= 0x0004,
};
};


struct snd_jack {
struct snd_jack {
+8 −1
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ static int snd_jack_dev_free(struct snd_device *device)
	else
	else
		input_free_device(jack->input_dev);
		input_free_device(jack->input_dev);


	kfree(jack->id);
	kfree(jack);
	kfree(jack);


	return 0;
	return 0;
@@ -87,7 +88,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
	if (jack == NULL)
	if (jack == NULL)
		return -ENOMEM;
		return -ENOMEM;


	jack->id = id;
	jack->id = kstrdup(id, GFP_KERNEL);


	jack->input_dev = input_allocate_device();
	jack->input_dev = input_allocate_device();
	if (jack->input_dev == NULL) {
	if (jack->input_dev == NULL) {
@@ -102,6 +103,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
	if (type & SND_JACK_HEADPHONE)
	if (type & SND_JACK_HEADPHONE)
		input_set_capability(jack->input_dev, EV_SW,
		input_set_capability(jack->input_dev, EV_SW,
				     SW_HEADPHONE_INSERT);
				     SW_HEADPHONE_INSERT);
	if (type & SND_JACK_LINEOUT)
		input_set_capability(jack->input_dev, EV_SW,
				     SW_LINEOUT_INSERT);
	if (type & SND_JACK_MICROPHONE)
	if (type & SND_JACK_MICROPHONE)
		input_set_capability(jack->input_dev, EV_SW,
		input_set_capability(jack->input_dev, EV_SW,
				     SW_MICROPHONE_INSERT);
				     SW_MICROPHONE_INSERT);
@@ -150,6 +154,9 @@ void snd_jack_report(struct snd_jack *jack, int status)
	if (jack->type & SND_JACK_HEADPHONE)
	if (jack->type & SND_JACK_HEADPHONE)
		input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
		input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
				    status & SND_JACK_HEADPHONE);
				    status & SND_JACK_HEADPHONE);
	if (jack->type & SND_JACK_LINEOUT)
		input_report_switch(jack->input_dev, SW_LINEOUT_INSERT,
				    status & SND_JACK_LINEOUT);
	if (jack->type & SND_JACK_MICROPHONE)
	if (jack->type & SND_JACK_MICROPHONE)
		input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
		input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
				    status & SND_JACK_MICROPHONE);
				    status & SND_JACK_MICROPHONE);
+1 −0
Original line number Original line Diff line number Diff line
@@ -501,6 +501,7 @@ config SND_HDA_INTEL
	tristate "Intel HD Audio"
	tristate "Intel HD Audio"
	select SND_PCM
	select SND_PCM
	select SND_VMASTER
	select SND_VMASTER
	select SND_JACK if INPUT=y || INPUT=SND
	help
	help
	  Say Y here to include support for Intel "High Definition
	  Say Y here to include support for Intel "High Definition
	  Audio" (Azalia) motherboard devices.
	  Audio" (Azalia) motherboard devices.
+333 −95
Original line number Original line Diff line number Diff line
@@ -107,6 +107,52 @@ static void hda_keep_power_on(struct hda_codec *codec);
static inline void hda_keep_power_on(struct hda_codec *codec) {}
static inline void hda_keep_power_on(struct hda_codec *codec) {}
#endif
#endif


const char *snd_hda_get_jack_location(u32 cfg)
{
	static char *bases[7] = {
		"N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
	};
	static unsigned char specials_idx[] = {
		0x07, 0x08,
		0x17, 0x18, 0x19,
		0x37, 0x38
	};
	static char *specials[] = {
		"Rear Panel", "Drive Bar",
		"Riser", "HDMI", "ATAPI",
		"Mobile-In", "Mobile-Out"
	};
	int i;
	cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
	if ((cfg & 0x0f) < 7)
		return bases[cfg & 0x0f];
	for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
		if (cfg == specials_idx[i])
			return specials[i];
	}
	return "UNKNOWN";
}

const char *snd_hda_get_jack_connectivity(u32 cfg)
{
	static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };

	return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
}

const char *snd_hda_get_jack_type(u32 cfg)
{
	static char *jack_types[16] = {
		"Line Out", "Speaker", "HP Out", "CD",
		"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
		"Line In", "Aux", "Mic", "Telephony",
		"SPDIF In", "Digitial In", "Reserved", "Other"
	};

	return jack_types[(cfg & AC_DEFCFG_DEVICE)
				>> AC_DEFCFG_DEVICE_SHIFT];
}

/**
/**
 * snd_hda_codec_read - send a command and get the response
 * snd_hda_codec_read - send a command and get the response
 * @codec: the HDA codec
 * @codec: the HDA codec
@@ -344,7 +390,7 @@ static void process_unsol_events(struct work_struct *work)
/*
/*
 * initialize unsolicited queue
 * initialize unsolicited queue
 */
 */
static int __devinit init_unsol_queue(struct hda_bus *bus)
static int init_unsol_queue(struct hda_bus *bus)
{
{
	struct hda_bus_unsolicited *unsol;
	struct hda_bus_unsolicited *unsol;


@@ -393,6 +439,20 @@ static int snd_hda_bus_dev_free(struct snd_device *device)
	return snd_hda_bus_free(bus);
	return snd_hda_bus_free(bus);
}
}


#ifdef CONFIG_SND_HDA_HWDEP
static int snd_hda_bus_dev_register(struct snd_device *device)
{
	struct hda_bus *bus = device->device_data;
	struct hda_codec *codec;
	list_for_each_entry(codec, &bus->codec_list, list) {
		snd_hda_hwdep_add_sysfs(codec);
	}
	return 0;
}
#else
#define snd_hda_bus_dev_register	NULL
#endif

/**
/**
 * snd_hda_bus_new - create a HDA bus
 * snd_hda_bus_new - create a HDA bus
 * @card: the card entry
 * @card: the card entry
@@ -408,6 +468,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
	struct hda_bus *bus;
	struct hda_bus *bus;
	int err;
	int err;
	static struct snd_device_ops dev_ops = {
	static struct snd_device_ops dev_ops = {
		.dev_register = snd_hda_bus_dev_register,
		.dev_free = snd_hda_bus_dev_free,
		.dev_free = snd_hda_bus_dev_free,
	};
	};


@@ -446,7 +507,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,


#ifdef CONFIG_SND_HDA_GENERIC
#ifdef CONFIG_SND_HDA_GENERIC
#define is_generic_config(codec) \
#define is_generic_config(codec) \
	(codec->bus->modelname && !strcmp(codec->bus->modelname, "generic"))
	(codec->modelname && !strcmp(codec->modelname, "generic"))
#else
#else
#define is_generic_config(codec)	0
#define is_generic_config(codec)	0
#endif
#endif
@@ -454,7 +515,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
/*
/*
 * find a matching codec preset
 * find a matching codec preset
 */
 */
static const struct hda_codec_preset __devinit *
static const struct hda_codec_preset *
find_codec_preset(struct hda_codec *codec)
find_codec_preset(struct hda_codec *codec)
{
{
	const struct hda_codec_preset **tbl, *preset;
	const struct hda_codec_preset **tbl, *preset;
@@ -481,15 +542,14 @@ find_codec_preset(struct hda_codec *codec)
}
}


/*
/*
 * snd_hda_get_codec_name - store the codec name
 * get_codec_name - store the codec name
 */
 */
void snd_hda_get_codec_name(struct hda_codec *codec,
static int get_codec_name(struct hda_codec *codec)
			    char *name, int namelen)
{
{
	const struct hda_vendor_id *c;
	const struct hda_vendor_id *c;
	const char *vendor = NULL;
	const char *vendor = NULL;
	u16 vendor_id = codec->vendor_id >> 16;
	u16 vendor_id = codec->vendor_id >> 16;
	char tmp[16];
	char tmp[16], name[32];


	for (c = hda_vendor_ids; c->id; c++) {
	for (c = hda_vendor_ids; c->id; c++) {
		if (c->id == vendor_id) {
		if (c->id == vendor_id) {
@@ -502,10 +562,15 @@ void snd_hda_get_codec_name(struct hda_codec *codec,
		vendor = tmp;
		vendor = tmp;
	}
	}
	if (codec->preset && codec->preset->name)
	if (codec->preset && codec->preset->name)
		snprintf(name, namelen, "%s %s", vendor, codec->preset->name);
		snprintf(name, sizeof(name), "%s %s", vendor,
			 codec->preset->name);
	else
	else
		snprintf(name, namelen, "%s ID %x", vendor,
		snprintf(name, sizeof(name), "%s ID %x", vendor,
			 codec->vendor_id & 0xffff);
			 codec->vendor_id & 0xffff);
	codec->name = kstrdup(name, GFP_KERNEL);
	if (!codec->name)
		return -ENOMEM;
	return 0;
}
}


/*
/*
@@ -570,11 +635,14 @@ static void snd_hda_codec_free(struct hda_codec *codec)
	flush_scheduled_work();
	flush_scheduled_work();
#endif
#endif
	list_del(&codec->list);
	list_del(&codec->list);
	snd_array_free(&codec->mixers);
	codec->bus->caddr_tbl[codec->addr] = NULL;
	codec->bus->caddr_tbl[codec->addr] = NULL;
	if (codec->patch_ops.free)
	if (codec->patch_ops.free)
		codec->patch_ops.free(codec);
		codec->patch_ops.free(codec);
	free_hda_cache(&codec->amp_cache);
	free_hda_cache(&codec->amp_cache);
	free_hda_cache(&codec->cmd_cache);
	free_hda_cache(&codec->cmd_cache);
	kfree(codec->name);
	kfree(codec->modelname);
	kfree(codec->wcaps);
	kfree(codec->wcaps);
	kfree(codec);
	kfree(codec);
}
}
@@ -616,6 +684,14 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
	mutex_init(&codec->spdif_mutex);
	mutex_init(&codec->spdif_mutex);
	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
	snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
	if (codec->bus->modelname) {
		codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
		if (!codec->modelname) {
			snd_hda_codec_free(codec);
			return -ENODEV;
		}
	}


#ifdef CONFIG_SND_HDA_POWER_SAVE
#ifdef CONFIG_SND_HDA_POWER_SAVE
	INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
	INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
@@ -661,12 +737,41 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
			snd_hda_codec_read(codec, nid, 0,
			snd_hda_codec_read(codec, nid, 0,
					   AC_VERB_GET_SUBSYSTEM_ID, 0);
					   AC_VERB_GET_SUBSYSTEM_ID, 0);
	}
	}
	if (bus->modelname)
		codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);

	err = snd_hda_codec_configure(codec);
	if (err < 0) {
		snd_hda_codec_free(codec);
		return err;
	}
	snd_hda_codec_proc_new(codec);

	snd_hda_create_hwdep(codec);

	sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id,
		codec->subsystem_id, codec->revision_id);
	snd_component_add(codec->bus->card, component);

	if (codecp)
		*codecp = codec;
	return 0;
}

int snd_hda_codec_configure(struct hda_codec *codec)
{
	int err;


	codec->preset = find_codec_preset(codec);
	codec->preset = find_codec_preset(codec);
	if (!codec->name) {
		err = get_codec_name(codec);
		if (err < 0)
			return err;
	}
	/* audio codec should override the mixer name */
	/* audio codec should override the mixer name */
	if (codec->afg || !*bus->card->mixername)
	if (codec->afg || !*codec->bus->card->mixername)
		snd_hda_get_codec_name(codec, bus->card->mixername,
		strlcpy(codec->bus->card->mixername, codec->name,
				       sizeof(bus->card->mixername));
			sizeof(codec->bus->card->mixername));


	if (is_generic_config(codec)) {
	if (is_generic_config(codec)) {
		err = snd_hda_parse_generic_codec(codec);
		err = snd_hda_parse_generic_codec(codec);
@@ -683,27 +788,11 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
		printk(KERN_ERR "hda-codec: No codec parser is available\n");
		printk(KERN_ERR "hda-codec: No codec parser is available\n");


 patched:
 patched:
	if (err < 0) {
	if (!err && codec->patch_ops.unsol_event)
		snd_hda_codec_free(codec);
		err = init_unsol_queue(codec->bus);
	return err;
	return err;
}
}


	if (codec->patch_ops.unsol_event)
		init_unsol_queue(bus);

	snd_hda_codec_proc_new(codec);
#ifdef CONFIG_SND_HDA_HWDEP
	snd_hda_create_hwdep(codec);
#endif

	sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
	snd_component_add(codec->bus->card, component);

	if (codecp)
		*codecp = codec;
	return 0;
}

/**
/**
 * snd_hda_codec_setup_stream - set up the codec for streaming
 * snd_hda_codec_setup_stream - set up the codec for streaming
 * @codec: the CODEC to set up
 * @codec: the CODEC to set up
@@ -756,12 +845,12 @@ static void __devinit init_hda_cache(struct hda_cache_rec *cache,
{
{
	memset(cache, 0, sizeof(*cache));
	memset(cache, 0, sizeof(*cache));
	memset(cache->hash, 0xff, sizeof(cache->hash));
	memset(cache->hash, 0xff, sizeof(cache->hash));
	cache->record_size = record_size;
	snd_array_init(&cache->buf, record_size, 64);
}
}


static void free_hda_cache(struct hda_cache_rec *cache)
static void free_hda_cache(struct hda_cache_rec *cache)
{
{
	kfree(cache->buffer);
	snd_array_free(&cache->buf);
}
}


/* query the hash.  allocate an entry if not found. */
/* query the hash.  allocate an entry if not found. */
@@ -770,38 +859,18 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
{
{
	u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
	u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
	u16 cur = cache->hash[idx];
	u16 cur = cache->hash[idx];
	struct hda_cache_head *info_head = cache->buf.list;
	struct hda_cache_head *info;
	struct hda_cache_head *info;


	while (cur != 0xffff) {
	while (cur != 0xffff) {
		info = (struct hda_cache_head *)(cache->buffer +
		info = &info_head[cur];
						 cur * cache->record_size);
		if (info->key == key)
		if (info->key == key)
			return info;
			return info;
		cur = info->next;
		cur = info->next;
	}
	}


	/* add a new hash entry */
	/* add a new hash entry */
	if (cache->num_entries >= cache->size) {
	info = snd_array_new(&cache->buf);
		/* reallocate the array */
		unsigned int new_size = cache->size + 64;
		void *new_buffer;
		new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL);
		if (!new_buffer) {
			snd_printk(KERN_ERR "hda_codec: "
				   "can't malloc amp_info\n");
			return NULL;
		}
		if (cache->buffer) {
			memcpy(new_buffer, cache->buffer,
			       cache->size * cache->record_size);
			kfree(cache->buffer);
		}
		cache->size = new_size;
		cache->buffer = new_buffer;
	}
	cur = cache->num_entries++;
	info = (struct hda_cache_head *)(cache->buffer +
					 cur * cache->record_size);
	info->key = key;
	info->key = key;
	info->val = 0;
	info->val = 0;
	info->next = cache->hash[idx];
	info->next = cache->hash[idx];
@@ -942,10 +1011,10 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
/* resume the all amp commands from the cache */
/* resume the all amp commands from the cache */
void snd_hda_codec_resume_amp(struct hda_codec *codec)
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
{
	struct hda_amp_info *buffer = codec->amp_cache.buffer;
	struct hda_amp_info *buffer = codec->amp_cache.buf.list;
	int i;
	int i;


	for (i = 0; i < codec->amp_cache.size; i++, buffer++) {
	for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
		u32 key = buffer->head.key;
		u32 key = buffer->head.key;
		hda_nid_t nid;
		hda_nid_t nid;
		unsigned int idx, dir, ch;
		unsigned int idx, dir, ch;
@@ -1097,6 +1166,57 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
	return _snd_hda_find_mixer_ctl(codec, name, 0);
	return _snd_hda_find_mixer_ctl(codec, name, 0);
}
}


/* Add a control element and assign to the codec */
int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
{
	int err;
	struct snd_kcontrol **knewp;

	err = snd_ctl_add(codec->bus->card, kctl);
	if (err < 0)
		return err;
	knewp = snd_array_new(&codec->mixers);
	if (!knewp)
		return -ENOMEM;
	*knewp = kctl;
	return 0;
}

/* Clear all controls assigned to the given codec */
void snd_hda_ctls_clear(struct hda_codec *codec)
{
	int i;
	struct snd_kcontrol **kctls = codec->mixers.list;
	for (i = 0; i < codec->mixers.used; i++)
		snd_ctl_remove(codec->bus->card, kctls[i]);
	snd_array_free(&codec->mixers);
}

void snd_hda_codec_reset(struct hda_codec *codec)
{
	int i;

#ifdef CONFIG_SND_HDA_POWER_SAVE
	cancel_delayed_work(&codec->power_work);
	flush_scheduled_work();
#endif
	snd_hda_ctls_clear(codec);
	/* relase PCMs */
	for (i = 0; i < codec->num_pcms; i++) {
		if (codec->pcm_info[i].pcm)
			snd_device_free(codec->bus->card,
					codec->pcm_info[i].pcm);
	}
	if (codec->patch_ops.free)
		codec->patch_ops.free(codec);
	codec->spec = NULL;
	free_hda_cache(&codec->amp_cache);
	free_hda_cache(&codec->cmd_cache);
	codec->num_pcms = 0;
	codec->pcm_info = NULL;
	codec->preset = NULL;
}

/* create a virtual master control and add slaves */
/* create a virtual master control and add slaves */
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
			unsigned int *tlv, const char **slaves)
			unsigned int *tlv, const char **slaves)
@@ -1114,7 +1234,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
	kctl = snd_ctl_make_virtual_master(name, tlv);
	kctl = snd_ctl_make_virtual_master(name, tlv);
	if (!kctl)
	if (!kctl)
		return -ENOMEM;
		return -ENOMEM;
	err = snd_ctl_add(codec->bus->card, kctl);
	err = snd_hda_ctl_add(codec, kctl);
	if (err < 0)
	if (err < 0)
		return err;
		return err;
	
	
@@ -1578,7 +1698,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
		kctl = snd_ctl_new1(dig_mix, codec);
		kctl = snd_ctl_new1(dig_mix, codec);
		kctl->id.index = idx;
		kctl->id.index = idx;
		kctl->private_value = nid;
		kctl->private_value = nid;
		err = snd_ctl_add(codec->bus->card, kctl);
		err = snd_hda_ctl_add(codec, kctl);
		if (err < 0)
		if (err < 0)
			return err;
			return err;
	}
	}
@@ -1622,7 +1742,7 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
	if (!mout->dig_out_nid)
	if (!mout->dig_out_nid)
		return 0;
		return 0;
	/* ATTENTION: here mout is passed as private_data, instead of codec */
	/* ATTENTION: here mout is passed as private_data, instead of codec */
	return snd_ctl_add(codec->bus->card,
	return snd_hda_ctl_add(codec,
			   snd_ctl_new1(&spdif_share_sw, mout));
			   snd_ctl_new1(&spdif_share_sw, mout));
}
}


@@ -1724,7 +1844,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
	for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
	for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
		kctl = snd_ctl_new1(dig_mix, codec);
		kctl = snd_ctl_new1(dig_mix, codec);
		kctl->private_value = nid;
		kctl->private_value = nid;
		err = snd_ctl_add(codec->bus->card, kctl);
		err = snd_hda_ctl_add(codec, kctl);
		if (err < 0)
		if (err < 0)
			return err;
			return err;
	}
	}
@@ -1779,10 +1899,10 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
/* resume the all commands from the cache */
/* resume the all commands from the cache */
void snd_hda_codec_resume_cache(struct hda_codec *codec)
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
{
	struct hda_cache_head *buffer = codec->cmd_cache.buffer;
	struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
	int i;
	int i;


	for (i = 0; i < codec->cmd_cache.size; i++, buffer++) {
	for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
		u32 key = buffer->key;
		u32 key = buffer->key;
		if (!key)
		if (!key)
			continue;
			continue;
@@ -1867,6 +1987,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
	}
	}
}
}


#ifdef CONFIG_SND_HDA_HWDEP
/* execute additional init verbs */
static void hda_exec_init_verbs(struct hda_codec *codec)
{
	if (codec->init_verbs.list)
		snd_hda_sequence_write(codec, codec->init_verbs.list);
}
#else
static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
#endif

#ifdef SND_HDA_NEEDS_RESUME
#ifdef SND_HDA_NEEDS_RESUME
/*
/*
 * call suspend and power-down; used both from PM and power-save
 * call suspend and power-down; used both from PM and power-save
@@ -1893,6 +2024,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
	hda_set_power_state(codec,
	hda_set_power_state(codec,
			    codec->afg ? codec->afg : codec->mfg,
			    codec->afg ? codec->afg : codec->mfg,
			    AC_PWRST_D0);
			    AC_PWRST_D0);
	hda_exec_init_verbs(codec);
	if (codec->patch_ops.resume)
	if (codec->patch_ops.resume)
		codec->patch_ops.resume(codec);
		codec->patch_ops.resume(codec);
	else {
	else {
@@ -1918,6 +2050,15 @@ int __devinit snd_hda_build_controls(struct hda_bus *bus)
	struct hda_codec *codec;
	struct hda_codec *codec;


	list_for_each_entry(codec, &bus->codec_list, list) {
	list_for_each_entry(codec, &bus->codec_list, list) {
		int err = snd_hda_codec_build_controls(codec);
		if (err < 0)
			return err;
	}
	return 0;
}

int snd_hda_codec_build_controls(struct hda_codec *codec)
{
	int err = 0;
	int err = 0;
	/* fake as if already powered-on */
	/* fake as if already powered-on */
	hda_keep_power_on(codec);
	hda_keep_power_on(codec);
@@ -1925,6 +2066,7 @@ int __devinit snd_hda_build_controls(struct hda_bus *bus)
	hda_set_power_state(codec,
	hda_set_power_state(codec,
			    codec->afg ? codec->afg : codec->mfg,
			    codec->afg ? codec->afg : codec->mfg,
			    AC_PWRST_D0);
			    AC_PWRST_D0);
	hda_exec_init_verbs(codec);
	/* continue to initialize... */
	/* continue to initialize... */
	if (codec->patch_ops.init)
	if (codec->patch_ops.init)
		err = codec->patch_ops.init(codec);
		err = codec->patch_ops.init(codec);
@@ -1933,8 +2075,6 @@ int __devinit snd_hda_build_controls(struct hda_bus *bus)
	snd_hda_power_down(codec);
	snd_hda_power_down(codec);
	if (err < 0)
	if (err < 0)
		return err;
		return err;
	}

	return 0;
	return 0;
}
}


@@ -2235,7 +2375,7 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
	return 0;
	return 0;
}
}


static int __devinit set_pcm_default_values(struct hda_codec *codec,
static int set_pcm_default_values(struct hda_codec *codec,
				  struct hda_pcm_stream *info)
				  struct hda_pcm_stream *info)
{
{
	/* query support PCM information from the given NID */
	/* query support PCM information from the given NID */
@@ -2262,6 +2402,28 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
	return 0;
	return 0;
}
}


/*
 * attach a new PCM stream
 */
static int __devinit
snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
{
	struct hda_pcm_stream *info;
	int stream, err;

	if (!pcm->name)
		return -EINVAL;
	for (stream = 0; stream < 2; stream++) {
		info = &pcm->stream[stream];
		if (info->substreams) {
			err = set_pcm_default_values(codec, info);
			if (err < 0)
				return err;
		}
	}
	return codec->bus->ops.attach_pcm(codec, pcm);
}

/**
/**
 * snd_hda_build_pcms - build PCM information
 * snd_hda_build_pcms - build PCM information
 * @bus: the BUS
 * @bus: the BUS
@@ -2288,25 +2450,67 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
 *
 *
 * This function returns 0 if successfull, or a negative error code.
 * This function returns 0 if successfull, or a negative error code.
 */
 */
int __devinit snd_hda_build_pcms(struct hda_bus *bus)
int snd_hda_build_pcms(struct hda_bus *bus)
{
{
	static const char *dev_name[HDA_PCM_NTYPES] = {
		"Audio", "SPDIF", "HDMI", "Modem"
	};
	/* starting device index for each PCM type */
	static int dev_idx[HDA_PCM_NTYPES] = {
		[HDA_PCM_TYPE_AUDIO] = 0,
		[HDA_PCM_TYPE_SPDIF] = 1,
		[HDA_PCM_TYPE_HDMI] = 3,
		[HDA_PCM_TYPE_MODEM] = 6
	};
	/* normal audio device indices; not linear to keep compatibility */
	static int audio_idx[4] = { 0, 2, 4, 5 };
	struct hda_codec *codec;
	struct hda_codec *codec;
	int num_devs[HDA_PCM_NTYPES];


	memset(num_devs, 0, sizeof(num_devs));
	list_for_each_entry(codec, &bus->codec_list, list) {
	list_for_each_entry(codec, &bus->codec_list, list) {
		unsigned int pcm, s;
		unsigned int pcm;
		int err;
		int err;
		if (!codec->num_pcms) {
			if (!codec->patch_ops.build_pcms)
			if (!codec->patch_ops.build_pcms)
				continue;
				continue;
			err = codec->patch_ops.build_pcms(codec);
			err = codec->patch_ops.build_pcms(codec);
			if (err < 0)
			if (err < 0)
				return err;
				return err;
		}
		for (pcm = 0; pcm < codec->num_pcms; pcm++) {
		for (pcm = 0; pcm < codec->num_pcms; pcm++) {
			for (s = 0; s < 2; s++) {
			struct hda_pcm *cpcm = &codec->pcm_info[pcm];
				struct hda_pcm_stream *info;
			int type = cpcm->pcm_type;
				info = &codec->pcm_info[pcm].stream[s];
			int dev;
				if (!info->substreams)
			switch (type) {
			case HDA_PCM_TYPE_AUDIO:
				if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
					snd_printk(KERN_WARNING
						   "Too many audio devices\n");
					continue;
					continue;
				err = set_pcm_default_values(codec, info);
				}
				dev = audio_idx[num_devs[type]];
				break;
			case HDA_PCM_TYPE_SPDIF:
			case HDA_PCM_TYPE_HDMI:
			case HDA_PCM_TYPE_MODEM:
				if (num_devs[type]) {
					snd_printk(KERN_WARNING
						   "%s already defined\n",
						   dev_name[type]);
					continue;
				}
				dev = dev_idx[type];
				break;
			default:
				snd_printk(KERN_WARNING
					   "Invalid PCM type %d\n", type);
				continue;
			}
			num_devs[type]++;
			if (!cpcm->pcm) {
				cpcm->device = dev;
				err = snd_hda_attach_pcm(codec, cpcm);
				if (err < 0)
				if (err < 0)
					return err;
					return err;
			}
			}
@@ -2332,11 +2536,11 @@ int snd_hda_check_board_config(struct hda_codec *codec,
			       int num_configs, const char **models,
			       int num_configs, const char **models,
			       const struct snd_pci_quirk *tbl)
			       const struct snd_pci_quirk *tbl)
{
{
	if (codec->bus->modelname && models) {
	if (codec->modelname && models) {
		int i;
		int i;
		for (i = 0; i < num_configs; i++) {
		for (i = 0; i < num_configs; i++) {
			if (models[i] &&
			if (models[i] &&
			    !strcmp(codec->bus->modelname, models[i])) {
			    !strcmp(codec->modelname, models[i])) {
				snd_printd(KERN_INFO "hda_codec: model '%s' is "
				snd_printd(KERN_INFO "hda_codec: model '%s' is "
					   "selected\n", models[i]);
					   "selected\n", models[i]);
				return i;
				return i;
@@ -2389,7 +2593,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
		kctl = snd_ctl_new1(knew, codec);
		kctl = snd_ctl_new1(knew, codec);
		if (!kctl)
		if (!kctl)
			return -ENOMEM;
			return -ENOMEM;
		err = snd_ctl_add(codec->bus->card, kctl);
		err = snd_hda_ctl_add(codec, kctl);
		if (err < 0) {
		if (err < 0) {
			if (!codec->addr)
			if (!codec->addr)
				return err;
				return err;
@@ -2397,7 +2601,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
			if (!kctl)
			if (!kctl)
				return -ENOMEM;
				return -ENOMEM;
			kctl->id.device = codec->addr;
			kctl->id.device = codec->addr;
			err = snd_ctl_add(codec->bus->card, kctl);
			err = snd_hda_ctl_add(codec, kctl);
			if (err < 0)
			if (err < 0)
				return err;
				return err;
		}
		}
@@ -3138,3 +3342,37 @@ int snd_hda_codecs_inuse(struct hda_bus *bus)
}
}
#endif
#endif
#endif
#endif

/*
 * generic arrays
 */

/* get a new element from the given array
 * if it exceeds the pre-allocated array size, re-allocate the array
 */
void *snd_array_new(struct snd_array *array)
{
	if (array->used >= array->alloced) {
		int num = array->alloced + array->alloc_align;
		void *nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
		if (!nlist)
			return NULL;
		if (array->list) {
			memcpy(nlist, array->list,
			       array->elem_size * array->alloced);
			kfree(array->list);
		}
		array->list = nlist;
		array->alloced = num;
	}
	return array->list + (array->used++ * array->elem_size);
}

/* free the given array elements */
void snd_array_free(struct snd_array *array)
{
	kfree(array->list);
	array->used = 0;
	array->alloced = 0;
	array->list = NULL;
}
Loading