Merge branch 'topic/tlv-chmap' into for-next
This is a merge of a topic branch containing the support for the new channel map API using control elements.
This commit is contained in:
commit
e5d6db8e60
|
@ -0,0 +1,153 @@
|
||||||
|
ALSA PCM channel-mapping API
|
||||||
|
============================
|
||||||
|
Takashi Iwai <tiwai@suse.de>
|
||||||
|
|
||||||
|
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_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 */
|
||||||
|
SNDRV_CHMAP_RR, /* rear right */
|
||||||
|
SNDRV_CHMAP_FC, /* front center */
|
||||||
|
SNDRV_CHMAP_LFE, /* LFE */
|
||||||
|
SNDRV_CHMAP_SL, /* side left */
|
||||||
|
SNDRV_CHMAP_SR, /* side right */
|
||||||
|
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 */
|
||||||
|
SNDRV_CHMAP_FCH, /* front center high */
|
||||||
|
SNDRV_CHMAP_FRH, /* front right high */
|
||||||
|
SNDRV_CHMAP_TC, /* top center */
|
||||||
|
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
|
||||||
|
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.
|
|
@ -422,6 +422,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct snd_ac97;
|
struct snd_ac97;
|
||||||
|
struct snd_pcm_chmap;
|
||||||
|
|
||||||
struct snd_ac97_build_ops {
|
struct snd_ac97_build_ops {
|
||||||
int (*build_3d) (struct snd_ac97 *ac97);
|
int (*build_3d) (struct snd_ac97 *ac97);
|
||||||
|
@ -528,6 +529,8 @@ struct snd_ac97 {
|
||||||
struct delayed_work power_work;
|
struct delayed_work power_work;
|
||||||
#endif
|
#endif
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
|
struct snd_pcm_chmap *chmaps[2]; /* channel-maps (optional) */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_ac97_t(d) container_of(d, struct snd_ac97, dev)
|
#define to_ac97_t(d) container_of(d, struct snd_ac97, dev)
|
||||||
|
|
|
@ -472,6 +472,45 @@ enum {
|
||||||
SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
|
SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* channel positions */
|
||||||
|
enum {
|
||||||
|
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 */
|
||||||
|
SNDRV_CHMAP_RR, /* rear right */
|
||||||
|
SNDRV_CHMAP_FC, /* front center */
|
||||||
|
SNDRV_CHMAP_LFE, /* LFE */
|
||||||
|
SNDRV_CHMAP_SL, /* side left */
|
||||||
|
SNDRV_CHMAP_SR, /* side right */
|
||||||
|
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 */
|
||||||
|
SNDRV_CHMAP_FCH, /* front center high */
|
||||||
|
SNDRV_CHMAP_FRH, /* front right high */
|
||||||
|
SNDRV_CHMAP_TC, /* top center */
|
||||||
|
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
|
||||||
|
#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_PVERSION _IOR('A', 0x00, int)
|
||||||
#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info)
|
#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info)
|
||||||
#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int)
|
#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int)
|
||||||
|
|
|
@ -437,6 +437,7 @@ struct snd_pcm_str {
|
||||||
struct snd_info_entry *proc_xrun_debug_entry;
|
struct snd_info_entry *proc_xrun_debug_entry;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct snd_pcm {
|
struct snd_pcm {
|
||||||
|
@ -1086,4 +1087,51 @@ static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream
|
||||||
return "Capture";
|
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 */
|
#endif /* __SOUND_PCM_H */
|
||||||
|
|
|
@ -86,4 +86,12 @@
|
||||||
|
|
||||||
#define TLV_DB_GAIN_MUTE -9999999
|
#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 */
|
#endif /* __SOUND_TLV_H */
|
||||||
|
|
|
@ -1105,6 +1105,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
snd_unregister_device(devtype, pcm->card, pcm->device);
|
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:
|
unlock:
|
||||||
mutex_unlock(®ister_mutex);
|
mutex_unlock(®ister_mutex);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/control.h>
|
#include <sound/control.h>
|
||||||
|
#include <sound/tlv.h>
|
||||||
#include <sound/info.h>
|
#include <sound/info.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_params.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);
|
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);
|
||||||
|
|
|
@ -2595,6 +2595,21 @@ static void alc650_update_jacks(struct snd_ac97 *ac97)
|
||||||
shared ? 0 : 0x100);
|
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[] = {
|
static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
|
||||||
AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
|
AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
|
||||||
AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 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 */
|
/* 9: Line-In/Surround share */
|
||||||
/* 10: Mic/CLFE share */
|
/* 10: Mic/CLFE share */
|
||||||
/* 11-13: in IEC958 controls */
|
/* 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 */
|
#if 0 /* always set in patch_alc650 */
|
||||||
AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
|
AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
|
||||||
AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
|
AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
|
||||||
|
|
|
@ -1250,6 +1250,7 @@ static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
|
||||||
static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
|
static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
|
||||||
{
|
{
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
|
struct snd_pcm_chmap *chmap;
|
||||||
struct snd_ac97_bus *pbus = chip->ac97_bus;
|
struct snd_ac97_bus *pbus = chip->ac97_bus;
|
||||||
int err, i, num_pcms;
|
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),
|
snd_dma_pci_data(chip->pci),
|
||||||
64*1024, 128*1024);
|
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? */
|
/* no SPDIF support on codec? */
|
||||||
if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
|
if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1334,10 +1334,29 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
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)
|
static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
|
||||||
{
|
{
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
struct snd_pcm_substream *substream;
|
struct snd_pcm_substream *substream;
|
||||||
|
const struct snd_pcm_chmap_elem *map = NULL;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
|
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:
|
case 0:
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
|
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);
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
|
||||||
|
map = snd_pcm_std_chmaps;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
|
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);
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
|
||||||
|
map = surround_map;
|
||||||
break;
|
break;
|
||||||
case 2:
|
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_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
|
||||||
|
map = clfe_map;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
|
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);
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
|
||||||
|
map = side_map;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1388,6 +1411,11 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
|
||||||
return err;
|
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;
|
emu->pcm[device] = pcm;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -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_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||||
snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -395,12 +395,38 @@ static struct snd_pcm_ops ct_pcm_capture_ops = {
|
||||||
.page = snd_pcm_sgbuf_ops_page,
|
.page = snd_pcm_sgbuf_ops_page,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct snd_pcm_chmap_elem surround_map[] = {
|
||||||
|
{ .channels = 1,
|
||||||
|
.map = { SNDRV_CHMAP_MONO } },
|
||||||
|
{ .channels = 2,
|
||||||
|
.map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_pcm_chmap_elem clfe_map[] = {
|
||||||
|
{ .channels = 1,
|
||||||
|
.map = { SNDRV_CHMAP_MONO } },
|
||||||
|
{ .channels = 2,
|
||||||
|
.map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_pcm_chmap_elem side_map[] = {
|
||||||
|
{ .channels = 1,
|
||||||
|
.map = { SNDRV_CHMAP_MONO } },
|
||||||
|
{ .channels = 2,
|
||||||
|
.map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
/* Create ALSA pcm device */
|
/* Create ALSA pcm device */
|
||||||
int ct_alsa_pcm_create(struct ct_atc *atc,
|
int ct_alsa_pcm_create(struct ct_atc *atc,
|
||||||
enum CTALSADEVS device,
|
enum CTALSADEVS device,
|
||||||
const char *device_name)
|
const char *device_name)
|
||||||
{
|
{
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
|
const struct snd_pcm_chmap_elem *map;
|
||||||
|
int chs;
|
||||||
int err;
|
int err;
|
||||||
int playback_count, capture_count;
|
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_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
|
||||||
snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
|
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
|
#ifdef CONFIG_PM_SLEEP
|
||||||
atc->pcms[device] = pcm;
|
atc->pcms[device] = pcm;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -830,9 +830,22 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
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)
|
static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **rpcm)
|
||||||
{
|
{
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
|
const struct snd_pcm_chmap_elem *map = NULL;
|
||||||
int err;
|
int err;
|
||||||
int capture = 0;
|
int capture = 0;
|
||||||
|
|
||||||
|
@ -861,12 +874,15 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s
|
||||||
switch(device) {
|
switch(device) {
|
||||||
case 0:
|
case 0:
|
||||||
strcpy(pcm->name, "EMU10K1X Front");
|
strcpy(pcm->name, "EMU10K1X Front");
|
||||||
|
map = snd_pcm_std_chmaps;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
strcpy(pcm->name, "EMU10K1X Rear");
|
strcpy(pcm->name, "EMU10K1X Rear");
|
||||||
|
map = surround_map;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
strcpy(pcm->name, "EMU10K1X Center/LFE");
|
strcpy(pcm->name, "EMU10K1X Center/LFE");
|
||||||
|
map = clfe_map;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
emu->pcm = pcm;
|
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),
|
snd_dma_pci_data(emu->pci),
|
||||||
32*1024, 32*1024);
|
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)
|
if (rpcm)
|
||||||
*rpcm = pcm;
|
*rpcm = pcm;
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,10 @@
|
||||||
|
|
||||||
#ifdef CHIP1370
|
#ifdef CHIP1370
|
||||||
#define DRIVER_NAME "ENS1370"
|
#define DRIVER_NAME "ENS1370"
|
||||||
|
#define CHIP_NAME "ES1370" /* it can be ENS but just to keep compatibility... */
|
||||||
#else
|
#else
|
||||||
#define DRIVER_NAME "ENS1371"
|
#define DRIVER_NAME "ENS1371"
|
||||||
|
#define CHIP_NAME "ES1371"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -1258,6 +1260,14 @@ static struct snd_pcm_ops snd_ensoniq_capture_ops = {
|
||||||
.pointer = snd_ensoniq_capture_pointer,
|
.pointer = snd_ensoniq_capture_pointer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct snd_pcm_chmap_elem surround_map[] = {
|
||||||
|
{ .channels = 1,
|
||||||
|
.map = { SNDRV_CHMAP_MONO } },
|
||||||
|
{ .channels = 2,
|
||||||
|
.map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
|
static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
|
||||||
struct snd_pcm ** rpcm)
|
struct snd_pcm ** rpcm)
|
||||||
{
|
{
|
||||||
|
@ -1266,11 +1276,7 @@ static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
|
||||||
|
|
||||||
if (rpcm)
|
if (rpcm)
|
||||||
*rpcm = NULL;
|
*rpcm = NULL;
|
||||||
#ifdef CHIP1370
|
err = snd_pcm_new(ensoniq->card, CHIP_NAME "/1", device, 1, 1, &pcm);
|
||||||
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
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -1283,16 +1289,22 @@ static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
|
||||||
|
|
||||||
pcm->private_data = ensoniq;
|
pcm->private_data = ensoniq;
|
||||||
pcm->info_flags = 0;
|
pcm->info_flags = 0;
|
||||||
#ifdef CHIP1370
|
strcpy(pcm->name, CHIP_NAME " DAC2/ADC");
|
||||||
strcpy(pcm->name, "ES1370 DAC2/ADC");
|
|
||||||
#else
|
|
||||||
strcpy(pcm->name, "ES1371 DAC2/ADC");
|
|
||||||
#endif
|
|
||||||
ensoniq->pcm1 = pcm;
|
ensoniq->pcm1 = pcm;
|
||||||
|
|
||||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||||
snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
|
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)
|
if (rpcm)
|
||||||
*rpcm = pcm;
|
*rpcm = pcm;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1306,11 +1318,7 @@ static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device,
|
||||||
|
|
||||||
if (rpcm)
|
if (rpcm)
|
||||||
*rpcm = NULL;
|
*rpcm = NULL;
|
||||||
#ifdef CHIP1370
|
err = snd_pcm_new(ensoniq->card, CHIP_NAME "/2", device, 1, 0, &pcm);
|
||||||
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
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -1321,16 +1329,22 @@ static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device,
|
||||||
#endif
|
#endif
|
||||||
pcm->private_data = ensoniq;
|
pcm->private_data = ensoniq;
|
||||||
pcm->info_flags = 0;
|
pcm->info_flags = 0;
|
||||||
#ifdef CHIP1370
|
strcpy(pcm->name, CHIP_NAME " DAC1");
|
||||||
strcpy(pcm->name, "ES1370 DAC1");
|
|
||||||
#else
|
|
||||||
strcpy(pcm->name, "ES1371 DAC1");
|
|
||||||
#endif
|
|
||||||
ensoniq->pcm2 = pcm;
|
ensoniq->pcm2 = pcm;
|
||||||
|
|
||||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||||
snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
|
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)
|
if (rpcm)
|
||||||
*rpcm = pcm;
|
*rpcm = pcm;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1885,11 +1899,7 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry,
|
||||||
{
|
{
|
||||||
struct ensoniq *ensoniq = entry->private_data;
|
struct ensoniq *ensoniq = entry->private_data;
|
||||||
|
|
||||||
#ifdef CHIP1370
|
snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n");
|
||||||
snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
|
|
||||||
#else
|
|
||||||
snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
|
|
||||||
#endif
|
|
||||||
snd_iprintf(buffer, "Joystick enable : %s\n",
|
snd_iprintf(buffer, "Joystick enable : %s\n",
|
||||||
ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
|
ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
|
||||||
#ifdef CHIP1370
|
#ifdef CHIP1370
|
||||||
|
@ -2361,11 +2371,7 @@ static int __devinit snd_ensoniq_midi(struct ensoniq * ensoniq, int device,
|
||||||
*rrawmidi = NULL;
|
*rrawmidi = NULL;
|
||||||
if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
|
if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
|
||||||
return err;
|
return err;
|
||||||
#ifdef CHIP1370
|
strcpy(rmidi->name, CHIP_NAME);
|
||||||
strcpy(rmidi->name, "ES1370");
|
|
||||||
#else
|
|
||||||
strcpy(rmidi->name, "ES1371");
|
|
||||||
#endif
|
|
||||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output);
|
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);
|
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
|
||||||
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
|
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
|
||||||
|
|
|
@ -711,6 +711,13 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc
|
||||||
snd_dma_pci_data(chip->pci),
|
snd_dma_pci_data(chip->pci),
|
||||||
chip->multichannel ? 128*1024 : 64*1024, 128*1024);
|
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)
|
if (rpcm)
|
||||||
*rpcm = pcm;
|
*rpcm = pcm;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -3688,6 +3688,36 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_HDA(snd_hda_build_controls);
|
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 snd_hda_codec_build_controls(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -3699,6 +3729,12 @@ int snd_hda_codec_build_controls(struct hda_codec *codec)
|
||||||
err = codec->patch_ops.build_controls(codec);
|
err = codec->patch_ops.build_controls(codec);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
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 */
|
snd_hda_jack_report_sync(codec); /* call at the last init point */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -776,6 +776,7 @@ struct hda_pcm {
|
||||||
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
|
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
|
||||||
int device; /* device number to assign */
|
int device; /* device number to assign */
|
||||||
struct snd_pcm *pcm; /* assigned PCM instance */
|
struct snd_pcm *pcm; /* assigned PCM instance */
|
||||||
|
bool own_chmap; /* codec driver provides own channel maps */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* codec information */
|
/* codec information */
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/jack.h>
|
#include <sound/jack.h>
|
||||||
#include <sound/asoundef.h>
|
#include <sound/asoundef.h>
|
||||||
|
#include <sound/tlv.h>
|
||||||
#include "hda_codec.h"
|
#include "hda_codec.h"
|
||||||
#include "hda_local.h"
|
#include "hda_local.h"
|
||||||
#include "hda_jack.h"
|
#include "hda_jack.h"
|
||||||
|
@ -73,6 +74,8 @@ struct hdmi_spec_per_pin {
|
||||||
struct delayed_work work;
|
struct delayed_work work;
|
||||||
int repoll_count;
|
int repoll_count;
|
||||||
bool non_pcm;
|
bool non_pcm;
|
||||||
|
bool chmap_set; /* channel-map override by ALSA API? */
|
||||||
|
unsigned char chmap[8]; /* ALSA API channel-map */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hdmi_spec {
|
struct hdmi_spec {
|
||||||
|
@ -82,6 +85,7 @@ struct hdmi_spec {
|
||||||
int num_pins;
|
int num_pins;
|
||||||
struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
|
struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
|
||||||
struct hda_pcm pcm_rec[MAX_HDMI_PINS];
|
struct hda_pcm pcm_rec[MAX_HDMI_PINS];
|
||||||
|
unsigned int channels_max; /* max over all cvts */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non-generic ATI/NVIDIA specific
|
* 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,
|
hda_nid_t pin_nid,
|
||||||
bool non_pcm,
|
bool non_pcm,
|
||||||
int ca)
|
int ca)
|
||||||
|
@ -588,6 +592,136 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
||||||
hdmi_debug_channel_mapping(codec, pin_nid);
|
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
|
* Audio InfoFrame routines
|
||||||
|
@ -726,7 +860,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
|
||||||
if (!eld->monitor_present)
|
if (!eld->monitor_present)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!non_pcm && per_pin->chmap_set)
|
||||||
|
ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
|
||||||
|
else
|
||||||
ca = hdmi_channel_allocation(eld, channels);
|
ca = hdmi_channel_allocation(eld, channels);
|
||||||
|
if (ca < 0)
|
||||||
|
ca = 0;
|
||||||
|
|
||||||
memset(&ai, 0, sizeof(ai));
|
memset(&ai, 0, sizeof(ai));
|
||||||
if (eld->conn_type == 0) { /* HDMI */
|
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=%d channels=%d\n",
|
||||||
pin_nid,
|
pin_nid,
|
||||||
channels);
|
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_stop_infoframe_trans(codec, pin_nid);
|
||||||
hdmi_fill_audio_infoframe(codec, pin_nid,
|
hdmi_fill_audio_infoframe(codec, pin_nid,
|
||||||
ai.bytes, sizeof(ai));
|
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
|
/* For non-pcm audio switch, setup new channel mapping
|
||||||
* accordingly */
|
* accordingly */
|
||||||
if (per_pin->non_pcm != non_pcm)
|
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;
|
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->cvt_nid = cvt_nid;
|
||||||
per_cvt->channels_min = 2;
|
per_cvt->channels_min = 2;
|
||||||
if (chans <= 16)
|
if (chans <= 16) {
|
||||||
per_cvt->channels_max = chans;
|
per_cvt->channels_max = chans;
|
||||||
|
if (chans > spec->channels_max)
|
||||||
|
spec->channels_max = chans;
|
||||||
|
}
|
||||||
|
|
||||||
err = snd_hda_query_supported_pcm(codec, cvt_nid,
|
err = snd_hda_query_supported_pcm(codec, cvt_nid,
|
||||||
&per_cvt->rates,
|
&per_cvt->rates,
|
||||||
|
@ -1250,7 +1394,10 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
|
||||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||||
pinctl & ~PIN_OUT);
|
pinctl & ~PIN_OUT);
|
||||||
snd_hda_spdif_ctls_unassign(codec, pin_idx);
|
snd_hda_spdif_ctls_unassign(codec, pin_idx);
|
||||||
|
per_pin->chmap_set = false;
|
||||||
|
memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1261,6 +1408,135 @@ static const struct hda_pcm_ops generic_ops = {
|
||||||
.cleanup = generic_hdmi_playback_pcm_cleanup,
|
.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++) {
|
||||||
|
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)
|
static int generic_hdmi_build_pcms(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct hdmi_spec *spec = codec->spec;
|
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 = &spec->pcm_rec[pin_idx];
|
||||||
info->name = get_hdmi_pcm_name(pin_idx);
|
info->name = get_hdmi_pcm_name(pin_idx);
|
||||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||||
|
info->own_chmap = true;
|
||||||
|
|
||||||
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
|
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
|
||||||
pstr->substreams = 1;
|
pstr->substreams = 1;
|
||||||
|
@ -1330,6 +1607,27 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
|
||||||
hdmi_present_sense(per_pin, 0);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1857,6 +2155,43 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
||||||
return 0;
|
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)
|
static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct hdmi_spec *spec;
|
struct hdmi_spec *spec;
|
||||||
|
@ -1867,6 +2202,8 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
|
||||||
spec->multiout.max_channels = 8;
|
spec->multiout.max_channels = 8;
|
||||||
spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
|
spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
|
||||||
codec->patch_ops.init = nvhdmi_7x_init_8ch;
|
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
|
/* Initialize the audio infoframe channel mask and checksum to something
|
||||||
* valid */
|
* valid */
|
||||||
|
|
|
@ -2287,6 +2287,8 @@ static int alc_build_pcms(struct hda_codec *codec)
|
||||||
p = &alc_pcm_analog_playback;
|
p = &alc_pcm_analog_playback;
|
||||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
|
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].nid = spec->multiout.dac_nids[0];
|
||||||
|
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
|
||||||
|
spec->multiout.max_channels;
|
||||||
}
|
}
|
||||||
if (spec->adc_nids) {
|
if (spec->adc_nids) {
|
||||||
p = spec->stream_analog_capture;
|
p = spec->stream_analog_capture;
|
||||||
|
|
|
@ -1541,6 +1541,26 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
|
||||||
snd_dma_pci_data(chip->pci),
|
snd_dma_pci_data(chip->pci),
|
||||||
rec->prealloc_size, rec->prealloc_max_size);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
static int __devinit snd_via8233_pcm_new(struct via82xx *chip)
|
||||||
{
|
{
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
|
struct snd_pcm_chmap *chmap;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
chip->playback_devno = 0; /* x 4 */
|
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),
|
snd_dma_pci_data(chip->pci),
|
||||||
64*1024, VIA_MAX_BUFSIZE);
|
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 */
|
/* PCM #1: multi-channel playback and 2nd capture */
|
||||||
err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
|
err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
|
||||||
if (err < 0)
|
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_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
|
||||||
snd_dma_pci_data(chip->pci),
|
snd_dma_pci_data(chip->pci),
|
||||||
64*1024, VIA_MAX_BUFSIZE);
|
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;
|
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)
|
static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
|
||||||
{
|
{
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
|
struct snd_pcm_chmap *chmap;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
chip->multi_devno = 0;
|
chip->multi_devno = 0;
|
||||||
|
@ -1519,6 +1535,13 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
|
||||||
snd_dma_pci_data(chip->pci),
|
snd_dma_pci_data(chip->pci),
|
||||||
64*1024, VIA_MAX_BUFSIZE);
|
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? */
|
/* SPDIF supported? */
|
||||||
if (! ac97_can_spdif(chip->ac97))
|
if (! ac97_can_spdif(chip->ac97))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -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_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||||
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
|
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)
|
if (rpcm)
|
||||||
*rpcm = pcm;
|
*rpcm = pcm;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1257,6 +1262,14 @@ static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
|
||||||
.pointer = snd_ymfpci_playback_pointer,
|
.pointer = snd_ymfpci_playback_pointer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct snd_pcm_chmap_elem surround_map[] = {
|
||||||
|
{ .channels = 1,
|
||||||
|
.map = { SNDRV_CHMAP_MONO } },
|
||||||
|
{ .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)
|
int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
||||||
{
|
{
|
||||||
struct snd_pcm *pcm;
|
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_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||||
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
|
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)
|
if (rpcm)
|
||||||
*rpcm = pcm;
|
*rpcm = pcm;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue