Merge branch 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa

* 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa: (148 commits)
  [ALSA] intel8x0m - Free irq in suspend
  [ALSA] Move CONFIG_SND_AC97_POWER_SAVE to pci/Kconfig
  [ALSA] usb-audio: add mixer control names for the Aureon 5.1 MkII
  [ALSA] ES1938: remove duplicate field initialization
  [ALSA] usb-audio: increase number of packets per URB
  [ALSA] hda-codec - Fix headphone auto-toggle on sigmatel codec
  [ALSA] hda-intel - A slight cleanup of timeout check in azx_get_response()
  [ALSA] hda-codec - Fix mic input with STAC92xx codecs
  [ALSA] mixart: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] gus: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] opl4: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] sound core: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] hda-codec - Support multiple headphone pins
  [ALSA] hda_intel prefer 24bit instead of 20bit
  [ALSA] hda-codec - Add vendor ids for Motorola and Conexant
  [ALSA] hda-codec - Add device id for Motorola si3054-compatible codec
  [ALSA] Add missing compat ioctls for ALSA control API
  [ALSA] powermac - Fix Oops when conflicting with aoa driver
  [ALSA] aoa: add locking to tas codec
  [ALSA] hda-intel - Fix suspend/resume with MSI
  ...
This commit is contained in:
Linus Torvalds 2006-09-23 17:21:12 -07:00
commit f7425b160d
111 changed files with 5359 additions and 1840 deletions

View File

@ -758,6 +758,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size) position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
single_cmd - Use single immediate commands to communicate with single_cmd - Use single immediate commands to communicate with
codecs (for debugging only) codecs (for debugging only)
disable_msi - Disable Message Signaled Interrupt (MSI)
This module supports one card and autoprobe. This module supports one card and autoprobe.
@ -778,11 +779,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
6stack-digout 6-jack with a SPDIF out 6stack-digout 6-jack with a SPDIF out
w810 3-jack w810 3-jack
z71v 3-jack (HP shared SPDIF) z71v 3-jack (HP shared SPDIF)
asus 3-jack asus 3-jack (ASUS Mobo)
asus-w1v ASUS W1V
asus-dig ASUS with SPDIF out
asus-dig2 ASUS with SPDIF out (using GPIO2)
uniwill 3-jack uniwill 3-jack
F1734 2-jack F1734 2-jack
lg LG laptop (m1 express dual) lg LG laptop (m1 express dual)
lg-lw LG LW20 laptop lg-lw LG LW20/LW25 laptop
tcl TCL S700
clevo Clevo laptops (m520G, m665n)
test for testing/debugging purpose, almost all controls can be test for testing/debugging purpose, almost all controls can be
adjusted. Appearing only when compiled with adjusted. Appearing only when compiled with
$CONFIG_SND_DEBUG=y $CONFIG_SND_DEBUG=y
@ -790,6 +796,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
ALC260 ALC260
hp HP machines hp HP machines
hp-3013 HP machines (3013-variant)
fujitsu Fujitsu S7020 fujitsu Fujitsu S7020
acer Acer TravelMate acer Acer TravelMate
basic fixed pin assignment (old default model) basic fixed pin assignment (old default model)
@ -797,24 +804,32 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
ALC262 ALC262
fujitsu Fujitsu Laptop fujitsu Fujitsu Laptop
hp-bpc HP xw4400/6400/8400/9400 laptops
benq Benq ED8
basic fixed pin assignment w/o SPDIF basic fixed pin assignment w/o SPDIF
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
ALC882/885 ALC882/885
3stack-dig 3-jack with SPDIF I/O 3stack-dig 3-jack with SPDIF I/O
6stck-dig 6-jack digital with SPDIF I/O 6stck-dig 6-jack digital with SPDIF I/O
arima Arima W820Di1
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
ALC883/888 ALC883/888
3stack-dig 3-jack with SPDIF I/O 3stack-dig 3-jack with SPDIF I/O
6stack-dig 6-jack digital with SPDIF I/O 6stack-dig 6-jack digital with SPDIF I/O
6stack-dig-demo 6-stack digital for Intel demo board 3stack-6ch 3-jack 6-channel
3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
6stack-dig-demo 6-jack digital for Intel demo board
acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
ALC861/660 ALC861/660
3stack 3-jack 3stack 3-jack
3stack-dig 3-jack with SPDIF I/O 3stack-dig 3-jack with SPDIF I/O
6stack-dig 6-jack with SPDIF I/O 6stack-dig 6-jack with SPDIF I/O
3stack-660 3-jack (for ALC660)
uniwill-m31 Uniwill M31 laptop
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
CMI9880 CMI9880
@ -843,10 +858,21 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
3stack-dig ditto with SPDIF 3stack-dig ditto with SPDIF
laptop 3-jack with hp-jack automute laptop 3-jack with hp-jack automute
laptop-dig ditto with SPDIF laptop-dig ditto with SPDIF
auto auto-confgi reading BIOS (default) auto auto-config reading BIOS (default)
STAC7661(?) STAC9200/9205/9220/9221/9254
ref Reference board
3stack D945 3stack
5stack D945 5stack + SPDIF
STAC9227/9228/9229/927x
ref Reference board
3stack D965 3stack
5stack D965 5stack + SPDIF
STAC9872
vaio Setup for VAIO FE550G/SZ110 vaio Setup for VAIO FE550G/SZ110
vaio-ar Setup for VAIO AR
If the default configuration doesn't work and one of the above If the default configuration doesn't work and one of the above
matches with your device, report it together with the PCI matches with your device, report it together with the PCI
@ -1213,6 +1239,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module supports only 1 card. This module has no enable option. Module supports only 1 card. This module has no enable option.
Module snd-mts64
----------------
Module for Ego Systems (ESI) Miditerminal 4140
This module supports multiple devices.
Requires parport (CONFIG_PARPORT).
Module snd-nm256 Module snd-nm256
---------------- ----------------

View File

@ -1054,9 +1054,8 @@
<para> <para>
For a device which allows hotplugging, you can use For a device which allows hotplugging, you can use
<function>snd_card_free_in_thread</function>. This one will <function>snd_card_free_when_closed</function>. This one will
postpone the destruction and wait in a kernel-thread until all postpone the destruction until all devices are closed.
devices are closed.
</para> </para>
</section> </section>

View File

@ -27,6 +27,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/workqueue.h>
#include "pcm.h" #include "pcm.h"
#include "control.h" #include "control.h"
#include "info.h" #include "info.h"
@ -140,6 +141,20 @@
#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ #define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */
#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ #define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */
/* powerdown bits */
#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */
#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */
#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */
#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */
#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */
#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */
#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */
#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */
#define AC97_PD_PR4 0x1000 /* Power down AC-Link */
#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */
#define AC97_PD_PR6 0x4000 /* Headphone amplifier */
#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */
/* extended audio ID bit defines */ /* extended audio ID bit defines */
#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ #define AC97_EI_VRA 0x0001 /* Variable bit rate supported */
#define AC97_EI_DRA 0x0002 /* Double rate supported */ #define AC97_EI_DRA 0x0002 /* Double rate supported */
@ -359,6 +374,7 @@
#define AC97_SCAP_INV_EAPD (1<<7) /* inverted EAPD */ #define AC97_SCAP_INV_EAPD (1<<7) /* inverted EAPD */
#define AC97_SCAP_DETECT_BY_VENDOR (1<<8) /* use vendor registers for read tests */ #define AC97_SCAP_DETECT_BY_VENDOR (1<<8) /* use vendor registers for read tests */
#define AC97_SCAP_NO_SPDIF (1<<9) /* don't build SPDIF controls */ #define AC97_SCAP_NO_SPDIF (1<<9) /* don't build SPDIF controls */
#define AC97_SCAP_EAPD_LED (1<<10) /* EAPD as mute LED */
/* ac97->flags */ /* ac97->flags */
#define AC97_HAS_PC_BEEP (1<<0) /* force PC Speaker usage */ #define AC97_HAS_PC_BEEP (1<<0) /* force PC Speaker usage */
@ -491,6 +507,12 @@ struct snd_ac97 {
/* jack-sharing info */ /* jack-sharing info */
unsigned char indep_surround; unsigned char indep_surround;
unsigned char channel_mode; unsigned char channel_mode;
#ifdef CONFIG_SND_AC97_POWER_SAVE
unsigned int power_up; /* power states */
struct workqueue_struct *power_workq;
struct work_struct power_work;
#endif
struct device dev; struct device dev;
}; };
@ -532,6 +554,15 @@ unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg);
void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value); void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);
int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value); int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);
int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value); int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value);
#ifdef CONFIG_SND_AC97_POWER_SAVE
int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup);
#else
static inline int snd_ac97_update_power(struct snd_ac97 *ac97, int reg,
int powerup)
{
return 0;
}
#endif
#ifdef CONFIG_PM #ifdef CONFIG_PM
void snd_ac97_suspend(struct snd_ac97 *ac97); void snd_ac97_suspend(struct snd_ac97 *ac97);
void snd_ac97_resume(struct snd_ac97 *ac97); void snd_ac97_resume(struct snd_ac97 *ac97);
@ -583,6 +614,7 @@ struct ac97_pcm {
copy_flag: 1, /* lowlevel driver must fill all entries */ copy_flag: 1, /* lowlevel driver must fill all entries */
spdif: 1; /* spdif pcm */ spdif: 1; /* spdif pcm */
unsigned short aslots; /* active slots */ unsigned short aslots; /* active slots */
unsigned short cur_dbl; /* current double-rate state */
unsigned int rates; /* available rates */ unsigned int rates; /* available rates */
struct { struct {
unsigned short slots; /* driver input: requested AC97 slot numbers */ unsigned short slots; /* driver input: requested AC97 slot numbers */

View File

@ -179,14 +179,13 @@ enum { AD1848_MIX_SINGLE, AD1848_MIX_DOUBLE, AD1848_MIX_CAPTURE };
#define AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) \ #define AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) \
((left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22)) ((left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22))
int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int type, unsigned long value);
/* for ease of use */ /* for ease of use */
struct ad1848_mix_elem { struct ad1848_mix_elem {
const char *name; const char *name;
int index; int index;
int type; int type;
unsigned long private_value; unsigned long private_value;
unsigned int *tlv;
}; };
#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ #define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
@ -195,15 +194,26 @@ struct ad1848_mix_elem {
.type = AD1848_MIX_SINGLE, \ .type = AD1848_MIX_SINGLE, \
.private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) } .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) }
#define AD1848_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
{ .name = xname, \
.index = xindex, \
.type = AD1848_MIX_SINGLE, \
.private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert), \
.tlv = xtlv }
#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ #define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
{ .name = xname, \ { .name = xname, \
.index = xindex, \ .index = xindex, \
.type = AD1848_MIX_DOUBLE, \ .type = AD1848_MIX_DOUBLE, \
.private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) } .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) }
static inline int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, const struct ad1848_mix_elem *c) #define AD1848_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
{ { .name = xname, \
return snd_ad1848_add_ctl(chip, c->name, c->index, c->type, c->private_value); .index = xindex, \
} .type = AD1848_MIX_DOUBLE, \
.private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert), \
.tlv = xtlv }
int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, const struct ad1848_mix_elem *c);
#endif /* __SOUND_AD1848_H */ #endif /* __SOUND_AD1848_H */

View File

@ -39,26 +39,39 @@ struct snd_ak4xxx_ops {
#define AK4XXX_IMAGE_SIZE (AK4XXX_MAX_CHIPS * 16) /* 64 bytes */ #define AK4XXX_IMAGE_SIZE (AK4XXX_MAX_CHIPS * 16) /* 64 bytes */
/* DAC label and channels */
struct snd_akm4xxx_dac_channel {
char *name; /* mixer volume name */
unsigned int num_channels;
};
/* ADC labels and channels */
struct snd_akm4xxx_adc_channel {
char *name; /* capture gain volume label */
char *switch_name; /* capture switch */
unsigned int num_channels;
};
struct snd_akm4xxx { struct snd_akm4xxx {
struct snd_card *card; struct snd_card *card;
unsigned int num_adcs; /* AK4524 or AK4528 ADCs */ unsigned int num_adcs; /* AK4524 or AK4528 ADCs */
unsigned int num_dacs; /* AK4524 or AK4528 DACs */ unsigned int num_dacs; /* AK4524 or AK4528 DACs */
unsigned char images[AK4XXX_IMAGE_SIZE]; /* saved register image */ unsigned char images[AK4XXX_IMAGE_SIZE]; /* saved register image */
unsigned char ipga_gain[AK4XXX_MAX_CHIPS][2]; /* saved register image unsigned char volumes[AK4XXX_IMAGE_SIZE]; /* saved volume values */
* for IPGA (AK4528)
*/
unsigned long private_value[AK4XXX_MAX_CHIPS]; /* helper for driver */ unsigned long private_value[AK4XXX_MAX_CHIPS]; /* helper for driver */
void *private_data[AK4XXX_MAX_CHIPS]; /* helper for driver */ void *private_data[AK4XXX_MAX_CHIPS]; /* helper for driver */
/* template should fill the following fields */ /* template should fill the following fields */
unsigned int idx_offset; /* control index offset */ unsigned int idx_offset; /* control index offset */
enum { enum {
SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4524, SND_AK4528, SND_AK4529,
SND_AK4355, SND_AK4358, SND_AK4381 SND_AK4355, SND_AK4358, SND_AK4381,
SND_AK5365
} type; } type;
unsigned int *num_stereo; /* array of combined counts
* for the mixer /* (array) information of combined codecs */
*/ struct snd_akm4xxx_dac_channel *dac_info;
char **channel_names; /* array of mixer channel names */ struct snd_akm4xxx_adc_channel *adc_info;
struct snd_ak4xxx_ops ops; struct snd_ak4xxx_ops ops;
}; };
@ -72,9 +85,9 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak);
(ak)->images[(chip) * 16 + (reg)] (ak)->images[(chip) * 16 + (reg)]
#define snd_akm4xxx_set(ak,chip,reg,val) \ #define snd_akm4xxx_set(ak,chip,reg,val) \
((ak)->images[(chip) * 16 + (reg)] = (val)) ((ak)->images[(chip) * 16 + (reg)] = (val))
#define snd_akm4xxx_get_ipga(ak,chip,reg) \ #define snd_akm4xxx_get_vol(ak,chip,reg) \
(ak)->ipga_gain[chip][(reg)-4] (ak)->volumes[(chip) * 16 + (reg)]
#define snd_akm4xxx_set_ipga(ak,chip,reg,val) \ #define snd_akm4xxx_set_vol(ak,chip,reg,val) \
((ak)->ipga_gain[chip][(reg)-4] = (val)) ((ak)->volumes[(chip) * 16 + (reg)] = (val))
#endif /* __SOUND_AK4XXX_ADDA_H */ #endif /* __SOUND_AK4XXX_ADDA_H */

View File

@ -688,7 +688,7 @@ struct snd_timer_tread {
* * * *
****************************************************************************/ ****************************************************************************/
#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 3) #define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4)
struct snd_ctl_card_info { struct snd_ctl_card_info {
int card; /* card number */ int card; /* card number */
@ -727,10 +727,15 @@ typedef int __bitwise snd_ctl_elem_iface_t;
#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) #define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1)
#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) #define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ #define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */
#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<2) /* when was control changed */ #define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3) /* when was control changed */
#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) /* TLV read is possible */
#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) /* TLV write is possible */
#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6) /* TLV command is possible */
#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ #define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */
#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ #define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */
#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ #define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */
#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28) /* kernel use a TLV callback */
#define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) /* user space element */ #define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) /* user space element */
#define SNDRV_CTL_ELEM_ACCESS_DINDIRECT (1<<30) /* indirect access for matrix dimensions in the info structure */ #define SNDRV_CTL_ELEM_ACCESS_DINDIRECT (1<<30) /* indirect access for matrix dimensions in the info structure */
#define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access for element value in the value structure */ #define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access for element value in the value structure */
@ -818,6 +823,12 @@ struct snd_ctl_elem_value {
unsigned char reserved[128-sizeof(struct timespec)]; unsigned char reserved[128-sizeof(struct timespec)];
}; };
struct snd_ctl_tlv {
unsigned int numid; /* control element numeric identification */
unsigned int length; /* in bytes aligned to 4 */
unsigned int tlv[0]; /* first TLV */
};
enum { enum {
SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int),
SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info), SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info),
@ -831,6 +842,9 @@ enum {
SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info), SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info),
SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info), SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info),
SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id), SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id),
SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct snd_ctl_tlv),
SNDRV_CTL_IOCTL_TLV_WRITE = _IOWR('U', 0x1b, struct snd_ctl_tlv),
SNDRV_CTL_IOCTL_TLV_COMMAND = _IOWR('U', 0x1c, struct snd_ctl_tlv),
SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int),
SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info), SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info),
SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int),
@ -855,6 +869,7 @@ enum sndrv_ctl_event_type {
#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */ #define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */
#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */ #define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */
#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */ #define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */
#define SNDRV_CTL_EVENT_MASK_TLV (1<<3) /* element TLV tree was changed */
#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */ #define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */
struct snd_ctl_event { struct snd_ctl_event {

View File

@ -30,6 +30,11 @@ struct snd_kcontrol;
typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo);
typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol,
int op_flag, /* 0=read,1=write,-1=command */
unsigned int size,
unsigned int __user *tlv);
struct snd_kcontrol_new { struct snd_kcontrol_new {
snd_ctl_elem_iface_t iface; /* interface identifier */ snd_ctl_elem_iface_t iface; /* interface identifier */
@ -42,6 +47,10 @@ struct snd_kcontrol_new {
snd_kcontrol_info_t *info; snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get; snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put; snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
unsigned int *p;
} tlv;
unsigned long private_value; unsigned long private_value;
}; };
@ -58,6 +67,10 @@ struct snd_kcontrol {
snd_kcontrol_info_t *info; snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get; snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put; snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
unsigned int *p;
} tlv;
unsigned long private_value; unsigned long private_value;
void *private_data; void *private_data;
void (*private_free)(struct snd_kcontrol *kcontrol); void (*private_free)(struct snd_kcontrol *kcontrol);

View File

@ -25,8 +25,8 @@
#include <linux/sched.h> /* wake_up() */ #include <linux/sched.h> /* wake_up() */
#include <linux/mutex.h> /* struct mutex */ #include <linux/mutex.h> /* struct mutex */
#include <linux/rwsem.h> /* struct rw_semaphore */ #include <linux/rwsem.h> /* struct rw_semaphore */
#include <linux/workqueue.h> /* struct workqueue_struct */
#include <linux/pm.h> /* pm_message_t */ #include <linux/pm.h> /* pm_message_t */
#include <linux/device.h>
/* forward declarations */ /* forward declarations */
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
@ -71,7 +71,6 @@ struct snd_device_ops {
int (*dev_free)(struct snd_device *dev); int (*dev_free)(struct snd_device *dev);
int (*dev_register)(struct snd_device *dev); int (*dev_register)(struct snd_device *dev);
int (*dev_disconnect)(struct snd_device *dev); int (*dev_disconnect)(struct snd_device *dev);
int (*dev_unregister)(struct snd_device *dev);
}; };
struct snd_device { struct snd_device {
@ -131,8 +130,8 @@ struct snd_card {
state */ state */
spinlock_t files_lock; /* lock the files for this card */ spinlock_t files_lock; /* lock the files for this card */
int shutdown; /* this card is going down */ int shutdown; /* this card is going down */
int free_on_last_close; /* free in context of file_release */
wait_queue_head_t shutdown_sleep; wait_queue_head_t shutdown_sleep;
struct work_struct free_workq; /* for free in workqueue */
struct device *dev; struct device *dev;
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -188,6 +187,7 @@ struct snd_minor {
int device; /* device number */ int device; /* device number */
const struct file_operations *f_ops; /* file operations */ const struct file_operations *f_ops; /* file operations */
void *private_data; /* private data for f_ops->open */ void *private_data; /* private data for f_ops->open */
struct class_device *class_dev; /* class device for sysfs */
}; };
/* sound.c */ /* sound.c */
@ -202,6 +202,8 @@ int snd_register_device(int type, struct snd_card *card, int dev,
const char *name); const char *name);
int snd_unregister_device(int type, struct snd_card *card, int dev); int snd_unregister_device(int type, struct snd_card *card, int dev);
void *snd_lookup_minor_data(unsigned int minor, int type); void *snd_lookup_minor_data(unsigned int minor, int type);
int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
const struct class_device_attribute *attr);
#ifdef CONFIG_SND_OSSEMUL #ifdef CONFIG_SND_OSSEMUL
int snd_register_oss_device(int type, struct snd_card *card, int dev, int snd_register_oss_device(int type, struct snd_card *card, int dev,
@ -244,7 +246,7 @@ struct snd_card *snd_card_new(int idx, const char *id,
struct module *module, int extra_size); struct module *module, int extra_size);
int snd_card_disconnect(struct snd_card *card); int snd_card_disconnect(struct snd_card *card);
int snd_card_free(struct snd_card *card); int snd_card_free(struct snd_card *card);
int snd_card_free_in_thread(struct snd_card *card); int snd_card_free_when_closed(struct snd_card *card);
int snd_card_register(struct snd_card *card); int snd_card_register(struct snd_card *card);
int snd_card_info_init(void); int snd_card_info_init(void);
int snd_card_info_done(void); int snd_card_info_done(void);

View File

@ -1524,6 +1524,10 @@ struct snd_emu10k1_fx8010_control_gpr {
unsigned int value[32]; /* initial values */ unsigned int value[32]; /* initial values */
unsigned int min; /* minimum range */ unsigned int min; /* minimum range */
unsigned int max; /* maximum range */ unsigned int max; /* maximum range */
union {
snd_kcontrol_tlv_rw_t *c;
unsigned int *p;
} tlv;
unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */ unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */
}; };

View File

@ -71,7 +71,6 @@ struct snd_info_entry {
mode_t mode; mode_t mode;
long size; long size;
unsigned short content; unsigned short content;
unsigned short disconnected: 1;
union { union {
struct snd_info_entry_text text; struct snd_info_entry_text text;
struct snd_info_entry_ops *ops; struct snd_info_entry_ops *ops;
@ -83,6 +82,8 @@ struct snd_info_entry {
void (*private_free)(struct snd_info_entry *entry); void (*private_free)(struct snd_info_entry *entry);
struct proc_dir_entry *p; struct proc_dir_entry *p;
struct mutex access; struct mutex access;
struct list_head children;
struct list_head list;
}; };
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
@ -122,8 +123,8 @@ int snd_info_restore_text(struct snd_info_entry * entry);
int snd_info_card_create(struct snd_card * card); int snd_info_card_create(struct snd_card * card);
int snd_info_card_register(struct snd_card * card); int snd_info_card_register(struct snd_card * card);
int snd_info_card_free(struct snd_card * card); int snd_info_card_free(struct snd_card * card);
void snd_info_card_disconnect(struct snd_card * card);
int snd_info_register(struct snd_info_entry * entry); int snd_info_register(struct snd_info_entry * entry);
int snd_info_unregister(struct snd_info_entry * entry);
/* for card drivers */ /* for card drivers */
int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp); int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp);
@ -156,8 +157,8 @@ static inline void snd_info_free_entry(struct snd_info_entry * entry) { ; }
static inline int snd_info_card_create(struct snd_card * card) { return 0; } static inline int snd_info_card_create(struct snd_card * card) { return 0; }
static inline int snd_info_card_register(struct snd_card * card) { return 0; } static inline int snd_info_card_register(struct snd_card * card) { return 0; }
static inline int snd_info_card_free(struct snd_card * card) { return 0; } static inline int snd_info_card_free(struct snd_card * card) { return 0; }
static inline void snd_info_card_disconnect(struct snd_card * card) { }
static inline int snd_info_register(struct snd_info_entry * entry) { return 0; } static inline int snd_info_register(struct snd_info_entry * entry) { return 0; }
static inline int snd_info_unregister(struct snd_info_entry * entry) { return 0; }
static inline int snd_card_proc_new(struct snd_card *card, const char *name, static inline int snd_card_proc_new(struct snd_card *card, const char *name,
struct snd_info_entry **entryp) { return -EINVAL; } struct snd_info_entry **entryp) { return -EINVAL; }

View File

@ -190,7 +190,7 @@ struct snd_pcm_ops {
struct snd_pcm_file { struct snd_pcm_file {
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
struct snd_pcm_file *next; int no_compat_mmap;
}; };
struct snd_pcm_hw_rule; struct snd_pcm_hw_rule;
@ -384,7 +384,6 @@ struct snd_pcm_substream {
struct snd_info_entry *proc_prealloc_entry; struct snd_info_entry *proc_prealloc_entry;
#endif #endif
/* misc flags */ /* misc flags */
unsigned int no_mmap_ctrl: 1;
unsigned int hw_opened: 1; unsigned int hw_opened: 1;
}; };
@ -402,7 +401,6 @@ struct snd_pcm_str {
/* -- OSS things -- */ /* -- OSS things -- */
struct snd_pcm_oss_stream oss; struct snd_pcm_oss_stream oss;
#endif #endif
struct snd_pcm_file *files;
#ifdef CONFIG_SND_VERBOSE_PROCFS #ifdef CONFIG_SND_VERBOSE_PROCFS
struct snd_info_entry *proc_root; struct snd_info_entry *proc_root;
struct snd_info_entry *proc_info_entry; struct snd_info_entry *proc_info_entry;

View File

@ -129,7 +129,6 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer); int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer);
int snd_timer_global_free(struct snd_timer *timer); int snd_timer_global_free(struct snd_timer *timer);
int snd_timer_global_register(struct snd_timer *timer); int snd_timer_global_register(struct snd_timer *timer);
int snd_timer_global_unregister(struct snd_timer *timer);
int snd_timer_open(struct snd_timer_instance **ti, char *owner, struct snd_timer_id *tid, unsigned int slave_id); int snd_timer_open(struct snd_timer_instance **ti, char *owner, struct snd_timer_id *tid, unsigned int slave_id);
int snd_timer_close(struct snd_timer_instance *timeri); int snd_timer_close(struct snd_timer_instance *timeri);

60
include/sound/tlv.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef __SOUND_TLV_H
#define __SOUND_TLV_H
/*
* Advanced Linux Sound Architecture - ALSA - Driver
* Copyright (c) 2006 by Jaroslav Kysela <perex@suse.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* TLV structure is right behind the struct snd_ctl_tlv:
* unsigned int type - see SNDRV_CTL_TLVT_*
* unsigned int length
* .... data aligned to sizeof(unsigned int), use
* block_length = (length + (sizeof(unsigned int) - 1)) &
* ~(sizeof(unsigned int) - 1)) ....
*/
#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */
#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
#define TLV_DB_SCALE_ITEM(min, step, mute) \
SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
(min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0)
#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
/* linear volume between min_dB and max_dB (.01dB unit) */
#define TLV_DB_LINEAR_ITEM(min_dB, max_dB) \
SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \
(min_dB), (max_dB)
#define DECLARE_TLV_DB_LINEAR(name, min_dB, max_dB) \
unsigned int name[] = { TLV_DB_LINEAR_ITEM(min_dB, max_dB) }
/* dB range container */
/* Each item is: <min> <max> <TLV> */
/* The below assumes that each item TLV is 4 words like DB_SCALE or LINEAR */
#define TLV_DB_RANGE_HEAD(num) \
SNDRV_CTL_TLVT_DB_RANGE, 6 * (num) * sizeof(unsigned int)
#define TLV_DB_GAIN_MUTE -9999999
#endif /* __SOUND_TLV_H */

View File

@ -128,6 +128,7 @@ struct snd_vx_hardware {
unsigned int num_ins; unsigned int num_ins;
unsigned int num_outs; unsigned int num_outs;
unsigned int output_level_max; unsigned int output_level_max;
unsigned int *output_level_db_scale;
}; };
/* hwdep id string */ /* hwdep id string */

View File

@ -1,6 +1,8 @@
config SND_AOA_ONYX config SND_AOA_ONYX
tristate "support Onyx chip" tristate "support Onyx chip"
depends on SND_AOA depends on SND_AOA
select I2C
select I2C_POWERMAC
---help--- ---help---
This option enables support for the Onyx (pcm3052) This option enables support for the Onyx (pcm3052)
codec chip found in the latest Apple machines codec chip found in the latest Apple machines
@ -18,6 +20,8 @@ config SND_AOA_ONYX
config SND_AOA_TAS config SND_AOA_TAS
tristate "support TAS chips" tristate "support TAS chips"
depends on SND_AOA depends on SND_AOA
select I2C
select I2C_POWERMAC
---help--- ---help---
This option enables support for the tas chips This option enables support for the tas chips
found in a lot of Apple Machines, especially found in a lot of Apple Machines, especially

View File

@ -66,6 +66,8 @@
#include <asm/prom.h> #include <asm/prom.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("tas codec driver for snd-aoa"); MODULE_DESCRIPTION("tas codec driver for snd-aoa");
@ -91,6 +93,10 @@ struct tas {
u8 bass, treble; u8 bass, treble;
u8 acr; u8 acr;
int drc_range; int drc_range;
/* protects hardware access against concurrency from
* userspace when hitting controls and during
* codec init/suspend/resume */
struct mutex mtx;
}; };
static int tas_reset_init(struct tas *tas); static int tas_reset_init(struct tas *tas);
@ -231,8 +237,10 @@ static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->cached_volume_l; ucontrol->value.integer.value[0] = tas->cached_volume_l;
ucontrol->value.integer.value[1] = tas->cached_volume_r; ucontrol->value.integer.value[1] = tas->cached_volume_r;
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -241,14 +249,18 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
if (tas->cached_volume_l == ucontrol->value.integer.value[0] if (tas->cached_volume_l == ucontrol->value.integer.value[0]
&& tas->cached_volume_r == ucontrol->value.integer.value[1]) && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
tas->cached_volume_l = ucontrol->value.integer.value[0]; tas->cached_volume_l = ucontrol->value.integer.value[0];
tas->cached_volume_r = ucontrol->value.integer.value[1]; tas->cached_volume_r = ucontrol->value.integer.value[1];
if (tas->hw_enabled) if (tas->hw_enabled)
tas_set_volume(tas); tas_set_volume(tas);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -276,8 +288,10 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = !tas->mute_l; ucontrol->value.integer.value[0] = !tas->mute_l;
ucontrol->value.integer.value[1] = !tas->mute_r; ucontrol->value.integer.value[1] = !tas->mute_r;
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -286,14 +300,18 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
if (tas->mute_l == !ucontrol->value.integer.value[0] if (tas->mute_l == !ucontrol->value.integer.value[0]
&& tas->mute_r == !ucontrol->value.integer.value[1]) && tas->mute_r == !ucontrol->value.integer.value[1]) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
tas->mute_l = !ucontrol->value.integer.value[0]; tas->mute_l = !ucontrol->value.integer.value[0];
tas->mute_r = !ucontrol->value.integer.value[1]; tas->mute_r = !ucontrol->value.integer.value[1];
if (tas->hw_enabled) if (tas->hw_enabled)
tas_set_volume(tas); tas_set_volume(tas);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -322,8 +340,10 @@ static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value; int idx = kcontrol->private_value;
mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->mixer_l[idx]; ucontrol->value.integer.value[0] = tas->mixer_l[idx];
ucontrol->value.integer.value[1] = tas->mixer_r[idx]; ucontrol->value.integer.value[1] = tas->mixer_r[idx];
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -334,15 +354,19 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value; int idx = kcontrol->private_value;
mutex_lock(&tas->mtx);
if (tas->mixer_l[idx] == ucontrol->value.integer.value[0] if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
&& tas->mixer_r[idx] == ucontrol->value.integer.value[1]) && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
tas->mixer_l[idx] = ucontrol->value.integer.value[0]; tas->mixer_l[idx] = ucontrol->value.integer.value[0];
tas->mixer_r[idx] = ucontrol->value.integer.value[1]; tas->mixer_r[idx] = ucontrol->value.integer.value[1];
if (tas->hw_enabled) if (tas->hw_enabled)
tas_set_mixer(tas); tas_set_mixer(tas);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -375,7 +399,9 @@ static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->drc_range; ucontrol->value.integer.value[0] = tas->drc_range;
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -384,12 +410,16 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
if (tas->drc_range == ucontrol->value.integer.value[0]) mutex_lock(&tas->mtx);
if (tas->drc_range == ucontrol->value.integer.value[0]) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
tas->drc_range = ucontrol->value.integer.value[0]; tas->drc_range = ucontrol->value.integer.value[0];
if (tas->hw_enabled) if (tas->hw_enabled)
tas3004_set_drc(tas); tas3004_set_drc(tas);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -417,7 +447,9 @@ static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->drc_enabled; ucontrol->value.integer.value[0] = tas->drc_enabled;
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -426,12 +458,16 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
if (tas->drc_enabled == ucontrol->value.integer.value[0]) mutex_lock(&tas->mtx);
if (tas->drc_enabled == ucontrol->value.integer.value[0]) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
tas->drc_enabled = ucontrol->value.integer.value[0]; tas->drc_enabled = ucontrol->value.integer.value[0];
if (tas->hw_enabled) if (tas->hw_enabled)
tas3004_set_drc(tas); tas3004_set_drc(tas);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -463,7 +499,9 @@ static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B); ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -471,15 +509,21 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
int oldacr = tas->acr; int oldacr;
mutex_lock(&tas->mtx);
oldacr = tas->acr;
tas->acr &= ~TAS_ACR_INPUT_B; tas->acr &= ~TAS_ACR_INPUT_B;
if (ucontrol->value.enumerated.item[0]) if (ucontrol->value.enumerated.item[0])
tas->acr |= TAS_ACR_INPUT_B; tas->acr |= TAS_ACR_INPUT_B;
if (oldacr == tas->acr) if (oldacr == tas->acr) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
if (tas->hw_enabled) if (tas->hw_enabled)
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -518,7 +562,9 @@ static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->treble; ucontrol->value.integer.value[0] = tas->treble;
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -527,12 +573,16 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
if (tas->treble == ucontrol->value.integer.value[0]) mutex_lock(&tas->mtx);
if (tas->treble == ucontrol->value.integer.value[0]) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
tas->treble = ucontrol->value.integer.value[0]; tas->treble = ucontrol->value.integer.value[0];
if (tas->hw_enabled) if (tas->hw_enabled)
tas_set_treble(tas); tas_set_treble(tas);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -560,7 +610,9 @@ static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->bass; ucontrol->value.integer.value[0] = tas->bass;
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -569,12 +621,16 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
{ {
struct tas *tas = snd_kcontrol_chip(kcontrol); struct tas *tas = snd_kcontrol_chip(kcontrol);
if (tas->bass == ucontrol->value.integer.value[0]) mutex_lock(&tas->mtx);
if (tas->bass == ucontrol->value.integer.value[0]) {
mutex_unlock(&tas->mtx);
return 0; return 0;
}
tas->bass = ucontrol->value.integer.value[0]; tas->bass = ucontrol->value.integer.value[0];
if (tas->hw_enabled) if (tas->hw_enabled)
tas_set_bass(tas); tas_set_bass(tas);
mutex_unlock(&tas->mtx);
return 1; return 1;
} }
@ -628,16 +684,16 @@ static int tas_reset_init(struct tas *tas)
tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT; tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp)) if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
return -ENODEV; goto outerr;
tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL | tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL |
TAS_ACR_B_MON_SEL_RIGHT; TAS_ACR_B_MON_SEL_RIGHT;
if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
return -ENODEV; goto outerr;
tmp = 0; tmp = 0;
if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp)) if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
return -ENODEV; goto outerr;
tas3004_set_drc(tas); tas3004_set_drc(tas);
@ -649,9 +705,11 @@ static int tas_reset_init(struct tas *tas)
tas->acr &= ~TAS_ACR_ANALOG_PDOWN; tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
return -ENODEV; goto outerr;
return 0; return 0;
outerr:
return -ENODEV;
} }
static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock) static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock)
@ -666,11 +724,13 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
break; break;
case CLOCK_SWITCH_SLAVE: case CLOCK_SWITCH_SLAVE:
/* Clocks are back, re-init the codec */ /* Clocks are back, re-init the codec */
mutex_lock(&tas->mtx);
tas_reset_init(tas); tas_reset_init(tas);
tas_set_volume(tas); tas_set_volume(tas);
tas_set_mixer(tas); tas_set_mixer(tas);
tas->hw_enabled = 1; tas->hw_enabled = 1;
tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio); tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
mutex_unlock(&tas->mtx);
break; break;
default: default:
/* doesn't happen as of now */ /* doesn't happen as of now */
@ -684,19 +744,23 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
* our i2c device is suspended, and then take note of that! */ * our i2c device is suspended, and then take note of that! */
static int tas_suspend(struct tas *tas) static int tas_suspend(struct tas *tas)
{ {
mutex_lock(&tas->mtx);
tas->hw_enabled = 0; tas->hw_enabled = 0;
tas->acr |= TAS_ACR_ANALOG_PDOWN; tas->acr |= TAS_ACR_ANALOG_PDOWN;
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
static int tas_resume(struct tas *tas) static int tas_resume(struct tas *tas)
{ {
/* reset codec */ /* reset codec */
mutex_lock(&tas->mtx);
tas_reset_init(tas); tas_reset_init(tas);
tas_set_volume(tas); tas_set_volume(tas);
tas_set_mixer(tas); tas_set_mixer(tas);
tas->hw_enabled = 1; tas->hw_enabled = 1;
mutex_unlock(&tas->mtx);
return 0; return 0;
} }
@ -739,11 +803,14 @@ static int tas_init_codec(struct aoa_codec *codec)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&tas->mtx);
if (tas_reset_init(tas)) { if (tas_reset_init(tas)) {
printk(KERN_ERR PFX "tas failed to initialise\n"); printk(KERN_ERR PFX "tas failed to initialise\n");
mutex_unlock(&tas->mtx);
return -ENXIO; return -ENXIO;
} }
tas->hw_enabled = 1; tas->hw_enabled = 1;
mutex_unlock(&tas->mtx);
if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
aoa_get_card(), aoa_get_card(),
@ -822,6 +889,7 @@ static int tas_create(struct i2c_adapter *adapter,
if (!tas) if (!tas)
return -ENOMEM; return -ENOMEM;
mutex_init(&tas->mtx);
tas->i2c.driver = &tas_driver; tas->i2c.driver = &tas_driver;
tas->i2c.adapter = adapter; tas->i2c.adapter = adapter;
tas->i2c.addr = addr; tas->i2c.addr = addr;
@ -850,6 +918,7 @@ static int tas_create(struct i2c_adapter *adapter,
detach: detach:
i2c_detach_client(&tas->i2c); i2c_detach_client(&tas->i2c);
fail: fail:
mutex_destroy(&tas->mtx);
kfree(tas); kfree(tas);
return -EINVAL; return -EINVAL;
} }
@ -908,6 +977,7 @@ static int tas_i2c_detach(struct i2c_client *client)
/* power down codec chip */ /* power down codec chip */
tas_write_reg(tas, TAS_REG_ACR, 1, &tmp); tas_write_reg(tas, TAS_REG_ACR, 1, &tmp);
mutex_destroy(&tas->mtx);
kfree(tas); kfree(tas);
return 0; return 0;
} }

View File

@ -75,6 +75,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ctl->change_sleep); init_waitqueue_head(&ctl->change_sleep);
spin_lock_init(&ctl->read_lock); spin_lock_init(&ctl->read_lock);
ctl->card = card; ctl->card = card;
ctl->prefer_pcm_subdevice = -1;
ctl->prefer_rawmidi_subdevice = -1;
ctl->pid = current->pid; ctl->pid = current->pid;
file->private_data = ctl; file->private_data = ctl;
write_lock_irqsave(&card->ctl_files_rwlock, flags); write_lock_irqsave(&card->ctl_files_rwlock, flags);
@ -236,11 +238,16 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
kctl.id.index = ncontrol->index; kctl.id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1; kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE| (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
SNDRV_CTL_ELEM_ACCESS_INDIRECT|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info = ncontrol->info; kctl.info = ncontrol->info;
kctl.get = ncontrol->get; kctl.get = ncontrol->get;
kctl.put = ncontrol->put; kctl.put = ncontrol->put;
kctl.tlv.p = ncontrol->tlv.p;
kctl.private_value = ncontrol->private_value; kctl.private_value = ncontrol->private_value;
kctl.private_data = private_data; kctl.private_data = private_data;
return snd_ctl_new(&kctl, access); return snd_ctl_new(&kctl, access);
@ -882,6 +889,8 @@ struct user_element {
struct snd_ctl_elem_info info; struct snd_ctl_elem_info info;
void *elem_data; /* element data */ void *elem_data; /* element data */
unsigned long elem_data_size; /* size of element data in bytes */ unsigned long elem_data_size; /* size of element data in bytes */
void *tlv_data; /* TLV data */
unsigned long tlv_data_size; /* TLV data size */
void *priv_data; /* private data (like strings for enumerated type) */ void *priv_data; /* private data (like strings for enumerated type) */
unsigned long priv_data_size; /* size of private data in bytes */ unsigned long priv_data_size; /* size of private data in bytes */
}; };
@ -916,9 +925,48 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
int op_flag,
unsigned int size,
unsigned int __user *tlv)
{
struct user_element *ue = kcontrol->private_data;
int change = 0;
void *new_data;
if (op_flag > 0) {
if (size > 1024 * 128) /* sane value */
return -EINVAL;
new_data = kmalloc(size, GFP_KERNEL);
if (new_data == NULL)
return -ENOMEM;
if (copy_from_user(new_data, tlv, size)) {
kfree(new_data);
return -EFAULT;
}
change = ue->tlv_data_size != size;
if (!change)
change = memcmp(ue->tlv_data, new_data, size);
kfree(ue->tlv_data);
ue->tlv_data = new_data;
ue->tlv_data_size = size;
} else {
if (! ue->tlv_data_size || ! ue->tlv_data)
return -ENXIO;
if (size < ue->tlv_data_size)
return -ENOSPC;
if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
return -EFAULT;
}
return change;
}
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
{ {
kfree(kcontrol->private_data); struct user_element *ue = kcontrol->private_data;
if (ue->tlv_data)
kfree(ue->tlv_data);
kfree(ue);
} }
static int snd_ctl_elem_add(struct snd_ctl_file *file, static int snd_ctl_elem_add(struct snd_ctl_file *file,
@ -937,7 +985,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
return -EINVAL; return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE)); SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
info->id.numid = 0; info->id.numid = 0;
memset(&kctl, 0, sizeof(kctl)); memset(&kctl, 0, sizeof(kctl));
down_write(&card->controls_rwsem); down_write(&card->controls_rwsem);
@ -963,6 +1012,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
kctl.get = snd_ctl_elem_user_get; kctl.get = snd_ctl_elem_user_get;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl.put = snd_ctl_elem_user_put; kctl.put = snd_ctl_elem_user_put;
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
kctl.tlv.c = snd_ctl_elem_user_tlv;
access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
switch (info->type) { switch (info->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
private_size = sizeof(char); private_size = sizeof(char);
@ -997,6 +1050,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (ue == NULL) if (ue == NULL)
return -ENOMEM; return -ENOMEM;
ue->info = *info; ue->info = *info;
ue->info.access = 0;
ue->elem_data = (char *)ue + sizeof(*ue); ue->elem_data = (char *)ue + sizeof(*ue);
ue->elem_data_size = private_size; ue->elem_data_size = private_size;
kctl.private_free = snd_ctl_elem_user_free; kctl.private_free = snd_ctl_elem_user_free;
@ -1067,6 +1121,67 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
return 0; return 0;
} }
static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
struct snd_ctl_tlv __user *_tlv,
int op_flag)
{
struct snd_card *card = file->card;
struct snd_ctl_tlv tlv;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int len;
int err = 0;
if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
return -EFAULT;
if (tlv.length < sizeof(unsigned int) * 3)
return -EINVAL;
down_read(&card->controls_rwsem);
kctl = snd_ctl_find_numid(card, tlv.numid);
if (kctl == NULL) {
err = -ENOENT;
goto __kctl_end;
}
if (kctl->tlv.p == NULL) {
err = -ENXIO;
goto __kctl_end;
}
vd = &kctl->vd[tlv.numid - kctl->id.numid];
if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
(op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
(op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
err = -ENXIO;
goto __kctl_end;
}
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
if (file && vd->owner != NULL && vd->owner != file) {
err = -EPERM;
goto __kctl_end;
}
err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
if (err > 0) {
up_read(&card->controls_rwsem);
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
return 0;
}
} else {
if (op_flag) {
err = -ENXIO;
goto __kctl_end;
}
len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
if (tlv.length < len) {
err = -ENOMEM;
goto __kctl_end;
}
if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
err = -EFAULT;
}
__kctl_end:
up_read(&card->controls_rwsem);
return err;
}
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct snd_ctl_file *ctl; struct snd_ctl_file *ctl;
@ -1086,11 +1201,11 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_CARD_INFO: case SNDRV_CTL_IOCTL_CARD_INFO:
return snd_ctl_card_info(card, ctl, cmd, argp); return snd_ctl_card_info(card, ctl, cmd, argp);
case SNDRV_CTL_IOCTL_ELEM_LIST: case SNDRV_CTL_IOCTL_ELEM_LIST:
return snd_ctl_elem_list(ctl->card, argp); return snd_ctl_elem_list(card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO: case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info_user(ctl, argp); return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ: case SNDRV_CTL_IOCTL_ELEM_READ:
return snd_ctl_elem_read_user(ctl->card, argp); return snd_ctl_elem_read_user(card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE: case SNDRV_CTL_IOCTL_ELEM_WRITE:
return snd_ctl_elem_write_user(ctl, argp); return snd_ctl_elem_write_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_LOCK: case SNDRV_CTL_IOCTL_ELEM_LOCK:
@ -1105,6 +1220,12 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
return snd_ctl_elem_remove(ctl, argp); return snd_ctl_elem_remove(ctl, argp);
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip); return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
return snd_ctl_tlv_ioctl(ctl, argp, 0);
case SNDRV_CTL_IOCTL_TLV_WRITE:
return snd_ctl_tlv_ioctl(ctl, argp, 1);
case SNDRV_CTL_IOCTL_TLV_COMMAND:
return snd_ctl_tlv_ioctl(ctl, argp, -1);
case SNDRV_CTL_IOCTL_POWER: case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT; return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE: case SNDRV_CTL_IOCTL_POWER_STATE:
@ -1338,6 +1459,11 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
struct snd_card *card = device->device_data; struct snd_card *card = device->device_data;
struct list_head *flist; struct list_head *flist;
struct snd_ctl_file *ctl; struct snd_ctl_file *ctl;
int err, cardnum;
snd_assert(card != NULL, return -ENXIO);
cardnum = card->number;
snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
down_read(&card->controls_rwsem); down_read(&card->controls_rwsem);
list_for_each(flist, &card->ctl_files) { list_for_each(flist, &card->ctl_files) {
@ -1346,6 +1472,10 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
} }
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
card, -1)) < 0)
return err;
return 0; return 0;
} }
@ -1366,23 +1496,6 @@ static int snd_ctl_dev_free(struct snd_device *device)
return 0; return 0;
} }
/*
* de-registration of the control device
*/
static int snd_ctl_dev_unregister(struct snd_device *device)
{
struct snd_card *card = device->device_data;
int err, cardnum;
snd_assert(card != NULL, return -ENXIO);
cardnum = card->number;
snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
card, -1)) < 0)
return err;
return snd_ctl_dev_free(device);
}
/* /*
* create control core: * create control core:
* called from init.c * called from init.c
@ -1393,7 +1506,6 @@ int snd_ctl_create(struct snd_card *card)
.dev_free = snd_ctl_dev_free, .dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register, .dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect, .dev_disconnect = snd_ctl_dev_disconnect,
.dev_unregister = snd_ctl_dev_unregister
}; };
snd_assert(card != NULL, return -ENXIO); snd_assert(card != NULL, return -ENXIO);

View File

@ -407,6 +407,10 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
case SNDRV_CTL_IOCTL_POWER_STATE: case SNDRV_CTL_IOCTL_POWER_STATE:
case SNDRV_CTL_IOCTL_ELEM_LOCK: case SNDRV_CTL_IOCTL_ELEM_LOCK:
case SNDRV_CTL_IOCTL_ELEM_UNLOCK: case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
case SNDRV_CTL_IOCTL_ELEM_REMOVE:
case SNDRV_CTL_IOCTL_TLV_READ:
case SNDRV_CTL_IOCTL_TLV_WRITE:
case SNDRV_CTL_IOCTL_TLV_COMMAND:
return snd_ctl_ioctl(file, cmd, (unsigned long)argp); return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
case SNDRV_CTL_IOCTL_ELEM_LIST32: case SNDRV_CTL_IOCTL_ELEM_LIST32:
return snd_ctl_elem_list_compat(ctl->card, argp); return snd_ctl_elem_list_compat(ctl->card, argp);

View File

@ -71,7 +71,7 @@ EXPORT_SYMBOL(snd_device_new);
* @device_data: the data pointer to release * @device_data: the data pointer to release
* *
* Removes the device from the list on the card and invokes the * Removes the device from the list on the card and invokes the
* callback, dev_unregister or dev_free, corresponding to the state. * callbacks, dev_disconnect and dev_free, corresponding to the state.
* Then release the device. * Then release the device.
* *
* Returns zero if successful, or a negative error code on failure or if the * Returns zero if successful, or a negative error code on failure or if the
@ -90,17 +90,15 @@ int snd_device_free(struct snd_card *card, void *device_data)
continue; continue;
/* unlink */ /* unlink */
list_del(&dev->list); list_del(&dev->list);
if ((dev->state == SNDRV_DEV_REGISTERED || if (dev->state == SNDRV_DEV_REGISTERED &&
dev->state == SNDRV_DEV_DISCONNECTED) && dev->ops->dev_disconnect)
dev->ops->dev_unregister) { if (dev->ops->dev_disconnect(dev))
if (dev->ops->dev_unregister(dev)) snd_printk(KERN_ERR
snd_printk(KERN_ERR "device unregister failure\n"); "device disconnect failure\n");
} else {
if (dev->ops->dev_free) { if (dev->ops->dev_free) {
if (dev->ops->dev_free(dev)) if (dev->ops->dev_free(dev))
snd_printk(KERN_ERR "device free failure\n"); snd_printk(KERN_ERR "device free failure\n");
} }
}
kfree(dev); kfree(dev);
return 0; return 0;
} }

View File

@ -42,7 +42,7 @@ static DEFINE_MUTEX(register_mutex);
static int snd_hwdep_free(struct snd_hwdep *hwdep); static int snd_hwdep_free(struct snd_hwdep *hwdep);
static int snd_hwdep_dev_free(struct snd_device *device); static int snd_hwdep_dev_free(struct snd_device *device);
static int snd_hwdep_dev_register(struct snd_device *device); static int snd_hwdep_dev_register(struct snd_device *device);
static int snd_hwdep_dev_unregister(struct snd_device *device); static int snd_hwdep_dev_disconnect(struct snd_device *device);
static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device) static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
@ -353,7 +353,7 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = snd_hwdep_dev_free, .dev_free = snd_hwdep_dev_free,
.dev_register = snd_hwdep_dev_register, .dev_register = snd_hwdep_dev_register,
.dev_unregister = snd_hwdep_dev_unregister .dev_disconnect = snd_hwdep_dev_disconnect,
}; };
snd_assert(rhwdep != NULL, return -EINVAL); snd_assert(rhwdep != NULL, return -EINVAL);
@ -439,7 +439,7 @@ static int snd_hwdep_dev_register(struct snd_device *device)
return 0; return 0;
} }
static int snd_hwdep_dev_unregister(struct snd_device *device) static int snd_hwdep_dev_disconnect(struct snd_device *device)
{ {
struct snd_hwdep *hwdep = device->device_data; struct snd_hwdep *hwdep = device->device_data;
@ -454,9 +454,9 @@ static int snd_hwdep_dev_unregister(struct snd_device *device)
snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
#endif #endif
snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
list_del(&hwdep->list); list_del_init(&hwdep->list);
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
return snd_hwdep_free(hwdep); return 0;
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
@ -497,7 +497,7 @@ static void __init snd_hwdep_proc_init(void)
static void __exit snd_hwdep_proc_done(void) static void __exit snd_hwdep_proc_done(void)
{ {
snd_info_unregister(snd_hwdep_proc_entry); snd_info_free_entry(snd_hwdep_proc_entry);
} }
#else /* !CONFIG_PROC_FS */ #else /* !CONFIG_PROC_FS */
#define snd_hwdep_proc_init() #define snd_hwdep_proc_init()

View File

@ -78,6 +78,7 @@ struct snd_info_private_data {
static int snd_info_version_init(void); static int snd_info_version_init(void);
static int snd_info_version_done(void); static int snd_info_version_done(void);
static void snd_info_disconnect(struct snd_info_entry *entry);
/* resize the proc r/w buffer */ /* resize the proc r/w buffer */
@ -174,15 +175,15 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
switch (entry->content) { switch (entry->content) {
case SNDRV_INFO_CONTENT_TEXT: case SNDRV_INFO_CONTENT_TEXT:
switch (orig) { switch (orig) {
case 0: /* SEEK_SET */ case SEEK_SET:
file->f_pos = offset; file->f_pos = offset;
ret = file->f_pos; ret = file->f_pos;
goto out; goto out;
case 1: /* SEEK_CUR */ case SEEK_CUR:
file->f_pos += offset; file->f_pos += offset;
ret = file->f_pos; ret = file->f_pos;
goto out; goto out;
case 2: /* SEEK_END */ case SEEK_END:
default: default:
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
@ -304,7 +305,7 @@ static int snd_info_entry_open(struct inode *inode, struct file *file)
mutex_lock(&info_mutex); mutex_lock(&info_mutex);
p = PDE(inode); p = PDE(inode);
entry = p == NULL ? NULL : (struct snd_info_entry *)p->data; entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
if (entry == NULL || entry->disconnected) { if (entry == NULL || ! entry->p) {
mutex_unlock(&info_mutex); mutex_unlock(&info_mutex);
return -ENODEV; return -ENODEV;
} }
@ -586,10 +587,10 @@ int __exit snd_info_done(void)
snd_info_version_done(); snd_info_version_done();
if (snd_proc_root) { if (snd_proc_root) {
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
snd_info_unregister(snd_seq_root); snd_info_free_entry(snd_seq_root);
#endif #endif
#ifdef CONFIG_SND_OSSEMUL #ifdef CONFIG_SND_OSSEMUL
snd_info_unregister(snd_oss_root); snd_info_free_entry(snd_oss_root);
#endif #endif
snd_remove_proc_entry(&proc_root, snd_proc_root); snd_remove_proc_entry(&proc_root, snd_proc_root);
} }
@ -648,17 +649,28 @@ int snd_info_card_register(struct snd_card *card)
* de-register the card proc file * de-register the card proc file
* called from init.c * called from init.c
*/ */
int snd_info_card_free(struct snd_card *card) void snd_info_card_disconnect(struct snd_card *card)
{ {
snd_assert(card != NULL, return -ENXIO); snd_assert(card != NULL, return);
mutex_lock(&info_mutex);
if (card->proc_root_link) { if (card->proc_root_link) {
snd_remove_proc_entry(snd_proc_root, card->proc_root_link); snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
card->proc_root_link = NULL; card->proc_root_link = NULL;
} }
if (card->proc_root) { if (card->proc_root)
snd_info_unregister(card->proc_root); snd_info_disconnect(card->proc_root);
card->proc_root = NULL; mutex_unlock(&info_mutex);
} }
/*
* release the card proc file resources
* called from init.c
*/
int snd_info_card_free(struct snd_card *card)
{
snd_assert(card != NULL, return -ENXIO);
snd_info_free_entry(card->proc_root);
card->proc_root = NULL;
return 0; return 0;
} }
@ -767,6 +779,8 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
entry->mode = S_IFREG | S_IRUGO; entry->mode = S_IFREG | S_IRUGO;
entry->content = SNDRV_INFO_CONTENT_TEXT; entry->content = SNDRV_INFO_CONTENT_TEXT;
mutex_init(&entry->access); mutex_init(&entry->access);
INIT_LIST_HEAD(&entry->children);
INIT_LIST_HEAD(&entry->list);
return entry; return entry;
} }
@ -819,6 +833,24 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
EXPORT_SYMBOL(snd_info_create_card_entry); EXPORT_SYMBOL(snd_info_create_card_entry);
static void snd_info_disconnect(struct snd_info_entry *entry)
{
struct list_head *p, *n;
struct proc_dir_entry *root;
list_for_each_safe(p, n, &entry->children) {
snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
}
if (! entry->p)
return;
list_del_init(&entry->list);
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
snd_assert(root, return);
snd_remove_proc_entry(root, entry->p);
entry->p = NULL;
}
static int snd_info_dev_free_entry(struct snd_device *device) static int snd_info_dev_free_entry(struct snd_device *device)
{ {
struct snd_info_entry *entry = device->device_data; struct snd_info_entry *entry = device->device_data;
@ -832,19 +864,6 @@ static int snd_info_dev_register_entry(struct snd_device *device)
return snd_info_register(entry); return snd_info_register(entry);
} }
static int snd_info_dev_disconnect_entry(struct snd_device *device)
{
struct snd_info_entry *entry = device->device_data;
entry->disconnected = 1;
return 0;
}
static int snd_info_dev_unregister_entry(struct snd_device *device)
{
struct snd_info_entry *entry = device->device_data;
return snd_info_unregister(entry);
}
/** /**
* snd_card_proc_new - create an info entry for the given card * snd_card_proc_new - create an info entry for the given card
* @card: the card instance * @card: the card instance
@ -871,8 +890,7 @@ int snd_card_proc_new(struct snd_card *card, const char *name,
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = snd_info_dev_free_entry, .dev_free = snd_info_dev_free_entry,
.dev_register = snd_info_dev_register_entry, .dev_register = snd_info_dev_register_entry,
.dev_disconnect = snd_info_dev_disconnect_entry, /* disconnect is done via snd_info_card_disconnect() */
.dev_unregister = snd_info_dev_unregister_entry
}; };
struct snd_info_entry *entry; struct snd_info_entry *entry;
int err; int err;
@ -901,6 +919,11 @@ void snd_info_free_entry(struct snd_info_entry * entry)
{ {
if (entry == NULL) if (entry == NULL)
return; return;
if (entry->p) {
mutex_lock(&info_mutex);
snd_info_disconnect(entry);
mutex_unlock(&info_mutex);
}
kfree(entry->name); kfree(entry->name);
if (entry->private_free) if (entry->private_free)
entry->private_free(entry); entry->private_free(entry);
@ -935,38 +958,14 @@ int snd_info_register(struct snd_info_entry * entry)
p->size = entry->size; p->size = entry->size;
p->data = entry; p->data = entry;
entry->p = p; entry->p = p;
if (entry->parent)
list_add_tail(&entry->list, &entry->parent->children);
mutex_unlock(&info_mutex); mutex_unlock(&info_mutex);
return 0; return 0;
} }
EXPORT_SYMBOL(snd_info_register); EXPORT_SYMBOL(snd_info_register);
/**
* snd_info_unregister - de-register the info entry
* @entry: the info entry
*
* De-registers the info entry and releases the instance.
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_info_unregister(struct snd_info_entry * entry)
{
struct proc_dir_entry *root;
if (! entry)
return 0;
snd_assert(entry->p != NULL, return -ENXIO);
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
snd_assert(root, return -ENXIO);
mutex_lock(&info_mutex);
snd_remove_proc_entry(root, entry->p);
mutex_unlock(&info_mutex);
snd_info_free_entry(entry);
return 0;
}
EXPORT_SYMBOL(snd_info_unregister);
/* /*
*/ */
@ -999,8 +998,7 @@ static int __init snd_info_version_init(void)
static int __exit snd_info_version_done(void) static int __exit snd_info_version_done(void)
{ {
if (snd_info_version_entry) snd_info_free_entry(snd_info_version_entry);
snd_info_unregister(snd_info_version_entry);
return 0; return 0;
} }

View File

@ -131,10 +131,8 @@ int snd_info_minor_register(void)
int snd_info_minor_unregister(void) int snd_info_minor_unregister(void)
{ {
if (snd_sndstat_proc_entry) { snd_info_free_entry(snd_sndstat_proc_entry);
snd_info_unregister(snd_sndstat_proc_entry);
snd_sndstat_proc_entry = NULL; snd_sndstat_proc_entry = NULL;
}
return 0; return 0;
} }

View File

@ -81,8 +81,6 @@ static inline int init_info_for_card(struct snd_card *card)
#define init_info_for_card(card) #define init_info_for_card(card)
#endif #endif
static void snd_card_free_thread(void * __card);
/** /**
* snd_card_new - create and initialize a soundcard structure * snd_card_new - create and initialize a soundcard structure
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)] * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
@ -145,7 +143,6 @@ struct snd_card *snd_card_new(int idx, const char *xid,
INIT_LIST_HEAD(&card->ctl_files); INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock); spin_lock_init(&card->files_lock);
init_waitqueue_head(&card->shutdown_sleep); init_waitqueue_head(&card->shutdown_sleep);
INIT_WORK(&card->free_workq, snd_card_free_thread, card);
#ifdef CONFIG_PM #ifdef CONFIG_PM
mutex_init(&card->power_lock); mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep); init_waitqueue_head(&card->power_sleep);
@ -310,6 +307,7 @@ int snd_card_disconnect(struct snd_card *card)
if (err < 0) if (err < 0)
snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
snd_info_card_disconnect(card);
return 0; return 0;
} }
@ -326,22 +324,10 @@ EXPORT_SYMBOL(snd_card_disconnect);
* Returns zero. Frees all associated devices and frees the control * Returns zero. Frees all associated devices and frees the control
* interface associated to given soundcard. * interface associated to given soundcard.
*/ */
int snd_card_free(struct snd_card *card) static int snd_card_do_free(struct snd_card *card)
{ {
struct snd_shutdown_f_ops *s_f_ops; struct snd_shutdown_f_ops *s_f_ops;
if (card == NULL)
return -EINVAL;
mutex_lock(&snd_card_mutex);
snd_cards[card->number] = NULL;
mutex_unlock(&snd_card_mutex);
#ifdef CONFIG_PM
wake_up(&card->power_sleep);
#endif
/* wait, until all devices are ready for the free operation */
wait_event(card->shutdown_sleep, card->files == NULL);
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if (snd_mixer_oss_notify_callback) if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
@ -360,7 +346,7 @@ int snd_card_free(struct snd_card *card)
} }
if (card->private_free) if (card->private_free)
card->private_free(card); card->private_free(card);
snd_info_unregister(card->proc_id); snd_info_free_entry(card->proc_id);
if (snd_info_card_free(card) < 0) { if (snd_info_card_free(card) < 0) {
snd_printk(KERN_WARNING "unable to free card info\n"); snd_printk(KERN_WARNING "unable to free card info\n");
/* Not fatal error */ /* Not fatal error */
@ -370,62 +356,60 @@ int snd_card_free(struct snd_card *card)
card->s_f_ops = s_f_ops->next; card->s_f_ops = s_f_ops->next;
kfree(s_f_ops); kfree(s_f_ops);
} }
mutex_lock(&snd_card_mutex);
snd_cards_lock &= ~(1 << card->number);
mutex_unlock(&snd_card_mutex);
kfree(card); kfree(card);
return 0; return 0;
} }
static int snd_card_free_prepare(struct snd_card *card)
{
if (card == NULL)
return -EINVAL;
(void) snd_card_disconnect(card);
mutex_lock(&snd_card_mutex);
snd_cards[card->number] = NULL;
snd_cards_lock &= ~(1 << card->number);
mutex_unlock(&snd_card_mutex);
#ifdef CONFIG_PM
wake_up(&card->power_sleep);
#endif
return 0;
}
int snd_card_free_when_closed(struct snd_card *card)
{
int free_now = 0;
int ret = snd_card_free_prepare(card);
if (ret)
return ret;
spin_lock(&card->files_lock);
if (card->files == NULL)
free_now = 1;
else
card->free_on_last_close = 1;
spin_unlock(&card->files_lock);
if (free_now)
snd_card_do_free(card);
return 0;
}
EXPORT_SYMBOL(snd_card_free_when_closed);
int snd_card_free(struct snd_card *card)
{
int ret = snd_card_free_prepare(card);
if (ret)
return ret;
/* wait, until all devices are ready for the free operation */
wait_event(card->shutdown_sleep, card->files == NULL);
snd_card_do_free(card);
return 0;
}
EXPORT_SYMBOL(snd_card_free); EXPORT_SYMBOL(snd_card_free);
static void snd_card_free_thread(void * __card)
{
struct snd_card *card = __card;
struct module * module = card->module;
if (!try_module_get(module)) {
snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number);
module = NULL;
}
snd_card_free(card);
module_put(module);
}
/**
* snd_card_free_in_thread - call snd_card_free() in thread
* @card: soundcard structure
*
* This function schedules the call of snd_card_free() function in a
* work queue. When all devices are released (non-busy), the work
* is woken up and calls snd_card_free().
*
* When a card can be disconnected at any time by hotplug service,
* this function should be used in disconnect (or detach) callback
* instead of calling snd_card_free() directly.
*
* Returns - zero otherwise a negative error code if the start of thread failed.
*/
int snd_card_free_in_thread(struct snd_card *card)
{
if (card->files == NULL) {
snd_card_free(card);
return 0;
}
if (schedule_work(&card->free_workq))
return 0;
snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number);
/* try to free the structure immediately */
snd_card_free(card);
return -EFAULT;
}
EXPORT_SYMBOL(snd_card_free_in_thread);
static void choose_default_id(struct snd_card *card) static void choose_default_id(struct snd_card *card)
{ {
int i, len, idx_flag = 0, loops = SNDRV_CARDS; int i, len, idx_flag = 0, loops = SNDRV_CARDS;
@ -625,9 +609,9 @@ int __init snd_card_info_init(void)
int __exit snd_card_info_done(void) int __exit snd_card_info_done(void)
{ {
snd_info_unregister(snd_card_info_entry); snd_info_free_entry(snd_card_info_entry);
#ifdef MODULE #ifdef MODULE
snd_info_unregister(snd_card_module_info_entry); snd_info_free_entry(snd_card_module_info_entry);
#endif #endif
return 0; return 0;
} }
@ -708,15 +692,16 @@ EXPORT_SYMBOL(snd_card_file_add);
* *
* This function removes the file formerly added to the card via * This function removes the file formerly added to the card via
* snd_card_file_add() function. * snd_card_file_add() function.
* If all files are removed and the release of the card is * If all files are removed and snd_card_free_when_closed() was
* scheduled, it will wake up the the thread to call snd_card_free() * called beforehand, it processes the pending release of
* (see snd_card_free_in_thread() function). * resources.
* *
* Returns zero or a negative error code. * Returns zero or a negative error code.
*/ */
int snd_card_file_remove(struct snd_card *card, struct file *file) int snd_card_file_remove(struct snd_card *card, struct file *file)
{ {
struct snd_monitor_file *mfile, *pfile = NULL; struct snd_monitor_file *mfile, *pfile = NULL;
int last_close = 0;
spin_lock(&card->files_lock); spin_lock(&card->files_lock);
mfile = card->files; mfile = card->files;
@ -731,9 +716,14 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
pfile = mfile; pfile = mfile;
mfile = mfile->next; mfile = mfile->next;
} }
spin_unlock(&card->files_lock);
if (card->files == NULL) if (card->files == NULL)
last_close = 1;
spin_unlock(&card->files_lock);
if (last_close) {
wake_up(&card->shutdown_sleep); wake_up(&card->shutdown_sleep);
if (card->free_on_last_close)
snd_card_do_free(card);
}
if (!mfile) { if (!mfile) {
snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
return -ENOENT; return -ENOENT;

View File

@ -1193,11 +1193,9 @@ static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer) static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
{ {
if (mixer->proc_entry) { snd_info_free_entry(mixer->proc_entry);
snd_info_unregister(mixer->proc_entry);
mixer->proc_entry = NULL; mixer->proc_entry = NULL;
} }
}
#else /* !CONFIG_PROC_FS */ #else /* !CONFIG_PROC_FS */
#define snd_mixer_oss_proc_init(mix) #define snd_mixer_oss_proc_init(mix)
#define snd_mixer_oss_proc_done(mix) #define snd_mixer_oss_proc_done(mix)
@ -1312,21 +1310,19 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
card->mixer_oss = mixer; card->mixer_oss = mixer;
snd_mixer_oss_build(mixer); snd_mixer_oss_build(mixer);
snd_mixer_oss_proc_init(mixer); snd_mixer_oss_proc_init(mixer);
} else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) { } else {
mixer = card->mixer_oss;
if (mixer == NULL || !mixer->oss_dev_alloc)
return 0;
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
mixer->oss_dev_alloc = 0;
} else { /* free */
mixer = card->mixer_oss; mixer = card->mixer_oss;
if (mixer == NULL) if (mixer == NULL)
return 0; return 0;
if (mixer->oss_dev_alloc) {
#ifdef SNDRV_OSS_INFO_DEV_MIXERS #ifdef SNDRV_OSS_INFO_DEV_MIXERS
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
#endif #endif
if (mixer->oss_dev_alloc)
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
mixer->oss_dev_alloc = 0;
}
if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT)
return 0;
snd_mixer_oss_proc_done(mixer); snd_mixer_oss_proc_done(mixer);
return snd_mixer_oss_free1(mixer); return snd_mixer_oss_free1(mixer);
} }

View File

@ -2846,13 +2846,11 @@ static void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
int stream; int stream;
for (stream = 0; stream < 2; ++stream) { for (stream = 0; stream < 2; ++stream) {
struct snd_pcm_str *pstr = &pcm->streams[stream]; struct snd_pcm_str *pstr = &pcm->streams[stream];
if (pstr->oss.proc_entry) { snd_info_free_entry(pstr->oss.proc_entry);
snd_info_unregister(pstr->oss.proc_entry);
pstr->oss.proc_entry = NULL; pstr->oss.proc_entry = NULL;
snd_pcm_oss_proc_free_setup_list(pstr); snd_pcm_oss_proc_free_setup_list(pstr);
} }
} }
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */ #else /* !CONFIG_SND_VERBOSE_PROCFS */
#define snd_pcm_oss_proc_init(pcm) #define snd_pcm_oss_proc_init(pcm)
#define snd_pcm_oss_proc_done(pcm) #define snd_pcm_oss_proc_done(pcm)
@ -2931,6 +2929,12 @@ static int snd_pcm_oss_disconnect_minor(struct snd_pcm *pcm)
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
pcm->card, 1); pcm->card, 1);
} }
if (dsp_map[pcm->card->number] == (int)pcm->device) {
#ifdef SNDRV_OSS_INFO_DEV_AUDIO
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
#endif
}
pcm->oss.reg = 0;
} }
return 0; return 0;
} }
@ -2938,15 +2942,7 @@ static int snd_pcm_oss_disconnect_minor(struct snd_pcm *pcm)
static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm) static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm)
{ {
snd_pcm_oss_disconnect_minor(pcm); snd_pcm_oss_disconnect_minor(pcm);
if (pcm->oss.reg) {
if (dsp_map[pcm->card->number] == (int)pcm->device) {
#ifdef SNDRV_OSS_INFO_DEV_AUDIO
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
#endif
}
pcm->oss.reg = 0;
snd_pcm_oss_proc_done(pcm); snd_pcm_oss_proc_done(pcm);
}
return 0; return 0;
} }

View File

@ -42,7 +42,6 @@ static int snd_pcm_free(struct snd_pcm *pcm);
static int snd_pcm_dev_free(struct snd_device *device); static int snd_pcm_dev_free(struct snd_device *device);
static int snd_pcm_dev_register(struct snd_device *device); static int snd_pcm_dev_register(struct snd_device *device);
static int snd_pcm_dev_disconnect(struct snd_device *device); static int snd_pcm_dev_disconnect(struct snd_device *device);
static int snd_pcm_dev_unregister(struct snd_device *device);
static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device) static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
{ {
@ -494,19 +493,13 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
{ {
#ifdef CONFIG_SND_PCM_XRUN_DEBUG #ifdef CONFIG_SND_PCM_XRUN_DEBUG
if (pstr->proc_xrun_debug_entry) { snd_info_free_entry(pstr->proc_xrun_debug_entry);
snd_info_unregister(pstr->proc_xrun_debug_entry);
pstr->proc_xrun_debug_entry = NULL; pstr->proc_xrun_debug_entry = NULL;
}
#endif #endif
if (pstr->proc_info_entry) { snd_info_free_entry(pstr->proc_info_entry);
snd_info_unregister(pstr->proc_info_entry);
pstr->proc_info_entry = NULL; pstr->proc_info_entry = NULL;
} snd_info_free_entry(pstr->proc_root);
if (pstr->proc_root) {
snd_info_unregister(pstr->proc_root);
pstr->proc_root = NULL; pstr->proc_root = NULL;
}
return 0; return 0;
} }
@ -573,26 +566,16 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
{ {
if (substream->proc_info_entry) { snd_info_free_entry(substream->proc_info_entry);
snd_info_unregister(substream->proc_info_entry);
substream->proc_info_entry = NULL; substream->proc_info_entry = NULL;
} snd_info_free_entry(substream->proc_hw_params_entry);
if (substream->proc_hw_params_entry) {
snd_info_unregister(substream->proc_hw_params_entry);
substream->proc_hw_params_entry = NULL; substream->proc_hw_params_entry = NULL;
} snd_info_free_entry(substream->proc_sw_params_entry);
if (substream->proc_sw_params_entry) {
snd_info_unregister(substream->proc_sw_params_entry);
substream->proc_sw_params_entry = NULL; substream->proc_sw_params_entry = NULL;
} snd_info_free_entry(substream->proc_status_entry);
if (substream->proc_status_entry) {
snd_info_unregister(substream->proc_status_entry);
substream->proc_status_entry = NULL; substream->proc_status_entry = NULL;
} snd_info_free_entry(substream->proc_root);
if (substream->proc_root) {
snd_info_unregister(substream->proc_root);
substream->proc_root = NULL; substream->proc_root = NULL;
}
return 0; return 0;
} }
#else /* !CONFIG_SND_VERBOSE_PROCFS */ #else /* !CONFIG_SND_VERBOSE_PROCFS */
@ -696,7 +679,6 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
.dev_free = snd_pcm_dev_free, .dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register, .dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect, .dev_disconnect = snd_pcm_dev_disconnect,
.dev_unregister = snd_pcm_dev_unregister
}; };
snd_assert(rpcm != NULL, return -EINVAL); snd_assert(rpcm != NULL, return -EINVAL);
@ -740,6 +722,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
substream = pstr->substream; substream = pstr->substream;
while (substream) { while (substream) {
substream_next = substream->next; substream_next = substream->next;
snd_pcm_timer_done(substream);
snd_pcm_substream_proc_done(substream); snd_pcm_substream_proc_done(substream);
kfree(substream); kfree(substream);
substream = substream_next; substream = substream_next;
@ -756,7 +739,12 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
static int snd_pcm_free(struct snd_pcm *pcm) static int snd_pcm_free(struct snd_pcm *pcm)
{ {
struct snd_pcm_notify *notify;
snd_assert(pcm != NULL, return -ENXIO); snd_assert(pcm != NULL, return -ENXIO);
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_unregister(pcm);
}
if (pcm->private_free) if (pcm->private_free)
pcm->private_free(pcm); pcm->private_free(pcm);
snd_pcm_lib_preallocate_free_for_all(pcm); snd_pcm_lib_preallocate_free_for_all(pcm);
@ -804,6 +792,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
kctl = snd_ctl_file(list); kctl = snd_ctl_file(list);
if (kctl->pid == current->pid) { if (kctl->pid == current->pid) {
prefer_subdevice = kctl->prefer_pcm_subdevice; prefer_subdevice = kctl->prefer_pcm_subdevice;
if (prefer_subdevice != -1)
break; break;
} }
} }
@ -918,6 +907,28 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
substream->pstr->substream_opened--; substream->pstr->substream_opened--;
} }
static ssize_t show_pcm_class(struct class_device *class_device, char *buf)
{
struct snd_pcm *pcm;
const char *str;
static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
[SNDRV_PCM_CLASS_GENERIC] = "generic",
[SNDRV_PCM_CLASS_MULTI] = "multi",
[SNDRV_PCM_CLASS_MODEM] = "modem",
[SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
};
if (! (pcm = class_get_devdata(class_device)) ||
pcm->dev_class > SNDRV_PCM_CLASS_LAST)
str = "none";
else
str = strs[pcm->dev_class];
return snprintf(buf, PAGE_SIZE, "%s\n", str);
}
static struct class_device_attribute pcm_attrs =
__ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
static int snd_pcm_dev_register(struct snd_device *device) static int snd_pcm_dev_register(struct snd_device *device)
{ {
int cidx, err; int cidx, err;
@ -956,6 +967,8 @@ static int snd_pcm_dev_register(struct snd_device *device)
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
return err; return err;
} }
snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
&pcm_attrs);
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream); snd_pcm_timer_init(substream);
} }
@ -971,35 +984,22 @@ static int snd_pcm_dev_register(struct snd_device *device)
static int snd_pcm_dev_disconnect(struct snd_device *device) static int snd_pcm_dev_disconnect(struct snd_device *device)
{ {
struct snd_pcm *pcm = device->device_data; struct snd_pcm *pcm = device->device_data;
struct list_head *list; struct snd_pcm_notify *notify;
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
int cidx; int cidx, devtype;
mutex_lock(&register_mutex); mutex_lock(&register_mutex);
if (list_empty(&pcm->list))
goto unlock;
list_del_init(&pcm->list); list_del_init(&pcm->list);
for (cidx = 0; cidx < 2; cidx++) for (cidx = 0; cidx < 2; cidx++)
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
if (substream->runtime) if (substream->runtime)
substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
list_for_each(list, &snd_pcm_notify_list) { list_for_each_entry(notify, &snd_pcm_notify_list, list) {
struct snd_pcm_notify *notify;
notify = list_entry(list, struct snd_pcm_notify, list);
notify->n_disconnect(pcm); notify->n_disconnect(pcm);
} }
mutex_unlock(&register_mutex);
return 0;
}
static int snd_pcm_dev_unregister(struct snd_device *device)
{
int cidx, devtype;
struct snd_pcm_substream *substream;
struct list_head *list;
struct snd_pcm *pcm = device->device_data;
snd_assert(pcm != NULL, return -ENXIO);
mutex_lock(&register_mutex);
list_del(&pcm->list);
for (cidx = 0; cidx < 2; cidx++) { for (cidx = 0; cidx < 2; cidx++) {
devtype = -1; devtype = -1;
switch (cidx) { switch (cidx) {
@ -1011,23 +1011,20 @@ static int snd_pcm_dev_unregister(struct snd_device *device)
break; break;
} }
snd_unregister_device(devtype, pcm->card, pcm->device); snd_unregister_device(devtype, pcm->card, pcm->device);
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_done(substream);
}
list_for_each(list, &snd_pcm_notify_list) {
struct snd_pcm_notify *notify;
notify = list_entry(list, struct snd_pcm_notify, list);
notify->n_unregister(pcm);
} }
unlock:
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
return snd_pcm_free(pcm); return 0;
} }
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{ {
struct list_head *p; struct list_head *p;
snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL); snd_assert(notify != NULL &&
notify->n_register != NULL &&
notify->n_unregister != NULL &&
notify->n_disconnect, return -EINVAL);
mutex_lock(&register_mutex); mutex_lock(&register_mutex);
if (nfree) { if (nfree) {
list_del(&notify->list); list_del(&notify->list);
@ -1090,8 +1087,7 @@ static void snd_pcm_proc_init(void)
static void snd_pcm_proc_done(void) static void snd_pcm_proc_done(void)
{ {
if (snd_pcm_proc_entry) snd_info_free_entry(snd_pcm_proc_entry);
snd_info_unregister(snd_pcm_proc_entry);
} }
#else /* !CONFIG_PROC_FS */ #else /* !CONFIG_PROC_FS */

View File

@ -478,7 +478,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
* mmap of PCM status/control records because of the size * mmap of PCM status/control records because of the size
* incompatibility. * incompatibility.
*/ */
substream->no_mmap_ctrl = 1; pcm_file->no_compat_mmap = 1;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION: case SNDRV_PCM_IOCTL_PVERSION:

View File

@ -101,7 +101,7 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
{ {
snd_pcm_lib_preallocate_dma_free(substream); snd_pcm_lib_preallocate_dma_free(substream);
#ifdef CONFIG_SND_VERBOSE_PROCFS #ifdef CONFIG_SND_VERBOSE_PROCFS
snd_info_unregister(substream->proc_prealloc_entry); snd_info_free_entry(substream->proc_prealloc_entry);
substream->proc_prealloc_entry = NULL; substream->proc_prealloc_entry = NULL;
#endif #endif
return 0; return 0;

View File

@ -1992,35 +1992,9 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
return 0; return 0;
} }
static void snd_pcm_add_file(struct snd_pcm_str *str,
struct snd_pcm_file *pcm_file)
{
pcm_file->next = str->files;
str->files = pcm_file;
}
static void snd_pcm_remove_file(struct snd_pcm_str *str,
struct snd_pcm_file *pcm_file)
{
struct snd_pcm_file * pcm_file1;
if (str->files == pcm_file) {
str->files = pcm_file->next;
} else {
pcm_file1 = str->files;
while (pcm_file1 && pcm_file1->next != pcm_file)
pcm_file1 = pcm_file1->next;
if (pcm_file1 != NULL)
pcm_file1->next = pcm_file->next;
}
}
static void pcm_release_private(struct snd_pcm_substream *substream) static void pcm_release_private(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_file *pcm_file = substream->file;
snd_pcm_unlink(substream); snd_pcm_unlink(substream);
snd_pcm_remove_file(substream->pstr, pcm_file);
kfree(pcm_file);
} }
void snd_pcm_release_substream(struct snd_pcm_substream *substream) void snd_pcm_release_substream(struct snd_pcm_substream *substream)
@ -2060,7 +2034,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
return 0; return 0;
} }
substream->no_mmap_ctrl = 0;
err = snd_pcm_hw_constraints_init(substream); err = snd_pcm_hw_constraints_init(substream);
if (err < 0) { if (err < 0) {
snd_printd("snd_pcm_hw_constraints_init failed\n"); snd_printd("snd_pcm_hw_constraints_init failed\n");
@ -2105,19 +2078,16 @@ static int snd_pcm_open_file(struct file *file,
if (err < 0) if (err < 0)
return err; return err;
if (substream->ref_count > 1)
pcm_file = substream->file;
else {
pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
if (pcm_file == NULL) { if (pcm_file == NULL) {
snd_pcm_release_substream(substream); snd_pcm_release_substream(substream);
return -ENOMEM; return -ENOMEM;
} }
pcm_file->substream = substream;
if (substream->ref_count == 1) {
str = substream->pstr; str = substream->pstr;
substream->file = pcm_file; substream->file = pcm_file;
substream->pcm_release = pcm_release_private; substream->pcm_release = pcm_release_private;
pcm_file->substream = substream;
snd_pcm_add_file(str, pcm_file);
} }
file->private_data = pcm_file; file->private_data = pcm_file;
*rpcm_file = pcm_file; *rpcm_file = pcm_file;
@ -2209,6 +2179,7 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
fasync_helper(-1, file, 0, &substream->runtime->fasync); fasync_helper(-1, file, 0, &substream->runtime->fasync);
mutex_lock(&pcm->open_mutex); mutex_lock(&pcm->open_mutex);
snd_pcm_release_substream(substream); snd_pcm_release_substream(substream);
kfree(pcm_file);
mutex_unlock(&pcm->open_mutex); mutex_unlock(&pcm->open_mutex);
wake_up(&pcm->open_wait); wake_up(&pcm->open_wait);
module_put(pcm->card->module); module_put(pcm->card->module);
@ -3270,11 +3241,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
offset = area->vm_pgoff << PAGE_SHIFT; offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) { switch (offset) {
case SNDRV_PCM_MMAP_OFFSET_STATUS: case SNDRV_PCM_MMAP_OFFSET_STATUS:
if (substream->no_mmap_ctrl) if (pcm_file->no_compat_mmap)
return -ENXIO; return -ENXIO;
return snd_pcm_mmap_status(substream, file, area); return snd_pcm_mmap_status(substream, file, area);
case SNDRV_PCM_MMAP_OFFSET_CONTROL: case SNDRV_PCM_MMAP_OFFSET_CONTROL:
if (substream->no_mmap_ctrl) if (pcm_file->no_compat_mmap)
return -ENXIO; return -ENXIO;
return snd_pcm_mmap_control(substream, file, area); return snd_pcm_mmap_control(substream, file, area);
default: default:

View File

@ -55,7 +55,6 @@ static int snd_rawmidi_free(struct snd_rawmidi *rawmidi);
static int snd_rawmidi_dev_free(struct snd_device *device); static int snd_rawmidi_dev_free(struct snd_device *device);
static int snd_rawmidi_dev_register(struct snd_device *device); static int snd_rawmidi_dev_register(struct snd_device *device);
static int snd_rawmidi_dev_disconnect(struct snd_device *device); static int snd_rawmidi_dev_disconnect(struct snd_device *device);
static int snd_rawmidi_dev_unregister(struct snd_device *device);
static LIST_HEAD(snd_rawmidi_devices); static LIST_HEAD(snd_rawmidi_devices);
static DEFINE_MUTEX(register_mutex); static DEFINE_MUTEX(register_mutex);
@ -431,6 +430,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
kctl = snd_ctl_file(list); kctl = snd_ctl_file(list);
if (kctl->pid == current->pid) { if (kctl->pid == current->pid) {
subdevice = kctl->prefer_rawmidi_subdevice; subdevice = kctl->prefer_rawmidi_subdevice;
if (subdevice != -1)
break; break;
} }
} }
@ -1426,7 +1426,6 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
.dev_free = snd_rawmidi_dev_free, .dev_free = snd_rawmidi_dev_free,
.dev_register = snd_rawmidi_dev_register, .dev_register = snd_rawmidi_dev_register,
.dev_disconnect = snd_rawmidi_dev_disconnect, .dev_disconnect = snd_rawmidi_dev_disconnect,
.dev_unregister = snd_rawmidi_dev_unregister
}; };
snd_assert(rrawmidi != NULL, return -EINVAL); snd_assert(rrawmidi != NULL, return -EINVAL);
@ -1479,6 +1478,14 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
static int snd_rawmidi_free(struct snd_rawmidi *rmidi) static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
{ {
snd_assert(rmidi != NULL, return -ENXIO); snd_assert(rmidi != NULL, return -ENXIO);
snd_info_free_entry(rmidi->proc_entry);
rmidi->proc_entry = NULL;
mutex_lock(&register_mutex);
if (rmidi->ops && rmidi->ops->dev_unregister)
rmidi->ops->dev_unregister(rmidi);
mutex_unlock(&register_mutex);
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
if (rmidi->private_free) if (rmidi->private_free)
@ -1587,21 +1594,6 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
mutex_lock(&register_mutex); mutex_lock(&register_mutex);
list_del_init(&rmidi->list); list_del_init(&rmidi->list);
mutex_unlock(&register_mutex);
return 0;
}
static int snd_rawmidi_dev_unregister(struct snd_device *device)
{
struct snd_rawmidi *rmidi = device->device_data;
snd_assert(rmidi != NULL, return -ENXIO);
mutex_lock(&register_mutex);
list_del(&rmidi->list);
if (rmidi->proc_entry) {
snd_info_unregister(rmidi->proc_entry);
rmidi->proc_entry = NULL;
}
#ifdef CONFIG_SND_OSSEMUL #ifdef CONFIG_SND_OSSEMUL
if (rmidi->ossreg) { if (rmidi->ossreg) {
if ((int)rmidi->device == midi_map[rmidi->card->number]) { if ((int)rmidi->device == midi_map[rmidi->card->number]) {
@ -1615,17 +1607,9 @@ static int snd_rawmidi_dev_unregister(struct snd_device *device)
rmidi->ossreg = 0; rmidi->ossreg = 0;
} }
#endif /* CONFIG_SND_OSSEMUL */ #endif /* CONFIG_SND_OSSEMUL */
if (rmidi->ops && rmidi->ops->dev_unregister)
rmidi->ops->dev_unregister(rmidi);
snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) return 0;
if (rmidi->seq_dev) {
snd_device_free(rmidi->card, rmidi->seq_dev);
rmidi->seq_dev = NULL;
}
#endif
return snd_rawmidi_free(rmidi);
} }
/** /**

View File

@ -156,7 +156,7 @@ static int __init rtctimer_init(void)
static void __exit rtctimer_exit(void) static void __exit rtctimer_exit(void)
{ {
if (rtctimer) { if (rtctimer) {
snd_timer_global_unregister(rtctimer); snd_timer_global_free(rtctimer);
rtctimer = NULL; rtctimer = NULL;
} }
} }

View File

@ -303,8 +303,7 @@ register_proc(void)
static void static void
unregister_proc(void) unregister_proc(void)
{ {
if (info_entry) snd_info_free_entry(info_entry);
snd_info_unregister(info_entry);
info_entry = NULL; info_entry = NULL;
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */

View File

@ -90,7 +90,6 @@ static int snd_seq_device_free(struct snd_seq_device *dev);
static int snd_seq_device_dev_free(struct snd_device *device); static int snd_seq_device_dev_free(struct snd_device *device);
static int snd_seq_device_dev_register(struct snd_device *device); static int snd_seq_device_dev_register(struct snd_device *device);
static int snd_seq_device_dev_disconnect(struct snd_device *device); static int snd_seq_device_dev_disconnect(struct snd_device *device);
static int snd_seq_device_dev_unregister(struct snd_device *device);
static int init_device(struct snd_seq_device *dev, struct ops_list *ops); static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
static int free_device(struct snd_seq_device *dev, struct ops_list *ops); static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
@ -189,7 +188,6 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
.dev_free = snd_seq_device_dev_free, .dev_free = snd_seq_device_dev_free,
.dev_register = snd_seq_device_dev_register, .dev_register = snd_seq_device_dev_register,
.dev_disconnect = snd_seq_device_dev_disconnect, .dev_disconnect = snd_seq_device_dev_disconnect,
.dev_unregister = snd_seq_device_dev_unregister
}; };
if (result) if (result)
@ -308,15 +306,6 @@ static int snd_seq_device_dev_disconnect(struct snd_device *device)
return 0; return 0;
} }
/*
* unregister the existing device
*/
static int snd_seq_device_dev_unregister(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
return snd_seq_device_free(dev);
}
/* /*
* register device driver * register device driver
* id = driver id * id = driver id
@ -573,7 +562,7 @@ static void __exit alsa_seq_device_exit(void)
{ {
remove_drivers(); remove_drivers();
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
snd_info_unregister(info_entry); snd_info_free_entry(info_entry);
#endif #endif
if (num_ops) if (num_ops)
snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops); snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);

View File

@ -64,9 +64,9 @@ int __init snd_seq_info_init(void)
int __exit snd_seq_info_done(void) int __exit snd_seq_info_done(void)
{ {
snd_info_unregister(queues_entry); snd_info_free_entry(queues_entry);
snd_info_unregister(clients_entry); snd_info_free_entry(clients_entry);
snd_info_unregister(timer_entry); snd_info_free_entry(timer_entry);
return 0; return 0;
} }
#endif #endif

View File

@ -268,7 +268,11 @@ int snd_register_device(int type, struct snd_card *card, int dev,
snd_minors[minor] = preg; snd_minors[minor] = preg;
if (card) if (card)
device = card->dev; device = card->dev;
class_device_create(sound_class, NULL, MKDEV(major, minor), device, "%s", name); preg->class_dev = class_device_create(sound_class, NULL,
MKDEV(major, minor),
device, "%s", name);
if (preg->class_dev)
class_set_devdata(preg->class_dev, private_data);
mutex_unlock(&sound_mutex); mutex_unlock(&sound_mutex);
return 0; return 0;
@ -276,6 +280,24 @@ int snd_register_device(int type, struct snd_card *card, int dev,
EXPORT_SYMBOL(snd_register_device); EXPORT_SYMBOL(snd_register_device);
/* find the matching minor record
* return the index of snd_minor, or -1 if not found
*/
static int find_snd_minor(int type, struct snd_card *card, int dev)
{
int cardnum, minor;
struct snd_minor *mptr;
cardnum = card ? card->number : -1;
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
if ((mptr = snd_minors[minor]) != NULL &&
mptr->type == type &&
mptr->card == cardnum &&
mptr->device == dev)
return minor;
return -1;
}
/** /**
* snd_unregister_device - unregister the device on the given card * snd_unregister_device - unregister the device on the given card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX * @type: the device type, SNDRV_DEVICE_TYPE_XXX
@ -289,32 +311,42 @@ EXPORT_SYMBOL(snd_register_device);
*/ */
int snd_unregister_device(int type, struct snd_card *card, int dev) int snd_unregister_device(int type, struct snd_card *card, int dev)
{ {
int cardnum, minor; int minor;
struct snd_minor *mptr;
cardnum = card ? card->number : -1;
mutex_lock(&sound_mutex); mutex_lock(&sound_mutex);
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) minor = find_snd_minor(type, card, dev);
if ((mptr = snd_minors[minor]) != NULL && if (minor < 0) {
mptr->type == type &&
mptr->card == cardnum &&
mptr->device == dev)
break;
if (minor == ARRAY_SIZE(snd_minors)) {
mutex_unlock(&sound_mutex); mutex_unlock(&sound_mutex);
return -EINVAL; return -EINVAL;
} }
class_device_destroy(sound_class, MKDEV(major, minor)); class_device_destroy(sound_class, MKDEV(major, minor));
kfree(snd_minors[minor]);
snd_minors[minor] = NULL; snd_minors[minor] = NULL;
mutex_unlock(&sound_mutex); mutex_unlock(&sound_mutex);
kfree(mptr);
return 0; return 0;
} }
EXPORT_SYMBOL(snd_unregister_device); EXPORT_SYMBOL(snd_unregister_device);
int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
const struct class_device_attribute *attr)
{
int minor, ret = -EINVAL;
struct class_device *cdev;
mutex_lock(&sound_mutex);
minor = find_snd_minor(type, card, dev);
if (minor >= 0 && (cdev = snd_minors[minor]->class_dev) != NULL)
ret = class_device_create_file(cdev, attr);
mutex_unlock(&sound_mutex);
return ret;
}
EXPORT_SYMBOL(snd_add_device_sysfs_file);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
/* /*
* INFO PART * INFO PART
@ -387,8 +419,7 @@ int __init snd_minor_info_init(void)
int __exit snd_minor_info_done(void) int __exit snd_minor_info_done(void)
{ {
if (snd_minor_info_entry) snd_info_free_entry(snd_minor_info_entry);
snd_info_unregister(snd_minor_info_entry);
return 0; return 0;
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */

View File

@ -270,8 +270,7 @@ int __init snd_minor_info_oss_init(void)
int __exit snd_minor_info_oss_done(void) int __exit snd_minor_info_oss_done(void)
{ {
if (snd_minor_info_oss_entry) snd_info_free_entry(snd_minor_info_oss_entry);
snd_info_unregister(snd_minor_info_oss_entry);
return 0; return 0;
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */

View File

@ -88,7 +88,7 @@ static DEFINE_MUTEX(register_mutex);
static int snd_timer_free(struct snd_timer *timer); static int snd_timer_free(struct snd_timer *timer);
static int snd_timer_dev_free(struct snd_device *device); static int snd_timer_dev_free(struct snd_device *device);
static int snd_timer_dev_register(struct snd_device *device); static int snd_timer_dev_register(struct snd_device *device);
static int snd_timer_dev_unregister(struct snd_device *device); static int snd_timer_dev_disconnect(struct snd_device *device);
static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);
@ -718,7 +718,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
} }
} }
if (timer->flags & SNDRV_TIMER_FLG_RESCHED) if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
snd_timer_reschedule(timer, ticks_left); snd_timer_reschedule(timer, timer->sticks);
if (timer->running) { if (timer->running) {
if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
timer->hw.stop(timer); timer->hw.stop(timer);
@ -773,7 +773,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = snd_timer_dev_free, .dev_free = snd_timer_dev_free,
.dev_register = snd_timer_dev_register, .dev_register = snd_timer_dev_register,
.dev_unregister = snd_timer_dev_unregister .dev_disconnect = snd_timer_dev_disconnect,
}; };
snd_assert(tid != NULL, return -EINVAL); snd_assert(tid != NULL, return -EINVAL);
@ -813,6 +813,21 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
static int snd_timer_free(struct snd_timer *timer) static int snd_timer_free(struct snd_timer *timer)
{ {
snd_assert(timer != NULL, return -ENXIO); snd_assert(timer != NULL, return -ENXIO);
mutex_lock(&register_mutex);
if (! list_empty(&timer->open_list_head)) {
struct list_head *p, *n;
struct snd_timer_instance *ti;
snd_printk(KERN_WARNING "timer %p is busy?\n", timer);
list_for_each_safe(p, n, &timer->open_list_head) {
list_del_init(p);
ti = list_entry(p, struct snd_timer_instance, open_list);
ti->timer = NULL;
}
}
list_del(&timer->device_list);
mutex_unlock(&register_mutex);
if (timer->private_free) if (timer->private_free)
timer->private_free(timer); timer->private_free(timer);
kfree(timer); kfree(timer);
@ -867,30 +882,13 @@ static int snd_timer_dev_register(struct snd_device *dev)
return 0; return 0;
} }
static int snd_timer_unregister(struct snd_timer *timer) static int snd_timer_dev_disconnect(struct snd_device *device)
{
struct list_head *p, *n;
struct snd_timer_instance *ti;
snd_assert(timer != NULL, return -ENXIO);
mutex_lock(&register_mutex);
if (! list_empty(&timer->open_list_head)) {
snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer);
list_for_each_safe(p, n, &timer->open_list_head) {
list_del_init(p);
ti = list_entry(p, struct snd_timer_instance, open_list);
ti->timer = NULL;
}
}
list_del(&timer->device_list);
mutex_unlock(&register_mutex);
return snd_timer_free(timer);
}
static int snd_timer_dev_unregister(struct snd_device *device)
{ {
struct snd_timer *timer = device->device_data; struct snd_timer *timer = device->device_data;
return snd_timer_unregister(timer); mutex_lock(&register_mutex);
list_del_init(&timer->device_list);
mutex_unlock(&register_mutex);
return 0;
} }
void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp) void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
@ -955,18 +953,12 @@ int snd_timer_global_register(struct snd_timer *timer)
return snd_timer_dev_register(&dev); return snd_timer_dev_register(&dev);
} }
int snd_timer_global_unregister(struct snd_timer *timer)
{
return snd_timer_unregister(timer);
}
/* /*
* System timer * System timer
*/ */
struct snd_timer_system_private { struct snd_timer_system_private {
struct timer_list tlist; struct timer_list tlist;
struct timer * timer;
unsigned long last_expires; unsigned long last_expires;
unsigned long last_jiffies; unsigned long last_jiffies;
unsigned long correction; unsigned long correction;
@ -978,7 +970,7 @@ static void snd_timer_s_function(unsigned long data)
struct snd_timer_system_private *priv = timer->private_data; struct snd_timer_system_private *priv = timer->private_data;
unsigned long jiff = jiffies; unsigned long jiff = jiffies;
if (time_after(jiff, priv->last_expires)) if (time_after(jiff, priv->last_expires))
priv->correction = (long)jiff - (long)priv->last_expires; priv->correction += (long)jiff - (long)priv->last_expires;
snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
} }
@ -994,7 +986,7 @@ static int snd_timer_s_start(struct snd_timer * timer)
njiff++; njiff++;
} else { } else {
njiff += timer->sticks - priv->correction; njiff += timer->sticks - priv->correction;
priv->correction -= timer->sticks; priv->correction = 0;
} }
priv->last_expires = priv->tlist.expires = njiff; priv->last_expires = priv->tlist.expires = njiff;
add_timer(&priv->tlist); add_timer(&priv->tlist);
@ -1013,6 +1005,7 @@ static int snd_timer_s_stop(struct snd_timer * timer)
timer->sticks = priv->last_expires - jiff; timer->sticks = priv->last_expires - jiff;
else else
timer->sticks = 1; timer->sticks = 1;
priv->correction = 0;
return 0; return 0;
} }
@ -1126,7 +1119,7 @@ static void __init snd_timer_proc_init(void)
static void __exit snd_timer_proc_done(void) static void __exit snd_timer_proc_done(void)
{ {
snd_info_unregister(snd_timer_proc_entry); snd_info_free_entry(snd_timer_proc_entry);
} }
#else /* !CONFIG_PROC_FS */ #else /* !CONFIG_PROC_FS */
#define snd_timer_proc_init() #define snd_timer_proc_init()
@ -1982,7 +1975,7 @@ static void __exit alsa_timer_exit(void)
/* unregister the system timer */ /* unregister the system timer */
list_for_each_safe(p, n, &snd_timer_list) { list_for_each_safe(p, n, &snd_timer_list) {
struct snd_timer *timer = list_entry(p, struct snd_timer, device_list); struct snd_timer *timer = list_entry(p, struct snd_timer, device_list);
snd_timer_unregister(timer); snd_timer_free(timer);
} }
snd_timer_proc_done(); snd_timer_proc_done();
#ifdef SNDRV_OSS_INFO_DEV_TIMERS #ifdef SNDRV_OSS_INFO_DEV_TIMERS
@ -2005,5 +1998,4 @@ EXPORT_SYMBOL(snd_timer_notify);
EXPORT_SYMBOL(snd_timer_global_new); EXPORT_SYMBOL(snd_timer_global_new);
EXPORT_SYMBOL(snd_timer_global_free); EXPORT_SYMBOL(snd_timer_global_free);
EXPORT_SYMBOL(snd_timer_global_register); EXPORT_SYMBOL(snd_timer_global_register);
EXPORT_SYMBOL(snd_timer_global_unregister);
EXPORT_SYMBOL(snd_timer_interrupt); EXPORT_SYMBOL(snd_timer_interrupt);

View File

@ -73,6 +73,19 @@ config SND_MTPAV
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-mtpav. will be called snd-mtpav.
config SND_MTS64
tristate "ESI Miditerminal 4140 driver"
depends on SND && PARPORT
select SND_RAWMIDI
help
The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with
additional SMPTE Timecode capabilities for the parallel port.
Say 'Y' to include support for this device.
To compile this driver as a module, chose 'M' here: the module
will be called snd-mts64.
config SND_SERIAL_U16550 config SND_SERIAL_U16550
tristate "UART16550 serial MIDI driver" tristate "UART16550 serial MIDI driver"
depends on SND depends on SND

View File

@ -5,6 +5,7 @@
snd-dummy-objs := dummy.o snd-dummy-objs := dummy.o
snd-mtpav-objs := mtpav.o snd-mtpav-objs := mtpav.o
snd-mts64-objs := mts64.o
snd-serial-u16550-objs := serial-u16550.o snd-serial-u16550-objs := serial-u16550.o
snd-virmidi-objs := virmidi.o snd-virmidi-objs := virmidi.o
@ -13,5 +14,6 @@ obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/

View File

@ -29,6 +29,7 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/rawmidi.h> #include <sound/rawmidi.h>
#include <sound/initval.h> #include <sound/initval.h>
@ -285,7 +286,7 @@ static struct snd_pcm_hardware snd_card_dummy_playback =
.channels_max = USE_CHANNELS_MAX, .channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = MAX_BUFFER_SIZE, .buffer_bytes_max = MAX_BUFFER_SIZE,
.period_bytes_min = 64, .period_bytes_min = 64,
.period_bytes_max = MAX_BUFFER_SIZE, .period_bytes_max = MAX_PERIOD_SIZE,
.periods_min = USE_PERIODS_MIN, .periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX, .periods_max = USE_PERIODS_MAX,
.fifo_size = 0, .fifo_size = 0,
@ -443,10 +444,13 @@ static int __init snd_card_dummy_pcm(struct snd_dummy *dummy, int device, int su
} }
#define DUMMY_VOLUME(xname, xindex, addr) \ #define DUMMY_VOLUME(xname, xindex, addr) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .index = xindex, \
.info = snd_dummy_volume_info, \ .info = snd_dummy_volume_info, \
.get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \ .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \
.private_value = addr } .private_value = addr, \
.tlv = { .p = db_scale_dummy } }
static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol, static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
@ -497,6 +501,8 @@ static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
#define DUMMY_CAPSRC(xname, xindex, addr) \ #define DUMMY_CAPSRC(xname, xindex, addr) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_dummy_capsrc_info, \ .info = snd_dummy_capsrc_info, \
@ -547,13 +553,13 @@ static struct snd_kcontrol_new snd_dummy_controls[] = {
DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER), DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH),
DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER), DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE),
DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER), DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC),
DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER) DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD)
}; };
static int __init snd_card_dummy_new_mixer(struct snd_dummy *dummy) static int __init snd_card_dummy_new_mixer(struct snd_dummy *dummy)

View File

@ -211,7 +211,7 @@ static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev)
struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev); struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev);
snd_card_disconnect(card); snd_card_disconnect(card);
snd_card_free_in_thread(card); snd_card_free_when_closed(card);
} }
static struct pnp_driver snd_mpu401_pnp_driver = { static struct pnp_driver snd_mpu401_pnp_driver = {

1091
sound/drivers/mts64.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -105,13 +105,13 @@ static long long snd_opl4_mem_proc_llseek(struct snd_info_entry *entry, void *fi
struct file *file, long long offset, int orig) struct file *file, long long offset, int orig)
{ {
switch (orig) { switch (orig) {
case 0: /* SEEK_SET */ case SEEK_SET:
file->f_pos = offset; file->f_pos = offset;
break; break;
case 1: /* SEEK_CUR */ case SEEK_CUR:
file->f_pos += offset; file->f_pos += offset;
break; break;
case 2: /* SEEK_END, offset is negative */ case SEEK_END: /* offset is negative */
file->f_pos = entry->size + offset; file->f_pos = entry->size + offset;
break; break;
default: default:
@ -159,8 +159,7 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
void snd_opl4_free_proc(struct snd_opl4 *opl4) void snd_opl4_free_proc(struct snd_opl4 *opl4)
{ {
if (opl4->proc_entry) snd_info_free_entry(opl4->proc_entry);
snd_info_unregister(opl4->proc_entry);
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */

View File

@ -23,6 +23,7 @@
#include <sound/driver.h> #include <sound/driver.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <sound/vx_core.h> #include <sound/vx_core.h>
#include "vx_cmd.h" #include "vx_cmd.h"
@ -455,10 +456,13 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static struct snd_kcontrol_new vx_control_output_level = { static struct snd_kcontrol_new vx_control_output_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume", .name = "Master Playback Volume",
.info = vx_output_level_info, .info = vx_output_level_info,
.get = vx_output_level_get, .get = vx_output_level_get,
.put = vx_output_level_put, .put = vx_output_level_put,
/* tlv will be filled later */
}; };
/* /*
@ -712,12 +716,17 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return 0; return 0;
} }
static DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
static struct snd_kcontrol_new vx_control_audio_gain = { static struct snd_kcontrol_new vx_control_audio_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */ /* name will be filled later */
.info = vx_audio_gain_info, .info = vx_audio_gain_info,
.get = vx_audio_gain_get, .get = vx_audio_gain_get,
.put = vx_audio_gain_put .put = vx_audio_gain_put,
.tlv = { .p = db_scale_audio_gain },
}; };
static struct snd_kcontrol_new vx_control_output_switch = { static struct snd_kcontrol_new vx_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -729,9 +738,12 @@ static struct snd_kcontrol_new vx_control_output_switch = {
static struct snd_kcontrol_new vx_control_monitor_gain = { static struct snd_kcontrol_new vx_control_monitor_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Volume", .name = "Monitoring Volume",
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = vx_audio_gain_info, /* shared */ .info = vx_audio_gain_info, /* shared */
.get = vx_audio_monitor_get, .get = vx_audio_monitor_get,
.put = vx_audio_monitor_put .put = vx_audio_monitor_put,
.tlv = { .p = db_scale_audio_gain },
}; };
static struct snd_kcontrol_new vx_control_monitor_switch = { static struct snd_kcontrol_new vx_control_monitor_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -918,6 +930,7 @@ int snd_vx_mixer_new(struct vx_core *chip)
for (i = 0; i < chip->hw->num_outs; i++) { for (i = 0; i < chip->hw->num_outs; i++) {
temp = vx_control_output_level; temp = vx_control_output_level;
temp.index = i; temp.index = i;
temp.tlv.p = chip->hw->output_level_db_scale;
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
return err; return err;
} }

View File

@ -28,12 +28,14 @@
#include <linux/init.h> #include <linux/init.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <sound/ak4xxx-adda.h> #include <sound/ak4xxx-adda.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>"); MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters"); MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* write the given register and save the data to the cache */
void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
unsigned char val) unsigned char val)
{ {
@ -41,15 +43,7 @@ void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
ak->ops.write(ak, chip, reg, val); ak->ops.write(ak, chip, reg, val);
/* save the data */ /* save the data */
if (ak->type == SND_AK4524 || ak->type == SND_AK4528) {
if ((reg != 0x04 && reg != 0x05) || (val & 0x80) == 0)
snd_akm4xxx_set(ak, chip, reg, val); snd_akm4xxx_set(ak, chip, reg, val);
else
snd_akm4xxx_set_ipga(ak, chip, reg, val);
} else {
/* AK4529, or else */
snd_akm4xxx_set(ak, chip, reg, val);
}
ak->ops.unlock(ak, chip); ak->ops.unlock(ak, chip);
} }
@ -73,12 +67,6 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state)
for (reg = 0x04; reg < maxreg; reg++) for (reg = 0x04; reg < maxreg; reg++)
snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg)); snd_akm4xxx_get(ak, chip, reg));
if (ak->type == SND_AK4528)
continue;
/* IPGA */
for (reg = 0x04; reg < 0x06; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get_ipga(ak, chip, reg));
} }
} }
@ -137,11 +125,48 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
case SND_AK4381: case SND_AK4381:
ak4381_reset(ak, state); ak4381_reset(ak, state);
break; break;
default:
break;
} }
} }
EXPORT_SYMBOL(snd_akm4xxx_reset); EXPORT_SYMBOL(snd_akm4xxx_reset);
/*
* Volume conversion table for non-linear volumes
* from -63.5dB (mute) to 0dB step 0.5dB
*
* Used for AK4524 input/ouput attenuation, AK4528, and
* AK5365 input attenuation
*/
static unsigned char vol_cvt_datt[128] = {
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a,
0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f,
0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1c,
0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x23,
0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2d,
0x2e, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x3f, 0x40,
0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a,
0x4b, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54,
0x55, 0x56, 0x58, 0x59, 0x5b, 0x5c, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x69,
0x6a, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x72, 0x73,
0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f,
};
/*
* dB tables
*/
static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
/* /*
* initialize all the ak4xxx chips * initialize all the ak4xxx chips
*/ */
@ -155,8 +180,6 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x01, 0x03, /* 1: ADC/DAC enable */ 0x01, 0x03, /* 1: ADC/DAC enable */
0x04, 0x00, /* 4: ADC left muted */ 0x04, 0x00, /* 4: ADC left muted */
0x05, 0x00, /* 5: ADC right muted */ 0x05, 0x00, /* 5: ADC right muted */
0x04, 0x80, /* 4: ADC IPGA gain 0dB */
0x05, 0x80, /* 5: ADC IPGA gain 0dB */
0x06, 0x00, /* 6: DAC left muted */ 0x06, 0x00, /* 6: DAC left muted */
0x07, 0x00, /* 7: DAC right muted */ 0x07, 0x00, /* 7: DAC right muted */
0xff, 0xff 0xff, 0xff
@ -238,6 +261,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
int chip, num_chips; int chip, num_chips;
unsigned char *ptr, reg, data, *inits; unsigned char *ptr, reg, data, *inits;
memset(ak->images, 0, sizeof(ak->images));
memset(ak->volumes, 0, sizeof(ak->volumes));
switch (ak->type) { switch (ak->type) {
case SND_AK4524: case SND_AK4524:
inits = inits_ak4524; inits = inits_ak4524;
@ -263,6 +289,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
inits = inits_ak4381; inits = inits_ak4381;
num_chips = ak->num_dacs / 2; num_chips = ak->num_dacs / 2;
break; break;
case SND_AK5365:
/* FIXME: any init sequence? */
return;
default: default:
snd_BUG(); snd_BUG();
return; return;
@ -280,14 +309,23 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
EXPORT_SYMBOL(snd_akm4xxx_init); EXPORT_SYMBOL(snd_akm4xxx_init);
/*
* Mixer callbacks
*/
#define AK_IPGA (1<<20) /* including IPGA */
#define AK_VOL_CVT (1<<21) /* need dB conversion */
#define AK_NEEDSMSB (1<<22) /* need MSB update bit */
#define AK_INVERT (1<<23) /* data is inverted */
#define AK_GET_CHIP(val) (((val) >> 8) & 0xff) #define AK_GET_CHIP(val) (((val) >> 8) & 0xff)
#define AK_GET_ADDR(val) ((val) & 0xff) #define AK_GET_ADDR(val) ((val) & 0xff)
#define AK_GET_SHIFT(val) (((val) >> 16) & 0x7f) #define AK_GET_SHIFT(val) (((val) >> 16) & 0x0f)
#define AK_GET_VOL_CVT(val) (((val) >> 21) & 1)
#define AK_GET_IPGA(val) (((val) >> 20) & 1)
#define AK_GET_NEEDSMSB(val) (((val) >> 22) & 1)
#define AK_GET_INVERT(val) (((val) >> 23) & 1) #define AK_GET_INVERT(val) (((val) >> 23) & 1)
#define AK_GET_MASK(val) (((val) >> 24) & 0xff) #define AK_GET_MASK(val) (((val) >> 24) & 0xff)
#define AK_COMPOSE(chip,addr,shift,mask) \ #define AK_COMPOSE(chip,addr,shift,mask) \
(((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24)) (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
#define AK_INVERT (1<<23)
static int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol, static int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
@ -307,31 +345,39 @@ static int snd_akm4xxx_volume_get(struct snd_kcontrol *kcontrol,
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value); int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value);
unsigned int mask = AK_GET_MASK(kcontrol->private_value);
unsigned char val = snd_akm4xxx_get(ak, chip, addr);
ucontrol->value.integer.value[0] = invert ? mask - val : val; ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
return 0; return 0;
} }
static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
unsigned char nval)
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
unsigned int mask = AK_GET_MASK(kcontrol->private_value);
int chip = AK_GET_CHIP(kcontrol->private_value);
if (snd_akm4xxx_get_vol(ak, chip, addr) == nval)
return 0;
snd_akm4xxx_set_vol(ak, chip, addr, nval);
if (AK_GET_VOL_CVT(kcontrol->private_value) && nval < 128)
nval = vol_cvt_datt[nval];
if (AK_GET_IPGA(kcontrol->private_value) && nval >= 128)
nval++; /* need to correct + 1 since both 127 and 128 are 0dB */
if (AK_GET_INVERT(kcontrol->private_value))
nval = mask - nval;
if (AK_GET_NEEDSMSB(kcontrol->private_value))
nval |= 0x80;
snd_akm4xxx_write(ak, chip, addr, nval);
return 1;
}
static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol, static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value),
int chip = AK_GET_CHIP(kcontrol->private_value); ucontrol->value.integer.value[0]);
int addr = AK_GET_ADDR(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value);
unsigned int mask = AK_GET_MASK(kcontrol->private_value);
unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
int change;
if (invert)
nval = mask - nval;
change = snd_akm4xxx_get(ak, chip, addr) != nval;
if (change)
snd_akm4xxx_write(ak, chip, addr, nval);
return change;
} }
static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol, static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
@ -352,77 +398,21 @@ static int snd_akm4xxx_stereo_volume_get(struct snd_kcontrol *kcontrol,
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value); int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value);
unsigned int mask = AK_GET_MASK(kcontrol->private_value);
unsigned char val = snd_akm4xxx_get(ak, chip, addr);
ucontrol->value.integer.value[0] = invert ? mask - val : val;
val = snd_akm4xxx_get(ak, chip, addr+1);
ucontrol->value.integer.value[1] = invert ? mask - val : val;
ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
ucontrol->value.integer.value[1] = snd_akm4xxx_get_vol(ak, chip, addr+1);
return 0; return 0;
} }
static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol, static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value); int change;
unsigned int mask = AK_GET_MASK(kcontrol->private_value);
unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
int change0, change1;
if (invert) change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]);
nval = mask - nval; change |= put_ak_reg(kcontrol, addr + 1,
change0 = snd_akm4xxx_get(ak, chip, addr) != nval; ucontrol->value.integer.value[1]);
if (change0)
snd_akm4xxx_write(ak, chip, addr, nval);
nval = ucontrol->value.integer.value[1] % (mask+1);
if (invert)
nval = mask - nval;
change1 = snd_akm4xxx_get(ak, chip, addr+1) != nval;
if (change1)
snd_akm4xxx_write(ak, chip, addr+1, nval);
return change0 || change1;
}
static int snd_akm4xxx_ipga_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 36;
return 0;
}
static int snd_akm4xxx_ipga_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
ucontrol->value.integer.value[0] =
snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f;
return 0;
}
static int snd_akm4xxx_ipga_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
int change = snd_akm4xxx_get_ipga(ak, chip, addr) != nval;
if (change)
snd_akm4xxx_write(ak, chip, addr, nval);
return change; return change;
} }
@ -472,178 +462,279 @@ static int snd_akm4xxx_deemphasis_put(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static int ak4xxx_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
int shift = AK_GET_SHIFT(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value);
unsigned char val = snd_akm4xxx_get(ak, chip, addr);
if (invert)
val = ! val;
ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
return 0;
}
static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
int shift = AK_GET_SHIFT(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value);
long flag = ucontrol->value.integer.value[0];
unsigned char val, oval;
int change;
if (invert)
flag = ! flag;
oval = snd_akm4xxx_get(ak, chip, addr);
if (flag)
val = oval | (1<<shift);
else
val = oval & ~(1<<shift);
change = (oval != val);
if (change)
snd_akm4xxx_write(ak, chip, addr, val);
return change;
}
/* /*
* build AK4xxx controls * build AK4xxx controls
*/ */
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) static int build_dac_controls(struct snd_akm4xxx *ak)
{ {
unsigned int idx, num_emphs; int idx, err, mixer_ch, num_stereo;
struct snd_kcontrol *ctl; struct snd_kcontrol_new knew;
int err;
int mixer_ch = 0;
int num_stereo;
ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
if (! ctl)
return -ENOMEM;
mixer_ch = 0;
for (idx = 0; idx < ak->num_dacs; ) { for (idx = 0; idx < ak->num_dacs; ) {
memset(ctl, 0, sizeof(*ctl)); memset(&knew, 0, sizeof(knew));
if (ak->channel_names == NULL) { if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
strcpy(ctl->id.name, "DAC Volume"); knew.name = "DAC Volume";
knew.index = mixer_ch + ak->idx_offset * 2;
num_stereo = 1; num_stereo = 1;
ctl->id.index = mixer_ch + ak->idx_offset * 2;
} else { } else {
strcpy(ctl->id.name, ak->channel_names[mixer_ch]); knew.name = ak->dac_info[mixer_ch].name;
num_stereo = ak->num_stereo[mixer_ch]; num_stereo = ak->dac_info[mixer_ch].num_channels;
ctl->id.index = 0;
} }
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
ctl->count = 1; knew.count = 1;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
if (num_stereo == 2) { if (num_stereo == 2) {
ctl->info = snd_akm4xxx_stereo_volume_info; knew.info = snd_akm4xxx_stereo_volume_info;
ctl->get = snd_akm4xxx_stereo_volume_get; knew.get = snd_akm4xxx_stereo_volume_get;
ctl->put = snd_akm4xxx_stereo_volume_put; knew.put = snd_akm4xxx_stereo_volume_put;
} else { } else {
ctl->info = snd_akm4xxx_volume_info; knew.info = snd_akm4xxx_volume_info;
ctl->get = snd_akm4xxx_volume_get; knew.get = snd_akm4xxx_volume_get;
ctl->put = snd_akm4xxx_volume_put; knew.put = snd_akm4xxx_volume_put;
} }
switch (ak->type) { switch (ak->type) {
case SND_AK4524: case SND_AK4524:
/* register 6 & 7 */ /* register 6 & 7 */
ctl->private_value = knew.private_value =
AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127) |
AK_VOL_CVT;
knew.tlv.p = db_scale_vol_datt;
break; break;
case SND_AK4528: case SND_AK4528:
/* register 4 & 5 */ /* register 4 & 5 */
ctl->private_value = knew.private_value =
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) |
AK_VOL_CVT;
knew.tlv.p = db_scale_vol_datt;
break; break;
case SND_AK4529: { case SND_AK4529: {
/* registers 2-7 and b,c */ /* registers 2-7 and b,c */
int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb;
ctl->private_value = knew.private_value =
AK_COMPOSE(0, val, 0, 255) | AK_INVERT; AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
knew.tlv.p = db_scale_8bit;
break; break;
} }
case SND_AK4355: case SND_AK4355:
/* register 4-9, chip #0 only */ /* register 4-9, chip #0 only */
ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); knew.private_value = AK_COMPOSE(0, idx + 4, 0, 255);
knew.tlv.p = db_scale_8bit;
break; break;
case SND_AK4358: case SND_AK4358: {
if (idx >= 6) /* register 4-9 and 11-12, chip #0 only */
/* register 4-9, chip #0 only */ int addr = idx < 6 ? idx + 4 : idx + 5;
ctl->private_value = knew.private_value =
AK_COMPOSE(0, idx + 5, 0, 255); AK_COMPOSE(0, addr, 0, 127) | AK_NEEDSMSB;
else knew.tlv.p = db_scale_7bit;
/* register 4-9, chip #0 only */
ctl->private_value =
AK_COMPOSE(0, idx + 4, 0, 255);
break; break;
}
case SND_AK4381: case SND_AK4381:
/* register 3 & 4 */ /* register 3 & 4 */
ctl->private_value = knew.private_value =
AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
knew.tlv.p = db_scale_linear;
break; break;
default: default:
err = -EINVAL; return -EINVAL;
goto __error;
} }
ctl->private_data = ak; err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
err = snd_ctl_add(ak->card,
snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
SNDRV_CTL_ELEM_ACCESS_WRITE));
if (err < 0) if (err < 0)
goto __error; return err;
idx += num_stereo; idx += num_stereo;
mixer_ch++; mixer_ch++;
} }
for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) { return 0;
memset(ctl, 0, sizeof(*ctl));
strcpy(ctl->id.name, "ADC Volume");
ctl->id.index = idx + ak->idx_offset * 2;
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
ctl->count = 1;
ctl->info = snd_akm4xxx_volume_info;
ctl->get = snd_akm4xxx_volume_get;
ctl->put = snd_akm4xxx_volume_put;
/* register 4 & 5 */
ctl->private_value =
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127);
ctl->private_data = ak;
err = snd_ctl_add(ak->card,
snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
SNDRV_CTL_ELEM_ACCESS_WRITE));
if (err < 0)
goto __error;
memset(ctl, 0, sizeof(*ctl));
strcpy(ctl->id.name, "IPGA Analog Capture Volume");
ctl->id.index = idx + ak->idx_offset * 2;
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
ctl->count = 1;
ctl->info = snd_akm4xxx_ipga_gain_info;
ctl->get = snd_akm4xxx_ipga_gain_get;
ctl->put = snd_akm4xxx_ipga_gain_put;
/* register 4 & 5 */
ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0);
ctl->private_data = ak;
err = snd_ctl_add(ak->card,
snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
SNDRV_CTL_ELEM_ACCESS_WRITE));
if (err < 0)
goto __error;
} }
if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
num_emphs = 1; static int build_adc_controls(struct snd_akm4xxx *ak)
{
int idx, err, mixer_ch, num_stereo;
struct snd_kcontrol_new knew;
mixer_ch = 0;
for (idx = 0; idx < ak->num_adcs;) {
memset(&knew, 0, sizeof(knew));
if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
knew.name = "ADC Volume";
knew.index = mixer_ch + ak->idx_offset * 2;
num_stereo = 1;
} else {
knew.name = ak->adc_info[mixer_ch].name;
num_stereo = ak->adc_info[mixer_ch].num_channels;
}
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.count = 1;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
if (num_stereo == 2) {
knew.info = snd_akm4xxx_stereo_volume_info;
knew.get = snd_akm4xxx_stereo_volume_get;
knew.put = snd_akm4xxx_stereo_volume_put;
} else {
knew.info = snd_akm4xxx_volume_info;
knew.get = snd_akm4xxx_volume_get;
knew.put = snd_akm4xxx_volume_put;
}
/* register 4 & 5 */
if (ak->type == SND_AK5365)
knew.private_value =
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
AK_VOL_CVT | AK_IPGA;
else else
num_emphs = ak->num_dacs / 2; knew.private_value =
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
AK_VOL_CVT | AK_IPGA;
knew.tlv.p = db_scale_vol_datt;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
return err;
if (ak->type == SND_AK5365 && (idx % 2) == 0) {
if (! ak->adc_info ||
! ak->adc_info[mixer_ch].switch_name)
knew.name = "Capture Switch";
else
knew.name = ak->adc_info[mixer_ch].switch_name;
knew.info = ak4xxx_switch_info;
knew.get = ak4xxx_switch_get;
knew.put = ak4xxx_switch_put;
knew.access = 0;
/* register 2, bit 0 (SMUTE): 0 = normal operation,
1 = mute */
knew.private_value =
AK_COMPOSE(idx/2, 2, 0, 0) | AK_INVERT;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
return err;
}
idx += num_stereo;
mixer_ch++;
}
return 0;
}
static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
{
int idx, err;
struct snd_kcontrol_new knew;
for (idx = 0; idx < num_emphs; idx++) { for (idx = 0; idx < num_emphs; idx++) {
memset(ctl, 0, sizeof(*ctl)); memset(&knew, 0, sizeof(knew));
strcpy(ctl->id.name, "Deemphasis"); knew.name = "Deemphasis";
ctl->id.index = idx + ak->idx_offset; knew.index = idx + ak->idx_offset;
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
ctl->count = 1; knew.count = 1;
ctl->info = snd_akm4xxx_deemphasis_info; knew.info = snd_akm4xxx_deemphasis_info;
ctl->get = snd_akm4xxx_deemphasis_get; knew.get = snd_akm4xxx_deemphasis_get;
ctl->put = snd_akm4xxx_deemphasis_put; knew.put = snd_akm4xxx_deemphasis_put;
switch (ak->type) { switch (ak->type) {
case SND_AK4524: case SND_AK4524:
case SND_AK4528: case SND_AK4528:
/* register 3 */ /* register 3 */
ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break; break;
case SND_AK4529: { case SND_AK4529: {
int shift = idx == 3 ? 6 : (2 - idx) * 2; int shift = idx == 3 ? 6 : (2 - idx) * 2;
/* register 8 with shift */ /* register 8 with shift */
ctl->private_value = AK_COMPOSE(0, 8, shift, 0); knew.private_value = AK_COMPOSE(0, 8, shift, 0);
break; break;
} }
case SND_AK4355: case SND_AK4355:
case SND_AK4358: case SND_AK4358:
ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break; break;
case SND_AK4381: case SND_AK4381:
ctl->private_value = AK_COMPOSE(idx, 1, 1, 0); knew.private_value = AK_COMPOSE(idx, 1, 1, 0);
break; break;
default:
return -EINVAL;
} }
ctl->private_data = ak; err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
err = snd_ctl_add(ak->card,
snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
SNDRV_CTL_ELEM_ACCESS_WRITE));
if (err < 0) if (err < 0)
goto __error;
}
err = 0;
__error:
kfree(ctl);
return err; return err;
} }
return 0;
}
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{
int err, num_emphs;
err = build_dac_controls(ak);
if (err < 0)
return err;
err = build_adc_controls(ak);
if (err < 0)
return err;
if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
num_emphs = 1;
else
num_emphs = ak->num_dacs / 2;
err = build_deemphasis(ak, num_emphs);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(snd_akm4xxx_build_controls); EXPORT_SYMBOL(snd_akm4xxx_build_controls);

View File

@ -24,6 +24,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/tlv.h>
#include <sound/ad1816a.h> #include <sound/ad1816a.h>
#include <asm/io.h> #include <asm/io.h>
@ -765,6 +766,13 @@ static int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return change; return change;
} }
#define AD1816A_SINGLE_TLV(xname, reg, shift, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .info = snd_ad1816a_info_single, \
.get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
.tlv = { .p = (xtlv) } }
#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ #define AD1816A_SINGLE(xname, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \
.get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \
@ -822,6 +830,14 @@ static int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change; return change;
} }
#define AD1816A_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .info = snd_ad1816a_info_double, \
.get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \
.private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
.tlv = { .p = (xtlv) } }
#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ #define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \
.get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \
@ -890,28 +906,44 @@ static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = { static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = {
AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), AD1816A_DOUBLE_TLV("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1,
db_scale_5bit),
AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), AD1816A_DOUBLE_TLV("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1,
db_scale_6bit),
AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), AD1816A_DOUBLE_TLV("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1,
db_scale_5bit_12db_max),
AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), AD1816A_DOUBLE_TLV("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1,
db_scale_5bit_12db_max),
AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), AD1816A_DOUBLE_TLV("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1,
db_scale_5bit_12db_max),
AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), AD1816A_DOUBLE_TLV("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1,
db_scale_6bit),
AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1),
AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1), AD1816A_SINGLE_TLV("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1,
db_scale_5bit_12db_max),
AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0),
AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), AD1816A_DOUBLE_TLV("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1,
db_scale_5bit_12db_max),
AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1),
AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), AD1816A_SINGLE_TLV("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1,
db_scale_4bit),
AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1),
AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), AD1816A_SINGLE_TLV("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1,
db_scale_5bit),
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source", .name = "Capture Source",
@ -920,7 +952,8 @@ AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1),
.put = snd_ad1816a_put_mux, .put = snd_ad1816a_put_mux,
}, },
AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1),
AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), AD1816A_DOUBLE_TLV("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0,
db_scale_rec_gain),
AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1),
AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0),
}; };

View File

@ -29,6 +29,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/ad1848.h> #include <sound/ad1848.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <asm/io.h> #include <asm/io.h>
@ -118,6 +119,8 @@ void snd_ad1848_out(struct snd_ad1848 *chip,
#endif #endif
} }
EXPORT_SYMBOL(snd_ad1848_out);
static void snd_ad1848_dout(struct snd_ad1848 *chip, static void snd_ad1848_dout(struct snd_ad1848 *chip,
unsigned char reg, unsigned char value) unsigned char reg, unsigned char value)
{ {
@ -941,6 +944,8 @@ int snd_ad1848_create(struct snd_card *card,
return 0; return 0;
} }
EXPORT_SYMBOL(snd_ad1848_create);
static struct snd_pcm_ops snd_ad1848_playback_ops = { static struct snd_pcm_ops snd_ad1848_playback_ops = {
.open = snd_ad1848_playback_open, .open = snd_ad1848_playback_open,
.close = snd_ad1848_playback_close, .close = snd_ad1848_playback_close,
@ -988,12 +993,16 @@ int snd_ad1848_pcm(struct snd_ad1848 *chip, int device, struct snd_pcm **rpcm)
return 0; return 0;
} }
EXPORT_SYMBOL(snd_ad1848_pcm);
const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction) const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction)
{ {
return direction == SNDRV_PCM_STREAM_PLAYBACK ? return direction == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_ad1848_playback_ops : &snd_ad1848_capture_ops; &snd_ad1848_playback_ops : &snd_ad1848_capture_ops;
} }
EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
/* /*
* MIXER part * MIXER part
*/ */
@ -1171,7 +1180,8 @@ static int snd_ad1848_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
/* /*
*/ */
int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int type, unsigned long value) int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip,
const struct ad1848_mix_elem *c)
{ {
static struct snd_kcontrol_new newctls[] = { static struct snd_kcontrol_new newctls[] = {
[AD1848_MIX_SINGLE] = { [AD1848_MIX_SINGLE] = {
@ -1196,32 +1206,46 @@ int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int
struct snd_kcontrol *ctl; struct snd_kcontrol *ctl;
int err; int err;
ctl = snd_ctl_new1(&newctls[type], chip); ctl = snd_ctl_new1(&newctls[c->type], chip);
if (! ctl) if (! ctl)
return -ENOMEM; return -ENOMEM;
strlcpy(ctl->id.name, name, sizeof(ctl->id.name)); strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name));
ctl->id.index = index; ctl->id.index = c->index;
ctl->private_value = value; ctl->private_value = c->private_value;
if (c->tlv) {
ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
ctl->tlv.p = c->tlv;
}
if ((err = snd_ctl_add(chip->card, ctl)) < 0) if ((err = snd_ctl_add(chip->card, ctl)) < 0)
return err; return err;
return 0; return 0;
} }
EXPORT_SYMBOL(snd_ad1848_add_ctl_elem);
static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static struct ad1848_mix_elem snd_ad1848_controls[] = { static struct ad1848_mix_elem snd_ad1848_controls[] = {
AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1,
db_scale_6bit),
AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1), AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
db_scale_5bit_12db_max),
AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), db_scale_5bit_12db_max),
AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0,
db_scale_rec_gain),
{ {
.name = "Capture Source", .name = "Capture Source",
.type = AD1848_MIX_CAPTURE, .type = AD1848_MIX_CAPTURE,
}, },
AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0,
db_scale_6bit),
}; };
int snd_ad1848_mixer(struct snd_ad1848 *chip) int snd_ad1848_mixer(struct snd_ad1848 *chip)
@ -1245,12 +1269,7 @@ int snd_ad1848_mixer(struct snd_ad1848 *chip)
return 0; return 0;
} }
EXPORT_SYMBOL(snd_ad1848_out);
EXPORT_SYMBOL(snd_ad1848_create);
EXPORT_SYMBOL(snd_ad1848_pcm);
EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
EXPORT_SYMBOL(snd_ad1848_mixer); EXPORT_SYMBOL(snd_ad1848_mixer);
EXPORT_SYMBOL(snd_ad1848_add_ctl);
/* /*
* INIT part * INIT part

View File

@ -2038,7 +2038,80 @@ MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver.");
static struct platform_device *platform_devices[SNDRV_CARDS]; static struct platform_device *platform_devices[SNDRV_CARDS];
#ifdef CONFIG_PNP #ifdef CONFIG_PNP
static int pnp_registered; static int pnp_registered, pnpc_registered;
static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
{ .id = "ESS1869" },
{ .id = "" } /* end */
};
MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
/* PnP main device initialization */
static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev,
struct pnp_resource_table *cfg)
{
int err;
pnp_init_resource_table(cfg);
if (port[dev] != SNDRV_AUTO_PORT)
pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
if (fm_port[dev] != SNDRV_AUTO_PORT)
pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
if (mpu_port[dev] != SNDRV_AUTO_PORT)
pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
if (dma1[dev] != SNDRV_AUTO_DMA)
pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
if (dma2[dev] != SNDRV_AUTO_DMA)
pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
if (irq[dev] != SNDRV_AUTO_IRQ)
pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
if (pnp_device_is_isapnp(pdev)) {
err = pnp_manual_config_dev(pdev, cfg, 0);
if (err < 0)
snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
}
err = pnp_activate_dev(pdev);
if (err < 0) {
snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
return -EBUSY;
}
/* ok. hack using Vendor-Defined Card-Level registers */
/* skip csn and logdev initialization - already done in isapnp_configure */
if (pnp_device_is_isapnp(pdev)) {
isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */
if (mpu_port[dev] != SNDRV_AUTO_PORT)
isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */
isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */
isapnp_cfg_end();
}
port[dev] = pnp_port_start(pdev, 0);
fm_port[dev] = pnp_port_start(pdev, 1);
mpu_port[dev] = pnp_port_start(pdev, 2);
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
return 0;
}
static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
struct pnp_dev *pdev)
{
struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
acard->dev = pdev;
if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
kfree(cfg);
return -EBUSY;
}
kfree(cfg);
return 0;
}
static struct pnp_card_device_id snd_audiodrive_pnpids[] = { static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
/* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
@ -2061,13 +2134,11 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids); MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard, static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
struct pnp_card_link *card, struct pnp_card_link *card,
const struct pnp_card_device_id *id) const struct pnp_card_device_id *id)
{ {
struct pnp_dev *pdev;
struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
int err;
if (!cfg) if (!cfg)
return -ENOMEM; return -ENOMEM;
@ -2082,58 +2153,16 @@ static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
return -EBUSY; return -EBUSY;
} }
/* Control port initialization */ /* Control port initialization */
err = pnp_activate_dev(acard->devc); if (pnp_activate_dev(acard->devc) < 0) {
if (err < 0) {
snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n"); snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
kfree(cfg);
return -EAGAIN; return -EAGAIN;
} }
snd_printdd("pnp: port=0x%llx\n", snd_printdd("pnp: port=0x%llx\n",
(unsigned long long)pnp_port_start(acard->devc, 0)); (unsigned long long)pnp_port_start(acard->devc, 0));
/* PnP initialization */ if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
pdev = acard->dev;
pnp_init_resource_table(cfg);
if (port[dev] != SNDRV_AUTO_PORT)
pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
if (fm_port[dev] != SNDRV_AUTO_PORT)
pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
if (mpu_port[dev] != SNDRV_AUTO_PORT)
pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
if (dma1[dev] != SNDRV_AUTO_DMA)
pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
if (dma2[dev] != SNDRV_AUTO_DMA)
pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
if (irq[dev] != SNDRV_AUTO_IRQ)
pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
err = pnp_manual_config_dev(pdev, cfg, 0);
if (err < 0)
snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
err = pnp_activate_dev(pdev);
if (err < 0) {
snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
kfree(cfg); kfree(cfg);
return -EBUSY; return -EBUSY;
} }
/* ok. hack using Vendor-Defined Card-Level registers */
/* skip csn and logdev initialization - already done in isapnp_configure */
if (pnp_device_is_isapnp(pdev)) {
isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */
if (mpu_port[dev] != SNDRV_AUTO_PORT)
isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */
isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */
isapnp_cfg_end();
} else {
snd_printk(KERN_ERR PFX "unable to install ISA PnP hack, expect malfunction\n");
}
port[dev] = pnp_port_start(pdev, 0);
fm_port[dev] = pnp_port_start(pdev, 1);
mpu_port[dev] = pnp_port_start(pdev, 2);
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
kfree(cfg); kfree(cfg);
return 0; return 0;
} }
@ -2302,7 +2331,69 @@ static struct platform_driver snd_es18xx_nonpnp_driver = {
#ifdef CONFIG_PNP #ifdef CONFIG_PNP
static unsigned int __devinitdata es18xx_pnp_devices; static unsigned int __devinitdata es18xx_pnp_devices;
static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard, static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
const struct pnp_device_id *id)
{
static int dev;
int err;
struct snd_card *card;
if (pnp_device_is_isapnp(pdev))
return -ENOENT; /* we have another procedure - card */
for (; dev < SNDRV_CARDS; dev++) {
if (enable[dev] && isapnp[dev])
break;
}
if (dev >= SNDRV_CARDS)
return -ENODEV;
card = snd_es18xx_card_new(dev);
if (! card)
return -ENOMEM;
if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) {
snd_card_free(card);
return err;
}
snd_card_set_dev(card, &pdev->dev);
if ((err = snd_audiodrive_probe(card, dev)) < 0) {
snd_card_free(card);
return err;
}
pnp_set_drvdata(pdev, card);
dev++;
es18xx_pnp_devices++;
return 0;
}
static void __devexit snd_audiodrive_pnp_remove(struct pnp_dev * pdev)
{
snd_card_free(pnp_get_drvdata(pdev));
pnp_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
return snd_es18xx_suspend(pnp_get_drvdata(pdev), state);
}
static int snd_audiodrive_pnp_resume(struct pnp_dev *pdev)
{
return snd_es18xx_resume(pnp_get_drvdata(pdev));
}
#endif
static struct pnp_driver es18xx_pnp_driver = {
.name = "es18xx-pnpbios",
.id_table = snd_audiodrive_pnpbiosids,
.probe = snd_audiodrive_pnp_detect,
.remove = __devexit_p(snd_audiodrive_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_audiodrive_pnp_suspend,
.resume = snd_audiodrive_pnp_resume,
#endif
};
static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid) const struct pnp_card_device_id *pid)
{ {
static int dev; static int dev;
@ -2320,7 +2411,7 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
if (! card) if (! card)
return -ENOMEM; return -ENOMEM;
if ((res = snd_audiodrive_pnp(dev, card->private_data, pcard, pid)) < 0) { if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) {
snd_card_free(card); snd_card_free(card);
return res; return res;
} }
@ -2336,19 +2427,19 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
return 0; return 0;
} }
static void __devexit snd_audiodrive_pnp_remove(struct pnp_card_link * pcard) static void __devexit snd_audiodrive_pnpc_remove(struct pnp_card_link * pcard)
{ {
snd_card_free(pnp_get_card_drvdata(pcard)); snd_card_free(pnp_get_card_drvdata(pcard));
pnp_set_card_drvdata(pcard, NULL); pnp_set_card_drvdata(pcard, NULL);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int snd_audiodrive_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) static int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
{ {
return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state); return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state);
} }
static int snd_audiodrive_pnp_resume(struct pnp_card_link *pcard) static int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard)
{ {
return snd_es18xx_resume(pnp_get_card_drvdata(pcard)); return snd_es18xx_resume(pnp_get_card_drvdata(pcard));
} }
@ -2359,11 +2450,11 @@ static struct pnp_card_driver es18xx_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE, .flags = PNP_DRIVER_RES_DISABLE,
.name = "es18xx", .name = "es18xx",
.id_table = snd_audiodrive_pnpids, .id_table = snd_audiodrive_pnpids,
.probe = snd_audiodrive_pnp_detect, .probe = snd_audiodrive_pnpc_detect,
.remove = __devexit_p(snd_audiodrive_pnp_remove), .remove = __devexit_p(snd_audiodrive_pnpc_remove),
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = snd_audiodrive_pnp_suspend, .suspend = snd_audiodrive_pnpc_suspend,
.resume = snd_audiodrive_pnp_resume, .resume = snd_audiodrive_pnpc_resume,
#endif #endif
}; };
#endif /* CONFIG_PNP */ #endif /* CONFIG_PNP */
@ -2373,8 +2464,10 @@ static void __init_or_module snd_es18xx_unregister_all(void)
int i; int i;
#ifdef CONFIG_PNP #ifdef CONFIG_PNP
if (pnp_registered) if (pnpc_registered)
pnp_unregister_card_driver(&es18xx_pnpc_driver); pnp_unregister_card_driver(&es18xx_pnpc_driver);
if (pnp_registered)
pnp_unregister_driver(&es18xx_pnp_driver);
#endif #endif
for (i = 0; i < ARRAY_SIZE(platform_devices); ++i) for (i = 0; i < ARRAY_SIZE(platform_devices); ++i)
platform_device_unregister(platform_devices[i]); platform_device_unregister(platform_devices[i]);
@ -2405,11 +2498,13 @@ static int __init alsa_card_es18xx_init(void)
} }
#ifdef CONFIG_PNP #ifdef CONFIG_PNP
err = pnp_register_card_driver(&es18xx_pnpc_driver); err = pnp_register_driver(&es18xx_pnp_driver);
if (!err) { if (!err)
pnp_registered = 1; pnp_registered = 1;
err = pnp_register_card_driver(&es18xx_pnpc_driver);
if (!err)
pnpc_registered = 1;
cards += es18xx_pnp_devices; cards += es18xx_pnp_devices;
}
#endif #endif
if(!cards) { if(!cards) {

View File

@ -61,13 +61,13 @@ static long long snd_gf1_mem_proc_llseek(struct snd_info_entry *entry,
struct gus_proc_private *priv = entry->private_data; struct gus_proc_private *priv = entry->private_data;
switch (orig) { switch (orig) {
case 0: /* SEEK_SET */ case SEEK_SET:
file->f_pos = offset; file->f_pos = offset;
break; break;
case 1: /* SEEK_CUR */ case SEEK_CUR:
file->f_pos += offset; file->f_pos += offset;
break; break;
case 2: /* SEEK_END, offset is negative */ case SEEK_END: /* offset is negative */
file->f_pos = priv->size + offset; file->f_pos = priv->size + offset;
break; break;
default: default:

View File

@ -33,6 +33,7 @@
#include <sound/mpu401.h> #include <sound/mpu401.h>
#include <sound/opl3.h> #include <sound/opl3.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/tlv.h>
#include <asm/io.h> #include <asm/io.h>
@ -337,6 +338,14 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *
.info = snd_opl3sa2_info_single, \ .info = snd_opl3sa2_info_single, \
.get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \ .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
#define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .index = xindex, \
.info = snd_opl3sa2_info_single, \
.get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
.tlv = { .p = (xtlv) } }
static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
@ -395,6 +404,14 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
.info = snd_opl3sa2_info_double, \ .info = snd_opl3sa2_info_double, \
.get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \ .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
#define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .index = xindex, \
.info = snd_opl3sa2_info_double, \
.get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
.tlv = { .p = (xtlv) } }
static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
@ -469,11 +486,16 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static struct snd_kcontrol_new snd_opl3sa2_controls[] = { static struct snd_kcontrol_new snd_opl3sa2_controls[] = {
OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1), OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1,
db_scale_master),
OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1), OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),
OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1) OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1,
db_scale_5bit_12db_max),
}; };
static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = { static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {

View File

@ -475,6 +475,7 @@ config SND_FM801_TEA575X
depends on SND_FM801_TEA575X_BOOL depends on SND_FM801_TEA575X_BOOL
default SND_FM801 default SND_FM801
select VIDEO_V4L1 select VIDEO_V4L1
select VIDEO_DEV
config SND_HDA_INTEL config SND_HDA_INTEL
tristate "Intel HD Audio" tristate "Intel HD Audio"
@ -743,4 +744,17 @@ config SND_YMFPCI
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-ymfpci. will be called snd-ymfpci.
config SND_AC97_POWER_SAVE
bool "AC97 Power-Saving Mode"
depends on SND_AC97_CODEC && EXPERIMENTAL
default n
help
Say Y here to enable the aggressive power-saving support of
AC97 codecs. In this mode, the power-mode is dynamically
controlled at each open/close.
The mode is activated by passing power_save=1 option to
snd-ac97-codec driver. You can toggle it dynamically over
sysfs, too.
endmenu endmenu

View File

@ -31,6 +31,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/tlv.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/initval.h> #include <sound/initval.h>
@ -47,6 +48,11 @@ static int enable_loopback;
module_param(enable_loopback, bool, 0444); module_param(enable_loopback, bool, 0444);
MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
#ifdef CONFIG_SND_AC97_POWER_SAVE
static int power_save;
module_param(power_save, bool, 0644);
MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
#endif
/* /*
*/ */
@ -151,7 +157,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk { 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk
{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL }, { 0x4e534331, 0xffffffff, "LM4549", NULL, NULL },
{ 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix { 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix
{ 0x50534304, 0xffffffff, "UCB1400", NULL, NULL }, { 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL },
{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH }, { 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH },
{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL }, { 0x54524102, 0xffffffff, "TR28022", NULL, NULL },
{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL }, { 0x54524106, 0xffffffff, "TR28026", NULL, NULL },
@ -187,6 +193,8 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
}; };
static void update_power_regs(struct snd_ac97 *ac97);
/* /*
* I/O routines * I/O routines
*/ */
@ -554,6 +562,18 @@ int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
} }
err = snd_ac97_update_bits(ac97, reg, val_mask, val); err = snd_ac97_update_bits(ac97, reg, val_mask, val);
snd_ac97_page_restore(ac97, page_save); snd_ac97_page_restore(ac97, page_save);
#ifdef CONFIG_SND_AC97_POWER_SAVE
/* check analog mixer power-down */
if ((val_mask & 0x8000) &&
(kcontrol->private_value & (1<<30))) {
if (val & 0x8000)
ac97->power_up &= ~(1 << (reg>>1));
else
ac97->power_up |= 1 << (reg>>1);
if (power_save)
update_power_regs(ac97);
}
#endif
return err; return err;
} }
@ -962,6 +982,10 @@ static int snd_ac97_bus_dev_free(struct snd_device *device)
static int snd_ac97_free(struct snd_ac97 *ac97) static int snd_ac97_free(struct snd_ac97 *ac97)
{ {
if (ac97) { if (ac97) {
#ifdef CONFIG_SND_AC97_POWER_SAVE
if (ac97->power_workq)
destroy_workqueue(ac97->power_workq);
#endif
snd_ac97_proc_done(ac97); snd_ac97_proc_done(ac97);
if (ac97->bus) if (ac97->bus)
ac97->bus->codec[ac97->num] = NULL; ac97->bus->codec[ac97->num] = NULL;
@ -1117,7 +1141,9 @@ struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, str
/* /*
* create mute switch(es) for normal stereo controls * create mute switch(es) for normal stereo controls
*/ */
static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, int check_stereo, struct snd_ac97 *ac97) static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
int check_stereo, int check_amix,
struct snd_ac97 *ac97)
{ {
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
int err; int err;
@ -1137,10 +1163,14 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
} }
if (mute_mask == 0x8080) { if (mute_mask == 0x8080) {
struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
if (check_amix)
tmp.private_value |= (1 << 30);
tmp.index = ac97->num; tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97); kctl = snd_ctl_new1(&tmp, ac97);
} else { } else {
struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1); struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1);
if (check_amix)
tmp.private_value |= (1 << 30);
tmp.index = ac97->num; tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97); kctl = snd_ctl_new1(&tmp, ac97);
} }
@ -1152,6 +1182,32 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
return 0; return 0;
} }
/*
* set dB information
*/
static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static unsigned int *find_db_scale(unsigned int maxval)
{
switch (maxval) {
case 0x0f: return db_scale_4bit;
case 0x1f: return db_scale_5bit;
case 0x3f: return db_scale_6bit;
}
return NULL;
}
static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv)
{
kctl->tlv.p = tlv;
if (tlv)
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
}
/* /*
* create a volume for normal stereo/mono controls * create a volume for normal stereo/mono controls
*/ */
@ -1174,6 +1230,10 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
tmp.index = ac97->num; tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97); kctl = snd_ctl_new1(&tmp, ac97);
} }
if (reg >= AC97_PHONE && reg <= AC97_PCM)
set_tlv_db_scale(kctl, db_scale_5bit_12db_max);
else
set_tlv_db_scale(kctl, find_db_scale(lo_max));
err = snd_ctl_add(card, kctl); err = snd_ctl_add(card, kctl);
if (err < 0) if (err < 0)
return err; return err;
@ -1186,7 +1246,9 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
/* /*
* create a mute-switch and a volume for normal stereo/mono controls * create a mute-switch and a volume for normal stereo/mono controls
*/ */
static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int reg, int check_stereo, struct snd_ac97 *ac97) static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
int reg, int check_stereo, int check_amix,
struct snd_ac97 *ac97)
{ {
int err; int err;
char name[44]; char name[44];
@ -1197,7 +1259,9 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int
if (snd_ac97_try_bit(ac97, reg, 15)) { if (snd_ac97_try_bit(ac97, reg, 15)) {
sprintf(name, "%s Switch", pfx); sprintf(name, "%s Switch", pfx);
if ((err = snd_ac97_cmute_new_stereo(card, name, reg, check_stereo, ac97)) < 0) if ((err = snd_ac97_cmute_new_stereo(card, name, reg,
check_stereo, check_amix,
ac97)) < 0)
return err; return err;
} }
check_volume_resolution(ac97, reg, &lo_max, &hi_max); check_volume_resolution(ac97, reg, &lo_max, &hi_max);
@ -1209,8 +1273,10 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int
return 0; return 0;
} }
#define snd_ac97_cmix_new(card, pfx, reg, ac97) snd_ac97_cmix_new_stereo(card, pfx, reg, 0, ac97) #define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \
#define snd_ac97_cmute_new(card, name, reg, ac97) snd_ac97_cmute_new_stereo(card, name, reg, 0, ac97) snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97)
#define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \
snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97)
static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97); static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97);
@ -1226,9 +1292,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */ /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) {
if (ac97->flags & AC97_HAS_NO_MASTER_VOL) if (ac97->flags & AC97_HAS_NO_MASTER_VOL)
err = snd_ac97_cmute_new(card, "Master Playback Switch", AC97_MASTER, ac97); err = snd_ac97_cmute_new(card, "Master Playback Switch",
AC97_MASTER, 0, ac97);
else else
err = snd_ac97_cmix_new(card, "Master Playback", AC97_MASTER, ac97); err = snd_ac97_cmix_new(card, "Master Playback",
AC97_MASTER, 0, ac97);
if (err < 0) if (err < 0)
return err; return err;
} }
@ -1245,6 +1313,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max); snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
kctl->private_value &= ~(0xff << 16); kctl->private_value &= ~(0xff << 16);
kctl->private_value |= (int)max << 16; kctl->private_value |= (int)max << 16;
set_tlv_db_scale(kctl, find_db_scale(max));
snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max); snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max);
} }
@ -1258,6 +1327,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max); snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
kctl->private_value &= ~(0xff << 16); kctl->private_value &= ~(0xff << 16);
kctl->private_value |= (int)max << 16; kctl->private_value |= (int)max << 16;
set_tlv_db_scale(kctl, find_db_scale(max));
snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8); snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8);
} }
@ -1265,19 +1335,23 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER))
&& !(ac97->flags & AC97_AD_MULTI)) { && !(ac97->flags & AC97_AD_MULTI)) {
/* Surround Master (0x38) is with stereo mutes */ /* Surround Master (0x38) is with stereo mutes */
if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0) if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
AC97_SURROUND_MASTER, 1, 0,
ac97)) < 0)
return err; return err;
} }
/* build headphone controls */ /* build headphone controls */
if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
if ((err = snd_ac97_cmix_new(card, "Headphone Playback", AC97_HEADPHONE, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "Headphone Playback",
AC97_HEADPHONE, 0, ac97)) < 0)
return err; return err;
} }
/* build master mono controls */ /* build master mono controls */
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", AC97_MASTER_MONO, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "Master Mono Playback",
AC97_MASTER_MONO, 0, ac97)) < 0)
return err; return err;
} }
@ -1301,8 +1375,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
((ac97->flags & AC97_HAS_PC_BEEP) || ((ac97->flags & AC97_HAS_PC_BEEP) ||
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) { snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
return err; return err;
set_tlv_db_scale(kctl, db_scale_4bit);
snd_ac97_write_cache(ac97, AC97_PC_BEEP, snd_ac97_write_cache(ac97, AC97_PC_BEEP,
snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e); snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
} }
@ -1310,7 +1385,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Phone controls */ /* build Phone controls */
if (!(ac97->flags & AC97_HAS_NO_PHONE)) { if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "Phone Playback",
AC97_PHONE, 1, ac97)) < 0)
return err; return err;
} }
} }
@ -1318,7 +1394,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build MIC controls */ /* build MIC controls */
if (!(ac97->flags & AC97_HAS_NO_MIC)) { if (!(ac97->flags & AC97_HAS_NO_MIC)) {
if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) { if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "Mic Playback",
AC97_MIC, 1, ac97)) < 0)
return err; return err;
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0)
return err; return err;
@ -1327,14 +1404,16 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Line controls */ /* build Line controls */
if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) { if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
if ((err = snd_ac97_cmix_new(card, "Line Playback", AC97_LINE, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "Line Playback",
AC97_LINE, 1, ac97)) < 0)
return err; return err;
} }
/* build CD controls */ /* build CD controls */
if (!(ac97->flags & AC97_HAS_NO_CD)) { if (!(ac97->flags & AC97_HAS_NO_CD)) {
if (snd_ac97_try_volume_mix(ac97, AC97_CD)) { if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "CD Playback",
AC97_CD, 1, ac97)) < 0)
return err; return err;
} }
} }
@ -1342,7 +1421,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Video controls */ /* build Video controls */
if (!(ac97->flags & AC97_HAS_NO_VIDEO)) { if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "Video Playback",
AC97_VIDEO, 1, ac97)) < 0)
return err; return err;
} }
} }
@ -1350,7 +1430,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Aux controls */ /* build Aux controls */
if (!(ac97->flags & AC97_HAS_NO_AUX)) { if (!(ac97->flags & AC97_HAS_NO_AUX)) {
if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0) if ((err = snd_ac97_cmix_new(card, "Aux Playback",
AC97_AUX, 1, ac97)) < 0)
return err; return err;
} }
} }
@ -1363,31 +1444,38 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
else else
init_val = 0x9f1f; init_val = 0x9f1f;
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
return err; return err;
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[0] = init_val; ac97->spec.ad18xx.pcmreg[0] = init_val;
if (ac97->scaps & AC97_SCAP_SURROUND_DAC) { if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
return err; return err;
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[1] = init_val; ac97->spec.ad18xx.pcmreg[1] = init_val;
} }
if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) { if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
return err; return err;
set_tlv_db_scale(kctl, db_scale_5bit);
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
return err; return err;
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[2] = init_val; ac97->spec.ad18xx.pcmreg[2] = init_val;
} }
snd_ac97_write_cache(ac97, AC97_PCM, init_val); snd_ac97_write_cache(ac97, AC97_PCM, init_val);
} else { } else {
if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) { if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) {
if (ac97->flags & AC97_HAS_NO_PCM_VOL) if (ac97->flags & AC97_HAS_NO_PCM_VOL)
err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97); err = snd_ac97_cmute_new(card,
"PCM Playback Switch",
AC97_PCM, 0, ac97);
else else
err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97); err = snd_ac97_cmix_new(card, "PCM Playback",
AC97_PCM, 0, ac97);
if (err < 0) if (err < 0)
return err; return err;
} }
@ -1398,19 +1486,23 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
return err; return err;
if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) { if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0) err = snd_ac97_cmute_new(card, "Capture Switch",
AC97_REC_GAIN, 0, ac97);
if (err < 0)
return err; return err;
} }
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
return err; return err;
set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000); snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
} }
/* build MIC Capture controls */ /* build MIC Capture controls */
if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
return err; return err;
set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000); snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
} }
@ -1481,6 +1573,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
} }
/* build S/PDIF controls */ /* build S/PDIF controls */
/* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */
if (ac97->subsystem_vendor == 0x1043 &&
ac97->subsystem_device == 0x810f)
ac97->ext_id |= AC97_EI_SPDIF;
if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) { if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
if (ac97->build_ops->build_spdif) { if (ac97->build_ops->build_spdif) {
if ((err = ac97->build_ops->build_spdif(ac97)) < 0) if ((err = ac97->build_ops->build_spdif(ac97)) < 0)
@ -1817,18 +1915,25 @@ static int snd_ac97_dev_register(struct snd_device *device)
return 0; return 0;
} }
/* unregister ac97 codec */ /* disconnect ac97 codec */
static int snd_ac97_dev_unregister(struct snd_device *device) static int snd_ac97_dev_disconnect(struct snd_device *device)
{ {
struct snd_ac97 *ac97 = device->device_data; struct snd_ac97 *ac97 = device->device_data;
if (ac97->dev.bus) if (ac97->dev.bus)
device_unregister(&ac97->dev); device_unregister(&ac97->dev);
return snd_ac97_free(ac97); return 0;
} }
/* build_ops to do nothing */ /* build_ops to do nothing */
static struct snd_ac97_build_ops null_build_ops; static struct snd_ac97_build_ops null_build_ops;
#ifdef CONFIG_SND_AC97_POWER_SAVE
static void do_update_power(void *data)
{
update_power_regs(data);
}
#endif
/** /**
* snd_ac97_mixer - create an Codec97 component * snd_ac97_mixer - create an Codec97 component
* @bus: the AC97 bus which codec is attached to * @bus: the AC97 bus which codec is attached to
@ -1860,7 +1965,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = snd_ac97_dev_free, .dev_free = snd_ac97_dev_free,
.dev_register = snd_ac97_dev_register, .dev_register = snd_ac97_dev_register,
.dev_unregister = snd_ac97_dev_unregister, .dev_disconnect = snd_ac97_dev_disconnect,
}; };
snd_assert(rac97 != NULL, return -EINVAL); snd_assert(rac97 != NULL, return -EINVAL);
@ -1883,6 +1988,10 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
bus->codec[ac97->num] = ac97; bus->codec[ac97->num] = ac97;
mutex_init(&ac97->reg_mutex); mutex_init(&ac97->reg_mutex);
mutex_init(&ac97->page_mutex); mutex_init(&ac97->page_mutex);
#ifdef CONFIG_SND_AC97_POWER_SAVE
ac97->power_workq = create_workqueue("ac97");
INIT_WORK(&ac97->power_work, do_update_power, ac97);
#endif
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
if (ac97->pci) { if (ac97->pci) {
@ -2117,15 +2226,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
return -ENOMEM; return -ENOMEM;
} }
} }
/* make sure the proper powerdown bits are cleared */ if (ac97_is_audio(ac97))
if (ac97->scaps && ac97_is_audio(ac97)) { update_power_regs(ac97);
reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
reg &= ~AC97_EA_PRJ;
if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
reg &= ~(AC97_EA_PRI | AC97_EA_PRK);
snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
}
snd_ac97_proc_init(ac97); snd_ac97_proc_init(ac97);
if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) { if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) {
snd_ac97_free(ac97); snd_ac97_free(ac97);
@ -2153,22 +2255,155 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97)
snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f); snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f);
} }
power = ac97->regs[AC97_POWERDOWN] | 0x8000; /* EAPD */ /* surround, CLFE, mic powerdown */
power |= 0x4000; /* Headphone amplifier powerdown */ power = ac97->regs[AC97_EXTENDED_STATUS];
power |= 0x0300; /* ADC & DAC powerdown */ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
power |= AC97_EA_PRJ;
if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
power |= AC97_EA_PRI | AC97_EA_PRK;
power |= AC97_EA_PRL;
snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power);
/* powerdown external amplifier */
if (ac97->scaps & AC97_SCAP_INV_EAPD)
power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD;
else if (! (ac97->scaps & AC97_SCAP_EAPD_LED))
power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD;
power |= AC97_PD_PR6; /* Headphone amplifier powerdown */
power |= AC97_PD_PR0 | AC97_PD_PR1; /* ADC & DAC powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power); snd_ac97_write(ac97, AC97_POWERDOWN, power);
udelay(100); udelay(100);
power |= 0x0400; /* Analog Mixer powerdown (Vref on) */ power |= AC97_PD_PR2 | AC97_PD_PR3; /* Analog Mixer powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power); snd_ac97_write(ac97, AC97_POWERDOWN, power);
#ifdef CONFIG_SND_AC97_POWER_SAVE
if (power_save) {
udelay(100); udelay(100);
#if 0 /* AC-link powerdown, internal Clk disable */
/* FIXME: this causes click noises on some boards at resume */ /* FIXME: this may cause click noises on some boards */
power |= 0x3800; /* AC-link powerdown, internal Clk disable */ power |= AC97_PD_PR4 | AC97_PD_PR5;
snd_ac97_write(ac97, AC97_POWERDOWN, power); snd_ac97_write(ac97, AC97_POWERDOWN, power);
}
#endif #endif
} }
struct ac97_power_reg {
unsigned short reg;
unsigned short power_reg;
unsigned short mask;
};
enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE };
static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
[PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0},
[PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1},
[PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS,
AC97_EA_PRI | AC97_EA_PRK},
[PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS,
AC97_EA_PRJ},
[PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS,
AC97_EA_PRL},
};
#ifdef CONFIG_SND_AC97_POWER_SAVE
/**
* snd_ac97_update_power - update the powerdown register
* @ac97: the codec instance
* @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE
* @powerup: non-zero when power up the part
*
* Update the AC97 powerdown register bits of the given part.
*/
int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
{
int i;
if (! ac97)
return 0;
if (reg) {
/* SPDIF requires DAC power, too */
if (reg == AC97_SPDIF)
reg = AC97_PCM_FRONT_DAC_RATE;
for (i = 0; i < PWIDX_SIZE; i++) {
if (power_regs[i].reg == reg) {
if (powerup)
ac97->power_up |= (1 << i);
else
ac97->power_up &= ~(1 << i);
break;
}
}
}
if (! power_save)
return 0;
if (! powerup && ac97->power_workq)
/* adjust power-down bits after two seconds delay
* (for avoiding loud click noises for many (OSS) apps
* that open/close frequently)
*/
queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2);
else
update_power_regs(ac97);
return 0;
}
EXPORT_SYMBOL(snd_ac97_update_power);
#endif /* CONFIG_SND_AC97_POWER_SAVE */
static void update_power_regs(struct snd_ac97 *ac97)
{
unsigned int power_up, bits;
int i;
#ifdef CONFIG_SND_AC97_POWER_SAVE
if (power_save)
power_up = ac97->power_up;
else {
#endif
power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
power_up |= (1 << PWIDX_MIC);
if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
power_up |= (1 << PWIDX_SURR);
if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
power_up |= (1 << PWIDX_CLFE);
#ifdef CONFIG_SND_AC97_POWER_SAVE
}
#endif
if (power_up) {
if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) {
/* needs power-up analog mix and vref */
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR3, 0);
msleep(1);
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR2, 0);
}
}
for (i = 0; i < PWIDX_SIZE; i++) {
if (power_up & (1 << i))
bits = 0;
else
bits = power_regs[i].mask;
snd_ac97_update_bits(ac97, power_regs[i].power_reg,
power_regs[i].mask, bits);
}
if (! power_up) {
if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) {
/* power down analog mix and vref */
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR2, AC97_PD_PR2);
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR3, AC97_PD_PR3);
}
}
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
/** /**
* snd_ac97_suspend - General suspend function for AC97 codec * snd_ac97_suspend - General suspend function for AC97 codec
@ -2484,6 +2719,7 @@ static int tune_mute_led(struct snd_ac97 *ac97)
msw->put = master_mute_sw_put; msw->put = master_mute_sw_put;
snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
ac97->scaps |= AC97_SCAP_EAPD_LED;
return 0; return 0;
} }

View File

@ -32,6 +32,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include "ac97_patch.h" #include "ac97_patch.h"
#include "ac97_id.h" #include "ac97_id.h"
@ -51,6 +52,20 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
return 0; return 0;
} }
/* replace with a new TLV */
static void reset_tlv(struct snd_ac97 *ac97, const char *name,
unsigned int *tlv)
{
struct snd_ctl_elem_id sid;
struct snd_kcontrol *kctl;
memset(&sid, 0, sizeof(sid));
strcpy(sid.name, name);
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kctl = snd_ctl_find_id(ac97->bus->card, &sid);
if (kctl && kctl->tlv.p)
kctl->tlv.p = tlv;
}
/* set to the page, update bits and restore the page */ /* set to the page, update bits and restore the page */
static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page) static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page)
{ {
@ -466,7 +481,7 @@ int patch_wolfson05(struct snd_ac97 * ac97)
ac97->build_ops = &patch_wolfson_wm9705_ops; ac97->build_ops = &patch_wolfson_wm9705_ops;
#ifdef CONFIG_TOUCHSCREEN_WM9705 #ifdef CONFIG_TOUCHSCREEN_WM9705
/* WM9705 touchscreen uses AUX and VIDEO for touch */ /* WM9705 touchscreen uses AUX and VIDEO for touch */
ac97->flags |=3D AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX; ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
#endif #endif
return 0; return 0;
} }
@ -1380,6 +1395,17 @@ static void ad1888_resume(struct snd_ac97 *ac97)
#endif #endif
static const struct snd_ac97_res_table ad1819_restbl[] = {
{ AC97_PHONE, 0x9f1f },
{ AC97_MIC, 0x9f1f },
{ AC97_LINE, 0x9f1f },
{ AC97_CD, 0x9f1f },
{ AC97_VIDEO, 0x9f1f },
{ AC97_AUX, 0x9f1f },
{ AC97_PCM, 0x9f1f },
{ } /* terminator */
};
int patch_ad1819(struct snd_ac97 * ac97) int patch_ad1819(struct snd_ac97 * ac97)
{ {
unsigned short scfg; unsigned short scfg;
@ -1387,6 +1413,7 @@ int patch_ad1819(struct snd_ac97 * ac97)
// patch for Analog Devices // patch for Analog Devices
scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */ snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */
ac97->res_table = ad1819_restbl;
return 0; return 0;
} }
@ -1522,12 +1549,16 @@ static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = {
AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */ AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
}; };
static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
static int patch_ad1885_specific(struct snd_ac97 * ac97) static int patch_ad1885_specific(struct snd_ac97 * ac97)
{ {
int err; int err;
if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0) if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
return err; return err;
reset_tlv(ac97, "Headphone Playback Volume",
db_scale_6bit_6db_max);
return 0; return 0;
} }
@ -1551,12 +1582,27 @@ int patch_ad1885(struct snd_ac97 * ac97)
return 0; return 0;
} }
static int patch_ad1886_specific(struct snd_ac97 * ac97)
{
reset_tlv(ac97, "Headphone Playback Volume",
db_scale_6bit_6db_max);
return 0;
}
static struct snd_ac97_build_ops patch_ad1886_build_ops = {
.build_specific = &patch_ad1886_specific,
#ifdef CONFIG_PM
.resume = ad18xx_resume
#endif
};
int patch_ad1886(struct snd_ac97 * ac97) int patch_ad1886(struct snd_ac97 * ac97)
{ {
patch_ad1881(ac97); patch_ad1881(ac97);
/* Presario700 workaround */ /* Presario700 workaround */
/* for Jack Sense/SPDIF Register misetting causing */ /* for Jack Sense/SPDIF Register misetting causing */
snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010); snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
ac97->build_ops = &patch_ad1886_build_ops;
return 0; return 0;
} }
@ -2015,6 +2061,8 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = {
/* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */ /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
}; };
static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
static int patch_alc650_specific(struct snd_ac97 * ac97) static int patch_alc650_specific(struct snd_ac97 * ac97)
{ {
int err; int err;
@ -2025,6 +2073,9 @@ static int patch_alc650_specific(struct snd_ac97 * ac97)
if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0) if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
return err; return err;
} }
if (ac97->id != AC97_ID_ALC650F)
reset_tlv(ac97, "Master Playback Volume",
db_scale_5bit_3db_max);
return 0; return 0;
} }
@ -2208,7 +2259,8 @@ int patch_alc655(struct snd_ac97 * ac97)
val &= ~(1 << 1); /* Pin 47 is spdif input pin */ val &= ~(1 << 1); /* Pin 47 is spdif input pin */
else { /* ALC655 */ else { /* ALC655 */
if (ac97->subsystem_vendor == 0x1462 && if (ac97->subsystem_vendor == 0x1462 &&
ac97->subsystem_device == 0x0131) /* MSI S270 laptop */ (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */
ac97->subsystem_device == 0x0161)) /* LG K1 Express */
val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */ val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
else else
val |= (1 << 1); /* Pin 47 is spdif input pin */ val |= (1 << 1); /* Pin 47 is spdif input pin */
@ -2759,6 +2811,10 @@ int patch_vt1616(struct snd_ac97 * ac97)
*/ */
int patch_vt1617a(struct snd_ac97 * ac97) int patch_vt1617a(struct snd_ac97 * ac97)
{ {
/* bring analog power consumption to normal, like WinXP driver
* for EPIA SP
*/
snd_ac97_write_cache(ac97, 0x5c, 0x20);
ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */
ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
return 0; return 0;
@ -2872,3 +2928,41 @@ int patch_lm4550(struct snd_ac97 *ac97)
ac97->res_table = lm4550_restbl; ac97->res_table = lm4550_restbl;
return 0; return 0;
} }
/*
* UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
*/
static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
/* enable/disable headphone driver which allows direct connection to
stereo headphone without the use of external DC blocking
capacitors */
AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
/* Filter used to compensate the DC offset is added in the ADC to remove idle
tones from the audio band. */
AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
/* Control smart-low-power mode feature. Allows automatic power down
of unused blocks in the ADC analog front end and the PLL. */
AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
};
static int patch_ucb1400_specific(struct snd_ac97 * ac97)
{
int idx, err;
for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
return err;
return 0;
}
static struct snd_ac97_build_ops patch_ucb1400_ops = {
.build_specific = patch_ucb1400_specific,
};
int patch_ucb1400(struct snd_ac97 * ac97)
{
ac97->build_ops = &patch_ucb1400_ops;
/* enable headphone driver and smart low power mode by default */
snd_ac97_write(ac97, 0x6a, 0x0050);
snd_ac97_write(ac97, 0x6c, 0x0030);
return 0;
}

View File

@ -58,5 +58,6 @@ int patch_cm9780(struct snd_ac97 * ac97);
int patch_vt1616(struct snd_ac97 * ac97); int patch_vt1616(struct snd_ac97 * ac97);
int patch_vt1617a(struct snd_ac97 * ac97); int patch_vt1617a(struct snd_ac97 * ac97);
int patch_it2646(struct snd_ac97 * ac97); int patch_it2646(struct snd_ac97 * ac97);
int patch_ucb1400(struct snd_ac97 * ac97);
int mpatch_si3036(struct snd_ac97 * ac97); int mpatch_si3036(struct snd_ac97 * ac97);
int patch_lm4550(struct snd_ac97 * ac97); int patch_lm4550(struct snd_ac97 * ac97);

View File

@ -269,6 +269,7 @@ int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
return -EINVAL; return -EINVAL;
} }
snd_ac97_update_power(ac97, reg, 1);
switch (reg) { switch (reg) {
case AC97_PCM_MIC_ADC_RATE: case AC97_PCM_MIC_ADC_RATE:
if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */ if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */
@ -606,6 +607,7 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
goto error; goto error;
} }
} }
pcm->cur_dbl = r;
spin_unlock_irq(&pcm->bus->bus_lock); spin_unlock_irq(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) { for (i = 3; i < 12; i++) {
if (!(slots & (1 << i))) if (!(slots & (1 << i)))
@ -651,6 +653,21 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
unsigned short slots = pcm->aslots; unsigned short slots = pcm->aslots;
int i, cidx; int i, cidx;
#ifdef CONFIG_SND_AC97_POWER_SAVE
int r = pcm->cur_dbl;
for (i = 3; i < 12; i++) {
if (!(slots & (1 << i)))
continue;
for (cidx = 0; cidx < 4; cidx++) {
if (pcm->r[r].rslots[cidx] & (1 << i)) {
int reg = get_slot_reg(pcm, cidx, i, r);
snd_ac97_update_power(pcm->r[r].codec[cidx],
reg, 0);
}
}
}
#endif
bus = pcm->bus; bus = pcm->bus;
spin_lock_irq(&pcm->bus->bus_lock); spin_lock_irq(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) { for (i = 3; i < 12; i++) {
@ -660,6 +677,7 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
bus->used_slots[pcm->stream][cidx] &= ~(1 << i); bus->used_slots[pcm->stream][cidx] &= ~(1 << i);
} }
pcm->aslots = 0; pcm->aslots = 0;
pcm->cur_dbl = 0;
spin_unlock_irq(&pcm->bus->bus_lock); spin_unlock_irq(&pcm->bus->bus_lock);
return 0; return 0;
} }

View File

@ -457,15 +457,11 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97)
void snd_ac97_proc_done(struct snd_ac97 * ac97) void snd_ac97_proc_done(struct snd_ac97 * ac97)
{ {
if (ac97->proc_regs) { snd_info_free_entry(ac97->proc_regs);
snd_info_unregister(ac97->proc_regs);
ac97->proc_regs = NULL; ac97->proc_regs = NULL;
} snd_info_free_entry(ac97->proc);
if (ac97->proc) {
snd_info_unregister(ac97->proc);
ac97->proc = NULL; ac97->proc = NULL;
} }
}
void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus) void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
{ {
@ -485,8 +481,6 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
void snd_ac97_bus_proc_done(struct snd_ac97_bus * bus) void snd_ac97_bus_proc_done(struct snd_ac97_bus * bus)
{ {
if (bus->proc) { snd_info_free_entry(bus->proc);
snd_info_unregister(bus->proc);
bus->proc = NULL; bus->proc = NULL;
} }
}

View File

@ -27,6 +27,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/ak4531_codec.h> #include <sound/ak4531_codec.h>
#include <sound/tlv.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_DESCRIPTION("Universal routines for AK4531 codec"); MODULE_DESCRIPTION("Universal routines for AK4531 codec");
@ -63,6 +64,14 @@ static void snd_ak4531_dump(struct snd_ak4531 *ak4531)
.info = snd_ak4531_info_single, \ .info = snd_ak4531_info_single, \
.get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \ .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
.private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) } .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) }
#define AK4531_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .index = xindex, \
.info = snd_ak4531_info_single, \
.get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
.private_value = reg | (shift << 16) | (mask << 24) | (invert << 22), \
.tlv = { .p = (xtlv) } }
static int snd_ak4531_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) static int snd_ak4531_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
@ -122,6 +131,14 @@ static int snd_ak4531_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
.info = snd_ak4531_info_double, \ .info = snd_ak4531_info_double, \
.get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \ .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
.private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) } .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
#define AK4531_DOUBLE_TLV(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .index = xindex, \
.info = snd_ak4531_info_double, \
.get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
.private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22), \
.tlv = { .p = (xtlv) } }
static int snd_ak4531_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) static int snd_ak4531_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
@ -250,50 +267,62 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
static DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
static DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
static struct snd_kcontrol_new snd_ak4531_controls[] = { static struct snd_kcontrol_new snd_ak4531_controls[] = {
AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1), AK4531_DOUBLE_TLV("Master Playback Switch", 0,
AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
db_scale_master),
AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1), AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1),
AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1), AK4531_SINGLE_TLV("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1,
db_scale_mono),
AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1), AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1),
AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1), AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1),
AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1), AK4531_DOUBLE_TLV("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1,
db_scale_input),
AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0), AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0),
AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0), AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0),
AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1), AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1),
AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1), AK4531_DOUBLE_TLV("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1,
db_scale_input),
AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0), AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0),
AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5), AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5),
AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1), AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1),
AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1), AK4531_DOUBLE_TLV("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1,
db_scale_input),
AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0), AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0),
AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1), AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1),
AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1), AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1),
AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1), AK4531_DOUBLE_TLV("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1,
db_scale_input),
AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0), AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0),
AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3), AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3),
AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), AK4531_DOUBLE_TLV("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1,
db_scale_input),
AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), AK4531_SINGLE_TLV("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1, db_scale_input),
AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0), AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0),
AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0), AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0),
AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1), AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1),
AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1), AK4531_SINGLE_TLV("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1, db_scale_input),
AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0), AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0),
AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0), AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0),
AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1), AK4531_SINGLE_TLV("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1, db_scale_input),
AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1), AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1),
AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0), AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0),
AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0), AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0),

View File

@ -70,9 +70,13 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/tlv.h>
#include "ca0106.h" #include "ca0106.h"
static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol, static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
@ -469,18 +473,24 @@ static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
#define CA_VOLUME(xname,chid,reg) \ #define CA_VOLUME(xname,chid,reg) \
{ \ { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_ca0106_volume_info, \ .info = snd_ca0106_volume_info, \
.get = snd_ca0106_volume_get, \ .get = snd_ca0106_volume_get, \
.put = snd_ca0106_volume_put, \ .put = snd_ca0106_volume_put, \
.tlv = { .p = snd_ca0106_db_scale1 }, \
.private_value = ((chid) << 8) | (reg) \ .private_value = ((chid) << 8) | (reg) \
} }
#define I2C_VOLUME(xname,chid) \ #define I2C_VOLUME(xname,chid) \
{ \ { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_ca0106_i2c_volume_info, \ .info = snd_ca0106_i2c_volume_info, \
.get = snd_ca0106_i2c_volume_get, \ .get = snd_ca0106_i2c_volume_get, \
.put = snd_ca0106_i2c_volume_put, \ .put = snd_ca0106_i2c_volume_put, \
.tlv = { .p = snd_ca0106_db_scale2 }, \
.private_value = chid \ .private_value = chid \
} }

View File

@ -33,6 +33,7 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/rawmidi.h> #include <sound/rawmidi.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/tlv.h>
#include <sound/opl3.h> #include <sound/opl3.h>
#include <sound/initval.h> #include <sound/initval.h>
@ -1054,6 +1055,8 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
static struct snd_kcontrol_new snd_cs4281_fm_vol = static struct snd_kcontrol_new snd_cs4281_fm_vol =
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1062,6 +1065,7 @@ static struct snd_kcontrol_new snd_cs4281_fm_vol =
.get = snd_cs4281_get_volume, .get = snd_cs4281_get_volume,
.put = snd_cs4281_put_volume, .put = snd_cs4281_put_volume,
.private_value = ((BA0_FMLVC << 16) | BA0_FMRVC), .private_value = ((BA0_FMLVC << 16) | BA0_FMRVC),
.tlv = { .p = db_scale_dsp },
}; };
static struct snd_kcontrol_new snd_cs4281_pcm_vol = static struct snd_kcontrol_new snd_cs4281_pcm_vol =
@ -1072,6 +1076,7 @@ static struct snd_kcontrol_new snd_cs4281_pcm_vol =
.get = snd_cs4281_get_volume, .get = snd_cs4281_get_volume,
.put = snd_cs4281_put_volume, .put = snd_cs4281_put_volume,
.private_value = ((BA0_PPLVC << 16) | BA0_PPRVC), .private_value = ((BA0_PPLVC << 16) | BA0_PPRVC),
.tlv = { .p = db_scale_dsp },
}; };
static void snd_cs4281_mixer_free_ac97_bus(struct snd_ac97_bus *bus) static void snd_cs4281_mixer_free_ac97_bus(struct snd_ac97_bus *bus)

View File

@ -868,35 +868,23 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
struct dsp_spos_instance * ins = chip->dsp_spos_instance; struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i; int i;
if (ins->proc_sym_info_entry) { snd_info_free_entry(ins->proc_sym_info_entry);
snd_info_unregister(ins->proc_sym_info_entry);
ins->proc_sym_info_entry = NULL; ins->proc_sym_info_entry = NULL;
}
if (ins->proc_modules_info_entry) { snd_info_free_entry(ins->proc_modules_info_entry);
snd_info_unregister(ins->proc_modules_info_entry);
ins->proc_modules_info_entry = NULL; ins->proc_modules_info_entry = NULL;
}
if (ins->proc_parameter_dump_info_entry) { snd_info_free_entry(ins->proc_parameter_dump_info_entry);
snd_info_unregister(ins->proc_parameter_dump_info_entry);
ins->proc_parameter_dump_info_entry = NULL; ins->proc_parameter_dump_info_entry = NULL;
}
if (ins->proc_sample_dump_info_entry) { snd_info_free_entry(ins->proc_sample_dump_info_entry);
snd_info_unregister(ins->proc_sample_dump_info_entry);
ins->proc_sample_dump_info_entry = NULL; ins->proc_sample_dump_info_entry = NULL;
}
if (ins->proc_scb_info_entry) { snd_info_free_entry(ins->proc_scb_info_entry);
snd_info_unregister(ins->proc_scb_info_entry);
ins->proc_scb_info_entry = NULL; ins->proc_scb_info_entry = NULL;
}
if (ins->proc_task_info_entry) { snd_info_free_entry(ins->proc_task_info_entry);
snd_info_unregister(ins->proc_task_info_entry);
ins->proc_task_info_entry = NULL; ins->proc_task_info_entry = NULL;
}
mutex_lock(&chip->spos_mutex); mutex_lock(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) { for (i = 0; i < ins->nscb; ++i) {
@ -905,10 +893,8 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
} }
mutex_unlock(&chip->spos_mutex); mutex_unlock(&chip->spos_mutex);
if (ins->proc_dsp_dir) { snd_info_free_entry(ins->proc_dsp_dir);
snd_info_unregister (ins->proc_dsp_dir);
ins->proc_dsp_dir = NULL; ins->proc_dsp_dir = NULL;
}
return 0; return 0;
} }

View File

@ -233,7 +233,7 @@ void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name); snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name);
snd_info_unregister(scb->proc_info); snd_info_free_entry(scb->proc_info);
scb->proc_info = NULL; scb->proc_info = NULL;
snd_assert (scb_info != NULL, return); snd_assert (scb_info != NULL, return);

View File

@ -4,7 +4,7 @@
snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o
ifdef CONFIG_PM ifeq ($(CONFIG_PM),y)
snd-cs5535audio-objs += cs5535audio_pm.o snd-cs5535audio-objs += cs5535audio_pm.o
endif endif

View File

@ -232,7 +232,7 @@ static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state)
return 0; return 0;
} }
int snd_emu10k1_resume(struct pci_dev *pci) static int snd_emu10k1_resume(struct pci_dev *pci)
{ {
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = pci_get_drvdata(pci);
struct snd_emu10k1 *emu = card->private_data; struct snd_emu10k1 *emu = card->private_data;

View File

@ -927,6 +927,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0151_chip = 1, .ca0151_chip = 1,
.spk71 = 1, .spk71 = 1,
.spdif_bug = 1, .spdif_bug = 1,
.adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} , .ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102, {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
.driver = "Audigy2", .name = "Audigy 2 EX [1005]", .driver = "Audigy2", .name = "Audigy 2 EX [1005]",

View File

@ -1626,12 +1626,7 @@ static struct pci_driver driver = {
// initialization of the module // initialization of the module
static int __init alsa_card_emu10k1x_init(void) static int __init alsa_card_emu10k1x_init(void)
{ {
int err; return pci_register_driver(&driver);
if ((err = pci_register_driver(&driver)) > 0)
return err;
return 0;
} }
// clean up the module // clean up the module

View File

@ -35,6 +35,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/tlv.h>
#include <sound/emu10k1.h> #include <sound/emu10k1.h>
#if 0 /* for testing purposes - digital out -> capture */ #if 0 /* for testing purposes - digital out -> capture */
@ -266,6 +267,7 @@ static const u32 treble_table[41][5] = {
{ 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
}; };
/* dB gain = (float) 20 * log10( float(db_table_value) / 0x8000000 ) */
static const u32 db_table[101] = { static const u32 db_table[101] = {
0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540, 0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8, 0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
@ -290,6 +292,9 @@ static const u32 db_table[101] = {
0x7fffffff, 0x7fffffff,
}; };
/* EMU10k1/EMU10k2 DSP control db gain */
static DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const u32 onoff_table[2] = { static const u32 onoff_table[2] = {
0x00000000, 0x00000001 0x00000000, 0x00000001
}; };
@ -755,6 +760,11 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
knew.device = gctl->id.device; knew.device = gctl->id.device;
knew.subdevice = gctl->id.subdevice; knew.subdevice = gctl->id.subdevice;
knew.info = snd_emu10k1_gpr_ctl_info; knew.info = snd_emu10k1_gpr_ctl_info;
if (gctl->tlv.p) {
knew.tlv.p = gctl->tlv.p;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
}
knew.get = snd_emu10k1_gpr_ctl_get; knew.get = snd_emu10k1_gpr_ctl_get;
knew.put = snd_emu10k1_gpr_ctl_put; knew.put = snd_emu10k1_gpr_ctl_put;
memset(nctl, 0, sizeof(*nctl)); memset(nctl, 0, sizeof(*nctl));
@ -1013,6 +1023,7 @@ snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
ctl->min = 0; ctl->min = 0;
ctl->max = 100; ctl->max = 100;
ctl->tlv.p = snd_emu10k1_db_scale1;
ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
} }
@ -1027,6 +1038,7 @@ snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
ctl->min = 0; ctl->min = 0;
ctl->max = 100; ctl->max = 100;
ctl->tlv.p = snd_emu10k1_db_scale1;
ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
} }

View File

@ -100,6 +100,7 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/tlv.h>
#include <sound/emu10k1.h> #include <sound/emu10k1.h>
#include "p16v.h" #include "p16v.h"
@ -784,12 +785,16 @@ static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol,
} }
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
#define P16V_VOL(xname,xreg,xhl) { \ #define P16V_VOL(xname,xreg,xhl) { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_p16v_volume_info, \ .info = snd_p16v_volume_info, \
.get = snd_p16v_volume_get, \ .get = snd_p16v_volume_get, \
.put = snd_p16v_volume_put, \ .put = snd_p16v_volume_put, \
.tlv = { .p = snd_p16v_db_scale1 }, \
.private_value = ((xreg) | ((xhl) << 8)) \ .private_value = ((xreg) | ((xhl) << 8)) \
} }

View File

@ -62,6 +62,7 @@
#include <sound/opl3.h> #include <sound/opl3.h>
#include <sound/mpu401.h> #include <sound/mpu401.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/tlv.h>
#include <asm/io.h> #include <asm/io.h>
@ -1164,6 +1165,14 @@ static int snd_es1938_reg_read(struct es1938 *chip, unsigned char reg)
return snd_es1938_read(chip, reg); return snd_es1938_read(chip, reg);
} }
#define ES1938_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
.name = xname, .index = xindex, \
.info = snd_es1938_info_single, \
.get = snd_es1938_get_single, .put = snd_es1938_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
.tlv = { .p = xtlv } }
#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \ #define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1938_info_single, \ .info = snd_es1938_info_single, \
@ -1217,6 +1226,14 @@ static int snd_es1938_put_single(struct snd_kcontrol *kcontrol,
return snd_es1938_reg_bits(chip, reg, mask, val) != val; return snd_es1938_reg_bits(chip, reg, mask, val) != val;
} }
#define ES1938_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
.name = xname, .index = xindex, \
.info = snd_es1938_info_double, \
.get = snd_es1938_get_double, .put = snd_es1938_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
.tlv = { .p = xtlv } }
#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ #define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1938_info_double, \ .info = snd_es1938_info_double, \
@ -1297,8 +1314,41 @@ static int snd_es1938_put_double(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static unsigned int db_scale_master[] = {
TLV_DB_RANGE_HEAD(2),
0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1),
54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0),
};
static unsigned int db_scale_audio1[] = {
TLV_DB_RANGE_HEAD(2),
0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0),
};
static unsigned int db_scale_audio2[] = {
TLV_DB_RANGE_HEAD(2),
0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0),
};
static unsigned int db_scale_mic[] = {
TLV_DB_RANGE_HEAD(2),
0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(0, 150, 0),
};
static unsigned int db_scale_line[] = {
TLV_DB_RANGE_HEAD(2),
0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
};
static DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
static struct snd_kcontrol_new snd_es1938_controls[] = { static struct snd_kcontrol_new snd_es1938_controls[] = {
ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0,
db_scale_master),
ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1309,19 +1359,27 @@ ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Hardware Master Playback Switch", .name = "Hardware Master Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = snd_es1938_info_hw_switch, .info = snd_es1938_info_hw_switch,
.get = snd_es1938_get_hw_switch, .get = snd_es1938_get_hw_switch,
.tlv = { .p = db_scale_master },
}, },
ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0), ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), ES1938_DOUBLE_TLV("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0,
db_scale_line),
ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), ES1938_DOUBLE_TLV("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0,
ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), db_scale_mic),
ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), ES1938_DOUBLE_TLV("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), db_scale_line),
ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), ES1938_DOUBLE_TLV("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0,
db_scale_mic),
ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0,
db_scale_line),
ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
db_scale_capture),
ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0), ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
@ -1332,16 +1390,26 @@ ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
.get = snd_es1938_get_mux, .get = snd_es1938_get_mux,
.put = snd_es1938_put_mux, .put = snd_es1938_put_mux,
}, },
ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), ES1938_DOUBLE_TLV("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), db_scale_line),
ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), ES1938_DOUBLE_TLV("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0,
ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), db_scale_audio2),
ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), ES1938_DOUBLE_TLV("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0,
ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), db_scale_mic),
ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), ES1938_DOUBLE_TLV("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0,
ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0), db_scale_line),
ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), ES1938_DOUBLE_TLV("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0,
ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0), db_scale_mic),
ES1938_DOUBLE_TLV("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0,
db_scale_line),
ES1938_DOUBLE_TLV("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0,
db_scale_line),
ES1938_DOUBLE_TLV("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0,
db_scale_line),
ES1938_DOUBLE_TLV("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0,
db_scale_audio2),
ES1938_DOUBLE_TLV("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0,
db_scale_audio1),
ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,

View File

@ -1905,7 +1905,7 @@ static void es1968_update_hw_volume(unsigned long private_data)
/* Figure out which volume control button was pushed, /* Figure out which volume control button was pushed,
based on differences from the default register based on differences from the default register
values. */ values. */
x = inb(chip->io_port + 0x1c); x = inb(chip->io_port + 0x1c) & 0xee;
/* Reset the volume control registers. */ /* Reset the volume control registers. */
outb(0x88, chip->io_port + 0x1c); outb(0x88, chip->io_port + 0x1c);
outb(0x88, chip->io_port + 0x1d); outb(0x88, chip->io_port + 0x1d);
@ -1921,7 +1921,8 @@ static void es1968_update_hw_volume(unsigned long private_data)
/* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */ /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
spin_lock_irqsave(&chip->ac97_lock, flags); spin_lock_irqsave(&chip->ac97_lock, flags);
val = chip->ac97->regs[AC97_MASTER]; val = chip->ac97->regs[AC97_MASTER];
if (x & 1) { switch (x) {
case 0x88:
/* mute */ /* mute */
val ^= 0x8000; val ^= 0x8000;
chip->ac97->regs[AC97_MASTER] = val; chip->ac97->regs[AC97_MASTER] = val;
@ -1929,26 +1930,31 @@ static void es1968_update_hw_volume(unsigned long private_data)
outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->master_switch->id); &chip->master_switch->id);
} else { break;
val &= 0x7fff; case 0xaa:
if (((x>>1) & 7) > 4) {
/* volume up */ /* volume up */
if ((val & 0xff) > 0) if ((val & 0x7f) > 0)
val--; val--;
if ((val & 0xff00) > 0) if ((val & 0x7f00) > 0)
val -= 0x0100; val -= 0x0100;
} else {
/* volume down */
if ((val & 0xff) < 0x1f)
val++;
if ((val & 0xff00) < 0x1f00)
val += 0x0100;
}
chip->ac97->regs[AC97_MASTER] = val; chip->ac97->regs[AC97_MASTER] = val;
outw(val, chip->io_port + ESM_AC97_DATA); outw(val, chip->io_port + ESM_AC97_DATA);
outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->master_volume->id); &chip->master_volume->id);
break;
case 0x66:
/* volume down */
if ((val & 0x7f) < 0x1f)
val++;
if ((val & 0x7f00) < 0x1f00)
val += 0x0100;
chip->ac97->regs[AC97_MASTER] = val;
outw(val, chip->io_port + ESM_AC97_DATA);
outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->master_volume->id);
break;
} }
spin_unlock_irqrestore(&chip->ac97_lock, flags); spin_unlock_irqrestore(&chip->ac97_lock, flags);
} }

View File

@ -2,6 +2,7 @@
* The driver for the ForteMedia FM801 based soundcards * The driver for the ForteMedia FM801 based soundcards
* Copyright (c) by Jaroslav Kysela <perex@suse.cz> * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* *
* Support FM only card by Andy Shevchenko <andy@smile.org.ua>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -28,6 +29,7 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/tlv.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/mpu401.h> #include <sound/mpu401.h>
#include <sound/opl3.h> #include <sound/opl3.h>
@ -54,6 +56,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
* 1 = MediaForte 256-PCS * 1 = MediaForte 256-PCS
* 2 = MediaForte 256-PCPR * 2 = MediaForte 256-PCPR
* 3 = MediaForte 64-PCR * 3 = MediaForte 64-PCR
* 16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card
* High 16-bits are video (radio) device number + 1 * High 16-bits are video (radio) device number + 1
*/ */
static int tea575x_tuner[SNDRV_CARDS]; static int tea575x_tuner[SNDRV_CARDS];
@ -158,6 +161,7 @@ struct fm801 {
unsigned int multichannel: 1, /* multichannel support */ unsigned int multichannel: 1, /* multichannel support */
secondary: 1; /* secondary codec */ secondary: 1; /* secondary codec */
unsigned char secondary_addr; /* address of the secondary codec */ unsigned char secondary_addr; /* address of the secondary codec */
unsigned int tea575x_tuner; /* tuner flags */
unsigned short ply_ctrl; /* playback control */ unsigned short ply_ctrl; /* playback control */
unsigned short cap_ctrl; /* capture control */ unsigned short cap_ctrl; /* capture control */
@ -318,10 +322,8 @@ static unsigned int channels[] = {
2, 4, 6 2, 4, 6
}; };
#define CHANNELS sizeof(channels) / sizeof(channels[0])
static struct snd_pcm_hw_constraint_list hw_constraints_channels = { static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
.count = CHANNELS, .count = ARRAY_SIZE(channels),
.list = channels, .list = channels,
.mask = 0, .mask = 0,
}; };
@ -1052,6 +1054,13 @@ static int snd_fm801_put_single(struct snd_kcontrol *kcontrol,
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \
.get = snd_fm801_get_double, .put = snd_fm801_put_double, \ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
.private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.name = xname, .info = snd_fm801_info_double, \
.get = snd_fm801_get_double, .put = snd_fm801_put_double, \
.private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
.tlv = { .p = (xtlv) } }
static int snd_fm801_info_double(struct snd_kcontrol *kcontrol, static int snd_fm801_info_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
@ -1148,14 +1157,19 @@ static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
} }
static DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls) #define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = { static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = {
FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1), FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1,
db_scale_dsp),
FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1), FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1), FM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1,
db_scale_dsp),
FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1), FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1), FM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1,
db_scale_dsp),
FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1253,6 +1267,9 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
int id; int id;
unsigned short cmdw; unsigned short cmdw;
if (chip->tea575x_tuner & 0x0010)
goto __ac97_ok;
/* codec cold reset + AC'97 warm reset */ /* codec cold reset + AC'97 warm reset */
outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL)); outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */ inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
@ -1290,6 +1307,8 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750)); wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
} }
__ac97_ok:
/* init volume */ /* init volume */
outw(0x0808, FM801_REG(chip, PCM_VOL)); outw(0x0808, FM801_REG(chip, PCM_VOL));
outw(0x9f1f, FM801_REG(chip, FM_VOL)); outw(0x9f1f, FM801_REG(chip, FM_VOL));
@ -1298,9 +1317,12 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
/* I2S control - I2S mode */ /* I2S control - I2S mode */
outw(0x0003, FM801_REG(chip, I2S_MODE)); outw(0x0003, FM801_REG(chip, I2S_MODE));
/* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */ /* interrupt setup */
cmdw = inw(FM801_REG(chip, IRQ_MASK)); cmdw = inw(FM801_REG(chip, IRQ_MASK));
cmdw &= ~0x0083; if (chip->irq < 0)
cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
else
cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
outw(cmdw, FM801_REG(chip, IRQ_MASK)); outw(cmdw, FM801_REG(chip, IRQ_MASK));
/* interrupt clear */ /* interrupt clear */
@ -1365,12 +1387,14 @@ static int __devinit snd_fm801_create(struct snd_card *card,
chip->card = card; chip->card = card;
chip->pci = pci; chip->pci = pci;
chip->irq = -1; chip->irq = -1;
chip->tea575x_tuner = tea575x_tuner;
if ((err = pci_request_regions(pci, "FM801")) < 0) { if ((err = pci_request_regions(pci, "FM801")) < 0) {
kfree(chip); kfree(chip);
pci_disable_device(pci); pci_disable_device(pci);
return err; return err;
} }
chip->port = pci_resource_start(pci, 0); chip->port = pci_resource_start(pci, 0);
if ((tea575x_tuner & 0x0010) == 0) {
if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_DISABLED|IRQF_SHARED, if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_DISABLED|IRQF_SHARED,
"FM801", chip)) { "FM801", chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq); snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
@ -1379,6 +1403,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
} }
chip->irq = pci->irq; chip->irq = pci->irq;
pci_set_master(pci); pci_set_master(pci);
}
pci_read_config_byte(pci, PCI_REVISION_ID, &rev); pci_read_config_byte(pci, PCI_REVISION_ID, &rev);
if (rev >= 0xb1) /* FM801-AU */ if (rev >= 0xb1) /* FM801-AU */
@ -1394,12 +1419,12 @@ static int __devinit snd_fm801_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev); snd_card_set_dev(card, &pci->dev);
#ifdef TEA575X_RADIO #ifdef TEA575X_RADIO
if (tea575x_tuner > 0 && (tea575x_tuner & 0xffff) < 4) { if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) {
chip->tea.dev_nr = tea575x_tuner >> 16; chip->tea.dev_nr = tea575x_tuner >> 16;
chip->tea.card = card; chip->tea.card = card;
chip->tea.freq_fixup = 10700; chip->tea.freq_fixup = 10700;
chip->tea.private_data = chip; chip->tea.private_data = chip;
chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0xffff) - 1]; chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1];
snd_tea575x_init(&chip->tea); snd_tea575x_init(&chip->tea);
} }
#endif #endif
@ -1439,6 +1464,9 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i", sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq); card->shortname, chip->port, chip->irq);
if (tea575x_tuner[dev] & 0x0010)
goto __fm801_tuner_only;
if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;
@ -1465,6 +1493,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
return err; return err;
} }
__fm801_tuner_only:
if ((err = snd_card_register(card)) < 0) { if ((err = snd_card_register(card)) < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;

View File

@ -29,6 +29,7 @@
#include <sound/core.h> #include <sound/core.h>
#include "hda_codec.h" #include "hda_codec.h"
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/tlv.h>
#include <sound/initval.h> #include <sound/initval.h>
#include "hda_local.h" #include "hda_local.h"
@ -50,8 +51,10 @@ struct hda_vendor_id {
/* codec vendor labels */ /* codec vendor labels */
static struct hda_vendor_id hda_vendor_ids[] = { static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x10ec, "Realtek" }, { 0x10ec, "Realtek" },
{ 0x1057, "Motorola" },
{ 0x11d4, "Analog Devices" }, { 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" }, { 0x13f6, "C-Media" },
{ 0x14f1, "Conexant" },
{ 0x434d, "C-Media" }, { 0x434d, "C-Media" },
{ 0x8384, "SigmaTel" }, { 0x8384, "SigmaTel" },
{} /* terminator */ {} /* terminator */
@ -841,6 +844,31 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change; return change;
} }
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol);
int dir = get_amp_direction(kcontrol);
u32 caps, val1, val2;
if (size < 4 * sizeof(unsigned int))
return -ENOMEM;
caps = query_amp_caps(codec, nid, dir);
val2 = (((caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT) + 1) * 25;
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 = ((int)val1) * ((int)val2);
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT;
if (put_user(2 * sizeof(unsigned int), _tlv + 1))
return -EFAULT;
if (put_user(val1, _tlv + 2))
return -EFAULT;
if (put_user(val2, _tlv + 3))
return -EFAULT;
return 0;
}
/* switch */ /* switch */
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
@ -1477,10 +1505,10 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
formats |= SNDRV_PCM_FMTBIT_S32_LE; formats |= SNDRV_PCM_FMTBIT_S32_LE;
if (val & AC_SUPPCM_BITS_32) if (val & AC_SUPPCM_BITS_32)
bps = 32; bps = 32;
else if (val & AC_SUPPCM_BITS_20)
bps = 20;
else if (val & AC_SUPPCM_BITS_24) else if (val & AC_SUPPCM_BITS_24)
bps = 24; bps = 24;
else if (val & AC_SUPPCM_BITS_20)
bps = 20;
} }
} }
else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */ else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */
@ -1916,7 +1944,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
/* front */ /* front */
snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
if (mout->hp_nid) if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
/* headphone out will just decode front left/right (stereo) */ /* headphone out will just decode front left/right (stereo) */
snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
/* extra outputs copied from front */ /* extra outputs copied from front */
@ -1984,7 +2012,7 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
* in the order of front, rear, CLFE, side, ... * in the order of front, rear, CLFE, side, ...
* *
* If more extra outputs (speaker and headphone) are found, the pins are * If more extra outputs (speaker and headphone) are found, the pins are
* assisnged to hp_pin and speaker_pins[], respectively. If no line-out jack * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
* is detected, one of speaker of HP pins is assigned as the primary * is detected, one of speaker of HP pins is assigned as the primary
* output, i.e. to line_out_pins[0]. So, line_outs is always positive * output, i.e. to line_out_pins[0]. So, line_outs is always positive
* if any analog output exists. * if any analog output exists.
@ -2046,14 +2074,26 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
cfg->speaker_outs++; cfg->speaker_outs++;
break; break;
case AC_JACK_HP_OUT: case AC_JACK_HP_OUT:
cfg->hp_pin = nid; if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
continue;
cfg->hp_pins[cfg->hp_outs] = nid;
cfg->hp_outs++;
break; break;
case AC_JACK_MIC_IN: case AC_JACK_MIC_IN: {
if (loc == AC_JACK_LOC_FRONT) int preferred, alt;
cfg->input_pins[AUTO_PIN_FRONT_MIC] = nid; if (loc == AC_JACK_LOC_FRONT) {
else preferred = AUTO_PIN_FRONT_MIC;
cfg->input_pins[AUTO_PIN_MIC] = nid; alt = AUTO_PIN_MIC;
} else {
preferred = AUTO_PIN_MIC;
alt = AUTO_PIN_FRONT_MIC;
}
if (!cfg->input_pins[preferred])
cfg->input_pins[preferred] = nid;
else if (!cfg->input_pins[alt])
cfg->input_pins[alt] = nid;
break; break;
}
case AC_JACK_LINE_IN: case AC_JACK_LINE_IN:
if (loc == AC_JACK_LOC_FRONT) if (loc == AC_JACK_LOC_FRONT)
cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid; cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
@ -2119,8 +2159,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
cfg->speaker_outs, cfg->speaker_pins[0], cfg->speaker_outs, cfg->speaker_pins[0],
cfg->speaker_pins[1], cfg->speaker_pins[2], cfg->speaker_pins[1], cfg->speaker_pins[2],
cfg->speaker_pins[3], cfg->speaker_pins[4]); cfg->speaker_pins[3], cfg->speaker_pins[4]);
snd_printd(" hp=0x%x, dig_out=0x%x, din_in=0x%x\n", snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
cfg->hp_pin, cfg->dig_out_pin, cfg->dig_in_pin); cfg->hp_outs, cfg->hp_pins[0],
cfg->hp_pins[1], cfg->hp_pins[2],
cfg->hp_pins[3], cfg->hp_pins[4]);
snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
" cd=0x%x, aux=0x%x\n", " cd=0x%x, aux=0x%x\n",
cfg->input_pins[AUTO_PIN_MIC], cfg->input_pins[AUTO_PIN_MIC],
@ -2141,10 +2183,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
sizeof(cfg->speaker_pins)); sizeof(cfg->speaker_pins));
cfg->speaker_outs = 0; cfg->speaker_outs = 0;
memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
} else if (cfg->hp_pin) { } else if (cfg->hp_outs) {
cfg->line_outs = 1; cfg->line_outs = cfg->hp_outs;
cfg->line_out_pins[0] = cfg->hp_pin; memcpy(cfg->line_out_pins, cfg->hp_pins,
cfg->hp_pin = 0; sizeof(cfg->hp_pins));
cfg->hp_outs = 0;
memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
} }
} }

View File

@ -46,11 +46,18 @@ struct hda_gnode {
}; };
/* patch-specific record */ /* patch-specific record */
#define MAX_PCM_VOLS 2
struct pcm_vol {
struct hda_gnode *node; /* Node for PCM volume */
unsigned int index; /* connection of PCM volume */
};
struct hda_gspec { struct hda_gspec {
struct hda_gnode *dac_node[2]; /* DAC node */ struct hda_gnode *dac_node[2]; /* DAC node */
struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
struct hda_gnode *pcm_vol_node[2]; /* Node for PCM volume */ struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */
unsigned int pcm_vol_index[2]; /* connection of PCM volume */ unsigned int pcm_vol_nodes; /* number of PCM volumes */
struct hda_gnode *adc_node; /* ADC node */ struct hda_gnode *adc_node; /* ADC node */
struct hda_gnode *cap_vol_node; /* Node for capture volume */ struct hda_gnode *cap_vol_node; /* Node for capture volume */
@ -285,9 +292,11 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
return node == spec->dac_node[dac_idx]; return node == spec->dac_node[dac_idx];
} }
spec->dac_node[dac_idx] = node; spec->dac_node[dac_idx] = node;
if (node->wid_caps & AC_WCAP_OUT_AMP) { if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
spec->pcm_vol_node[dac_idx] = node; spec->pcm_vol_nodes < MAX_PCM_VOLS) {
spec->pcm_vol_index[dac_idx] = 0; spec->pcm_vol[spec->pcm_vol_nodes].node = node;
spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
spec->pcm_vol_nodes++;
} }
return 1; /* found */ return 1; /* found */
} }
@ -307,13 +316,16 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
select_input_connection(codec, node, i); select_input_connection(codec, node, i);
unmute_input(codec, node, i); unmute_input(codec, node, i);
unmute_output(codec, node); unmute_output(codec, node);
if (! spec->pcm_vol_node[dac_idx]) { if (spec->dac_node[dac_idx] &&
if (node->wid_caps & AC_WCAP_IN_AMP) { spec->pcm_vol_nodes < MAX_PCM_VOLS &&
spec->pcm_vol_node[dac_idx] = node; !(spec->dac_node[dac_idx]->wid_caps &
spec->pcm_vol_index[dac_idx] = i; AC_WCAP_OUT_AMP)) {
} else if (node->wid_caps & AC_WCAP_OUT_AMP) { if ((node->wid_caps & AC_WCAP_IN_AMP) ||
spec->pcm_vol_node[dac_idx] = node; (node->wid_caps & AC_WCAP_OUT_AMP)) {
spec->pcm_vol_index[dac_idx] = 0; int n = spec->pcm_vol_nodes;
spec->pcm_vol[n].node = node;
spec->pcm_vol[n].index = i;
spec->pcm_vol_nodes++;
} }
} }
return 1; return 1;
@ -370,7 +382,9 @@ static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
/* set PIN-Out enable */ /* set PIN-Out enable */
snd_hda_codec_write(codec, node->nid, 0, snd_hda_codec_write(codec, node->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, AC_VERB_SET_PIN_WIDGET_CONTROL,
AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); AC_PINCTL_OUT_EN |
((node->pin_caps & AC_PINCAP_HP_DRV) ?
AC_PINCTL_HP_EN : 0));
return node; return node;
} }
} }
@ -461,14 +475,19 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
return "Front Line"; return "Front Line";
return "Line"; return "Line";
case AC_JACK_CD: case AC_JACK_CD:
#if 0
if (pinctl) if (pinctl)
*pinctl |= AC_PINCTL_VREF_GRD; *pinctl |= AC_PINCTL_VREF_GRD;
#endif
return "CD"; return "CD";
case AC_JACK_AUX: case AC_JACK_AUX:
if ((location & 0x0f) == AC_JACK_LOC_FRONT) if ((location & 0x0f) == AC_JACK_LOC_FRONT)
return "Front Aux"; return "Front Aux";
return "Aux"; return "Aux";
case AC_JACK_MIC_IN: case AC_JACK_MIC_IN:
if (node->pin_caps &
(AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT))
*pinctl |= AC_PINCTL_VREF_80;
if ((location & 0x0f) == AC_JACK_LOC_FRONT) if ((location & 0x0f) == AC_JACK_LOC_FRONT)
return "Front Mic"; return "Front Mic";
return "Mic"; return "Mic";
@ -556,6 +575,29 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 1; /* found */ return 1; /* found */
} }
/* add a capture source element */
static void add_cap_src(struct hda_gspec *spec, int idx)
{
struct hda_input_mux_item *csrc;
char *buf;
int num, ocap;
num = spec->input_mux.num_items;
csrc = &spec->input_mux.items[num];
buf = spec->cap_labels[num];
for (ocap = 0; ocap < num; ocap++) {
if (! strcmp(buf, spec->cap_labels[ocap])) {
/* same label already exists,
* put the index number to be unique
*/
sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
break;
}
}
csrc->index = idx;
spec->input_mux.num_items++;
}
/* /*
* parse input * parse input
*/ */
@ -576,28 +618,26 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
* if it reaches to a proper input PIN, add the path as the * if it reaches to a proper input PIN, add the path as the
* input path. * input path.
*/ */
/* first, check the direct connections to PIN widgets */
for (i = 0; i < adc_node->nconns; i++) { for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]); node = hda_get_node(spec, adc_node->conn_list[i]);
if (! node) if (node && node->type == AC_WID_PIN) {
continue;
err = parse_adc_sub_nodes(codec, spec, node); err = parse_adc_sub_nodes(codec, spec, node);
if (err < 0) if (err < 0)
return err; return err;
else if (err > 0) { else if (err > 0)
struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items]; add_cap_src(spec, i);
char *buf = spec->cap_labels[spec->input_mux.num_items];
int ocap;
for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) {
if (! strcmp(buf, spec->cap_labels[ocap])) {
/* same label already exists,
* put the index number to be unique
*/
sprintf(buf, "%s %d", spec->cap_labels[ocap],
spec->input_mux.num_items);
} }
} }
csrc->index = i; /* ... then check the rests, more complicated connections */
spec->input_mux.num_items++; for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type != AC_WID_PIN) {
err = parse_adc_sub_nodes(codec, spec, node);
if (err < 0)
return err;
else if (err > 0)
add_cap_src(spec, i);
} }
} }
@ -647,9 +687,6 @@ static int parse_input(struct hda_codec *codec)
/* /*
* create mixer controls if possible * create mixer controls if possible
*/ */
#define DIR_OUT 0x1
#define DIR_IN 0x2
static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
unsigned int index, const char *type, const char *dir_sfx) unsigned int index, const char *type, const char *dir_sfx)
{ {
@ -722,18 +759,37 @@ static int check_existing_control(struct hda_codec *codec, const char *type, con
/* /*
* build output mixer controls * build output mixer controls
*/ */
static int create_output_mixers(struct hda_codec *codec, const char **names)
{
struct hda_gspec *spec = codec->spec;
int i, err;
for (i = 0; i < spec->pcm_vol_nodes; i++) {
err = create_mixer(codec, spec->pcm_vol[i].node,
spec->pcm_vol[i].index,
names[i], "Playback");
if (err < 0)
return err;
}
return 0;
}
static int build_output_controls(struct hda_codec *codec) static int build_output_controls(struct hda_codec *codec)
{ {
struct hda_gspec *spec = codec->spec; struct hda_gspec *spec = codec->spec;
static const char *types[2] = { "Master", "Headphone" }; static const char *types_speaker[] = { "Speaker", "Headphone" };
int i, err; static const char *types_line[] = { "Front", "Headphone" };
for (i = 0; i < 2 && spec->pcm_vol_node[i]; i++) { switch (spec->pcm_vol_nodes) {
err = create_mixer(codec, spec->pcm_vol_node[i], case 1:
spec->pcm_vol_index[i], return create_mixer(codec, spec->pcm_vol[0].node,
types[i], "Playback"); spec->pcm_vol[0].index,
if (err < 0) "Master", "Playback");
return err; case 2:
if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
return create_output_mixers(codec, types_speaker);
else
return create_output_mixers(codec, types_line);
} }
return 0; return 0;
} }
@ -743,16 +799,7 @@ static int build_input_controls(struct hda_codec *codec)
{ {
struct hda_gspec *spec = codec->spec; struct hda_gspec *spec = codec->spec;
struct hda_gnode *adc_node = spec->adc_node; struct hda_gnode *adc_node = spec->adc_node;
int err; int i, err;
if (! adc_node)
return 0; /* not found */
/* create capture volume and switch controls if the ADC has an amp */
err = create_mixer(codec, adc_node, 0, NULL, "Capture");
/* create input MUX if multiple sources are available */
if (spec->input_mux.num_items > 1) {
static struct snd_kcontrol_new cap_sel = { static struct snd_kcontrol_new cap_sel = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source", .name = "Capture Source",
@ -760,11 +807,49 @@ static int build_input_controls(struct hda_codec *codec)
.get = capture_source_get, .get = capture_source_get,
.put = capture_source_put, .put = capture_source_put,
}; };
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0)
return err; if (! adc_node || ! spec->input_mux.num_items)
return 0; /* not found */
spec->cur_cap_src = 0; spec->cur_cap_src = 0;
select_input_connection(codec, adc_node, spec->input_mux.items[0].index); select_input_connection(codec, adc_node,
spec->input_mux.items[0].index);
/* create capture volume and switch controls if the ADC has an amp */
/* do we have only a single item? */
if (spec->input_mux.num_items == 1) {
err = create_mixer(codec, adc_node,
spec->input_mux.items[0].index,
NULL, "Capture");
if (err < 0)
return err;
return 0;
} }
/* create input MUX if multiple sources are available */
if ((err = snd_ctl_add(codec->bus->card,
snd_ctl_new1(&cap_sel, codec))) < 0)
return err;
/* no volume control? */
if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
return 0;
for (i = 0; i < spec->input_mux.num_items; i++) {
struct snd_kcontrol_new knew;
char name[32];
sprintf(name, "%s Capture Volume",
spec->input_mux.items[i].label);
knew = (struct snd_kcontrol_new)
HDA_CODEC_VOLUME(name, adc_node->nid,
spec->input_mux.items[i].index,
HDA_INPUT);
if ((err = snd_ctl_add(codec->bus->card,
snd_ctl_new1(&knew, codec))) < 0)
return err;
}
return 0; return 0;
} }

View File

@ -55,6 +55,7 @@ static char *model;
static int position_fix; static int position_fix;
static int probe_mask = -1; static int probe_mask = -1;
static int single_cmd; static int single_cmd;
static int disable_msi;
module_param(index, int, 0444); module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@ -68,6 +69,8 @@ module_param(probe_mask, int, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param(single_cmd, bool, 0444); module_param(single_cmd, bool, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only)."); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only).");
module_param(disable_msi, int, 0);
MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
/* just for backward compatibility */ /* just for backward compatibility */
@ -252,7 +255,7 @@ enum {
struct azx_dev { struct azx_dev {
u32 *bdl; /* virtual address of the BDL */ u32 *bdl; /* virtual address of the BDL */
dma_addr_t bdl_addr; /* physical address of the BDL */ dma_addr_t bdl_addr; /* physical address of the BDL */
volatile u32 *posbuf; /* position buffer pointer */ u32 *posbuf; /* position buffer pointer */
unsigned int bufsize; /* size of the play buffer in bytes */ unsigned int bufsize; /* size of the play buffer in bytes */
unsigned int fragsize; /* size of each period in bytes */ unsigned int fragsize; /* size of each period in bytes */
@ -332,6 +335,7 @@ struct azx {
int position_fix; int position_fix;
unsigned int initialized :1; unsigned int initialized :1;
unsigned int single_cmd :1; unsigned int single_cmd :1;
unsigned int polling_mode :1;
}; };
/* driver types */ /* driver types */
@ -516,12 +520,29 @@ static void azx_update_rirb(struct azx *chip)
static unsigned int azx_rirb_get_response(struct hda_codec *codec) static unsigned int azx_rirb_get_response(struct hda_codec *codec)
{ {
struct azx *chip = codec->bus->private_data; struct azx *chip = codec->bus->private_data;
int timeout = 50; unsigned long timeout;
while (chip->rirb.cmds) { again:
if (! --timeout) { timeout = jiffies + msecs_to_jiffies(1000);
snd_printk(KERN_ERR do {
"hda_intel: azx_get_response timeout, " if (chip->polling_mode) {
spin_lock_irq(&chip->reg_lock);
azx_update_rirb(chip);
spin_unlock_irq(&chip->reg_lock);
}
if (! chip->rirb.cmds)
return chip->rirb.res; /* the last value */
schedule_timeout_interruptible(1);
} while (time_after_eq(timeout, jiffies));
if (!chip->polling_mode) {
snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
"switching to polling mode...\n");
chip->polling_mode = 1;
goto again;
}
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode...\n"); "switching to single_cmd mode...\n");
chip->rirb.rp = azx_readb(chip, RIRBWP); chip->rirb.rp = azx_readb(chip, RIRBWP);
chip->rirb.cmds = 0; chip->rirb.cmds = 0;
@ -530,10 +551,6 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
azx_free_cmd_io(chip); azx_free_cmd_io(chip);
return -1; return -1;
} }
msleep(1);
}
return chip->rirb.res; /* the last value */
}
/* /*
* Use the single immediate command instead of CORB/RIRB for simplicity * Use the single immediate command instead of CORB/RIRB for simplicity
@ -645,7 +662,7 @@ static int azx_reset(struct azx *chip)
while (!azx_readb(chip, GCTL) && --count) while (!azx_readb(chip, GCTL) && --count)
msleep(1); msleep(1);
/* Brent Chartrand said to wait >= 540us for codecs to intialize */ /* Brent Chartrand said to wait >= 540us for codecs to initialize */
msleep(1); msleep(1);
/* check to see if controller is ready */ /* check to see if controller is ready */
@ -999,8 +1016,9 @@ static struct snd_pcm_hardware azx_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE /*|*/ /* No full-resume yet implemented */
/*SNDRV_PCM_INFO_RESUME*/), /* SNDRV_PCM_INFO_RESUME |*/
SNDRV_PCM_INFO_PAUSE),
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000, .rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000, .rate_min = 48000,
@ -1178,7 +1196,7 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
if (chip->position_fix == POS_FIX_POSBUF || if (chip->position_fix == POS_FIX_POSBUF ||
chip->position_fix == POS_FIX_AUTO) { chip->position_fix == POS_FIX_AUTO) {
/* use the position buffer */ /* use the position buffer */
pos = *azx_dev->posbuf; pos = le32_to_cpu(*azx_dev->posbuf);
if (chip->position_fix == POS_FIX_AUTO && if (chip->position_fix == POS_FIX_AUTO &&
azx_dev->period_intr == 1 && ! pos) { azx_dev->period_intr == 1 && ! pos) {
printk(KERN_WARNING printk(KERN_WARNING
@ -1222,7 +1240,12 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
struct snd_pcm *pcm; struct snd_pcm *pcm;
struct azx_pcm *apcm; struct azx_pcm *apcm;
snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL); /* if no substreams are defined for both playback and capture,
* it's just a placeholder. ignore it.
*/
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
return 0;
snd_assert(cpcm->name, return -EINVAL); snd_assert(cpcm->name, return -EINVAL);
err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
@ -1248,6 +1271,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
snd_dma_pci_data(chip->pci), snd_dma_pci_data(chip->pci),
1024 * 64, 1024 * 128); 1024 * 64, 1024 * 128);
chip->pcm[pcm_dev] = pcm; chip->pcm[pcm_dev] = pcm;
if (chip->pcm_devs < pcm_dev + 1)
chip->pcm_devs = pcm_dev + 1; chip->pcm_devs = pcm_dev + 1;
return 0; return 0;
@ -1326,7 +1350,7 @@ static int __devinit azx_init_stream(struct azx *chip)
struct azx_dev *azx_dev = &chip->azx_dev[i]; struct azx_dev *azx_dev = &chip->azx_dev[i];
azx_dev->bdl = (u32 *)(chip->bdl.area + off); azx_dev->bdl = (u32 *)(chip->bdl.area + off);
azx_dev->bdl_addr = chip->bdl.addr + off; azx_dev->bdl_addr = chip->bdl.addr + off;
azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8); azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
@ -1355,6 +1379,10 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
snd_pcm_suspend_all(chip->pcm[i]); snd_pcm_suspend_all(chip->pcm[i]);
snd_hda_suspend(chip->bus, state); snd_hda_suspend(chip->bus, state);
azx_free_cmd_io(chip); azx_free_cmd_io(chip);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
if (!disable_msi)
pci_disable_msi(chip->pci);
pci_disable_device(pci); pci_disable_device(pci);
pci_save_state(pci); pci_save_state(pci);
return 0; return 0;
@ -1367,6 +1395,12 @@ static int azx_resume(struct pci_dev *pci)
pci_restore_state(pci); pci_restore_state(pci);
pci_enable_device(pci); pci_enable_device(pci);
if (!disable_msi)
pci_enable_msi(pci);
/* FIXME: need proper error handling */
request_irq(pci->irq, azx_interrupt, IRQF_DISABLED|IRQF_SHARED,
"HDA Intel", chip);
chip->irq = pci->irq;
pci_set_master(pci); pci_set_master(pci);
azx_init_chip(chip); azx_init_chip(chip);
snd_hda_resume(chip->bus); snd_hda_resume(chip->bus);
@ -1398,12 +1432,14 @@ static int azx_free(struct azx *chip)
azx_writel(chip, DPLBASE, 0); azx_writel(chip, DPLBASE, 0);
azx_writel(chip, DPUBASE, 0); azx_writel(chip, DPUBASE, 0);
/* wait a little for interrupts to finish */ synchronize_irq(chip->irq);
msleep(1);
} }
if (chip->irq >= 0) if (chip->irq >= 0) {
free_irq(chip->irq, (void*)chip); free_irq(chip->irq, (void*)chip);
if (!disable_msi)
pci_disable_msi(chip->pci);
}
if (chip->remap_addr) if (chip->remap_addr)
iounmap(chip->remap_addr); iounmap(chip->remap_addr);
@ -1434,19 +1470,19 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
struct azx **rchip) struct azx **rchip)
{ {
struct azx *chip; struct azx *chip;
int err = 0; int err;
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = azx_dev_free, .dev_free = azx_dev_free,
}; };
*rchip = NULL; *rchip = NULL;
if ((err = pci_enable_device(pci)) < 0) err = pci_enable_device(pci);
if (err < 0)
return err; return err;
chip = kzalloc(sizeof(*chip), GFP_KERNEL); chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip) {
if (NULL == chip) {
snd_printk(KERN_ERR SFX "cannot allocate chip\n"); snd_printk(KERN_ERR SFX "cannot allocate chip\n");
pci_disable_device(pci); pci_disable_device(pci);
return -ENOMEM; return -ENOMEM;
@ -1472,7 +1508,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
} }
#endif #endif
if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) { err = pci_request_regions(pci, "ICH HD audio");
if (err < 0) {
kfree(chip); kfree(chip);
pci_disable_device(pci); pci_disable_device(pci);
return err; return err;
@ -1486,6 +1523,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
goto errout; goto errout;
} }
if (!disable_msi)
pci_enable_msi(pci);
if (request_irq(pci->irq, azx_interrupt, IRQF_DISABLED|IRQF_SHARED, if (request_irq(pci->irq, azx_interrupt, IRQF_DISABLED|IRQF_SHARED,
"HDA Intel", (void*)chip)) { "HDA Intel", (void*)chip)) {
snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq); snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
@ -1577,16 +1617,16 @@ static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *
{ {
struct snd_card *card; struct snd_card *card;
struct azx *chip; struct azx *chip;
int err = 0; int err;
card = snd_card_new(index, id, THIS_MODULE, 0); card = snd_card_new(index, id, THIS_MODULE, 0);
if (NULL == card) { if (!card) {
snd_printk(KERN_ERR SFX "Error creating card!\n"); snd_printk(KERN_ERR SFX "Error creating card!\n");
return -ENOMEM; return -ENOMEM;
} }
if ((err = azx_create(card, pci, pci_id->driver_data, err = azx_create(card, pci, pci_id->driver_data, &chip);
&chip)) < 0) { if (err < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;
} }

View File

@ -30,9 +30,13 @@
/* mono volume with index (index=0,1,...) (channel=1,2) */ /* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
.info = snd_hda_mixer_amp_volume_info, \ .info = snd_hda_mixer_amp_volume_info, \
.get = snd_hda_mixer_amp_volume_get, \ .get = snd_hda_mixer_amp_volume_get, \
.put = snd_hda_mixer_amp_volume_put, \ .put = snd_hda_mixer_amp_volume_put, \
.tlv = { .c = snd_hda_mixer_amp_tlv }, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
/* stereo volume with index */ /* stereo volume with index */
#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ #define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
@ -63,6 +67,7 @@
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv);
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
@ -224,7 +229,8 @@ struct auto_pin_cfg {
hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */ hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */
int speaker_outs; int speaker_outs;
hda_nid_t speaker_pins[5]; hda_nid_t speaker_pins[5];
hda_nid_t hp_pin; int hp_outs;
hda_nid_t hp_pins[5];
hda_nid_t input_pins[AUTO_PIN_LAST]; hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t dig_out_pin; hda_nid_t dig_out_pin;
hda_nid_t dig_in_pin; hda_nid_t dig_in_pin;

View File

@ -52,10 +52,9 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir) struct hda_codec *codec, hda_nid_t nid, int dir)
{ {
unsigned int caps; unsigned int caps;
if (dir == HDA_OUTPUT) caps = snd_hda_param_read(codec, nid,
caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP); dir == HDA_OUTPUT ?
else AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_IN_CAP);
if (caps == -1 || caps == 0) { if (caps == -1 || caps == 0) {
snd_iprintf(buffer, "N/A\n"); snd_iprintf(buffer, "N/A\n");
return; return;
@ -74,10 +73,7 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
unsigned int val; unsigned int val;
int i; int i;
if (dir == HDA_OUTPUT) dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
dir = AC_AMP_GET_OUTPUT;
else
dir = AC_AMP_GET_INPUT;
for (i = 0; i < indices; i++) { for (i = 0; i < indices; i++) {
snd_iprintf(buffer, " ["); snd_iprintf(buffer, " [");
if (stereo) { if (stereo) {

View File

@ -488,9 +488,13 @@ static struct snd_kcontrol_new ad1986a_mixers[] = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume", .name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
.info = ad1986a_pcm_amp_vol_info, .info = ad1986a_pcm_amp_vol_info,
.get = ad1986a_pcm_amp_vol_get, .get = ad1986a_pcm_amp_vol_get,
.put = ad1986a_pcm_amp_vol_put, .put = ad1986a_pcm_amp_vol_put,
.tlv = { .c = snd_hda_mixer_amp_tlv },
.private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT) .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
}, },
{ {
@ -637,6 +641,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
.info = snd_hda_mixer_amp_volume_info, .info = snd_hda_mixer_amp_volume_info,
.get = snd_hda_mixer_amp_volume_get, .get = snd_hda_mixer_amp_volume_get,
.put = ad1986a_laptop_master_vol_put, .put = ad1986a_laptop_master_vol_put,
.tlv = { .c = snd_hda_mixer_amp_tlv },
.private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
}, },
{ {
@ -791,6 +796,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = {
.config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */ .config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3, { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3,
.config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */ .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x81cb,
.config = AD1986A_3STACK }, /* ASUS M2NPV-VM */
{ .modelname = "laptop", .config = AD1986A_LAPTOP }, { .modelname = "laptop", .config = AD1986A_LAPTOP },
{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e, { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e,
.config = AD1986A_LAPTOP }, /* FSC V2060 */ .config = AD1986A_LAPTOP }, /* FSC V2060 */
@ -803,6 +810,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = {
.config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */ .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */
{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc024, { .pci_subvendor = 0x144d, .pci_subdevice = 0xc024,
.config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */ .config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */
{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc026,
.config = AD1986A_LAPTOP_EAPD }, /* Samsung X10-T2300 Culesa */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1153, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1153,
.config = AD1986A_LAPTOP_EAPD }, /* ASUS M9 */ .config = AD1986A_LAPTOP_EAPD }, /* ASUS M9 */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1213, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1213,
@ -1626,10 +1635,12 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec; struct ad198x_spec *spec = codec->spec;
if (spec->need_dac_fix) int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
spec->num_channel_mode,
&spec->multiout.max_channels);
if (! err && spec->need_dac_fix)
spec->multiout.num_dacs = spec->multiout.max_channels / 2; spec->multiout.num_dacs = spec->multiout.max_channels / 2;
return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, return err;
spec->num_channel_mode, &spec->multiout.max_channels);
} }
/* 6-stack mode */ /* 6-stack mode */
@ -2460,7 +2471,7 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
pin = spec->autocfg.speaker_pins[0]; pin = spec->autocfg.speaker_pins[0];
if (pin) /* connect to front */ if (pin) /* connect to front */
ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
pin = spec->autocfg.hp_pin; pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */ if (pin) /* connect to front */
ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
} }
@ -2512,7 +2523,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
(err = ad1988_auto_create_extra_out(codec, (err = ad1988_auto_create_extra_out(codec,
spec->autocfg.speaker_pins[0], spec->autocfg.speaker_pins[0],
"Speaker")) < 0 || "Speaker")) < 0 ||
(err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pin, (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 || "Headphone")) < 0 ||
(err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
return err; return err;

View File

@ -79,6 +79,7 @@ enum {
ALC262_BASIC, ALC262_BASIC,
ALC262_FUJITSU, ALC262_FUJITSU,
ALC262_HP_BPC, ALC262_HP_BPC,
ALC262_BENQ_ED8,
ALC262_AUTO, ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */ ALC262_MODEL_LAST /* last tag */
}; };
@ -89,6 +90,7 @@ enum {
ALC660_3ST, ALC660_3ST,
ALC861_3ST_DIG, ALC861_3ST_DIG,
ALC861_6ST_DIG, ALC861_6ST_DIG,
ALC861_UNIWILL_M31,
ALC861_AUTO, ALC861_AUTO,
ALC861_MODEL_LAST, ALC861_MODEL_LAST,
}; };
@ -97,6 +99,7 @@ enum {
enum { enum {
ALC882_3ST_DIG, ALC882_3ST_DIG,
ALC882_6ST_DIG, ALC882_6ST_DIG,
ALC882_ARIMA,
ALC882_AUTO, ALC882_AUTO,
ALC882_MODEL_LAST, ALC882_MODEL_LAST,
}; };
@ -108,6 +111,7 @@ enum {
ALC883_3ST_6ch, ALC883_3ST_6ch,
ALC883_6ST_DIG, ALC883_6ST_DIG,
ALC888_DEMO_BOARD, ALC888_DEMO_BOARD,
ALC883_ACER,
ALC883_AUTO, ALC883_AUTO,
ALC883_MODEL_LAST, ALC883_MODEL_LAST,
}; };
@ -153,6 +157,7 @@ struct alc_spec {
/* channel model */ /* channel model */
const struct hda_channel_mode *channel_mode; const struct hda_channel_mode *channel_mode;
int num_channel_mode; int num_channel_mode;
int need_dac_fix;
/* PCM information */ /* PCM information */
struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
@ -190,6 +195,7 @@ struct alc_config_preset {
hda_nid_t dig_in_nid; hda_nid_t dig_in_nid;
unsigned int num_channel_mode; unsigned int num_channel_mode;
const struct hda_channel_mode *channel_mode; const struct hda_channel_mode *channel_mode;
int need_dac_fix;
unsigned int num_mux_defs; unsigned int num_mux_defs;
const struct hda_input_mux *input_mux; const struct hda_input_mux *input_mux;
void (*unsol_event)(struct hda_codec *, unsigned int); void (*unsol_event)(struct hda_codec *, unsigned int);
@ -262,9 +268,12 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
spec->num_channel_mode, spec->num_channel_mode,
&spec->multiout.max_channels); &spec->multiout.max_channels);
if (! err && spec->need_dac_fix)
spec->multiout.num_dacs = spec->multiout.max_channels / 2;
return err;
} }
/* /*
@ -544,6 +553,7 @@ static void setup_preset(struct alc_spec *spec,
spec->channel_mode = preset->channel_mode; spec->channel_mode = preset->channel_mode;
spec->num_channel_mode = preset->num_channel_mode; spec->num_channel_mode = preset->num_channel_mode;
spec->need_dac_fix = preset->need_dac_fix;
spec->multiout.max_channels = spec->channel_mode[0].channels; spec->multiout.max_channels = spec->channel_mode[0].channels;
@ -1348,6 +1358,10 @@ static struct hda_verb alc880_pin_clevo_init_verbs[] = {
}; };
static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = { static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
/* change to EAPD mode */
{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
{0x20, AC_VERB_SET_PROC_COEF, 0x3060},
/* Headphone output */ /* Headphone output */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
/* Front output*/ /* Front output*/
@ -1782,25 +1796,9 @@ static int alc_build_pcms(struct hda_codec *codec)
} }
} }
/* If the use of more than one ADC is requested for the current /* SPDIF for stream index #1 */
* model, configure a second analog capture-only PCM.
*/
if (spec->num_adc_nids > 1) {
codec->num_pcms++;
info++;
info->name = spec->stream_name_analog;
/* No playback stream for second PCM */
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
if (spec->stream_analog_capture) {
snd_assert(spec->adc_nids, return -EINVAL);
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
}
}
if (spec->multiout.dig_out_nid || spec->dig_in_nid) { if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
codec->num_pcms++; codec->num_pcms = 2;
info++; info++;
info->name = spec->stream_name_digital; info->name = spec->stream_name_digital;
if (spec->multiout.dig_out_nid && if (spec->multiout.dig_out_nid &&
@ -1815,6 +1813,24 @@ static int alc_build_pcms(struct hda_codec *codec)
} }
} }
/* If the use of more than one ADC is requested for the current
* model, configure a second analog capture-only PCM.
*/
/* Additional Analaog capture for index #2 */
if (spec->num_adc_nids > 1 && spec->stream_analog_capture &&
spec->adc_nids) {
codec->num_pcms = 3;
info++;
info->name = spec->stream_name_analog;
/* No playback stream for second PCM */
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
if (spec->stream_analog_capture) {
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
}
}
return 0; return 0;
} }
@ -2130,7 +2146,10 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe212, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe213, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe234, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST },
@ -2145,6 +2164,7 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST }, { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST }, { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
/* TCL S700 */ /* TCL S700 */
{ .modelname = "tcl", .config = ALC880_TCL_S700 },
{ .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 }, { .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 },
/* Back 3 jack, front 2 jack (Internal add Aux-In) */ /* Back 3 jack, front 2 jack (Internal add Aux-In) */
@ -2156,8 +2176,13 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .modelname = "3stack-digout", .config = ALC880_3ST_DIG }, { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
{ .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG }, { .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG },
/* Clevo m520G NB */
{ .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, .config = ALC880_CLEVO }, /* Clevo laptops */
{ .modelname = "clevo", .config = ALC880_CLEVO },
{ .pci_subvendor = 0x1558, .pci_subdevice = 0x0520,
.config = ALC880_CLEVO }, /* Clevo m520G NB */
{ .pci_subvendor = 0x1558, .pci_subdevice = 0x0660,
.config = ALC880_CLEVO }, /* Clevo m665n */
/* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/ /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
@ -2222,12 +2247,16 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x10c2, .config = ALC880_ASUS_DIG }, /* Asus W6A */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS },
{ .modelname = "asus-w1v", .config = ALC880_ASUS_W1V },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V },
{ .modelname = "asus-dig", .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x8181, .config = ALC880_ASUS_DIG }, /* ASUS P4GPL-X */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x8181, .config = ALC880_ASUS_DIG }, /* ASUS P4GPL-X */
{ .modelname = "asus-dig2", .config = ALC880_ASUS_DIG2 },
{ .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 }, { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 },
{ .modelname = "uniwill", .config = ALC880_UNIWILL_DIG }, { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG },
@ -2243,6 +2272,7 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .modelname = "lg-lw", .config = ALC880_LG_LW }, { .modelname = "lg-lw", .config = ALC880_LG_LW },
{ .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW }, { .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW },
{ .pci_subvendor = 0x1854, .pci_subdevice = 0x0077, .config = ALC880_LG_LW },
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
{ .modelname = "test", .config = ALC880_TEST }, { .modelname = "test", .config = ALC880_TEST },
@ -2263,6 +2293,7 @@ static struct alc_config_preset alc880_presets[] = {
.dac_nids = alc880_dac_nids, .dac_nids = alc880_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
.channel_mode = alc880_threestack_modes, .channel_mode = alc880_threestack_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_3ST_DIG] = { [ALC880_3ST_DIG] = {
@ -2273,6 +2304,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID, .dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
.channel_mode = alc880_threestack_modes, .channel_mode = alc880_threestack_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_TCL_S700] = { [ALC880_TCL_S700] = {
@ -2365,6 +2397,7 @@ static struct alc_config_preset alc880_presets[] = {
.dac_nids = alc880_asus_dac_nids, .dac_nids = alc880_asus_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes, .channel_mode = alc880_asus_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_ASUS_DIG] = { [ALC880_ASUS_DIG] = {
@ -2376,6 +2409,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID, .dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes, .channel_mode = alc880_asus_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_ASUS_DIG2] = { [ALC880_ASUS_DIG2] = {
@ -2387,6 +2421,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID, .dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes, .channel_mode = alc880_asus_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_ASUS_W1V] = { [ALC880_ASUS_W1V] = {
@ -2398,6 +2433,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID, .dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes, .channel_mode = alc880_asus_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_UNIWILL_DIG] = { [ALC880_UNIWILL_DIG] = {
@ -2408,6 +2444,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID, .dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes, .channel_mode = alc880_asus_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_CLEVO] = { [ALC880_CLEVO] = {
@ -2419,6 +2456,7 @@ static struct alc_config_preset alc880_presets[] = {
.hp_nid = 0x03, .hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
.channel_mode = alc880_threestack_modes, .channel_mode = alc880_threestack_modes,
.need_dac_fix = 1,
.input_mux = &alc880_capture_source, .input_mux = &alc880_capture_source,
}, },
[ALC880_LG] = { [ALC880_LG] = {
@ -2430,6 +2468,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID, .dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes), .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes),
.channel_mode = alc880_lg_ch_modes, .channel_mode = alc880_lg_ch_modes,
.need_dac_fix = 1,
.input_mux = &alc880_lg_capture_source, .input_mux = &alc880_lg_capture_source,
.unsol_event = alc880_lg_unsol_event, .unsol_event = alc880_lg_unsol_event,
.init_hook = alc880_lg_automute, .init_hook = alc880_lg_automute,
@ -2714,7 +2753,7 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
pin = spec->autocfg.speaker_pins[0]; pin = spec->autocfg.speaker_pins[0];
if (pin) /* connect to front */ if (pin) /* connect to front */
alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
pin = spec->autocfg.hp_pin; pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */ if (pin) /* connect to front */
alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
} }
@ -2755,7 +2794,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
(err = alc880_auto_create_extra_out(spec, (err = alc880_auto_create_extra_out(spec,
spec->autocfg.speaker_pins[0], spec->autocfg.speaker_pins[0],
"Speaker")) < 0 || "Speaker")) < 0 ||
(err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pin, (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 || "Headphone")) < 0 ||
(err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
return err; return err;
@ -3697,7 +3736,7 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
return err; return err;
} }
nid = cfg->hp_pin; nid = cfg->hp_pins[0];
if (nid) { if (nid) {
err = alc260_add_playback_controls(spec, nid, "Headphone"); err = alc260_add_playback_controls(spec, nid, "Headphone");
if (err < 0) if (err < 0)
@ -3767,7 +3806,7 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
if (nid) if (nid)
alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
nid = spec->autocfg.hp_pin; nid = spec->autocfg.hp_pins[0];
if (nid) if (nid)
alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
} }
@ -3900,7 +3939,8 @@ static struct hda_board_config alc260_cfg_tbl[] = {
{ .pci_subvendor = 0x152d, .pci_subdevice = 0x0729, { .pci_subvendor = 0x152d, .pci_subdevice = 0x0729,
.config = ALC260_BASIC }, /* CTL Travel Master U553W */ .config = ALC260_BASIC }, /* CTL Travel Master U553W */
{ .modelname = "hp", .config = ALC260_HP }, { .modelname = "hp", .config = ALC260_HP },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP }, { .modelname = "hp-3013", .config = ALC260_HP_3013 },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP_3013 },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 },
@ -4266,6 +4306,13 @@ static struct hda_verb alc882_init_verbs[] = {
{ } { }
}; };
static struct hda_verb alc882_eapd_verbs[] = {
/* change to EAPD mode */
{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
{0x20, AC_VERB_SET_PROC_COEF, 0x3060},
{ }
};
/* /*
* generic initialization of ADC, input mixers and output mixers * generic initialization of ADC, input mixers and output mixers
*/ */
@ -4397,6 +4444,9 @@ static struct hda_board_config alc882_cfg_tbl[] = {
.config = ALC882_6ST_DIG }, /* Foxconn */ .config = ALC882_6ST_DIG }, /* Foxconn */
{ .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
.config = ALC882_6ST_DIG }, /* ECS to Intel*/ .config = ALC882_6ST_DIG }, /* ECS to Intel*/
{ .modelname = "arima", .config = ALC882_ARIMA },
{ .pci_subvendor = 0x161f, .pci_subdevice = 0x2054,
.config = ALC882_ARIMA }, /* Arima W820Di1 */
{ .modelname = "auto", .config = ALC882_AUTO }, { .modelname = "auto", .config = ALC882_AUTO },
{} {}
}; };
@ -4411,6 +4461,7 @@ static struct alc_config_preset alc882_presets[] = {
.dig_in_nid = ALC882_DIGIN_NID, .dig_in_nid = ALC882_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc882_ch_modes), .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
.channel_mode = alc882_ch_modes, .channel_mode = alc882_ch_modes,
.need_dac_fix = 1,
.input_mux = &alc882_capture_source, .input_mux = &alc882_capture_source,
}, },
[ALC882_6ST_DIG] = { [ALC882_6ST_DIG] = {
@ -4424,6 +4475,15 @@ static struct alc_config_preset alc882_presets[] = {
.channel_mode = alc882_sixstack_modes, .channel_mode = alc882_sixstack_modes,
.input_mux = &alc882_capture_source, .input_mux = &alc882_capture_source,
}, },
[ALC882_ARIMA] = {
.mixers = { alc882_base_mixer, alc882_chmode_mixer },
.init_verbs = { alc882_init_verbs, alc882_eapd_verbs },
.num_dacs = ARRAY_SIZE(alc882_dac_nids),
.dac_nids = alc882_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
.channel_mode = alc882_sixstack_modes,
.input_mux = &alc882_capture_source,
},
}; };
@ -4466,7 +4526,7 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
hda_nid_t pin; hda_nid_t pin;
pin = spec->autocfg.hp_pin; pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */ if (pin) /* connect to front */
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
} }
@ -4999,16 +5059,23 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
*/ */
static struct hda_board_config alc883_cfg_tbl[] = { static struct hda_board_config alc883_cfg_tbl[] = {
{ .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG }, { .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG },
{ .modelname = "3stack-6ch-dig", .config = ALC883_3ST_6ch_DIG },
{ .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
.config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/
{ .modelname = "3stack-6ch", .config = ALC883_3ST_6ch },
{ .pci_subvendor = 0x108e, .pci_subdevice = 0x534d,
.config = ALC883_3ST_6ch },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xd601,
.config = ALC883_3ST_6ch }, /* D102GGC */
{ .modelname = "6stack-dig", .config = ALC883_6ST_DIG }, { .modelname = "6stack-dig", .config = ALC883_6ST_DIG },
{ .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD },
{ .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668,
.config = ALC883_6ST_DIG }, /* MSI */ .config = ALC883_6ST_DIG }, /* MSI */
{ .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668,
.config = ALC883_6ST_DIG }, /* Foxconn */ .config = ALC883_6ST_DIG }, /* Foxconn */
{ .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD },
.config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/ { .modelname = "acer", .config = ALC883_ACER },
{ .pci_subvendor = 0x108e, .pci_subdevice = 0x534d, { .pci_subvendor = 0x1025, .pci_subdevice = 0/*0x0102*/,
.config = ALC883_3ST_6ch }, .config = ALC883_ACER },
{ .modelname = "auto", .config = ALC883_AUTO }, { .modelname = "auto", .config = ALC883_AUTO },
{} {}
}; };
@ -5038,6 +5105,7 @@ static struct alc_config_preset alc883_presets[] = {
.dig_in_nid = ALC883_DIGIN_NID, .dig_in_nid = ALC883_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
.channel_mode = alc883_3ST_6ch_modes, .channel_mode = alc883_3ST_6ch_modes,
.need_dac_fix = 1,
.input_mux = &alc883_capture_source, .input_mux = &alc883_capture_source,
}, },
[ALC883_3ST_6ch] = { [ALC883_3ST_6ch] = {
@ -5049,6 +5117,7 @@ static struct alc_config_preset alc883_presets[] = {
.adc_nids = alc883_adc_nids, .adc_nids = alc883_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
.channel_mode = alc883_3ST_6ch_modes, .channel_mode = alc883_3ST_6ch_modes,
.need_dac_fix = 1,
.input_mux = &alc883_capture_source, .input_mux = &alc883_capture_source,
}, },
[ALC883_6ST_DIG] = { [ALC883_6ST_DIG] = {
@ -5077,6 +5146,23 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_sixstack_modes, .channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source, .input_mux = &alc883_capture_source,
}, },
[ALC883_ACER] = {
.mixers = { alc883_base_mixer,
alc883_chmode_mixer },
/* On TravelMate laptops, GPIO 0 enables the internal speaker
* and the headphone jack. Turn this on and rely on the
* standard mute methods whenever the user wants to turn
* these outputs off.
*/
.init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs },
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
.adc_nids = alc883_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
},
}; };
@ -5121,7 +5207,7 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec)
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
hda_nid_t pin; hda_nid_t pin;
pin = spec->autocfg.hp_pin; pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */ if (pin) /* connect to front */
/* use dac 0 */ /* use dac 0 */
alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
@ -5217,8 +5303,10 @@ static int patch_alc883(struct hda_codec *codec)
spec->stream_digital_playback = &alc883_pcm_digital_playback; spec->stream_digital_playback = &alc883_pcm_digital_playback;
spec->stream_digital_capture = &alc883_pcm_digital_capture; spec->stream_digital_capture = &alc883_pcm_digital_capture;
if (! spec->adc_nids && spec->input_mux) {
spec->adc_nids = alc883_adc_nids; spec->adc_nids = alc883_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
}
codec->patch_ops = alc_patch_ops; codec->patch_ops = alc_patch_ops;
if (board_config == ALC883_AUTO) if (board_config == ALC883_AUTO)
@ -5481,6 +5569,7 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
.info = snd_hda_mixer_amp_volume_info, .info = snd_hda_mixer_amp_volume_info,
.get = snd_hda_mixer_amp_volume_get, .get = snd_hda_mixer_amp_volume_get,
.put = alc262_fujitsu_master_vol_put, .put = alc262_fujitsu_master_vol_put,
.tlv = { .c = snd_hda_mixer_amp_tlv },
.private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), .private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT),
}, },
{ {
@ -5499,6 +5588,13 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
{ } /* end */ { } /* end */
}; };
/* additional init verbs for Benq laptops */
static struct hda_verb alc262_EAPD_verbs[] = {
{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
{0x20, AC_VERB_SET_PROC_COEF, 0x3070},
{}
};
/* add playback controls from the parsed DAC table */ /* add playback controls from the parsed DAC table */
static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
{ {
@ -5534,7 +5630,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct
return err; return err;
} }
} }
nid = cfg->hp_pin; nid = cfg->hp_pins[0];
if (nid) { if (nid) {
/* spec->multiout.hp_nid = 2; */ /* spec->multiout.hp_nid = 2; */
if (nid == 0x16) { if (nid == 0x16) {
@ -5769,6 +5865,7 @@ static struct hda_board_config alc262_cfg_tbl[] = {
{ .modelname = "fujitsu", .config = ALC262_FUJITSU }, { .modelname = "fujitsu", .config = ALC262_FUJITSU },
{ .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397,
.config = ALC262_FUJITSU }, .config = ALC262_FUJITSU },
{ .modelname = "hp-bpc", .config = ALC262_HP_BPC },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x208c, { .pci_subvendor = 0x103c, .pci_subdevice = 0x208c,
.config = ALC262_HP_BPC }, /* xw4400 */ .config = ALC262_HP_BPC }, /* xw4400 */
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014,
@ -5777,6 +5874,9 @@ static struct hda_board_config alc262_cfg_tbl[] = {
.config = ALC262_HP_BPC }, /* xw8400 */ .config = ALC262_HP_BPC }, /* xw8400 */
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe, { .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe,
.config = ALC262_HP_BPC }, /* xw9400 */ .config = ALC262_HP_BPC }, /* xw9400 */
{ .modelname = "benq", .config = ALC262_BENQ_ED8 },
{ .pci_subvendor = 0x17ff, .pci_subdevice = 0x0560,
.config = ALC262_BENQ_ED8 },
{ .modelname = "auto", .config = ALC262_AUTO }, { .modelname = "auto", .config = ALC262_AUTO },
{} {}
}; };
@ -5814,6 +5914,16 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes, .channel_mode = alc262_modes,
.input_mux = &alc262_HP_capture_source, .input_mux = &alc262_HP_capture_source,
}, },
[ALC262_BENQ_ED8] = {
.mixers = { alc262_base_mixer },
.init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
.num_dacs = ARRAY_SIZE(alc262_dac_nids),
.dac_nids = alc262_dac_nids,
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
},
}; };
static int patch_alc262(struct hda_codec *codec) static int patch_alc262(struct hda_codec *codec)
@ -5942,6 +6052,23 @@ static struct hda_channel_mode alc861_threestack_modes[2] = {
{ 2, alc861_threestack_ch2_init }, { 2, alc861_threestack_ch2_init },
{ 6, alc861_threestack_ch6_init }, { 6, alc861_threestack_ch6_init },
}; };
/* Set mic1 as input and unmute the mixer */
static struct hda_verb alc861_uniwill_m31_ch2_init[] = {
{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
{ } /* end */
};
/* Set mic1 as output and mute mixer */
static struct hda_verb alc861_uniwill_m31_ch4_init[] = {
{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
{ } /* end */
};
static struct hda_channel_mode alc861_uniwill_m31_modes[2] = {
{ 2, alc861_uniwill_m31_ch2_init },
{ 4, alc861_uniwill_m31_ch4_init },
};
/* patch-ALC861 */ /* patch-ALC861 */
@ -6020,6 +6147,47 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = {
}, },
{ } /* end */ { } /* end */
}; };
static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
/* output mixer control */
HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
/*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
/* Input mixer control */
/* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
/* Capture mixer control */
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.count = 1,
.info = alc_mux_enum_info,
.get = alc_mux_enum_get,
.put = alc_mux_enum_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
.info = alc_ch_mode_info,
.get = alc_ch_mode_get,
.put = alc_ch_mode_put,
.private_value = ARRAY_SIZE(alc861_uniwill_m31_modes),
},
{ } /* end */
};
/* /*
* generic initialization of ADC, input mixers and output mixers * generic initialization of ADC, input mixers and output mixers
@ -6148,6 +6316,67 @@ static struct hda_verb alc861_threestack_init_verbs[] = {
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
{ } { }
}; };
static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
/*
* Unmute ADC0 and set the default input to mic-in
*/
/* port-A for surround (rear panel) */
{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
/* port-B for mic-in (rear panel) with vref */
{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
/* port-C for line-in (rear panel) */
{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
/* port-D for Front */
{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
/* port-E for HP out (front panel) */
{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, // this has to be set to VREF80
/* route front PCM to HP */
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
/* port-F for mic-in (front panel) with vref */
{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
/* port-G for CLFE (rear panel) */
{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
/* port-H for side (rear panel) */
{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
/* CD-in */
{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
/* route front mic to ADC1*/
{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Unmute DAC0~3 & spdif out*/
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* Unmute Mixer 14 (mic) 1c (Line in)*/
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/* Unmute Stereo Mixer 15 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
{ }
};
/* /*
* generic initialization of ADC, input mixers and output mixers * generic initialization of ADC, input mixers and output mixers
*/ */
@ -6401,7 +6630,7 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
hda_nid_t pin; hda_nid_t pin;
pin = spec->autocfg.hp_pin; pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */ if (pin) /* connect to front */
alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]); alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
} }
@ -6436,7 +6665,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 || if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
(err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || (err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
(err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 || (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0])) < 0 ||
(err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) (err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
return err; return err;
@ -6477,10 +6706,14 @@ static struct hda_board_config alc861_cfg_tbl[] = {
{ .modelname = "3stack", .config = ALC861_3ST }, { .modelname = "3stack", .config = ALC861_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600,
.config = ALC861_3ST }, .config = ALC861_3ST },
{ .modelname = "3stack-660", .config = ALC660_3ST },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7, { .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7,
.config = ALC660_3ST }, .config = ALC660_3ST },
{ .modelname = "3stack-dig", .config = ALC861_3ST_DIG }, { .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
{ .modelname = "6stack-dig", .config = ALC861_6ST_DIG }, { .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
{ .modelname = "uniwill-m31", .config = ALC861_UNIWILL_M31},
{ .pci_subvendor = 0x1584, .pci_subdevice = 0x9072,
.config = ALC861_UNIWILL_M31 },
{ .modelname = "auto", .config = ALC861_AUTO }, { .modelname = "auto", .config = ALC861_AUTO },
{} {}
}; };
@ -6493,6 +6726,7 @@ static struct alc_config_preset alc861_presets[] = {
.dac_nids = alc861_dac_nids, .dac_nids = alc861_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
.channel_mode = alc861_threestack_modes, .channel_mode = alc861_threestack_modes,
.need_dac_fix = 1,
.num_adc_nids = ARRAY_SIZE(alc861_adc_nids), .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
.adc_nids = alc861_adc_nids, .adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source, .input_mux = &alc861_capture_source,
@ -6505,6 +6739,7 @@ static struct alc_config_preset alc861_presets[] = {
.dig_out_nid = ALC861_DIGOUT_NID, .dig_out_nid = ALC861_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
.channel_mode = alc861_threestack_modes, .channel_mode = alc861_threestack_modes,
.need_dac_fix = 1,
.num_adc_nids = ARRAY_SIZE(alc861_adc_nids), .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
.adc_nids = alc861_adc_nids, .adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source, .input_mux = &alc861_capture_source,
@ -6528,10 +6763,25 @@ static struct alc_config_preset alc861_presets[] = {
.dac_nids = alc660_dac_nids, .dac_nids = alc660_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
.channel_mode = alc861_threestack_modes, .channel_mode = alc861_threestack_modes,
.need_dac_fix = 1,
.num_adc_nids = ARRAY_SIZE(alc861_adc_nids), .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
.adc_nids = alc861_adc_nids, .adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source, .input_mux = &alc861_capture_source,
}, },
[ALC861_UNIWILL_M31] = {
.mixers = { alc861_uniwill_m31_mixer },
.init_verbs = { alc861_uniwill_m31_init_verbs },
.num_dacs = ARRAY_SIZE(alc861_dac_nids),
.dac_nids = alc861_dac_nids,
.dig_out_nid = ALC861_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes),
.channel_mode = alc861_uniwill_m31_modes,
.need_dac_fix = 1,
.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
.adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source,
},
}; };

View File

@ -298,6 +298,7 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
{} {}
}; };

File diff suppressed because it is too large Load Diff

View File

@ -60,6 +60,7 @@
#include "ice1712.h" #include "ice1712.h"
#include "envy24ht.h" #include "envy24ht.h"
#include "aureon.h" #include "aureon.h"
#include <sound/tlv.h>
/* WM8770 registers */ /* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
@ -660,6 +661,12 @@ static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
static DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0);
static DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0);
static DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0);
/* /*
* Logarithmic volume values for WM8770 * Logarithmic volume values for WM8770
* Computed as 20 * Log10(255 / x) * Computed as 20 * Log10(255 / x)
@ -1409,10 +1416,13 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume", .name = "Master Playback Volume",
.info = wm_master_vol_info, .info = wm_master_vol_info,
.get = wm_master_vol_get, .get = wm_master_vol_get,
.put = wm_master_vol_put .put = wm_master_vol_put,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1424,11 +1434,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Front Playback Volume", .name = "Front Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (2 << 8) | 0 .private_value = (2 << 8) | 0,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1440,11 +1453,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Rear Playback Volume", .name = "Rear Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (2 << 8) | 2 .private_value = (2 << 8) | 2,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1456,11 +1472,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Center Playback Volume", .name = "Center Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (1 << 8) | 4 .private_value = (1 << 8) | 4,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1472,11 +1491,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "LFE Playback Volume", .name = "LFE Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (1 << 8) | 5 .private_value = (1 << 8) | 5,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1488,11 +1510,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Side Playback Volume", .name = "Side Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (2 << 8) | 6 .private_value = (2 << 8) | 6,
.tlv = { .p = db_scale_wm_dac }
} }
}; };
@ -1506,10 +1531,13 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume", .name = "PCM Playback Volume",
.info = wm_pcm_vol_info, .info = wm_pcm_vol_info,
.get = wm_pcm_vol_get, .get = wm_pcm_vol_get,
.put = wm_pcm_vol_put .put = wm_pcm_vol_put,
.tlv = { .p = db_scale_wm_pcm }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1520,10 +1548,13 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume", .name = "Capture Volume",
.info = wm_adc_vol_info, .info = wm_adc_vol_info,
.get = wm_adc_vol_get, .get = wm_adc_vol_get,
.put = wm_adc_vol_put .put = wm_adc_vol_put,
.tlv = { .p = db_scale_wm_adc }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1567,11 +1598,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "AC97 Playback Volume", .name = "AC97 Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_MASTER|AUREON_AC97_STEREO .private_value = AC97_MASTER|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_master }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1583,11 +1617,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "CD Playback Volume", .name = "CD Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_CD|AUREON_AC97_STEREO .private_value = AC97_CD|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1599,11 +1636,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Aux Playback Volume", .name = "Aux Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_AUX|AUREON_AC97_STEREO .private_value = AC97_AUX|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1615,11 +1655,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Line Playback Volume", .name = "Line Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_LINE|AUREON_AC97_STEREO .private_value = AC97_LINE|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1631,11 +1674,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Playback Volume", .name = "Mic Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_MIC .private_value = AC97_MIC,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1657,11 +1703,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "AC97 Playback Volume", .name = "AC97 Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_MASTER|AUREON_AC97_STEREO .private_value = AC97_MASTER|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_master }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1673,11 +1722,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "CD Playback Volume", .name = "CD Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_AUX|AUREON_AC97_STEREO .private_value = AC97_AUX|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1685,15 +1737,18 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
.info = aureon_ac97_mute_info, .info = aureon_ac97_mute_info,
.get = aureon_ac97_mute_get, .get = aureon_ac97_mute_get,
.put = aureon_ac97_mute_put, .put = aureon_ac97_mute_put,
.private_value = AC97_CD, .private_value = AC97_CD
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Phono Playback Volume", .name = "Phono Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_CD|AUREON_AC97_STEREO .private_value = AC97_CD|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1705,11 +1760,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Line Playback Volume", .name = "Line Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_LINE|AUREON_AC97_STEREO .private_value = AC97_LINE|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1721,11 +1779,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Playback Volume", .name = "Mic Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_MIC .private_value = AC97_MIC,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -1744,11 +1805,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Aux Playback Volume", .name = "Aux Playback Volume",
.info = aureon_ac97_vol_info, .info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get, .get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put, .put = aureon_ac97_vol_put,
.private_value = AC97_VIDEO|AUREON_AC97_STEREO .private_value = AC97_VIDEO|AUREON_AC97_STEREO,
.tlv = { .p = db_scale_ac97_gain }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,

View File

@ -62,6 +62,7 @@
#include <sound/cs8427.h> #include <sound/cs8427.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
@ -1377,6 +1378,7 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = { static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = {
{ {
@ -1390,12 +1392,15 @@ static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Multi Playback Volume", .name = "Multi Playback Volume",
.info = snd_ice1712_pro_mixer_volume_info, .info = snd_ice1712_pro_mixer_volume_info,
.get = snd_ice1712_pro_mixer_volume_get, .get = snd_ice1712_pro_mixer_volume_get,
.put = snd_ice1712_pro_mixer_volume_put, .put = snd_ice1712_pro_mixer_volume_put,
.private_value = 0, .private_value = 0,
.count = 10, .count = 10,
.tlv = { .p = db_scale_playback }
}, },
}; };
@ -1420,11 +1425,14 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitd
static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = { static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "H/W Multi Capture Volume", .name = "H/W Multi Capture Volume",
.info = snd_ice1712_pro_mixer_volume_info, .info = snd_ice1712_pro_mixer_volume_info,
.get = snd_ice1712_pro_mixer_volume_get, .get = snd_ice1712_pro_mixer_volume_get,
.put = snd_ice1712_pro_mixer_volume_put, .put = snd_ice1712_pro_mixer_volume_put,
.private_value = 10, .private_value = 10,
.tlv = { .p = db_scale_playback }
}; };
static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = { static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
@ -1857,7 +1865,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
{ {
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
static unsigned int xrate[13] = { static unsigned int xrate[13] = {
8000, 9600, 11025, 12000, 1600, 22050, 24000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000 32000, 44100, 48000, 64000, 88200, 96000
}; };
unsigned char oval; unsigned char oval;
@ -1924,7 +1932,7 @@ static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcont
{ {
int val; int val;
static unsigned int xrate[13] = { static unsigned int xrate[13] = {
8000, 9600, 11025, 12000, 1600, 22050, 24000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000 32000, 44100, 48000, 64000, 88200, 96000
}; };
@ -1941,7 +1949,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
static unsigned int xrate[13] = { static unsigned int xrate[13] = {
8000, 9600, 11025, 12000, 1600, 22050, 24000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000 32000, 44100, 48000, 64000, 88200, 96000
}; };
unsigned char oval; unsigned char oval;

View File

@ -46,6 +46,7 @@
#include "ice1712.h" #include "ice1712.h"
#include "envy24ht.h" #include "envy24ht.h"
#include "phase.h" #include "phase.h"
#include <sound/tlv.h>
/* WM8770 registers */ /* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
@ -696,6 +697,9 @@ static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ct
return 0; return 0;
} }
static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = { static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -706,10 +710,13 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume", .name = "Master Playback Volume",
.info = wm_master_vol_info, .info = wm_master_vol_info,
.get = wm_master_vol_get, .get = wm_master_vol_get,
.put = wm_master_vol_put .put = wm_master_vol_put,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -721,11 +728,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Front Playback Volume", .name = "Front Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (2 << 8) | 0 .private_value = (2 << 8) | 0,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -737,11 +747,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Rear Playback Volume", .name = "Rear Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (2 << 8) | 2 .private_value = (2 << 8) | 2,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -753,11 +766,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Center Playback Volume", .name = "Center Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (1 << 8) | 4 .private_value = (1 << 8) | 4,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -769,11 +785,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "LFE Playback Volume", .name = "LFE Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (1 << 8) | 5 .private_value = (1 << 8) | 5,
.tlv = { .p = db_scale_wm_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -785,11 +804,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Side Playback Volume", .name = "Side Playback Volume",
.info = wm_vol_info, .info = wm_vol_info,
.get = wm_vol_get, .get = wm_vol_get,
.put = wm_vol_put, .put = wm_vol_put,
.private_value = (2 << 8) | 6 .private_value = (2 << 8) | 6,
.tlv = { .p = db_scale_wm_dac }
} }
}; };
@ -803,10 +825,13 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume", .name = "PCM Playback Volume",
.info = wm_pcm_vol_info, .info = wm_pcm_vol_info,
.get = wm_pcm_vol_get, .get = wm_pcm_vol_get,
.put = wm_pcm_vol_put .put = wm_pcm_vol_put,
.tlv = { .p = db_scale_wm_pcm }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,

View File

@ -31,6 +31,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/tlv.h>
#include "ice1712.h" #include "ice1712.h"
#include "envy24ht.h" #include "envy24ht.h"
@ -564,6 +565,8 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return changed; return changed;
} }
static DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
/* /*
* mixers * mixers
*/ */
@ -571,17 +574,23 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static struct snd_kcontrol_new pontis_controls[] __devinitdata = { static struct snd_kcontrol_new pontis_controls[] __devinitdata = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume", .name = "PCM Playback Volume",
.info = wm_dac_vol_info, .info = wm_dac_vol_info,
.get = wm_dac_vol_get, .get = wm_dac_vol_get,
.put = wm_dac_vol_put, .put = wm_dac_vol_put,
.tlv = { .p = db_scale_volume },
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume", .name = "Capture Volume",
.info = wm_adc_vol_info, .info = wm_adc_vol_info,
.get = wm_adc_vol_get, .get = wm_adc_vol_get,
.put = wm_adc_vol_put, .put = wm_adc_vol_put,
.tlv = { .p = db_scale_volume },
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,

View File

@ -35,6 +35,7 @@
#include "envy24ht.h" #include "envy24ht.h"
#include "prodigy192.h" #include "prodigy192.h"
#include "stac946x.h" #include "stac946x.h"
#include <sound/tlv.h>
static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val) static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val)
{ {
@ -356,6 +357,9 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl
} }
#endif #endif
static DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
static DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
/* /*
* mixers * mixers
*/ */
@ -368,14 +372,18 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
.get = stac9460_dac_mute_get, .get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put, .put = stac9460_dac_mute_put,
.private_value = 1, .private_value = 1,
.tlv = { .p = db_scale_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume", .name = "Master Playback Volume",
.info = stac9460_dac_vol_info, .info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get, .get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put, .put = stac9460_dac_vol_put,
.private_value = 1, .private_value = 1,
.tlv = { .p = db_scale_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -387,11 +395,14 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "DAC Volume", .name = "DAC Volume",
.count = 6, .count = 6,
.info = stac9460_dac_vol_info, .info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get, .get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put, .put = stac9460_dac_vol_put,
.tlv = { .p = db_scale_dac }
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -404,11 +415,14 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "ADC Volume", .name = "ADC Volume",
.count = 1, .count = 1,
.info = stac9460_adc_vol_info, .info = stac9460_adc_vol_info,
.get = stac9460_adc_vol_get, .get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put, .put = stac9460_adc_vol_put,
.tlv = { .p = db_scale_adc }
}, },
#if 0 #if 0
{ {

View File

@ -87,16 +87,33 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
* initialize the chips on M-Audio Revolution cards * initialize the chips on M-Audio Revolution cards
*/ */
static unsigned int revo71_num_stereo_front[] = {2}; #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
static char *revo71_channel_names_front[] = {"PCM Playback Volume"};
static unsigned int revo71_num_stereo_surround[] = {1, 1, 2, 2}; static struct snd_akm4xxx_dac_channel revo71_front[] = {
static char *revo71_channel_names_surround[] = {"PCM Center Playback Volume", "PCM LFE Playback Volume", AK_DAC("PCM Playback Volume", 2)
"PCM Side Playback Volume", "PCM Rear Playback Volume"}; };
static unsigned int revo51_num_stereo[] = {2, 1, 1, 2}; static struct snd_akm4xxx_dac_channel revo71_surround[] = {
static char *revo51_channel_names[] = {"PCM Playback Volume", "PCM Center Playback Volume", AK_DAC("PCM Center Playback Volume", 1),
"PCM LFE Playback Volume", "PCM Rear Playback Volume"}; AK_DAC("PCM LFE Playback Volume", 1),
AK_DAC("PCM Side Playback Volume", 2),
AK_DAC("PCM Rear Playback Volume", 2),
};
static struct snd_akm4xxx_dac_channel revo51_dac[] = {
AK_DAC("PCM Playback Volume", 2),
AK_DAC("PCM Center Playback Volume", 1),
AK_DAC("PCM LFE Playback Volume", 1),
AK_DAC("PCM Rear Playback Volume", 2),
};
static struct snd_akm4xxx_adc_channel revo51_adc[] = {
{
.name = "PCM Capture Volume",
.switch_name = "PCM Capture Switch",
.num_channels = 2
},
};
static struct snd_akm4xxx akm_revo_front __devinitdata = { static struct snd_akm4xxx akm_revo_front __devinitdata = {
.type = SND_AK4381, .type = SND_AK4381,
@ -104,8 +121,7 @@ static struct snd_akm4xxx akm_revo_front __devinitdata = {
.ops = { .ops = {
.set_rate_val = revo_set_rate_val .set_rate_val = revo_set_rate_val
}, },
.num_stereo = revo71_num_stereo_front, .dac_info = revo71_front,
.channel_names = revo71_channel_names_front
}; };
static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = { static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
@ -127,8 +143,7 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = {
.ops = { .ops = {
.set_rate_val = revo_set_rate_val .set_rate_val = revo_set_rate_val
}, },
.num_stereo = revo71_num_stereo_surround, .dac_info = revo71_surround,
.channel_names = revo71_channel_names_surround
}; };
static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = { static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
@ -149,8 +164,7 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = {
.ops = { .ops = {
.set_rate_val = revo_set_rate_val .set_rate_val = revo_set_rate_val
}, },
.num_stereo = revo51_num_stereo, .dac_info = revo51_dac,
.channel_names = revo51_channel_names
}; };
static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
@ -159,7 +173,25 @@ static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
.data_mask = VT1724_REVO_CDOUT, .data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK, .clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.cs_addr = 0, .cs_addr = VT1724_REVO_CS1 | VT1724_REVO_CS2,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
static struct snd_akm4xxx akm_revo51_adc __devinitdata = {
.type = SND_AK5365,
.num_adcs = 2,
.adc_info = revo51_adc,
};
static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.add_flags = VT1724_REVO_CCLK, /* high at init */ .add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0, .mask_flags = 0,
@ -202,9 +234,13 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
break; break;
case VT1724_SUBDEVICE_REVOLUTION51: case VT1724_SUBDEVICE_REVOLUTION51:
ice->akm_codecs = 1; ice->akm_codecs = 2;
if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, &akm_revo51_priv, ice)) < 0) if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, &akm_revo51_priv, ice)) < 0)
return err; return err;
err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo51_adc,
&akm_revo51_adc_priv, ice);
if (err < 0)
return err;
/* unmute all codecs - needed! */ /* unmute all codecs - needed! */
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
break; break;

View File

@ -42,7 +42,7 @@ extern struct snd_ice1712_card_info snd_vt1724_revo_cards[];
#define VT1724_REVO_CCLK 0x02 #define VT1724_REVO_CCLK 0x02
#define VT1724_REVO_CDIN 0x04 /* not used */ #define VT1724_REVO_CDIN 0x04 /* not used */
#define VT1724_REVO_CDOUT 0x08 #define VT1724_REVO_CDOUT 0x08
#define VT1724_REVO_CS0 0x10 /* not used */ #define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for Rev. 5.1 */
#define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */ #define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */
#define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */ #define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */
#define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */ #define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */

View File

@ -2251,6 +2251,16 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
/* ACLink on, 2 channels */ /* ACLink on, 2 channels */
cnt = igetdword(chip, ICHREG(GLOB_CNT)); cnt = igetdword(chip, ICHREG(GLOB_CNT));
cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
#ifdef CONFIG_SND_AC97_POWER_SAVE
/* do cold reset - the full ac97 powerdown may leave the controller
* in a warm state but actually it cannot communicate with the codec.
*/
iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_AC97COLD);
cnt = igetdword(chip, ICHREG(GLOB_CNT));
udelay(10);
iputdword(chip, ICHREG(GLOB_CNT), cnt | ICH_AC97COLD);
msleep(1);
#else
/* finish cold or do warm reset */ /* finish cold or do warm reset */
cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
iputdword(chip, ICHREG(GLOB_CNT), cnt); iputdword(chip, ICHREG(GLOB_CNT), cnt);
@ -2265,6 +2275,7 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
return -EIO; return -EIO;
__ok: __ok:
#endif
if (probing) { if (probing) {
/* wait for any codec ready status. /* wait for any codec ready status.
* Once it becomes ready it should remain ready * Once it becomes ready it should remain ready
@ -2485,7 +2496,7 @@ static int intel8x0_resume(struct pci_dev *pci)
card->shortname, chip); card->shortname, chip);
chip->irq = pci->irq; chip->irq = pci->irq;
synchronize_irq(chip->irq); synchronize_irq(chip->irq);
snd_intel8x0_chip_init(chip, 1); snd_intel8x0_chip_init(chip, 0);
/* re-initialize mixer stuff */ /* re-initialize mixer stuff */
if (chip->device_type == DEVICE_INTEL_ICH4) { if (chip->device_type == DEVICE_INTEL_ICH4) {
@ -2615,6 +2626,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
/* not 48000Hz, tuning the clock.. */ /* not 48000Hz, tuning the clock.. */
chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock); printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock);
snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS

View File

@ -1045,6 +1045,8 @@ static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state)
for (i = 0; i < chip->pcm_devs; i++) for (i = 0; i < chip->pcm_devs; i++)
snd_pcm_suspend_all(chip->pcm[i]); snd_pcm_suspend_all(chip->pcm[i]);
snd_ac97_suspend(chip->ac97); snd_ac97_suspend(chip->ac97);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
pci_disable_device(pci); pci_disable_device(pci);
pci_save_state(pci); pci_save_state(pci);
return 0; return 0;
@ -1058,6 +1060,9 @@ static int intel8x0m_resume(struct pci_dev *pci)
pci_restore_state(pci); pci_restore_state(pci);
pci_enable_device(pci); pci_enable_device(pci);
pci_set_master(pci); pci_set_master(pci);
request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_DISABLED|IRQF_SHARED,
card->shortname, chip);
chip->irq = pci->irq;
snd_intel8x0_chip_init(chip, 0); snd_intel8x0_chip_init(chip, 0);
snd_ac97_resume(chip->ac97); snd_ac97_resume(chip->ac97);

View File

@ -1109,13 +1109,13 @@ static long long snd_mixart_BA0_llseek(struct snd_info_entry *entry,
offset = offset & ~3; /* 4 bytes aligned */ offset = offset & ~3; /* 4 bytes aligned */
switch(orig) { switch(orig) {
case 0: /* SEEK_SET */ case SEEK_SET:
file->f_pos = offset; file->f_pos = offset;
break; break;
case 1: /* SEEK_CUR */ case SEEK_CUR:
file->f_pos += offset; file->f_pos += offset;
break; break;
case 2: /* SEEK_END, offset is negative */ case SEEK_END: /* offset is negative */
file->f_pos = MIXART_BA0_SIZE + offset; file->f_pos = MIXART_BA0_SIZE + offset;
break; break;
default: default:
@ -1135,13 +1135,13 @@ static long long snd_mixart_BA1_llseek(struct snd_info_entry *entry,
offset = offset & ~3; /* 4 bytes aligned */ offset = offset & ~3; /* 4 bytes aligned */
switch(orig) { switch(orig) {
case 0: /* SEEK_SET */ case SEEK_SET:
file->f_pos = offset; file->f_pos = offset;
break; break;
case 1: /* SEEK_CUR */ case SEEK_CUR:
file->f_pos += offset; file->f_pos += offset;
break; break;
case 2: /* SEEK_END, offset is negative */ case SEEK_END: /* offset is negative */
file->f_pos = MIXART_BA1_SIZE + offset; file->f_pos = MIXART_BA1_SIZE + offset;
break; break;
default: default:

View File

@ -31,6 +31,7 @@
#include "mixart_core.h" #include "mixart_core.h"
#include "mixart_hwdep.h" #include "mixart_hwdep.h"
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include "mixart_mixer.h" #include "mixart_mixer.h"
static u32 mixart_analog_level[256] = { static u32 mixart_analog_level[256] = {
@ -388,12 +389,17 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return changed; return changed;
} }
static DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0);
static struct snd_kcontrol_new mixart_control_analog_level = { static struct snd_kcontrol_new mixart_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */ /* name will be filled later */
.info = mixart_analog_vol_info, .info = mixart_analog_vol_info,
.get = mixart_analog_vol_get, .get = mixart_analog_vol_get,
.put = mixart_analog_vol_put, .put = mixart_analog_vol_put,
.tlv = { .p = db_scale_analog },
}; };
/* shared */ /* shared */
@ -866,14 +872,19 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
return changed; return changed;
} }
static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
static struct snd_kcontrol_new snd_mixart_pcm_vol = static struct snd_kcontrol_new snd_mixart_pcm_vol =
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */ /* name will be filled later */
/* count will be filled later */ /* count will be filled later */
.info = mixart_digital_vol_info, /* shared */ .info = mixart_digital_vol_info, /* shared */
.get = mixart_pcm_vol_get, .get = mixart_pcm_vol_get,
.put = mixart_pcm_vol_put, .put = mixart_pcm_vol_put,
.tlv = { .p = db_scale_digital },
}; };
@ -984,10 +995,13 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
static struct snd_kcontrol_new mixart_control_monitor_vol = { static struct snd_kcontrol_new mixart_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Monitoring Volume", .name = "Monitoring Volume",
.info = mixart_digital_vol_info, /* shared */ .info = mixart_digital_vol_info, /* shared */
.get = mixart_monitor_vol_get, .get = mixart_monitor_vol_get,
.put = mixart_monitor_vol_put, .put = mixart_monitor_vol_put,
.tlv = { .p = db_scale_digital },
}; };
/* /*

View File

@ -31,6 +31,7 @@
#include "pcxhr_hwdep.h" #include "pcxhr_hwdep.h"
#include "pcxhr_core.h" #include "pcxhr_core.h"
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include "pcxhr_mixer.h" #include "pcxhr_mixer.h"
@ -43,6 +44,9 @@
#define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128 /* 0.0 dB */ #define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128 /* 0.0 dB */
#define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104 /* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */ #define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104 /* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */
static DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 0);
static DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -12800, 100, 0);
static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel) static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel)
{ {
int err, vol; int err, vol;
@ -130,10 +134,13 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new pcxhr_control_analog_level = { static struct snd_kcontrol_new pcxhr_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */ /* name will be filled later */
.info = pcxhr_analog_vol_info, .info = pcxhr_analog_vol_info,
.get = pcxhr_analog_vol_get, .get = pcxhr_analog_vol_get,
.put = pcxhr_analog_vol_put, .put = pcxhr_analog_vol_put,
/* tlv will be filled later */
}; };
/* shared */ /* shared */
@ -188,6 +195,7 @@ static struct snd_kcontrol_new pcxhr_control_output_switch = {
#define PCXHR_DIGITAL_LEVEL_MAX 0x1ff /* +18 dB */ #define PCXHR_DIGITAL_LEVEL_MAX 0x1ff /* +18 dB */
#define PCXHR_DIGITAL_ZERO_LEVEL 0x1b7 /* 0 dB */ #define PCXHR_DIGITAL_ZERO_LEVEL 0x1b7 /* 0 dB */
static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
#define MORE_THAN_ONE_STREAM_LEVEL 0x000001 #define MORE_THAN_ONE_STREAM_LEVEL 0x000001
#define VALID_STREAM_PAN_LEVEL_MASK 0x800000 #define VALID_STREAM_PAN_LEVEL_MASK 0x800000
@ -343,11 +351,14 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_pcxhr_pcm_vol = static struct snd_kcontrol_new snd_pcxhr_pcm_vol =
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */ /* name will be filled later */
/* count will be filled later */ /* count will be filled later */
.info = pcxhr_digital_vol_info, /* shared */ .info = pcxhr_digital_vol_info, /* shared */
.get = pcxhr_pcm_vol_get, .get = pcxhr_pcm_vol_get,
.put = pcxhr_pcm_vol_put, .put = pcxhr_pcm_vol_put,
.tlv = { .p = db_scale_digital },
}; };
@ -433,10 +444,13 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new pcxhr_control_monitor_vol = { static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Monitoring Volume", .name = "Monitoring Volume",
.info = pcxhr_digital_vol_info, /* shared */ .info = pcxhr_digital_vol_info, /* shared */
.get = pcxhr_monitor_vol_get, .get = pcxhr_monitor_vol_get,
.put = pcxhr_monitor_vol_put, .put = pcxhr_monitor_vol_put,
.tlv = { .p = db_scale_digital },
}; };
/* /*
@ -928,6 +942,7 @@ int pcxhr_create_mixer(struct pcxhr_mgr *mgr)
temp = pcxhr_control_analog_level; temp = pcxhr_control_analog_level;
temp.name = "Master Playback Volume"; temp.name = "Master Playback Volume";
temp.private_value = 0; /* playback */ temp.private_value = 0; /* playback */
temp.tlv.p = db_scale_analog_playback;
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err; return err;
/* output mute controls */ /* output mute controls */
@ -963,6 +978,7 @@ int pcxhr_create_mixer(struct pcxhr_mgr *mgr)
temp = pcxhr_control_analog_level; temp = pcxhr_control_analog_level;
temp.name = "Master Capture Volume"; temp.name = "Master Capture Volume";
temp.private_value = 1; /* capture */ temp.private_value = 1; /* capture */
temp.tlv.p = db_scale_analog_capture;
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err; return err;

View File

@ -673,8 +673,12 @@ static struct lbuspath lbus_rec_path = {
#define FIRMWARE_VERSIONS 1 #define FIRMWARE_VERSIONS 1
static union firmware_version firmware_versions[] = { static union firmware_version firmware_versions[] = {
{ {
.firmware.ASIC = 3,.firmware.CODEC = 2, .firmware = {
.firmware.AUXDSP = 3,.firmware.PROG = 773, .ASIC = 3,
.CODEC = 2,
.AUXDSP = 3,
.PROG = 773,
},
}, },
}; };

View File

@ -726,23 +726,37 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp)
} }
static int hdsp_check_for_firmware (struct hdsp *hdsp, int show_err) #ifdef HDSP_FW_LOADER
static int __devinit hdsp_request_fw_loader(struct hdsp *hdsp);
#endif
static int hdsp_check_for_firmware (struct hdsp *hdsp, int load_on_demand)
{ {
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0; if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
return 0;
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) { if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
snd_printk(KERN_ERR "Hammerfall-DSP: firmware not present.\n");
hdsp->state &= ~HDSP_FirmwareLoaded; hdsp->state &= ~HDSP_FirmwareLoaded;
if (! show_err) if (! load_on_demand)
return -EIO; return -EIO;
snd_printk(KERN_ERR "Hammerfall-DSP: firmware not present.\n");
/* try to load firmware */ /* try to load firmware */
if (hdsp->state & HDSP_FirmwareCached) { if (! (hdsp->state & HDSP_FirmwareCached)) {
if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) #ifdef HDSP_FW_LOADER
snd_printk(KERN_ERR "Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n"); if (! hdsp_request_fw_loader(hdsp))
} else { return 0;
snd_printk(KERN_ERR "Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n"); #endif
} snd_printk(KERN_ERR
"Hammerfall-DSP: No firmware loaded nor "
"cached, please upload firmware.\n");
return -EIO; return -EIO;
} }
if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
snd_printk(KERN_ERR
"Hammerfall-DSP: Firmware loading from "
"cache failed, please upload manually.\n");
return -EIO;
}
}
return 0; return 0;
} }
@ -3181,10 +3195,18 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
return; return;
} }
} else { } else {
snd_iprintf(buffer, "No firmware loaded nor cached, please upload firmware.\n"); int err = -EINVAL;
#ifdef HDSP_FW_LOADER
err = hdsp_request_fw_loader(hdsp);
#endif
if (err < 0) {
snd_iprintf(buffer,
"No firmware loaded nor cached, "
"please upload firmware.\n");
return; return;
} }
} }
}
status = hdsp_read(hdsp, HDSP_statusRegister); status = hdsp_read(hdsp, HDSP_statusRegister);
status2 = hdsp_read(hdsp, HDSP_status2Register); status2 = hdsp_read(hdsp, HDSP_status2Register);
@ -3851,7 +3873,7 @@ static int snd_hdsp_trigger(struct snd_pcm_substream *substream, int cmd)
if (hdsp_check_for_iobox (hdsp)) if (hdsp_check_for_iobox (hdsp))
return -EIO; return -EIO;
if (hdsp_check_for_firmware(hdsp, 1)) if (hdsp_check_for_firmware(hdsp, 0)) /* no auto-loading in trigger */
return -EIO; return -EIO;
spin_lock(&hdsp->lock); spin_lock(&hdsp->lock);

View File

@ -40,6 +40,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <sound/trident.h> #include <sound/trident.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
@ -2627,6 +2628,8 @@ static int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0);
static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol, static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
@ -2653,6 +2656,7 @@ static struct snd_kcontrol_new snd_trident_vol_music_control __devinitdata =
.get = snd_trident_vol_control_get, .get = snd_trident_vol_control_get,
.put = snd_trident_vol_control_put, .put = snd_trident_vol_control_put,
.private_value = 16, .private_value = 16,
.tlv = { .p = db_scale_gvol },
}; };
static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata = static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata =
@ -2663,6 +2667,7 @@ static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata =
.get = snd_trident_vol_control_get, .get = snd_trident_vol_control_get,
.put = snd_trident_vol_control_put, .put = snd_trident_vol_control_put,
.private_value = 0, .private_value = 0,
.tlv = { .p = db_scale_gvol },
}; };
/*--------------------------------------------------------------------------- /*---------------------------------------------------------------------------
@ -2730,6 +2735,7 @@ static struct snd_kcontrol_new snd_trident_pcm_vol_control __devinitdata =
.info = snd_trident_pcm_vol_control_info, .info = snd_trident_pcm_vol_control_info,
.get = snd_trident_pcm_vol_control_get, .get = snd_trident_pcm_vol_control_get,
.put = snd_trident_pcm_vol_control_put, .put = snd_trident_pcm_vol_control_put,
/* FIXME: no tlv yet */
}; };
/*--------------------------------------------------------------------------- /*---------------------------------------------------------------------------
@ -2839,6 +2845,8 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata = static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata =
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -2848,6 +2856,7 @@ static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata =
.info = snd_trident_pcm_rvol_control_info, .info = snd_trident_pcm_rvol_control_info,
.get = snd_trident_pcm_rvol_control_get, .get = snd_trident_pcm_rvol_control_get,
.put = snd_trident_pcm_rvol_control_put, .put = snd_trident_pcm_rvol_control_put,
.tlv = { .p = db_scale_crvol },
}; };
/*--------------------------------------------------------------------------- /*---------------------------------------------------------------------------
@ -2903,6 +2912,7 @@ static struct snd_kcontrol_new snd_trident_pcm_cvol_control __devinitdata =
.info = snd_trident_pcm_cvol_control_info, .info = snd_trident_pcm_cvol_control_info,
.get = snd_trident_pcm_cvol_control_get, .get = snd_trident_pcm_cvol_control_get,
.put = snd_trident_pcm_cvol_control_put, .put = snd_trident_pcm_cvol_control_put,
.tlv = { .p = db_scale_crvol },
}; };
static void snd_trident_notify_pcm_change1(struct snd_card *card, static void snd_trident_notify_pcm_change1(struct snd_card *card,

View File

@ -59,6 +59,7 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/tlv.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/mpu401.h> #include <sound/mpu401.h>
#include <sound/initval.h> #include <sound/initval.h>
@ -1277,7 +1278,18 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream)
if (! ratep->used) if (! ratep->used)
ratep->rate = 0; ratep->rate = 0;
spin_unlock_irq(&ratep->lock); spin_unlock_irq(&ratep->lock);
if (! ratep->rate) {
if (! viadev->direction) {
snd_ac97_update_power(chip->ac97,
AC97_PCM_FRONT_DAC_RATE, 0);
snd_ac97_update_power(chip->ac97,
AC97_PCM_SURR_DAC_RATE, 0);
snd_ac97_update_power(chip->ac97,
AC97_PCM_LFE_DAC_RATE, 0);
} else
snd_ac97_update_power(chip->ac97,
AC97_PCM_LR_ADC_RATE, 0);
}
viadev->substream = NULL; viadev->substream = NULL;
return 0; return 0;
} }
@ -1687,21 +1699,29 @@ static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1);
static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = { static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = {
.name = "PCM Playback Volume", .name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = snd_via8233_dxs_volume_info, .info = snd_via8233_dxs_volume_info,
.get = snd_via8233_pcmdxs_volume_get, .get = snd_via8233_pcmdxs_volume_get,
.put = snd_via8233_pcmdxs_volume_put, .put = snd_via8233_pcmdxs_volume_put,
.tlv = { .p = db_scale_dxs }
}; };
static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = { static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = {
.name = "VIA DXS Playback Volume", .name = "VIA DXS Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.count = 4, .count = 4,
.info = snd_via8233_dxs_volume_info, .info = snd_via8233_dxs_volume_info,
.get = snd_via8233_dxs_volume_get, .get = snd_via8233_dxs_volume_get,
.put = snd_via8233_dxs_volume_put, .put = snd_via8233_dxs_volume_put,
.tlv = { .p = db_scale_dxs }
}; };
/* /*
@ -2393,6 +2413,7 @@ static int __devinit check_dxs_list(struct pci_dev *pci, int revision)
{ .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */ { .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */
{ .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */ { .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */
{ .subvendor = 0x1734, .subdevice = 0x1093, .action = VIA_DXS_SRC }, /* FSC */ { .subvendor = 0x1734, .subdevice = 0x1093, .action = VIA_DXS_SRC }, /* FSC */
{ .subvendor = 0x1734, .subdevice = 0x10ab, .action = VIA_DXS_SRC }, /* FSC */
{ .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ { .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
{ .subvendor = 0x1849, .subdevice = 0x9739, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */ { .subvendor = 0x1849, .subdevice = 0x9739, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */
{ .subvendor = 0x1849, .subdevice = 0x9761, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */ { .subvendor = 0x1849, .subdevice = 0x9761, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */

View File

@ -26,6 +26,7 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/tlv.h>
#include "vx222.h" #include "vx222.h"
#define CARD_NAME "VX222" #define CARD_NAME "VX222"
@ -72,6 +73,9 @@ MODULE_DEVICE_TABLE(pci, snd_vx222_ids);
/* /*
*/ */
static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
static DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0);
static struct snd_vx_hardware vx222_old_hw = { static struct snd_vx_hardware vx222_old_hw = {
.name = "VX222/Old", .name = "VX222/Old",
@ -81,6 +85,7 @@ static struct snd_vx_hardware vx222_old_hw = {
.num_ins = 1, .num_ins = 1,
.num_outs = 1, .num_outs = 1,
.output_level_max = VX_ANALOG_OUT_LEVEL_MAX, .output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
.output_level_db_scale = db_scale_old_vol,
}; };
static struct snd_vx_hardware vx222_v2_hw = { static struct snd_vx_hardware vx222_v2_hw = {
@ -92,6 +97,7 @@ static struct snd_vx_hardware vx222_v2_hw = {
.num_ins = 1, .num_ins = 1,
.num_outs = 1, .num_outs = 1,
.output_level_max = VX2_AKM_LEVEL_MAX, .output_level_max = VX2_AKM_LEVEL_MAX,
.output_level_db_scale = db_scale_akm,
}; };
static struct snd_vx_hardware vx222_mic_hw = { static struct snd_vx_hardware vx222_mic_hw = {
@ -103,6 +109,7 @@ static struct snd_vx_hardware vx222_mic_hw = {
.num_ins = 1, .num_ins = 1,
.num_outs = 1, .num_outs = 1,
.output_level_max = VX2_AKM_LEVEL_MAX, .output_level_max = VX2_AKM_LEVEL_MAX,
.output_level_db_scale = db_scale_akm,
}; };

View File

@ -28,6 +28,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include <asm/io.h> #include <asm/io.h>
#include "vx222.h" #include "vx222.h"
@ -845,6 +846,8 @@ static void vx2_set_input_level(struct snd_vx222 *chip)
#define MIC_LEVEL_MAX 0xff #define MIC_LEVEL_MAX 0xff
static DECLARE_TLV_DB_SCALE(db_scale_mic, -6450, 50, 0);
/* /*
* controls API for input levels * controls API for input levels
*/ */
@ -922,18 +925,24 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static struct snd_kcontrol_new vx_control_input_level = { static struct snd_kcontrol_new vx_control_input_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume", .name = "Capture Volume",
.info = vx_input_level_info, .info = vx_input_level_info,
.get = vx_input_level_get, .get = vx_input_level_get,
.put = vx_input_level_put, .put = vx_input_level_put,
.tlv = { .p = db_scale_mic },
}; };
static struct snd_kcontrol_new vx_control_mic_level = { static struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Capture Volume", .name = "Mic Capture Volume",
.info = vx_mic_level_info, .info = vx_mic_level_info,
.get = vx_mic_level_get, .get = vx_mic_level_get,
.put = vx_mic_level_put, .put = vx_mic_level_put,
.tlv = { .p = db_scale_mic },
}; };
/* /*

View File

@ -36,6 +36,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/tlv.h>
#include <sound/ymfpci.h> #include <sound/ymfpci.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/mpu401.h> #include <sound/mpu401.h>
@ -1477,11 +1478,15 @@ static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol,
return change; return change;
} }
static DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0);
#define YMFPCI_DOUBLE(xname, xindex, reg) \ #define YMFPCI_DOUBLE(xname, xindex, reg) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_ymfpci_info_double, \ .info = snd_ymfpci_info_double, \
.get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \ .get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
.private_value = reg } .private_value = reg, \
.tlv = { .p = db_scale_native } }
static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {

View File

@ -206,7 +206,7 @@ static void snd_pdacf_detach(struct pcmcia_device *link)
snd_pdacf_powerdown(chip); snd_pdacf_powerdown(chip);
chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */ chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */
snd_card_disconnect(chip->card); snd_card_disconnect(chip->card);
snd_card_free_in_thread(chip->card); snd_card_free_when_closed(chip->card);
} }
/* /*

View File

@ -23,6 +23,7 @@
#include <sound/driver.h> #include <sound/driver.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include "vxpocket.h" #include "vxpocket.h"
#define MIC_LEVEL_MIN 0 #define MIC_LEVEL_MIN 0
@ -63,12 +64,17 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0; return 0;
} }
static DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0);
static struct snd_kcontrol_new vx_control_mic_level = { static struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Capture Volume", .name = "Mic Capture Volume",
.info = vx_mic_level_info, .info = vx_mic_level_info,
.get = vx_mic_level_get, .get = vx_mic_level_get,
.put = vx_mic_level_put, .put = vx_mic_level_put,
.tlv = { .p = db_scale_mic },
}; };
/* /*

Some files were not shown because too many files have changed in this diff Show More