diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-09 07:07:14 +0900 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-09 07:07:14 +0900 | 
| commit | f5a246eab9a268f51ba8189ea5b098a1bfff200e (patch) | |
| tree | a6ff7169e0bcaca498d9aec8b0624de1b74eaecb /sound/core/pcm_lib.c | |
| parent | d5bbd43d5f450c3fca058f5b85f3dfb4e8cc88c9 (diff) | |
| parent | 7ff34ad80b7080fafaac8efa9ef0061708eddd51 (diff) | |
| download | olio-linux-3.10-f5a246eab9a268f51ba8189ea5b098a1bfff200e.tar.xz olio-linux-3.10-f5a246eab9a268f51ba8189ea5b098a1bfff200e.zip  | |
Merge tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
 "This contains pretty many small commits covering fairly large range of
  files in sound/ directory.  Partly because of additional API support
  and partly because of constantly developed ASoC and ARM stuff.
  Some highlights:
   - Introduced the helper function and documentation for exposing the
     channel map via control API, as discussed in Plumbers; most of PCI
     drivers are covered, will follow more drivers later
   - Most of drivers have been replaced with the new PM callbacks (if
     the bus is supported)
   - HD-audio controller got the support of runtime PM and the support
     of D3 clock-stop.  Also changing the power_save option in sysfs
     kicks off immediately to enable / disable the power-save mode.
   - Another significant code change in HD-audio is the rewrite of
     firmware loading code.  Other than that, most of changes in
     HD-audio are continued cleanups and standardization for the generic
     auto parser and bug fixes (HBR, device-specific fixups), in
     addition to the support of channel-map API.
   - Addition of ASoC bindings for the compressed API, used by the
     mid-x86 drivers.
   - Lots of cleanups and API refreshes for ASoC codec drivers and
     DaVinci.
   - Conversion of OMAP to dmaengine.
   - New machine driver for Wolfson Microelectronics Bells.
   - New CODEC driver for Wolfson Microelectronics WM0010.
   - Enhancements to the ux500 and wm2000 drivers
   - A new driver for DA9055 and the support for regulator bypass mode."
Fix up various arm soc header file reorg conflicts.
* tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (339 commits)
  ALSA: hda - Add new codec ALC283 ALC290 support
  ALSA: hda - avoid unneccesary indices on "Headphone Jack" controls
  ALSA: hda - fix indices on boost volume on Conexant
  ALSA: aloop - add locking to timer access
  ALSA: hda - Fix hang caused by race during suspend.
  sound: Remove unnecessary semicolon
  ALSA: hda/realtek - Fix detection of ALC271X codec
  ALSA: hda - Add inverted internal mic quirk for Lenovo IdeaPad U310
  ALSA: hda - make Realtek/Sigmatel/Conexant use the generic unsol event
  ALSA: hda - make a generic unsol event handler
  ASoC: codecs: Add DA9055 codec driver
  ASoC: eukrea-tlv320: Convert it to platform driver
  ALSA: ASoC: add DT bindings for CS4271
  ASoC: wm_hubs: Ensure volume updates are handled during class W startup
  ASoC: wm5110: Adding missing volume update bits
  ASoC: wm5110: Add OUT3R support
  ASoC: wm5110: Add AEC loopback support
  ASoC: wm5110: Rename EPOUT to HPOUT3
  ASoC: arizona: Add more clock rates
  ASoC: arizona: Add more DSP options for mixer input muxes
  ...
Diffstat (limited to 'sound/core/pcm_lib.c')
| -rw-r--r-- | sound/core/pcm_lib.c | 214 | 
1 files changed, 214 insertions, 0 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 7ae67192339..f42c10a4331 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -26,6 +26,7 @@  #include <linux/export.h>  #include <sound/core.h>  #include <sound/control.h> +#include <sound/tlv.h>  #include <sound/info.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h> @@ -2302,3 +2303,216 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,  }  EXPORT_SYMBOL(snd_pcm_lib_readv); + +/* + * standard channel mapping helpers + */ + +/* default channel maps for multi-channel playbacks, up to 8 channels */ +const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = { +	{ .channels = 1, +	  .map = { SNDRV_CHMAP_MONO } }, +	{ .channels = 2, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, +	{ .channels = 4, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, +		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, +	{ .channels = 6, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, +		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, +	{ .channels = 8, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, +		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, +	{ } +}; +EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps); + +/* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */ +const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = { +	{ .channels = 1, +	  .map = { SNDRV_CHMAP_MONO } }, +	{ .channels = 2, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, +	{ .channels = 4, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, +		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, +	{ .channels = 6, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, +	{ .channels = 8, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, +	{ } +}; +EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps); + +static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch) +{ +	if (ch > info->max_channels) +		return false; +	return !info->channel_mask || (info->channel_mask & (1U << ch)); +} + +static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol, +			      struct snd_ctl_elem_info *uinfo) +{ +	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + +	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +	uinfo->count = 0; +	uinfo->count = info->max_channels; +	uinfo->value.integer.min = 0; +	uinfo->value.integer.max = SNDRV_CHMAP_LAST; +	return 0; +} + +/* get callback for channel map ctl element + * stores the channel position firstly matching with the current channels + */ +static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, +			     struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); +	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); +	struct snd_pcm_substream *substream; +	const struct snd_pcm_chmap_elem *map; + +	if (snd_BUG_ON(!info->chmap)) +		return -EINVAL; +	substream = snd_pcm_chmap_substream(info, idx); +	if (!substream) +		return -ENODEV; +	memset(ucontrol->value.integer.value, 0, +	       sizeof(ucontrol->value.integer.value)); +	if (!substream->runtime) +		return 0; /* no channels set */ +	for (map = info->chmap; map->channels; map++) { +		int i; +		if (map->channels == substream->runtime->channels && +		    valid_chmap_channels(info, map->channels)) { +			for (i = 0; i < map->channels; i++) +				ucontrol->value.integer.value[i] = map->map[i]; +			return 0; +		} +	} +	return -EINVAL; +} + +/* tlv callback for channel map ctl element + * expands the pre-defined channel maps in a form of TLV + */ +static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, +			     unsigned int size, unsigned int __user *tlv) +{ +	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); +	const struct snd_pcm_chmap_elem *map; +	unsigned int __user *dst; +	int c, count = 0; + +	if (snd_BUG_ON(!info->chmap)) +		return -EINVAL; +	if (size < 8) +		return -ENOMEM; +	if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) +		return -EFAULT; +	size -= 8; +	dst = tlv + 2; +	for (map = info->chmap; map->channels; map++) { +		int chs_bytes = map->channels * 4; +		if (!valid_chmap_channels(info, map->channels)) +			continue; +		if (size < 8) +			return -ENOMEM; +		if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || +		    put_user(chs_bytes, dst + 1)) +			return -EFAULT; +		dst += 2; +		size -= 8; +		count += 8; +		if (size < chs_bytes) +			return -ENOMEM; +		size -= chs_bytes; +		count += chs_bytes; +		for (c = 0; c < map->channels; c++) { +			if (put_user(map->map[c], dst)) +				return -EFAULT; +			dst++; +		} +	} +	if (put_user(count, tlv + 1)) +		return -EFAULT; +	return 0; +} + +static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) +{ +	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); +	info->pcm->streams[info->stream].chmap_kctl = NULL; +	kfree(info); +} + +/** + * snd_pcm_add_chmap_ctls - create channel-mapping control elements + * @pcm: the assigned PCM instance + * @stream: stream direction + * @chmap: channel map elements (for query) + * @max_channels: the max number of channels for the stream + * @private_value: the value passed to each kcontrol's private_value field + * @info_ret: store struct snd_pcm_chmap instance if non-NULL + * + * Create channel-mapping control elements assigned to the given PCM stream(s). + * Returns zero if succeed, or a negative error value. + */ +int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, +			   const struct snd_pcm_chmap_elem *chmap, +			   int max_channels, +			   unsigned long private_value, +			   struct snd_pcm_chmap **info_ret) +{ +	struct snd_pcm_chmap *info; +	struct snd_kcontrol_new knew = { +		.iface = SNDRV_CTL_ELEM_IFACE_PCM, +		.access = SNDRV_CTL_ELEM_ACCESS_READ | +			SNDRV_CTL_ELEM_ACCESS_TLV_READ | +			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, +		.info = pcm_chmap_ctl_info, +		.get = pcm_chmap_ctl_get, +		.tlv.c = pcm_chmap_ctl_tlv, +	}; +	int err; + +	info = kzalloc(sizeof(*info), GFP_KERNEL); +	if (!info) +		return -ENOMEM; +	info->pcm = pcm; +	info->stream = stream; +	info->chmap = chmap; +	info->max_channels = max_channels; +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) +		knew.name = "Playback Channel Map"; +	else +		knew.name = "Capture Channel Map"; +	knew.device = pcm->device; +	knew.count = pcm->streams[stream].substream_count; +	knew.private_value = private_value; +	info->kctl = snd_ctl_new1(&knew, info); +	if (!info->kctl) { +		kfree(info); +		return -ENOMEM; +	} +	info->kctl->private_free = pcm_chmap_ctl_private_free; +	err = snd_ctl_add(pcm->card, info->kctl); +	if (err < 0) +		return err; +	pcm->streams[stream].chmap_kctl = info->kctl; +	if (info_ret) +		*info_ret = info; +	return 0; +} +EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);  |