From 2d3391ec0ecca37efb6bc995906292f47522b471 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Jul 2012 18:27:00 +0200 Subject: [PATCH 01/21] ALSA: PCM: channel mapping API implementation This patch implements the basic data types for the standard channel mapping API handling. - The definitions of the channel positions and the new TLV types are added in sound/asound.h and sound/tlv.h, so that they can be referred from user-space. - Introduced a new helper function snd_pcm_add_chmap_ctls() to create control elements representing the channel maps for each PCM (sub)stream. - Some standard pre-defined channel maps are provided for convenience. Signed-off-by: Takashi Iwai --- include/sound/asound.h | 30 ++++++ include/sound/pcm.h | 48 +++++++++ include/sound/tlv.h | 8 ++ sound/core/pcm.c | 4 + sound/core/pcm_lib.c | 215 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 305 insertions(+) diff --git a/include/sound/asound.h b/include/sound/asound.h index 0876a1e76aef..376e75632e07 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -472,6 +472,36 @@ enum { SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, }; +/* channel positions */ +enum { + SNDRV_CHMAP_UNKNOWN = 0, + SNDRV_CHMAP_FL, /* front left */ + SNDRV_CHMAP_FC, /* front center */ + SNDRV_CHMAP_FR, /* front right */ + SNDRV_CHMAP_FLC, /* front left center */ + SNDRV_CHMAP_FRC, /* front right center */ + SNDRV_CHMAP_RL, /* rear left */ + SNDRV_CHMAP_RC, /* rear center */ + SNDRV_CHMAP_RR, /* rear right */ + SNDRV_CHMAP_RLC, /* rear left center */ + SNDRV_CHMAP_RRC, /* rear right center */ + SNDRV_CHMAP_SL, /* side left */ + SNDRV_CHMAP_SR, /* side right */ + SNDRV_CHMAP_LFE, /* LFE */ + SNDRV_CHMAP_FLW, /* front left wide */ + SNDRV_CHMAP_FRW, /* front right wide */ + SNDRV_CHMAP_FLH, /* front left high */ + SNDRV_CHMAP_FCH, /* front center high */ + SNDRV_CHMAP_FRH, /* front right high */ + SNDRV_CHMAP_TC, /* top center */ + SNDRV_CHMAP_NA, /* N/A, silent */ + SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA, +}; + +#define SNDRV_CHMAP_POSITION_MASK 0xffff +#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16) +#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16) + #define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int) #define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info) #define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index cdca2ab1e711..669c85a7fb03 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -437,6 +437,7 @@ struct snd_pcm_str { struct snd_info_entry *proc_xrun_debug_entry; #endif #endif + struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */ }; struct snd_pcm { @@ -1086,4 +1087,51 @@ static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream return "Capture"; } +/* + * PCM channel-mapping control API + */ +/* array element of channel maps */ +struct snd_pcm_chmap_elem { + unsigned char channels; + unsigned char map[15]; +}; + +/* channel map information; retrieved via snd_kcontrol_chip() */ +struct snd_pcm_chmap { + struct snd_pcm *pcm; /* assigned PCM instance */ + int stream; /* PLAYBACK or CAPTURE */ + struct snd_kcontrol *kctl; + const struct snd_pcm_chmap_elem *chmap; + unsigned int max_channels; + unsigned int channel_mask; /* optional: active channels bitmask */ + void *private_data; /* optional: private data pointer */ +}; + +/* get the PCM substream assigned to the given chmap info */ +static inline struct snd_pcm_substream * +snd_pcm_chmap_substream(struct snd_pcm_chmap *info, unsigned int idx) +{ + struct snd_pcm_substream *s; + for (s = info->pcm->streams[info->stream].substream; s; s = s->next) + if (s->number == idx) + return s; + return NULL; +} + +/* ALSA-standard channel maps (RL/RR prior to C/LFE) */ +extern const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[]; +/* Other world's standard channel maps (C/LFE prior to RL/RR) */ +extern const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[]; + +/* bit masks to be passed to snd_pcm_chmap.channel_mask field */ +#define SND_PCM_CHMAP_MASK_24 ((1U << 2) | (1U << 4)) +#define SND_PCM_CHMAP_MASK_246 (SND_PCM_CHMAP_MASK_24 | (1U << 6)) +#define SND_PCM_CHMAP_MASK_2468 (SND_PCM_CHMAP_MASK_246 | (1U << 8)) + +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); + #endif /* __SOUND_PCM_H */ diff --git a/include/sound/tlv.h b/include/sound/tlv.h index a64d8fe3f855..28c65e1ada21 100644 --- a/include/sound/tlv.h +++ b/include/sound/tlv.h @@ -86,4 +86,12 @@ #define TLV_DB_GAIN_MUTE -9999999 +/* + * channel-mapping TLV items + * TLV length must match with num_channels + */ +#define SNDRV_CTL_TLVT_CHMAP_FIXED 0x101 /* fixed channel position */ +#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */ +#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */ + #endif /* __SOUND_TLV_H */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 1a3070b4e5b5..f2991940b271 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1105,6 +1105,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) break; } snd_unregister_device(devtype, pcm->card, pcm->device); + if (pcm->streams[cidx].chmap_kctl) { + snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); + pcm->streams[cidx].chmap_kctl = NULL; + } } unlock: mutex_unlock(®ister_mutex); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 7ae671923393..565102705eda 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2302,3 +2303,217 @@ 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_FC } }, + { .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_FC } }, + { .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_VOLATILE | /* no notification */ + 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); From be84bbcccc757b86449daaf924e72f95c95dc00e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 15 Aug 2012 16:06:43 +0200 Subject: [PATCH 02/21] ALSA: Add a documentation for channel mapping API Signed-off-by: Takashi Iwai --- .../sound/alsa/Channel-Mapping-API.txt | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 Documentation/sound/alsa/Channel-Mapping-API.txt diff --git a/Documentation/sound/alsa/Channel-Mapping-API.txt b/Documentation/sound/alsa/Channel-Mapping-API.txt new file mode 100644 index 000000000000..df930aa4f4d0 --- /dev/null +++ b/Documentation/sound/alsa/Channel-Mapping-API.txt @@ -0,0 +1,144 @@ +ALSA PCM channel-mapping API +============================ + Takashi Iwai + +GENERAL +------- + +The channel mapping API allows user to query the possible channel maps +and the current channel map, also optionally to modify the channel map +of the current stream. + +A channel map is an array of position for each PCM channel. +Typically, a stereo PCM stream has a channel map of + { front_left, front_right } +while a 4.0 surround PCM stream has a channel map of + { front left, front right, rear left, rear right }. + +The problem, so far, was that we had no standard channel map +explicitly, and applications had no way to know which channel +corresponds to which (speaker) position. Thus, applications applied +wrong channels for 5.1 outputs, and you hear suddenly strange sound +from rear. Or, some devices secretly assume that center/LFE is the +third/fourth channels while others that C/LFE as 5th/6th channels. + +Also, some devices such as HDMI are configurable for different speaker +positions even with the same number of total channels. However, there +was no way to specify this because of lack of channel map +specification. These are the main motivations for the new channel +mapping API. + + +DESIGN +------ + +Actually, "the channel mapping API" doesn't introduce anything new in +the kernel/user-space ABI perspective. It uses only the existing +control element features. + +As a ground design, each PCM substream may contain a control element +providing the channel mapping information and configuration. This +element is specified by: + iface = SNDRV_CTL_ELEM_IFACE_PCM + name = "Playback Channel Map" or "Capture Channel Map" + device = the same device number for the assigned PCM substream + index = the same index number for the assigned PCM substream + +Note the name is different depending on the PCM substream direction. + +Each control element provides at least the TLV read operation and the +read operation. Optionally, the write operation can be provided to +allow user to change the channel map dynamically. + +* TLV + +The TLV operation gives the list of available channel +maps. A list item of a channel map is usually a TLV of + type data-bytes ch0 ch1 ch2... +where type is the TLV type value, the second argument is the total +bytes (not the numbers) of channel values, and the rest are the +position value for each channel. + +As a TLV type, either SNDRV_CTL_TLVT_CHMAP_FIXED, +SNDRV_CTL_TLV_CHMAP_VAR or SNDRV_CTL_TLVT_CHMAP_PAIRED can be used. +The _FIXED type is for a channel map with the fixed channel position +while the latter two are for flexible channel positions. _VAR type is +for a channel map where all channels are freely swappable and _PAIRED +type is where pair-wise channels are swappable. For example, when you +have {FL/FR/RL/RR} channel map, _PAIRED type would allow you to swap +only {RL/RR/FL/FR} while _VAR type would allow even swapping FL and +RR. + +These new TLV types are defined in sound/tlv.h. + +The available channel position values are defined in sound/asound.h, +here is a cut: + +/* channel positions */ +enum { + SNDRV_CHMAP_UNKNOWN = 0, + SNDRV_CHMAP_FL, /* front left */ + SNDRV_CHMAP_FC, /* front center */ + SNDRV_CHMAP_FR, /* front right */ + SNDRV_CHMAP_FLC, /* front left center */ + SNDRV_CHMAP_FRC, /* front right center */ + SNDRV_CHMAP_RL, /* rear left */ + SNDRV_CHMAP_RC, /* rear center */ + SNDRV_CHMAP_RR, /* rear right */ + SNDRV_CHMAP_RLC, /* rear left center */ + SNDRV_CHMAP_RRC, /* rear right center */ + SNDRV_CHMAP_SL, /* side left */ + SNDRV_CHMAP_SR, /* side right */ + SNDRV_CHMAP_LFE, /* LFE */ + SNDRV_CHMAP_FLW, /* front left wide */ + SNDRV_CHMAP_FRW, /* front right wide */ + SNDRV_CHMAP_FLH, /* front left high */ + SNDRV_CHMAP_FCH, /* front center high */ + SNDRV_CHMAP_FRH, /* front right high */ + SNDRV_CHMAP_TC, /* top center */ + SNDRV_CHMAP_NA, /* N/A, silent */ + SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA, +}; + +When a PCM stream can provide more than one channel map, you can +provide multiple channel maps in a TLV container type. The TLV data +to be returned will contain such as: + SNDRV_CTL_TLVT_CONTAINER 96 + SNDRV_CTL_TLVT_CHMAP_FIXED 4 SNDRV_CHMAP_FC + SNDRV_CTL_TLVT_CHMAP_FIXED 8 SNDRV_CHMAP_FL SNDRV_CHMAP_FR + SNDRV_CTL_TLVT_CHMAP_FIXED 16 NDRV_CHMAP_FL SNDRV_CHMAP_FR \ + SNDRV_CHMAP_RL SNDRV_CHMAP_RR + +The channel position is provided in LSB 16bits. The upper bits are +used for bit flags. + +#define SNDRV_CHMAP_POSITION_MASK 0xffff +#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16) +#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16) + +SNDRV_CHMAP_PHASE_INVERSE indicates the channel is phase inverted, +(thus summing left and right channels would result in almost silence). +Some digital mic devices have this. + +When SNDRV_CHMAP_DRIVER_SPEC is set, all the channel position values +don't follow the standard definition above but driver-specific. + +* READ OPERATION + +The control read operation is for providing the current channel map of +the given stream. The control element returns an integer array +containing the position of each channel. + +When this is performed before the number of the channel is specified +(i.e. hw_params is set), it should return all channels set to +UNKNOWN. + +* WRITE OPERATION + +The control write operation is optional, and only for devices that can +change the channel configuration on the fly, such as HDMI. User needs +to pass an integer value containing the valid channel positions for +all channels of the assigned PCM substream. + +This operation is allowed only at PCM PREPARED state. When called in +other states, it shall return an error. From 9c9a5175e65b2667001e6a3b6dedddebeee82aa2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 31 Jul 2012 11:35:35 +0200 Subject: [PATCH 03/21] ALSA: hda - Add standard channel maps Although HD-audio allows pair-wise channel configurations, only the fixed channel positions are used in this version. In future, this can be changed and allow user to modify the channel positions. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 36 +++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/patch_realtek.c | 2 ++ 3 files changed, 39 insertions(+) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ff97cf3e8bad..c4c62edca556 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3688,6 +3688,36 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) } EXPORT_SYMBOL_HDA(snd_hda_build_controls); +/* + * add standard channel maps if not specified + */ +static int add_std_chmaps(struct hda_codec *codec) +{ + int i, str, err; + + for (i = 0; i < codec->num_pcms; i++) { + for (str = 0; str < 2; str++) { + struct snd_pcm *pcm = codec->pcm_info[i].pcm; + struct hda_pcm_stream *hinfo = + &codec->pcm_info[i].stream[str]; + struct snd_pcm_chmap *chmap; + + if (codec->pcm_info[i].own_chmap) + continue; + if (!pcm || !hinfo->substreams) + continue; + err = snd_pcm_add_chmap_ctls(pcm, str, + snd_pcm_std_chmaps, + hinfo->channels_max, + 0, &chmap); + if (err < 0) + return err; + chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; + } + } + return 0; +} + int snd_hda_codec_build_controls(struct hda_codec *codec) { int err = 0; @@ -3699,6 +3729,12 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) err = codec->patch_ops.build_controls(codec); if (err < 0) return err; + + /* we create chmaps here instead of build_pcms */ + err = add_std_chmaps(codec); + if (err < 0) + return err; + snd_hda_jack_report_sync(codec); /* call at the last init point */ return 0; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 8fd9f636cfe1..ed3de8d151c8 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -776,6 +776,7 @@ struct hda_pcm { unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ int device; /* device number to assign */ struct snd_pcm *pcm; /* assigned PCM instance */ + bool own_chmap; /* codec driver provides own channel maps */ }; /* codec information */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 613499932b2b..1907ddad3840 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2287,6 +2287,8 @@ static int alc_build_pcms(struct hda_codec *codec) p = &alc_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; } if (spec->adc_nids) { p = spec->stream_analog_capture; From d45e6889ee69456a4d5b1bbb32252f460cd48fa9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 31 Jul 2012 11:36:00 +0200 Subject: [PATCH 04/21] ALSA: hda - Provide the proper channel mapping for generic HDMI driver ... instead of the standard fixed channel maps. The generic HDMI is based on the audio infoframe, and its configuration can be selected via CA bits. Thus we need a translation between the CA index and the verbose channel map list. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 308 ++++++++++++++++++++++++++++++++++++- 1 file changed, 303 insertions(+), 5 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 333d5338c15c..4a5d24fe8adf 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -73,6 +74,8 @@ struct hdmi_spec_per_pin { struct delayed_work work; int repoll_count; bool non_pcm; + bool chmap_set; /* channel-map override by ALSA API? */ + unsigned char chmap[8]; /* ALSA API channel-map */ }; struct hdmi_spec { @@ -82,6 +85,7 @@ struct hdmi_spec { int num_pins; struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; struct hda_pcm pcm_rec[MAX_HDMI_PINS]; + unsigned int channels_max; /* max over all cvts */ /* * Non-generic ATI/NVIDIA specific @@ -548,7 +552,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, bool non_pcm, int ca) @@ -588,6 +592,136 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hdmi_debug_channel_mapping(codec, pin_nid); } +struct channel_map_table { + unsigned char map; /* ALSA API channel map position */ + unsigned char cea_slot; /* CEA slot value */ + int spk_mask; /* speaker position bit mask */ +}; + +static struct channel_map_table map_tables[] = { + { SNDRV_CHMAP_FL, 0x00, FL }, + { SNDRV_CHMAP_FR, 0x01, FR }, + { SNDRV_CHMAP_RL, 0x04, RL }, + { SNDRV_CHMAP_RR, 0x05, RR }, + { SNDRV_CHMAP_LFE, 0x02, LFE }, + { SNDRV_CHMAP_FC, 0x03, FC }, + { SNDRV_CHMAP_RLC, 0x06, RLC }, + { SNDRV_CHMAP_RRC, 0x07, RRC }, + {} /* terminator */ +}; + +/* from ALSA API channel position to speaker bit mask */ +static int to_spk_mask(unsigned char c) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->map == c) + return t->spk_mask; + } + return 0; +} + +/* from ALSA API channel position to CEA slot */ +static int to_cea_slot(unsigned char c) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->map == c) + return t->cea_slot; + } + return 0x0f; +} + +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(unsigned char c) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->cea_slot == c) + return t->map; + } + return 0; +} + +/* from speaker bit mask to ALSA API channel position */ +static int spk_to_chmap(int spk) +{ + struct channel_map_table *t = map_tables; + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} + +/* get the CA index corresponding to the given ALSA API channel map */ +static int hdmi_manual_channel_allocation(int chs, unsigned char *map) +{ + int i, spks = 0, spk_mask = 0; + + for (i = 0; i < chs; i++) { + int mask = to_spk_mask(map[i]); + if (mask) { + spk_mask |= mask; + spks++; + } + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if ((chs == channel_allocations[i].channels || + spks == channel_allocations[i].channels) && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) + return channel_allocations[i].ca_index; + } + return -1; +} + +/* set up the channel slots for the given ALSA API channel map */ +static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid, + int chs, unsigned char *map) +{ + int i; + for (i = 0; i < 8; i++) { + int val, err; + if (i < chs) + val = to_cea_slot(map[i]); + else + val = 0xf; + val |= (i << 4); + err = snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, val); + if (err) + return -EINVAL; + } + return 0; +} + +/* store ALSA API channel map from the current default map */ +static void hdmi_setup_fake_chmap(unsigned char *map, int ca) +{ + int i; + for (i = 0; i < 8; i++) { + if (i < channel_allocations[ca].channels) + map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f); + else + map[i] = 0; + } +} + +static void hdmi_setup_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map) +{ + if (!non_pcm && map) { + hdmi_manual_setup_channel_mapping(codec, pin_nid, + channels, map); + } else { + hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); + hdmi_setup_fake_chmap(map, ca); + } +} /* * Audio InfoFrame routines @@ -726,7 +860,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, if (!eld->monitor_present) return; - ca = hdmi_channel_allocation(eld, channels); + if (!non_pcm && per_pin->chmap_set) + ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); + else + ca = hdmi_channel_allocation(eld, channels); + if (ca < 0) + ca = 0; memset(&ai, 0, sizeof(ai)); if (eld->conn_type == 0) { /* HDMI */ @@ -763,7 +902,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, "pin=%d channels=%d\n", pin_nid, channels); - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca); + hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, + channels, per_pin->chmap); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); @@ -772,7 +912,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, /* For non-pcm audio switch, setup new channel mapping * accordingly */ if (per_pin->non_pcm != non_pcm) - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca); + hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, + channels, per_pin->chmap); } per_pin->non_pcm = non_pcm; @@ -1098,8 +1239,11 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) per_cvt->cvt_nid = cvt_nid; per_cvt->channels_min = 2; - if (chans <= 16) + if (chans <= 16) { per_cvt->channels_max = chans; + if (chans > spec->channels_max) + spec->channels_max = chans; + } err = snd_hda_query_supported_pcm(codec, cvt_nid, &per_cvt->rates, @@ -1250,7 +1394,10 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl & ~PIN_OUT); snd_hda_spdif_ctls_unassign(codec, pin_idx); + per_pin->chmap_set = false; + memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); } + return 0; } @@ -1261,6 +1408,135 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, }; +/* + * ALSA API channel-map control callbacks + */ +static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = spec->channels_max; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +static int hdmi_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); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + const unsigned int valid_mask = + FL | FR | RL | RR | LFE | FC | RLC | RRC; + unsigned int __user *dst; + int chs, count = 0; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (chs = 2; chs <= spec->channels_max; chs += 2) { + int i, c; + struct cea_channel_speaker_allocation *cap; + cap = channel_allocations; + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { + int chs_bytes = chs * 4; + if (cap->channels != chs) + continue; + if (cap->spk_mask & ~valid_mask) + continue; + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, 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 = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + if (!spk) + continue; + if (put_user(spk_to_chmap(spk), dst)) + return -EFAULT; + dst++; + } + } + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + int pin_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + int i; + + for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) + ucontrol->value.integer.value[i] = per_pin->chmap[i]; + return 0; +} + +static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = info->private_data; + struct hdmi_spec *spec = codec->spec; + int pin_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + unsigned int ctl_idx; + struct snd_pcm_substream *substream; + unsigned char chmap[8]; + int i, ca, prepared = 0; + + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + substream = snd_pcm_chmap_substream(info, ctl_idx); + if (!substream || !substream->runtime) + return -EBADFD; + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PREPARED: + prepared = 1; + break; + default: + return -EBUSY; + } + memset(chmap, 0, sizeof(chmap)); + for (i = 0; i < ARRAY_SIZE(chmap); i++) + chmap[i] = ucontrol->value.integer.value[i]; + if (!memcmp(chmap, per_pin->chmap, sizeof(chmap))) + return 0; + ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); + if (ca < 0) + return -EINVAL; + per_pin->chmap_set = true; + memcpy(per_pin->chmap, chmap, sizeof(chmap)); + if (prepared) + hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm, + substream); + + return 0; +} + static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -1273,6 +1549,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) info = &spec->pcm_rec[pin_idx]; info->name = get_hdmi_pcm_name(pin_idx); info->pcm_type = HDA_PCM_TYPE_HDMI; + info->own_chmap = true; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; pstr->substreams = 1; @@ -1330,6 +1607,27 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) hdmi_present_sense(per_pin, 0); } + /* add channel maps */ + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct snd_pcm_chmap *chmap; + struct snd_kcontrol *kctl; + int i; + err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm, + SNDRV_PCM_STREAM_PLAYBACK, + NULL, 0, pin_idx, &chmap); + if (err < 0) + return err; + /* override handlers */ + chmap->private_data = codec; + kctl = chmap->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + kctl->info = hdmi_chmap_ctl_info; + kctl->get = hdmi_chmap_ctl_get; + kctl->put = hdmi_chmap_ctl_put; + kctl->tlv.c = hdmi_chmap_ctl_tlv; + } + return 0; } From 53775b0d0cb91ab217c9853efddc51597b58bbff Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 1 Aug 2012 12:17:41 +0200 Subject: [PATCH 05/21] ALSA: hda - Fix channel maps for Nvidia 7x 8ch HDMI codecs Some old Nvidia HDMI codecs with 8ch support only 2/8 or 2/6/8 channels and with the fixed CLFE-first map. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 4a5d24fe8adf..2c53ea889da9 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2155,6 +2155,43 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) return 0; } +static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int err = simple_playback_build_pcms(codec); + spec->pcm_rec[0].own_chmap = true; + return err; +} + +static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_pcm_chmap *chmap; + int err; + + err = simple_playback_build_controls(codec); + if (err < 0) + return err; + + /* add channel maps */ + err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm, + SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, 8, 0, &chmap); + if (err < 0) + return err; + switch (codec->preset->id) { + case 0x10de0002: + case 0x10de0003: + case 0x10de0005: + case 0x10de0006: + chmap->channel_mask = (1U << 2) | (1U << 8); + break; + case 0x10de0007: + chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8); + } + return 0; +} + static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -2165,6 +2202,8 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) spec->multiout.max_channels = 8; spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; codec->patch_ops.init = nvhdmi_7x_init_8ch; + codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms; + codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls; /* Initialize the audio infoframe channel mask and checksum to something * valid */ From 833a493b7ed2eb8f9059338a0ebf06bebbb6ae93 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 3 Aug 2012 17:59:36 +0200 Subject: [PATCH 06/21] ALSA: ac97: Implement channel map workaround for ALC650 ALC650 has a channel swap option between surround and CLFE channels, so we need to tweak the channel maps dynamically depending on the register bit. Now struct snd_ac97 can contain chmap pointers for playback and capture. The driver may store these and let ac97 driver changing the channel mapping dynamically. Signed-off-by: Takashi Iwai --- include/sound/ac97_codec.h | 3 +++ sound/pci/ac97/ac97_patch.c | 24 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 02cbb50225bb..4458b87649ff 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -422,6 +422,7 @@ */ struct snd_ac97; +struct snd_pcm_chmap; struct snd_ac97_build_ops { int (*build_3d) (struct snd_ac97 *ac97); @@ -528,6 +529,8 @@ struct snd_ac97 { struct delayed_work power_work; #endif struct device dev; + + struct snd_pcm_chmap *chmaps[2]; /* channel-maps (optional) */ }; #define to_ac97_t(d) container_of(d, struct snd_ac97, dev) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index a872d0a82976..66a3bc95fb84 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -2595,6 +2595,21 @@ static void alc650_update_jacks(struct snd_ac97 *ac97) shared ? 0 : 0x100); } +static int alc650_swap_surround_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + struct snd_pcm_chmap *map = ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK]; + + if (map) { + if (ucontrol->value.integer.value[0]) + map->chmap = snd_pcm_std_chmaps; + else + map->chmap = snd_pcm_alt_chmaps; + } + return snd_ac97_put_volsw(kcontrol, ucontrol); +} + static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = { AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0), AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0), @@ -2608,7 +2623,14 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = { /* 9: Line-In/Surround share */ /* 10: Mic/CLFE share */ /* 11-13: in IEC958 controls */ - AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Swap Surround Slot", + .info = snd_ac97_info_volsw, + .get = snd_ac97_get_volsw, + .put = alc650_swap_surround_put, + .private_value = AC97_SINGLE_VALUE(AC97_ALC650_MULTICH, 14, 1, 0), + }, #if 0 /* always set in patch_alc650 */ AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0), AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0), From e36e3b86c78cee9c7435eb33e0ef8a788193e812 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 3 Aug 2012 18:01:40 +0200 Subject: [PATCH 07/21] ALSA: Implement channel maps for standard onboard AC97 drivers Just set the channel maps depending on the hardware availability. Signed-off-by: Takashi Iwai --- sound/pci/atiixp.c | 9 +++++++++ sound/pci/fm801.c | 7 +++++++ sound/pci/intel8x0.c | 20 ++++++++++++++++++++ sound/pci/via82xx.c | 23 +++++++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index c744df5bb1c6..368df8b0853e 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1250,6 +1250,7 @@ static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = { static int __devinit snd_atiixp_pcm_new(struct atiixp *chip) { struct snd_pcm *pcm; + struct snd_pcm_chmap *chmap; struct snd_ac97_bus *pbus = chip->ac97_bus; int err, i, num_pcms; @@ -1293,6 +1294,14 @@ static int __devinit snd_atiixp_pcm_new(struct atiixp *chip) snd_dma_pci_data(chip->pci), 64*1024, 128*1024); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, chip->max_channels, 0, + &chmap); + if (err < 0) + return err; + chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; + chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap; + /* no SPDIF support on codec? */ if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates) return 0; diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index ce3e548de41d..cc2e91d15538 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -711,6 +711,13 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc snd_dma_pci_data(chip->pci), chip->multichannel ? 128*1024 : 64*1024, 128*1024); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, + chip->multichannel ? 6 : 2, 0, + NULL); + if (err < 0) + return err; + if (rpcm) *rpcm = pcm; return 0; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 5c4115289a9a..848102e5d864 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1541,6 +1541,26 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device, snd_dma_pci_data(chip->pci), rec->prealloc_size, rec->prealloc_max_size); + if (rec->ac97_idx == ICHD_PCMOUT && rec->playback_ops) { + struct snd_pcm_chmap *chmap; + int chs = 2; + if (rec->ac97_idx == ICHD_PCMOUT) { + if (chip->multi8) + chs = 8; + else if (chip->multi6) + chs = 6; + else if (chip->multi4) + chs = 4; + } + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, chs, 0, + &chmap); + if (err < 0) + return err; + chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; + chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap; + } + return 0; } diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index e3d32e2d574e..f0b4efdb483c 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1440,6 +1440,7 @@ static void init_viadev(struct via82xx *chip, int idx, unsigned int reg_offset, static int __devinit snd_via8233_pcm_new(struct via82xx *chip) { struct snd_pcm *pcm; + struct snd_pcm_chmap *chmap; int i, err; chip->playback_devno = 0; /* x 4 */ @@ -1467,6 +1468,12 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip) snd_dma_pci_data(chip->pci), 64*1024, VIA_MAX_BUFSIZE); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, 2, 0, + &chmap); + if (err < 0) + return err; + /* PCM #1: multi-channel playback and 2nd capture */ err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm); if (err < 0) @@ -1484,6 +1491,14 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), 64*1024, VIA_MAX_BUFSIZE); + + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, 6, 0, + &chmap); + if (err < 0) + return err; + chip->ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap; + return 0; } @@ -1493,6 +1508,7 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip) static int __devinit snd_via8233a_pcm_new(struct via82xx *chip) { struct snd_pcm *pcm; + struct snd_pcm_chmap *chmap; int err; chip->multi_devno = 0; @@ -1519,6 +1535,13 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip) snd_dma_pci_data(chip->pci), 64*1024, VIA_MAX_BUFSIZE); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, 6, 0, + &chmap); + if (err < 0) + return err; + chip->ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap; + /* SPDIF supported? */ if (! ac97_can_spdif(chip->ac97)) return 0; From f49921b894ec338696e47d985acc09df3cddbfd1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 3 Aug 2012 18:02:38 +0200 Subject: [PATCH 08/21] ALSA: cmipci: Implement channel mapping Simply enable the channel map according to the h/w capability. Signed-off-by: Takashi Iwai --- sound/pci/cmipci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 7c25906b5f88..22122ff26e34 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -1962,6 +1962,12 @@ static int __devinit snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(cm->pci), 64*1024, 128*1024); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, cm->max_channels, 0, + NULL); + if (err < 0) + return err; + return 0; } From 8d50cdc1f52843d4fe593c2613528c3f6677949a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 3 Aug 2012 18:03:17 +0200 Subject: [PATCH 09/21] ALSA: ctxfi: Implement channel maps Assign the multi-channel map to front PCM, and other channel map to each other channel PCM. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctpcm.c | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index d021876901bb..6c191429b126 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -395,12 +395,38 @@ static struct snd_pcm_ops ct_pcm_capture_ops = { .page = snd_pcm_sgbuf_ops_page, }; +static const struct snd_pcm_chmap_elem surround_map[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_RC } }, + { .channels = 2, + .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + +static const struct snd_pcm_chmap_elem clfe_map[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_FC } }, + { .channels = 2, + .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, + { } +}; + +static const struct snd_pcm_chmap_elem side_map[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_RC } }, + { .channels = 2, + .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, + { } +}; + /* Create ALSA pcm device */ int ct_alsa_pcm_create(struct ct_atc *atc, enum CTALSADEVS device, const char *device_name) { struct snd_pcm *pcm; + const struct snd_pcm_chmap_elem *map; + int chs; int err; int playback_count, capture_count; @@ -427,6 +453,30 @@ int ct_alsa_pcm_create(struct ct_atc *atc, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(atc->pci), 128*1024, 128*1024); + chs = 2; + switch (device) { + case FRONT: + chs = 8; + map = snd_pcm_std_chmaps; + break; + case SURROUND: + map = surround_map; + break; + case CLFE: + map = clfe_map; + break; + case SIDE: + map = side_map; + break; + default: + map = snd_pcm_std_chmaps; + break; + } + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs, + 0, NULL); + if (err < 0) + return err; + #ifdef CONFIG_PM_SLEEP atc->pcms[device] = pcm; #endif From 6e67683d718c627aa585a82ae0eb44a7058eae5a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Aug 2012 14:49:47 +0200 Subject: [PATCH 10/21] ALSA: Remove VOLATILE flag from chmap ctls The VOLATILE flag was added to control elements by snd_pcm_add_chmap_ctls() just because I didn't want to have a side-effect of "alsactl restore". But now the set operation doesn't allow to change the value unless the PCM stream is in PREAPRED state, there is no reason to keep this flag. Let's rip it off. Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 565102705eda..0a750ec5c400 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2479,7 +2479,6 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, struct snd_kcontrol_new knew = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .access = SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_VOLATILE | /* no notification */ SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, .info = pcm_chmap_ctl_info, From 080108c4747c7378c3601b8584237484f977d8a8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Aug 2012 14:47:18 +0200 Subject: [PATCH 11/21] ALSA: Follow channel position definitions to alsa-lib mixer There is already a set of channel position definitions in alsa-lib mixer.h, and it'd be more practical to keep the same order for the PCM channel map, too. The value is shifted with 1 to keep zero for UNKNOWN. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/Channel-Mapping-API.txt | 16 +++++++++------- include/sound/asound.h | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Documentation/sound/alsa/Channel-Mapping-API.txt b/Documentation/sound/alsa/Channel-Mapping-API.txt index df930aa4f4d0..4bbf12d5553f 100644 --- a/Documentation/sound/alsa/Channel-Mapping-API.txt +++ b/Documentation/sound/alsa/Channel-Mapping-API.txt @@ -76,20 +76,22 @@ here is a cut: /* channel positions */ enum { + /* this follows the alsa-lib mixer channel value + 1 */ SNDRV_CHMAP_UNKNOWN = 0, SNDRV_CHMAP_FL, /* front left */ - SNDRV_CHMAP_FC, /* front center */ SNDRV_CHMAP_FR, /* front right */ - SNDRV_CHMAP_FLC, /* front left center */ - SNDRV_CHMAP_FRC, /* front right center */ SNDRV_CHMAP_RL, /* rear left */ - SNDRV_CHMAP_RC, /* rear center */ SNDRV_CHMAP_RR, /* rear right */ - SNDRV_CHMAP_RLC, /* rear left center */ - SNDRV_CHMAP_RRC, /* rear right center */ + SNDRV_CHMAP_FC, /* front center */ + SNDRV_CHMAP_LFE, /* LFE */ SNDRV_CHMAP_SL, /* side left */ SNDRV_CHMAP_SR, /* side right */ - SNDRV_CHMAP_LFE, /* LFE */ + SNDRV_CHMAP_RC, /* rear center */ + /* new definitions */ + SNDRV_CHMAP_FLC, /* front left center */ + SNDRV_CHMAP_FRC, /* front right center */ + SNDRV_CHMAP_RLC, /* rear left center */ + SNDRV_CHMAP_RRC, /* rear right center */ SNDRV_CHMAP_FLW, /* front left wide */ SNDRV_CHMAP_FRW, /* front right wide */ SNDRV_CHMAP_FLH, /* front left high */ diff --git a/include/sound/asound.h b/include/sound/asound.h index 376e75632e07..27686da0f650 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -474,20 +474,22 @@ enum { /* channel positions */ enum { + /* this follows the alsa-lib mixer channel value + 1 */ SNDRV_CHMAP_UNKNOWN = 0, SNDRV_CHMAP_FL, /* front left */ - SNDRV_CHMAP_FC, /* front center */ SNDRV_CHMAP_FR, /* front right */ - SNDRV_CHMAP_FLC, /* front left center */ - SNDRV_CHMAP_FRC, /* front right center */ SNDRV_CHMAP_RL, /* rear left */ - SNDRV_CHMAP_RC, /* rear center */ SNDRV_CHMAP_RR, /* rear right */ - SNDRV_CHMAP_RLC, /* rear left center */ - SNDRV_CHMAP_RRC, /* rear right center */ + SNDRV_CHMAP_FC, /* front center */ + SNDRV_CHMAP_LFE, /* LFE */ SNDRV_CHMAP_SL, /* side left */ SNDRV_CHMAP_SR, /* side right */ - SNDRV_CHMAP_LFE, /* LFE */ + SNDRV_CHMAP_RC, /* rear center */ + /* new definitions */ + SNDRV_CHMAP_FLC, /* front left center */ + SNDRV_CHMAP_FRC, /* front right center */ + SNDRV_CHMAP_RLC, /* rear left center */ + SNDRV_CHMAP_RRC, /* rear right center */ SNDRV_CHMAP_FLW, /* front left wide */ SNDRV_CHMAP_FRW, /* front right wide */ SNDRV_CHMAP_FLH, /* front left high */ From 498dab3aa7f8851a880ffe9d8e66516851950c59 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Sep 2012 16:08:40 +0200 Subject: [PATCH 12/21] ALSA: hda - Allow 3/5/7 channel map for HDMI/DP Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 2c53ea889da9..71555cc54db1 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1441,7 +1441,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -EFAULT; size -= 8; dst = tlv + 2; - for (chs = 2; chs <= spec->channels_max; chs += 2) { + for (chs = 2; chs <= spec->channels_max; chs++) { int i, c; struct cea_channel_speaker_allocation *cap; cap = channel_allocations; From 2dc6fbf0078e8148b571f1bbec704ca5c8e2ec2c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 11 Sep 2012 14:24:43 +0200 Subject: [PATCH 13/21] ALSA: pcm - Use UNKNOWN chmap for mono streams In general, mono streams have no dedicated speaker assignment, thus they should be rather marked as UNKNOWN position. Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0a750ec5c400..9647a22ed69a 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2311,7 +2311,7 @@ EXPORT_SYMBOL(snd_pcm_lib_readv); /* 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_FC } }, + .map = { SNDRV_CHMAP_UNKNOWN } }, { .channels = 2, .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, { .channels = 4, @@ -2333,7 +2333,7 @@ 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_FC } }, + .map = { SNDRV_CHMAP_UNKNOWN } }, { .channels = 2, .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, { .channels = 4, From 7e8d613b536446139d93c956bb59f25aa934c520 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Sep 2012 15:19:20 +0200 Subject: [PATCH 14/21] ALSA: ctxfi: Fix mono channel map to UNKNOWN To follow the previous commit. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctpcm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 6c191429b126..d317107d98cc 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -397,7 +397,7 @@ static struct snd_pcm_ops ct_pcm_capture_ops = { static const struct snd_pcm_chmap_elem surround_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_RC } }, + .map = { SNDRV_CHMAP_UNKNOWN } }, { .channels = 2, .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, { } @@ -405,7 +405,7 @@ static const struct snd_pcm_chmap_elem surround_map[] = { static const struct snd_pcm_chmap_elem clfe_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_FC } }, + .map = { SNDRV_CHMAP_UNKNOWN } }, { .channels = 2, .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, { } @@ -413,7 +413,7 @@ static const struct snd_pcm_chmap_elem clfe_map[] = { static const struct snd_pcm_chmap_elem side_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_RC } }, + .map = { SNDRV_CHMAP_UNKNOWN } }, { .channels = 2, .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, { } From 1fe4d42e0e28c2c004b06dd590702604f47c2402 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Sep 2012 15:46:20 +0200 Subject: [PATCH 15/21] ALSA: ens1370: Reduce ifdefs ... just by defining CHIP_NAME and string concats. Signed-off-by: Takashi Iwai --- sound/pci/ens1370.c | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 2ba58d365959..1e615c4d12f8 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -55,8 +55,10 @@ #ifdef CHIP1370 #define DRIVER_NAME "ENS1370" +#define CHIP_NAME "ES1370" /* it can be ENS but just to keep compatibility... */ #else #define DRIVER_NAME "ENS1371" +#define CHIP_NAME "ES1371" #endif @@ -1266,11 +1268,7 @@ static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device, if (rpcm) *rpcm = NULL; -#ifdef CHIP1370 - err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm); -#else - err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm); -#endif + err = snd_pcm_new(ensoniq->card, CHIP_NAME "/1", device, 1, 1, &pcm); if (err < 0) return err; @@ -1283,11 +1281,7 @@ static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device, pcm->private_data = ensoniq; pcm->info_flags = 0; -#ifdef CHIP1370 - strcpy(pcm->name, "ES1370 DAC2/ADC"); -#else - strcpy(pcm->name, "ES1371 DAC2/ADC"); -#endif + strcpy(pcm->name, CHIP_NAME " DAC2/ADC"); ensoniq->pcm1 = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1306,11 +1300,7 @@ static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device, if (rpcm) *rpcm = NULL; -#ifdef CHIP1370 - err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm); -#else - err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm); -#endif + err = snd_pcm_new(ensoniq->card, CHIP_NAME "/2", device, 1, 0, &pcm); if (err < 0) return err; @@ -1321,11 +1311,7 @@ static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device, #endif pcm->private_data = ensoniq; pcm->info_flags = 0; -#ifdef CHIP1370 - strcpy(pcm->name, "ES1370 DAC1"); -#else - strcpy(pcm->name, "ES1371 DAC1"); -#endif + strcpy(pcm->name, CHIP_NAME " DAC1"); ensoniq->pcm2 = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1885,11 +1871,7 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry, { struct ensoniq *ensoniq = entry->private_data; -#ifdef CHIP1370 - snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n"); -#else - snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n"); -#endif + snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n"); snd_iprintf(buffer, "Joystick enable : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off"); #ifdef CHIP1370 @@ -2361,11 +2343,7 @@ static int __devinit snd_ensoniq_midi(struct ensoniq * ensoniq, int device, *rrawmidi = NULL; if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0) return err; -#ifdef CHIP1370 - strcpy(rmidi->name, "ES1370"); -#else - strcpy(rmidi->name, "ES1371"); -#endif + strcpy(rmidi->name, CHIP_NAME); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | From 21147f91f1be57c4cfc24e538e93c30636513ce8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Sep 2012 15:52:09 +0200 Subject: [PATCH 16/21] ALSA: ca0106: Define channel maps Provide channel maps for individual stereo streams of CA0106. Signed-off-by: Takashi Iwai --- sound/pci/ca0106/ca0106_main.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index fc6787699ba9..65c55910566b 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1334,10 +1334,29 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static const struct snd_pcm_chmap_elem surround_map[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + +static const struct snd_pcm_chmap_elem clfe_map[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, + { } +}; + +static const struct snd_pcm_chmap_elem side_map[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, + { } +}; + static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device) { struct snd_pcm *pcm; struct snd_pcm_substream *substream; + const struct snd_pcm_chmap_elem *map = NULL; int err; err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm); @@ -1350,18 +1369,22 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device) case 0: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops); + map = snd_pcm_std_chmaps; break; case 1: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops); + map = surround_map; break; case 2: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops); + map = clfe_map; break; case 3: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops); + map = side_map; break; } @@ -1388,6 +1411,11 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device) return err; } + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2, + 1 << 2, NULL); + if (err < 0) + return err; + emu->pcm[device] = pcm; return 0; From 3adc497f98dd062bcf4d832c659a3d706ece5249 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Sep 2012 15:52:47 +0200 Subject: [PATCH 17/21] ALSA: emu10k1x: Define channel maps Provide channel maps for individual stereo streams of emu10k1x. Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emu10k1x.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 5c8978b2c4d9..556fd6f456e3 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -830,9 +830,22 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static const struct snd_pcm_chmap_elem surround_map[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + +static const struct snd_pcm_chmap_elem clfe_map[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, + { } +}; + static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **rpcm) { struct snd_pcm *pcm; + const struct snd_pcm_chmap_elem *map = NULL; int err; int capture = 0; @@ -861,12 +874,15 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s switch(device) { case 0: strcpy(pcm->name, "EMU10K1X Front"); + map = snd_pcm_std_chmaps; break; case 1: strcpy(pcm->name, "EMU10K1X Rear"); + map = surround_map; break; case 2: strcpy(pcm->name, "EMU10K1X Center/LFE"); + map = clfe_map; break; } emu->pcm = pcm; @@ -875,6 +891,11 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s snd_dma_pci_data(emu->pci), 32*1024, 32*1024); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2, + 1 << 2, NULL); + if (err < 0) + return err; + if (rpcm) *rpcm = pcm; From 7fb6861d62b24fd0056a52275e5f74db0ef66f88 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Sep 2012 15:53:26 +0200 Subject: [PATCH 18/21] ALSA: ens1370: Define channel maps Provide channel maps for individual stereo streams of ENS1370 and ENS1371. Note that the configuration of ENS1370 uses the secondary PCM as the front unlike ENS1371. Signed-off-by: Takashi Iwai --- sound/pci/ens1370.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 1e615c4d12f8..9d432359d1cb 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1260,6 +1260,14 @@ static struct snd_pcm_ops snd_ensoniq_capture_ops = { .pointer = snd_ensoniq_capture_pointer, }; +static const struct snd_pcm_chmap_elem surround_map[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_UNKNOWN } }, + { .channels = 2, + .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device, struct snd_pcm ** rpcm) { @@ -1287,6 +1295,16 @@ static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); +#ifdef CHIP1370 + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + surround_map, 2, 0, NULL); +#else + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, 2, 0, NULL); +#endif + if (err < 0) + return err; + if (rpcm) *rpcm = pcm; return 0; @@ -1317,6 +1335,16 @@ static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); +#ifdef CHIP1370 + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, 2, 0, NULL); +#else + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + surround_map, 2, 0, NULL); +#endif + if (err < 0) + return err; + if (rpcm) *rpcm = pcm; return 0; From 0afdb8f2869610b7c2eb99d75ba8b9003b8e88d7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Sep 2012 16:14:37 +0200 Subject: [PATCH 19/21] ALSA: ymfpci: Define channel maps Provide channel maps for individual stereo streams of YMFPCI. Signed-off-by: Takashi Iwai --- sound/pci/ymfpci/ymfpci_main.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index ee8b6366e48d..50b0804df904 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1166,6 +1166,11 @@ int __devinit snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, 2, 0, NULL); + if (err < 0) + return err; + if (rpcm) *rpcm = pcm; return 0; @@ -1257,6 +1262,14 @@ static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = { .pointer = snd_ymfpci_playback_pointer, }; +static const struct snd_pcm_chmap_elem surround_map[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_UNKNOWN } }, + { .channels = 2, + .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm) { struct snd_pcm *pcm; @@ -1278,6 +1291,11 @@ int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + surround_map, 2, 0, NULL); + if (err < 0) + return err; + if (rpcm) *rpcm = pcm; return 0; From 7b31d0095e87221dc32c95642a2a714ea08259aa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Sep 2012 18:06:54 +0200 Subject: [PATCH 20/21] ALSA: Define more channel map positions For following the standard, define more channel map positions and shuffle the items a bit: - As both PulseAudio and gstreamer define MONO channel position explicitly, we should follow that, too. The mono streams point to this channel position unless they are explicitly assigned to certain channel positions. - Top-front-* and Top-rear-* positions are added, carried from PulseAudio's definitions. - Move NA and MONO definitions at the top of table right after UNKNOWN, since these are more abstract in comparison with other practical positions. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/Channel-Mapping-API.txt | 13 ++++++++++--- include/sound/asound.h | 13 ++++++++++--- sound/pci/ctxfi/ctpcm.c | 6 +++--- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Documentation/sound/alsa/Channel-Mapping-API.txt b/Documentation/sound/alsa/Channel-Mapping-API.txt index 4bbf12d5553f..3c43d1a4ca0e 100644 --- a/Documentation/sound/alsa/Channel-Mapping-API.txt +++ b/Documentation/sound/alsa/Channel-Mapping-API.txt @@ -76,8 +76,10 @@ here is a cut: /* channel positions */ enum { - /* this follows the alsa-lib mixer channel value + 1 */ SNDRV_CHMAP_UNKNOWN = 0, + SNDRV_CHMAP_NA, /* N/A, silent */ + SNDRV_CHMAP_MONO, /* mono stream */ + /* this follows the alsa-lib mixer channel value + 3 */ SNDRV_CHMAP_FL, /* front left */ SNDRV_CHMAP_FR, /* front right */ SNDRV_CHMAP_RL, /* rear left */ @@ -98,8 +100,13 @@ enum { SNDRV_CHMAP_FCH, /* front center high */ SNDRV_CHMAP_FRH, /* front right high */ SNDRV_CHMAP_TC, /* top center */ - SNDRV_CHMAP_NA, /* N/A, silent */ - SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA, + SNDRV_CHMAP_TFL, /* top front left */ + SNDRV_CHMAP_TFR, /* top front right */ + SNDRV_CHMAP_TFC, /* top front center */ + SNDRV_CHMAP_TRL, /* top rear left */ + SNDRV_CHMAP_TRR, /* top rear right */ + SNDRV_CHMAP_TRC, /* top rear center */ + SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC, }; When a PCM stream can provide more than one channel map, you can diff --git a/include/sound/asound.h b/include/sound/asound.h index 27686da0f650..dfe7d441748c 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -474,8 +474,10 @@ enum { /* channel positions */ enum { - /* this follows the alsa-lib mixer channel value + 1 */ SNDRV_CHMAP_UNKNOWN = 0, + SNDRV_CHMAP_NA, /* N/A, silent */ + SNDRV_CHMAP_MONO, /* mono stream */ + /* this follows the alsa-lib mixer channel value + 3 */ SNDRV_CHMAP_FL, /* front left */ SNDRV_CHMAP_FR, /* front right */ SNDRV_CHMAP_RL, /* rear left */ @@ -496,8 +498,13 @@ enum { SNDRV_CHMAP_FCH, /* front center high */ SNDRV_CHMAP_FRH, /* front right high */ SNDRV_CHMAP_TC, /* top center */ - SNDRV_CHMAP_NA, /* N/A, silent */ - SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA, + SNDRV_CHMAP_TFL, /* top front left */ + SNDRV_CHMAP_TFR, /* top front right */ + SNDRV_CHMAP_TFC, /* top front center */ + SNDRV_CHMAP_TRL, /* top rear left */ + SNDRV_CHMAP_TRR, /* top rear right */ + SNDRV_CHMAP_TRC, /* top rear center */ + SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC, }; #define SNDRV_CHMAP_POSITION_MASK 0xffff diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index d317107d98cc..e8a4feb1ed86 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -397,7 +397,7 @@ static struct snd_pcm_ops ct_pcm_capture_ops = { static const struct snd_pcm_chmap_elem surround_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_UNKNOWN } }, + .map = { SNDRV_CHMAP_MONO } }, { .channels = 2, .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, { } @@ -405,7 +405,7 @@ static const struct snd_pcm_chmap_elem surround_map[] = { static const struct snd_pcm_chmap_elem clfe_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_UNKNOWN } }, + .map = { SNDRV_CHMAP_MONO } }, { .channels = 2, .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, { } @@ -413,7 +413,7 @@ static const struct snd_pcm_chmap_elem clfe_map[] = { static const struct snd_pcm_chmap_elem side_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_UNKNOWN } }, + .map = { SNDRV_CHMAP_MONO } }, { .channels = 2, .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, { } From 5efbc2610a7b2aac6c51f8fc943c019106568939 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Sep 2012 14:48:46 +0200 Subject: [PATCH 21/21] ALSA: Fix leftover chmap UNKNOWN -> MONO conversions A few files have been slipped from the previous commit to add MONO channel type. Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 4 ++-- sound/pci/ens1370.c | 2 +- sound/pci/ymfpci/ymfpci_main.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9647a22ed69a..f42c10a43315 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2311,7 +2311,7 @@ EXPORT_SYMBOL(snd_pcm_lib_readv); /* 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_UNKNOWN } }, + .map = { SNDRV_CHMAP_MONO } }, { .channels = 2, .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, { .channels = 4, @@ -2333,7 +2333,7 @@ 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_UNKNOWN } }, + .map = { SNDRV_CHMAP_MONO } }, { .channels = 2, .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, { .channels = 4, diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 9d432359d1cb..5674cc316530 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1262,7 +1262,7 @@ static struct snd_pcm_ops snd_ensoniq_capture_ops = { static const struct snd_pcm_chmap_elem surround_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_UNKNOWN } }, + .map = { SNDRV_CHMAP_MONO } }, { .channels = 2, .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, { } diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 50b0804df904..3a6f03f9b02f 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1264,7 +1264,7 @@ static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = { static const struct snd_pcm_chmap_elem surround_map[] = { { .channels = 1, - .map = { SNDRV_CHMAP_UNKNOWN } }, + .map = { SNDRV_CHMAP_MONO } }, { .channels = 2, .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, { }