Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/blackfin-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/blackfin-2.6: (85 commits)
Blackfin char driver for Blackfin on-chip OTP memory (v3)
Blackfin Serial Driver: fix bug - use mod_timer to replace only add_timer.
Blackfin Serial Driver: the uart break anomaly has been given its own number, so switch to it
Blackfin Serial Driver: use BFIN_UART_NR_PORTS to help SIR driver in uart port.
Blackfin Serial Driver: Fix bug - kernel hangs when accessing uart 0 on bf537 when booting u-boot and linux on uart 1
Blackfin Serial Driver: punt unused lsr variable
Blackfin Serial Driver: Enable IR function when user application (irattach /dev/ttyBFx -s) call TIOCSETD ioctl with line discipline N_IRDA
[Blackfin] arch: add include/boot .gitignore files
[Blackfin] arch: Functional power management support: Add support for cpu frequency scaling
[Blackfin] arch: Functional power management support: Remove broken cpu frequency scaling drivers
[Blackfin] arch: Equalize include files: Add PLL_DIV Masks
[Blackfin] arch: Add a warning about the value of CLKIN.
[Blackfin] arch: take DDR DEVWD into consideration as well for BF548
[Blackfin] arch: Remove the circular buffering mechanism for exceptions
[Blackfin] arch: lose unnecessary dependency on CONFIG_BFIN_ICACHE for MPU
[Blackfin] arch: fix bug - before assign new channel to the map register, need clear the bits first.
[Blackfin] arch: add Blackfin on-chip SIR IrDA driver support
[Blackfin] arch: BF54x memsizes are in mbits, not mbytes
[Blackfin] arch: try to remove condition that causes double fault, by checking current before it gets dereferenced
[Blackfin] arch: Update anomaly list.
...
diff --git a/CREDITS b/CREDITS
index da0a56e..8fec7b3 100644
--- a/CREDITS
+++ b/CREDITS
@@ -403,6 +403,8 @@
N: Erik Inge Bolsø
E: knan@mo.himolde.no
D: Misc kernel hacks
+D: Updated PC speaker driver for 2.3
+S: Norway
N: Andreas E. Bombe
E: andreas.bombe@munich.netsurf.de
@@ -3116,6 +3118,12 @@
S: Sunnyvale, California 94088-4132
S: USA
+N: Stas Sergeev
+E: stsp@users.sourceforge.net
+D: PCM PC-Speaker driver
+D: misc fixes
+S: Russia
+
N: Simon Shapiro
E: shimon@i-Connect.Net
W: http://www.-i-Connect.Net/~shimon
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index e985cf5..fd4c32a 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -284,6 +284,13 @@
control correctly. If you have problems regarding this, try
another ALSA compliant mixer (alsamixer works).
+ Module snd-aw2
+ --------------
+
+ Module for Audiowerk2 sound card
+
+ This module supports multiple cards.
+
Module snd-azt2320
------------------
@@ -818,19 +825,25 @@
hippo_1 Hippo (Benq) with jack detection
sony-assamd Sony ASSAMD
ultra Samsung Q1 Ultra Vista model
+ lenovo-3000 Lenovo 3000 y410
basic fixed pin assignment w/o SPDIF
auto auto-config reading BIOS (default)
- ALC268
+ ALC267/268
+ quanta-il1 Quanta IL1 mini-notebook
3stack 3-stack model
toshiba Toshiba A205
acer Acer laptops
dell Dell OEM laptops (Vostro 1200)
+ zepto Zepto laptops
test for testing/debugging purpose, almost all controls can
adjusted. Appearing only when compiled with
$CONFIG_SND_DEBUG=y
auto auto-config reading BIOS (default)
+ ALC269
+ basic Basic preset
+
ALC662
3stack-dig 3-stack (2-channel) with SPDIF
3stack-6ch 3-stack (6-channel)
@@ -871,10 +884,11 @@
lenovo-nb0763 Lenovo NB0763
lenovo-ms7195-dig Lenovo MS7195
haier-w66 Haier W66
- 6stack-hp HP machines with 6stack (Nettle boards)
3stack-hp HP machines with 3stack (Lucknow, Samba boards)
6stack-dell Dell machines with 6stack (Inspiron 530)
mitac Mitac 8252D
+ clevo-m720 Clevo M720 laptop series
+ fujitsu-pi2515 Fujitsu AMILO Pi2515
auto auto-config reading BIOS (default)
ALC861/660
@@ -911,6 +925,12 @@
3stack 3-stack mode (default)
6stack 6-stack mode
+ AD1884A / AD1883 / AD1984A / AD1984B
+ desktop 3-stack desktop (default)
+ laptop laptop with HP jack sensing
+ mobile mobile devices with HP jack sensing
+ thinkpad Lenovo Thinkpad X300
+
AD1884
N/A
@@ -936,7 +956,7 @@
laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
ultra 2-channel with EAPD (Samsung Ultra tablet PC)
- AD1988
+ AD1988/AD1988B/AD1989A/AD1989B
6stack 6-jack
6stack-dig ditto with SPDIF
3stack 3-jack
@@ -979,6 +999,7 @@
dell-m26 Dell Inspiron 1501
dell-m27 Dell Inspiron E1705/9400
gateway Gateway laptops with EAPD control
+ panasonic Panasonic CF-74
STAC9205/9254
ref Reference board
@@ -1017,6 +1038,16 @@
3stack D965 3stack
5stack D965 5stack + SPDIF
dell-3stack Dell Dimension E520
+ dell-bios Fixes with Dell BIOS setup
+
+ STAC92HD71B*
+ ref Reference board
+ dell-m4-1 Dell desktops
+ dell-m4-2 Dell desktops
+
+ STAC92HD73*
+ ref Reference board
+ dell-m6 Dell desktops
STAC9872
vaio Setup for VAIO FE550G/SZ110
@@ -1590,6 +1621,16 @@
Power management is _not_ supported.
+ Module snd-pcsp
+ -----------------
+
+ Module for internal PC-Speaker.
+
+ nforce_wa - enable NForce chipset workaround. Expect bad sound.
+
+ This module supports system beeps, some kind of PCM playback and
+ even a few mixer controls.
+
Module snd-pcxhr
----------------
diff --git a/MAINTAINERS b/MAINTAINERS
index c0cc52a..f50e927 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2558,12 +2558,10 @@
S: Maintained
MAC80211
-P: Michael Wu
-M: flamingice@sourmilk.net
P: Johannes Berg
M: johannes@sipsolutions.net
-P: Jiri Benc
-M: jbenc@suse.cz
+P: Michael Wu
+M: flamingice@sourmilk.net
L: linux-wireless@vger.kernel.org
W: http://linuxwireless.org/
T: git kernel.org:/pub/scm/linux/kernel/git/linville/wireless-2.6.git
diff --git a/Makefile b/Makefile
index 3dbc826..d35c524 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 25
-EXTRAVERSION =
+EXTRAVERSION = -numa
NAME = Funky Weasel is Jiggy wit it
# *DOCUMENTATION*
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 8c71daf..9fee37e 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -75,6 +75,7 @@
lock_kernel();
mm = current->mm;
mm->end_code = bss_start + bss_len;
+ mm->start_brk = bss_start + bss_len;
mm->brk = bss_start + bss_len;
#if 0
printk("set_program_attributes(%lx %lx %lx %lx)\n",
diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c
index c107cc0..7835779 100644
--- a/arch/alpha/kernel/pci.c
+++ b/arch/alpha/kernel/pci.c
@@ -71,25 +71,13 @@
static void __init
quirk_cypress(struct pci_dev *dev)
{
- /* The Notorious Cy82C693 chip. */
-
- /* The Cypress IDE controller doesn't support native mode, but it
- has programmable addresses of IDE command/control registers.
- This violates PCI specifications, confuses the IDE subsystem and
- causes resource conflicts between the primary HD_CMD register and
- the floppy controller. Ugh. Fix that. */
- if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE) {
- dev->resource[0].flags = 0;
- dev->resource[1].flags = 0;
- }
-
/* The Cypress bridge responds on the PCI bus in the address range
0xffff0000-0xffffffff (conventional x86 BIOS ROM). There is no
way to turn this off. The bridge also supports several extended
BIOS ranges (disabled after power-up), and some consoles do turn
them on. So if we use a large direct-map window, or a large SG
window, we must avoid the entire 0xfff00000-0xffffffff region. */
- else if (dev->class >> 8 == PCI_CLASS_BRIDGE_ISA) {
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_ISA) {
if (__direct_map_base + __direct_map_size >= 0xfff00000UL)
__direct_map_size = 0xfff00000UL - __direct_map_base;
else {
@@ -391,7 +379,7 @@
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
}
-static void __init
+void __init
pcibios_claim_one_bus(struct pci_bus *b)
{
struct pci_dev *dev;
@@ -405,7 +393,8 @@
if (r->parent || !r->start || !r->flags)
continue;
- pci_claim_resource(dev, i);
+ if (pci_probe_only || (r->flags & IORESOURCE_PCI_FIXED))
+ pci_claim_resource(dev, i);
}
}
@@ -444,8 +433,7 @@
}
}
- if (pci_probe_only)
- pcibios_claim_console_setup();
+ pcibios_claim_console_setup();
pci_assign_unassigned_resources();
pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq);
diff --git a/arch/alpha/kernel/sys_nautilus.c b/arch/alpha/kernel/sys_nautilus.c
index 920196b..a7f23b5 100644
--- a/arch/alpha/kernel/sys_nautilus.c
+++ b/arch/alpha/kernel/sys_nautilus.c
@@ -187,6 +187,7 @@
}
extern void free_reserved_mem(void *, void *);
+extern void pcibios_claim_one_bus(struct pci_bus *);
static struct resource irongate_mem = {
.name = "Irongate PCI MEM",
@@ -205,6 +206,7 @@
/* Scan our single hose. */
bus = pci_scan_bus(0, alpha_mv.pci_ops, hose);
hose->bus = bus;
+ pcibios_claim_one_bus(bus);
irongate = pci_get_bus_and_slot(0, 0);
bus->self = irongate;
diff --git a/arch/arm/mach-integrator/time.c b/arch/arm/mach-integrator/time.c
index 5235f64..8508a0d 100644
--- a/arch/arm/mach-integrator/time.c
+++ b/arch/arm/mach-integrator/time.c
@@ -124,8 +124,11 @@
xtime.tv_sec = __raw_readl(rtc_base + RTC_DR);
+ /* note that 'dev' is merely used for irq disambiguation;
+ * it is not actually referenced in the irq handler
+ */
ret = request_irq(dev->irq[0], arm_rtc_interrupt, IRQF_DISABLED,
- "rtc-pl030", NULL);
+ "rtc-pl030", dev);
if (ret)
goto map_out;
diff --git a/arch/arm/mach-lh7a40x/arch-kev7a400.c b/arch/arm/mach-lh7a40x/arch-kev7a400.c
index 6d26661..2ef7d00 100644
--- a/arch/arm/mach-lh7a40x/arch-kev7a400.c
+++ b/arch/arm/mach-lh7a40x/arch-kev7a400.c
@@ -75,10 +75,9 @@
{
u32 mask = CPLD_LATCHED_INTS;
irq = IRQ_KEV7A400_CPLD;
- for (; mask; mask >>= 1, ++irq) {
+ for (; mask; mask >>= 1, ++irq)
if (mask & 1)
- desc[irq].handle (irq, desc);
- }
+ desc_handle_irq(irq, desc);
}
void __init lh7a40x_init_board_irq (void)
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 746cbb7..1b8229d 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -32,6 +32,7 @@
depends on !MMU
select CPU_32v4T
select CPU_ABRT_LV4T
+ select CPU_PABRT_NOIFAR
select CPU_CACHE_V4
help
A 32-bit RISC microprocessor based on the ARM7 processor core
@@ -85,6 +86,7 @@
depends on !MMU
select CPU_32v4T
select CPU_ABRT_LV4T
+ select CPU_PABRT_NOIFAR
select CPU_CACHE_V3 # although the core is v4t
select CPU_CP15_MPU
help
@@ -101,6 +103,7 @@
depends on !MMU
select CPU_32v4T
select CPU_ABRT_NOMMU
+ select CPU_PABRT_NOIFAR
select CPU_CACHE_V4
help
A 32-bit RISC microprocessor based on the ARM9 processor core
@@ -200,6 +203,7 @@
depends on !MMU
select CPU_32v4T
select CPU_ABRT_NOMMU
+ select CPU_PABRT_NOIFAR
select CPU_CACHE_VIVT
select CPU_CP15_MPU
help
@@ -217,6 +221,7 @@
depends on !MMU
select CPU_32v5
select CPU_ABRT_NOMMU
+ select CPU_PABRT_NOIFAR
select CPU_CACHE_VIVT
select CPU_CP15_MPU
help
@@ -351,6 +356,7 @@
default y
select CPU_32v5
select CPU_ABRT_EV5T
+ select CPU_PABRT_NOIFAR
select CPU_CACHE_VIVT
select CPU_CP15_MMU
select CPU_TLB_V4WBI if MMU
diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S
index 32fd7ea..5673f4d 100644
--- a/arch/arm/mm/proc-arm1020.S
+++ b/arch/arm/mm/proc-arm1020.S
@@ -471,6 +471,7 @@
.type arm1020_processor_functions, #object
arm1020_processor_functions:
.word v4t_early_abort
+ .word pabort_noifar
.word cpu_arm1020_proc_init
.word cpu_arm1020_proc_fin
.word cpu_arm1020_reset
@@ -478,7 +479,6 @@
.word cpu_arm1020_dcache_clean_area
.word cpu_arm1020_switch_mm
.word cpu_arm1020_set_pte_ext
- .word pabort_noifar
.size arm1020_processor_functions, . - arm1020_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S
index fe2b0ae..4343fdb 100644
--- a/arch/arm/mm/proc-arm1020e.S
+++ b/arch/arm/mm/proc-arm1020e.S
@@ -452,6 +452,7 @@
.type arm1020e_processor_functions, #object
arm1020e_processor_functions:
.word v4t_early_abort
+ .word pabort_noifar
.word cpu_arm1020e_proc_init
.word cpu_arm1020e_proc_fin
.word cpu_arm1020e_reset
@@ -459,7 +460,6 @@
.word cpu_arm1020e_dcache_clean_area
.word cpu_arm1020e_switch_mm
.word cpu_arm1020e_set_pte_ext
- .word pabort_noifar
.size arm1020e_processor_functions, . - arm1020e_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S
index 06dde67..2a4ea16 100644
--- a/arch/arm/mm/proc-arm1022.S
+++ b/arch/arm/mm/proc-arm1022.S
@@ -435,6 +435,7 @@
.type arm1022_processor_functions, #object
arm1022_processor_functions:
.word v4t_early_abort
+ .word pabort_noifar
.word cpu_arm1022_proc_init
.word cpu_arm1022_proc_fin
.word cpu_arm1022_reset
@@ -442,7 +443,6 @@
.word cpu_arm1022_dcache_clean_area
.word cpu_arm1022_switch_mm
.word cpu_arm1022_set_pte_ext
- .word pabort_noifar
.size arm1022_processor_functions, . - arm1022_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S
index f5506e6..77a1bab 100644
--- a/arch/arm/mm/proc-arm1026.S
+++ b/arch/arm/mm/proc-arm1026.S
@@ -430,6 +430,7 @@
.type arm1026_processor_functions, #object
arm1026_processor_functions:
.word v5t_early_abort
+ .word pabort_noifar
.word cpu_arm1026_proc_init
.word cpu_arm1026_proc_fin
.word cpu_arm1026_reset
@@ -437,7 +438,6 @@
.word cpu_arm1026_dcache_clean_area
.word cpu_arm1026_switch_mm
.word cpu_arm1026_set_pte_ext
- .word pabort_noifar
.size arm1026_processor_functions, . - arm1026_processor_functions
.section .rodata
diff --git a/arch/arm/mm/proc-arm6_7.S b/arch/arm/mm/proc-arm6_7.S
index 14b6a95..c371fc8 100644
--- a/arch/arm/mm/proc-arm6_7.S
+++ b/arch/arm/mm/proc-arm6_7.S
@@ -293,6 +293,7 @@
.type arm6_processor_functions, #object
ENTRY(arm6_processor_functions)
.word cpu_arm6_data_abort
+ .word pabort_noifar
.word cpu_arm6_proc_init
.word cpu_arm6_proc_fin
.word cpu_arm6_reset
@@ -300,7 +301,6 @@
.word cpu_arm6_dcache_clean_area
.word cpu_arm6_switch_mm
.word cpu_arm6_set_pte_ext
- .word pabort_noifar
.size arm6_processor_functions, . - arm6_processor_functions
/*
@@ -310,6 +310,7 @@
.type arm7_processor_functions, #object
ENTRY(arm7_processor_functions)
.word cpu_arm7_data_abort
+ .word pabort_noifar
.word cpu_arm7_proc_init
.word cpu_arm7_proc_fin
.word cpu_arm7_reset
@@ -317,7 +318,6 @@
.word cpu_arm7_dcache_clean_area
.word cpu_arm7_switch_mm
.word cpu_arm7_set_pte_ext
- .word pabort_noifar
.size arm7_processor_functions, . - arm7_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S
index ca5e7aa..d64f8e6 100644
--- a/arch/arm/mm/proc-arm720.S
+++ b/arch/arm/mm/proc-arm720.S
@@ -198,6 +198,7 @@
.type arm720_processor_functions, #object
ENTRY(arm720_processor_functions)
.word v4t_late_abort
+ .word pabort_noifar
.word cpu_arm720_proc_init
.word cpu_arm720_proc_fin
.word cpu_arm720_reset
@@ -205,7 +206,6 @@
.word cpu_arm720_dcache_clean_area
.word cpu_arm720_switch_mm
.word cpu_arm720_set_pte_ext
- .word pabort_noifar
.size arm720_processor_functions, . - arm720_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm740.S b/arch/arm/mm/proc-arm740.S
index 7069f49..3a57376 100644
--- a/arch/arm/mm/proc-arm740.S
+++ b/arch/arm/mm/proc-arm740.S
@@ -126,6 +126,7 @@
.type arm740_processor_functions, #object
ENTRY(arm740_processor_functions)
.word v4t_late_abort
+ .word pabort_noifar
.word cpu_arm740_proc_init
.word cpu_arm740_proc_fin
.word cpu_arm740_reset
diff --git a/arch/arm/mm/proc-arm7tdmi.S b/arch/arm/mm/proc-arm7tdmi.S
index d091c25..7b3ecde 100644
--- a/arch/arm/mm/proc-arm7tdmi.S
+++ b/arch/arm/mm/proc-arm7tdmi.S
@@ -64,6 +64,7 @@
.type arm7tdmi_processor_functions, #object
ENTRY(arm7tdmi_processor_functions)
.word v4t_late_abort
+ .word pabort_noifar
.word cpu_arm7tdmi_proc_init
.word cpu_arm7tdmi_proc_fin
.word cpu_arm7tdmi_reset
diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S
index 0170d4f..28cdb06 100644
--- a/arch/arm/mm/proc-arm920.S
+++ b/arch/arm/mm/proc-arm920.S
@@ -417,6 +417,7 @@
.type arm920_processor_functions, #object
arm920_processor_functions:
.word v4t_early_abort
+ .word pabort_noifar
.word cpu_arm920_proc_init
.word cpu_arm920_proc_fin
.word cpu_arm920_reset
@@ -424,7 +425,6 @@
.word cpu_arm920_dcache_clean_area
.word cpu_arm920_switch_mm
.word cpu_arm920_set_pte_ext
- .word pabort_noifar
.size arm920_processor_functions, . - arm920_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S
index b795249..94ddcb4 100644
--- a/arch/arm/mm/proc-arm922.S
+++ b/arch/arm/mm/proc-arm922.S
@@ -421,6 +421,7 @@
.type arm922_processor_functions, #object
arm922_processor_functions:
.word v4t_early_abort
+ .word pabort_noifar
.word cpu_arm922_proc_init
.word cpu_arm922_proc_fin
.word cpu_arm922_reset
@@ -428,7 +429,6 @@
.word cpu_arm922_dcache_clean_area
.word cpu_arm922_switch_mm
.word cpu_arm922_set_pte_ext
- .word pabort_noifar
.size arm922_processor_functions, . - arm922_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S
index e2988eb..065087a 100644
--- a/arch/arm/mm/proc-arm925.S
+++ b/arch/arm/mm/proc-arm925.S
@@ -484,6 +484,7 @@
.type arm925_processor_functions, #object
arm925_processor_functions:
.word v4t_early_abort
+ .word pabort_noifar
.word cpu_arm925_proc_init
.word cpu_arm925_proc_fin
.word cpu_arm925_reset
@@ -491,7 +492,6 @@
.word cpu_arm925_dcache_clean_area
.word cpu_arm925_switch_mm
.word cpu_arm925_set_pte_ext
- .word pabort_noifar
.size arm925_processor_functions, . - arm925_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S
index 62f7d1d..997db84 100644
--- a/arch/arm/mm/proc-arm926.S
+++ b/arch/arm/mm/proc-arm926.S
@@ -437,6 +437,7 @@
.type arm926_processor_functions, #object
arm926_processor_functions:
.word v5tj_early_abort
+ .word pabort_noifar
.word cpu_arm926_proc_init
.word cpu_arm926_proc_fin
.word cpu_arm926_reset
@@ -444,7 +445,6 @@
.word cpu_arm926_dcache_clean_area
.word cpu_arm926_switch_mm
.word cpu_arm926_set_pte_ext
- .word pabort_noifar
.size arm926_processor_functions, . - arm926_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S
index 786c593..44ead90 100644
--- a/arch/arm/mm/proc-arm940.S
+++ b/arch/arm/mm/proc-arm940.S
@@ -321,6 +321,7 @@
.type arm940_processor_functions, #object
ENTRY(arm940_processor_functions)
.word nommu_early_abort
+ .word pabort_noifar
.word cpu_arm940_proc_init
.word cpu_arm940_proc_fin
.word cpu_arm940_reset
diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S
index a60c142..2218b0c 100644
--- a/arch/arm/mm/proc-arm946.S
+++ b/arch/arm/mm/proc-arm946.S
@@ -376,6 +376,7 @@
.type arm946_processor_functions, #object
ENTRY(arm946_processor_functions)
.word nommu_early_abort
+ .word pabort_noifar
.word cpu_arm946_proc_init
.word cpu_arm946_proc_fin
.word cpu_arm946_reset
diff --git a/arch/arm/mm/proc-arm9tdmi.S b/arch/arm/mm/proc-arm9tdmi.S
index 4848eea..c85c1f5 100644
--- a/arch/arm/mm/proc-arm9tdmi.S
+++ b/arch/arm/mm/proc-arm9tdmi.S
@@ -64,6 +64,7 @@
.type arm9tdmi_processor_functions, #object
ENTRY(arm9tdmi_processor_functions)
.word nommu_early_abort
+ .word pabort_noifar
.word cpu_arm9tdmi_proc_init
.word cpu_arm9tdmi_proc_fin
.word cpu_arm9tdmi_reset
diff --git a/arch/arm/mm/proc-feroceon.S b/arch/arm/mm/proc-feroceon.S
index 2f169b2..90e7594 100644
--- a/arch/arm/mm/proc-feroceon.S
+++ b/arch/arm/mm/proc-feroceon.S
@@ -423,6 +423,7 @@
.type feroceon_processor_functions, #object
feroceon_processor_functions:
.word v5t_early_abort
+ .word pabort_noifar
.word cpu_feroceon_proc_init
.word cpu_feroceon_proc_fin
.word cpu_feroceon_reset
@@ -430,7 +431,6 @@
.word cpu_feroceon_dcache_clean_area
.word cpu_feroceon_switch_mm
.word cpu_feroceon_set_pte_ext
- .word pabort_noifar
.size feroceon_processor_functions, . - feroceon_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S
index 4db3d62..9818195 100644
--- a/arch/arm/mm/proc-sa110.S
+++ b/arch/arm/mm/proc-sa110.S
@@ -216,6 +216,7 @@
.type sa110_processor_functions, #object
ENTRY(sa110_processor_functions)
.word v4_early_abort
+ .word pabort_noifar
.word cpu_sa110_proc_init
.word cpu_sa110_proc_fin
.word cpu_sa110_reset
@@ -223,7 +224,6 @@
.word cpu_sa110_dcache_clean_area
.word cpu_sa110_switch_mm
.word cpu_sa110_set_pte_ext
- .word pabort_noifar
.size sa110_processor_functions, . - sa110_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-sa1100.S b/arch/arm/mm/proc-sa1100.S
index 3cdef04..c5fe27a 100644
--- a/arch/arm/mm/proc-sa1100.S
+++ b/arch/arm/mm/proc-sa1100.S
@@ -231,6 +231,7 @@
.type sa1100_processor_functions, #object
ENTRY(sa1100_processor_functions)
.word v4_early_abort
+ .word pabort_noifar
.word cpu_sa1100_proc_init
.word cpu_sa1100_proc_fin
.word cpu_sa1100_reset
@@ -238,7 +239,6 @@
.word cpu_sa1100_dcache_clean_area
.word cpu_sa1100_switch_mm
.word cpu_sa1100_set_pte_ext
- .word pabort_noifar
.size sa1100_processor_functions, . - sa1100_processor_functions
.section ".rodata"
diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S
index bf760ea..5702ec5 100644
--- a/arch/arm/mm/proc-v6.S
+++ b/arch/arm/mm/proc-v6.S
@@ -219,6 +219,7 @@
.type v6_processor_functions, #object
ENTRY(v6_processor_functions)
.word v6_early_abort
+ .word pabort_noifar
.word cpu_v6_proc_init
.word cpu_v6_proc_fin
.word cpu_v6_reset
@@ -226,7 +227,6 @@
.word cpu_v6_dcache_clean_area
.word cpu_v6_switch_mm
.word cpu_v6_set_pte_ext
- .word pabort_noifar
.size v6_processor_functions, . - v6_processor_functions
.type cpu_arch_name, #object
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index a1d7331..b49f9a4 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -205,6 +205,7 @@
.type v7_processor_functions, #object
ENTRY(v7_processor_functions)
.word v7_early_abort
+ .word pabort_ifar
.word cpu_v7_proc_init
.word cpu_v7_proc_fin
.word cpu_v7_reset
@@ -212,7 +213,6 @@
.word cpu_v7_dcache_clean_area
.word cpu_v7_switch_mm
.word cpu_v7_set_pte_ext
- .word pabort_ifar
.size v7_processor_functions, . - v7_processor_functions
.type cpu_arch_name, #object
diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S
index d95921a..3533741 100644
--- a/arch/arm/mm/proc-xsc3.S
+++ b/arch/arm/mm/proc-xsc3.S
@@ -450,6 +450,7 @@
.type xsc3_processor_functions, #object
ENTRY(xsc3_processor_functions)
.word v5t_early_abort
+ .word pabort_noifar
.word cpu_xsc3_proc_init
.word cpu_xsc3_proc_fin
.word cpu_xsc3_reset
diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S
index 1a6d898..2dd8527 100644
--- a/arch/arm/mm/proc-xscale.S
+++ b/arch/arm/mm/proc-xscale.S
@@ -527,6 +527,7 @@
.type xscale_processor_functions, #object
ENTRY(xscale_processor_functions)
.word v5t_early_abort
+ .word pabort_noifar
.word cpu_xscale_proc_init
.word cpu_xscale_proc_fin
.word cpu_xscale_reset
@@ -534,7 +535,6 @@
.word cpu_xscale_dcache_clean_area
.word cpu_xscale_switch_mm
.word cpu_xscale_set_pte_ext
- .word pabort_noifar
.size xscale_processor_functions, . - xscale_processor_functions
.section ".rodata"
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index 135644f..484c83d 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -1409,7 +1409,6 @@
st %o0, [%sp + STACKFRAME_SZ + PT_I0]
- .globl ret_sys_call
ret_sys_call:
ld [%curptr + TI_FLAGS], %l6
cmp %o0, -ERESTART_RESTARTBLOCK
diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c
index 1f730619..3e849e8 100644
--- a/arch/sparc/kernel/signal.c
+++ b/arch/sparc/kernel/signal.c
@@ -105,11 +105,6 @@
return -ERESTARTNOHAND;
}
-asmlinkage int sys_sigpause(unsigned int set)
-{
- return _sigpause_common(set);
-}
-
asmlinkage int sys_sigsuspend(old_sigset_t set)
{
return _sigpause_common(set);
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index df3eacb..8acc5cc 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -250,6 +250,26 @@
endmenu
+config NUMA
+ bool "NUMA support"
+
+config NODES_SHIFT
+ int
+ default "4"
+ depends on NEED_MULTIPLE_NODES
+
+# Some NUMA nodes have memory ranges that span
+# other nodes. Even though a pfn is valid and
+# between a node's start and end pfns, it may not
+# reside on that node. See memmap_init_zone()
+# for details.
+config NODES_SPAN_OTHER_NODES
+ def_bool y
+ depends on NEED_MULTIPLE_NODES
+
+config ARCH_POPULATES_NODE_MAP
+ def_bool y
+
config ARCH_SELECT_MEMORY_MODEL
def_bool y
diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig
index e183586..92f7968 100644
--- a/arch/sparc64/defconfig
+++ b/arch/sparc64/defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.25
-# Sun Apr 20 01:33:21 2008
+# Linux kernel version: 2.6.25-numa
+# Wed Apr 23 04:49:08 2008
#
CONFIG_SPARC=y
CONFIG_SPARC64=y
@@ -152,6 +152,8 @@
CONFIG_HUGETLB_PAGE_SIZE_4MB=y
# CONFIG_HUGETLB_PAGE_SIZE_512K is not set
# CONFIG_HUGETLB_PAGE_SIZE_64K is not set
+# CONFIG_NUMA is not set
+CONFIG_ARCH_POPULATES_NODE_MAP=y
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_DEFAULT=y
@@ -787,7 +789,6 @@
# CONFIG_SENSORS_PCF8574 is not set
# CONFIG_PCF8575 is not set
# CONFIG_SENSORS_PCF8591 is not set
-# CONFIG_TPS65010 is not set
# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_SENSORS_TSL2550 is not set
# CONFIG_I2C_DEBUG_CORE is not set
@@ -869,6 +870,7 @@
# Multifunction device drivers
#
# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
#
# Multimedia devices
@@ -1219,10 +1221,6 @@
# CONFIG_NEW_LEDS is not set
# CONFIG_INFINIBAND is not set
# CONFIG_RTC_CLASS is not set
-
-#
-# Userspace I/O
-#
# CONFIG_UIO is not set
#
@@ -1399,6 +1397,7 @@
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
# CONFIG_DEBUG_LIST is not set
# CONFIG_DEBUG_SG is not set
# CONFIG_BOOT_PRINTK_DELAY is not set
@@ -1425,53 +1424,82 @@
CONFIG_ASYNC_MEMCPY=m
CONFIG_ASYNC_XOR=m
CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_AEAD=y
CONFIG_CRYPTO_BLKCIPHER=y
-# CONFIG_CRYPTO_SEQIV is not set
CONFIG_CRYPTO_HASH=y
CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_GF128MUL=m
+CONFIG_CRYPTO_NULL=m
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_TEST=m
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_LRW=m
+CONFIG_CRYPTO_PCBC=m
+CONFIG_CRYPTO_XTS=m
+
+#
+# Hash modes
+#
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_XCBC=y
-CONFIG_CRYPTO_NULL=m
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=m
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_SHA256=m
CONFIG_CRYPTO_SHA512=m
-CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_TGR192=m
-CONFIG_CRYPTO_GF128MUL=m
-CONFIG_CRYPTO_ECB=m
-CONFIG_CRYPTO_CBC=y
-CONFIG_CRYPTO_PCBC=m
-CONFIG_CRYPTO_LRW=m
-CONFIG_CRYPTO_XTS=m
-# CONFIG_CRYPTO_CTR is not set
-# CONFIG_CRYPTO_GCM is not set
-# CONFIG_CRYPTO_CCM is not set
-# CONFIG_CRYPTO_CRYPTD is not set
-CONFIG_CRYPTO_DES=y
-CONFIG_CRYPTO_FCRYPT=m
-CONFIG_CRYPTO_BLOWFISH=m
-CONFIG_CRYPTO_TWOFISH=m
-CONFIG_CRYPTO_TWOFISH_COMMON=m
-CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_WP512=m
+
+#
+# Ciphers
+#
CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAMELLIA=m
CONFIG_CRYPTO_CAST5=m
CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_TEA=m
-CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_FCRYPT=m
CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_ANUBIS=m
-CONFIG_CRYPTO_SEED=m
# CONFIG_CRYPTO_SALSA20 is not set
+CONFIG_CRYPTO_SEED=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+
+#
+# Compression
+#
CONFIG_CRYPTO_DEFLATE=y
-CONFIG_CRYPTO_MICHAEL_MIC=m
-CONFIG_CRYPTO_CRC32C=m
-CONFIG_CRYPTO_CAMELLIA=m
-CONFIG_CRYPTO_TEST=m
-CONFIG_CRYPTO_AUTHENC=y
# CONFIG_CRYPTO_LZO is not set
CONFIG_CRYPTO_HW=y
# CONFIG_CRYPTO_DEV_HIFN_795X is not set
@@ -1492,3 +1520,4 @@
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
+CONFIG_HAVE_LMB=y
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c
index 04ab81c..bc26322 100644
--- a/arch/sparc64/kernel/ebus.c
+++ b/arch/sparc64/kernel/ebus.c
@@ -396,6 +396,7 @@
sd->op = &dev->ofdev;
sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu;
sd->stc = dev->bus->ofdev.dev.parent->archdata.stc;
+ sd->numa_node = dev->bus->ofdev.dev.parent->archdata.numa_node;
dev->ofdev.node = dp;
dev->ofdev.dev.parent = &dev->bus->ofdev.dev;
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S
index fb43c76..fd06e93 100644
--- a/arch/sparc64/kernel/entry.S
+++ b/arch/sparc64/kernel/entry.S
@@ -47,7 +47,7 @@
ba,pt %xcc, etrap
109: or %g7, %lo(109b), %g7
add %g0, %g0, %g0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
1: TRAP_LOAD_THREAD_REG(%g6, %g1)
ldub [%g6 + TI_FPSAVED], %g5
@@ -226,7 +226,7 @@
call do_fpother
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
.globl do_fpother_check_fitos
.align 32
@@ -489,7 +489,7 @@
call bad_trap
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
invoke_utrap:
sllx %g3, 3, %g3
@@ -607,7 +607,7 @@
call spitfire_access_error
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
/* This is the trap handler entry point for ECC correctable
* errors. They are corrected, but we listen for the trap
@@ -686,7 +686,7 @@
call spitfire_data_access_exception_tl1
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
__spitfire_data_access_exception:
rdpr %pstate, %g4
@@ -705,7 +705,7 @@
call spitfire_data_access_exception
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
.globl __spitfire_insn_access_exception
.globl __spitfire_insn_access_exception_tl1
@@ -725,7 +725,7 @@
call spitfire_insn_access_exception_tl1
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
__spitfire_insn_access_exception:
rdpr %pstate, %g4
@@ -743,7 +743,7 @@
call spitfire_insn_access_exception
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
/* These get patched into the trap table at boot time
* once we know we have a cheetah processor.
@@ -937,7 +937,7 @@
call cheetah_plus_parity_error
add %sp, PTREGS_OFF, %o1
ba,pt %xcc, rtrap
- clr %l6
+ nop
do_icpe_tl1:
rdpr %tl, %g1 ! Save original trap level
@@ -979,7 +979,7 @@
call cheetah_plus_parity_error
add %sp, PTREGS_OFF, %o1
ba,pt %xcc, rtrap
- clr %l6
+ nop
dcpe_icpe_tl1_common:
/* Flush D-cache, re-enable D/I caches in DCU and finally
@@ -1281,7 +1281,7 @@
call do_privact
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
.globl do_mna
do_mna:
@@ -1308,7 +1308,7 @@
call mem_address_unaligned
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
.globl do_lddfmna
do_lddfmna:
@@ -1326,7 +1326,7 @@
call handle_lddfmna
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
.globl do_stdfmna
do_stdfmna:
@@ -1344,7 +1344,7 @@
call handle_stdfmna
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
- clr %l6
+ nop
.globl breakpoint_trap
breakpoint_trap:
@@ -1424,13 +1424,13 @@
1: ldx [%curptr + TI_FLAGS], %l5
andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0
be,pt %icc, rtrap
- clr %l6
+ nop
add %sp, PTREGS_OFF, %o0
call syscall_trace
mov 1, %o1
ba,pt %xcc, rtrap
- clr %l6
+ nop
/* This is how fork() was meant to be done, 8 instruction entry.
*
@@ -1559,7 +1559,7 @@
/* Linux native system calls enter here... */
.align 32
- .globl linux_sparc_syscall, ret_sys_call
+ .globl linux_sparc_syscall
linux_sparc_syscall:
/* Direct access to user regs, much faster. */
cmp %g1, NR_SYSCALLS ! IEU1 Group
@@ -1605,7 +1605,7 @@
bne,pn %icc, linux_syscall_trace2
add %l1, 0x4, %l2 ! npc = npc+4
stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC]
- ba,pt %xcc, rtrap_clr_l6
+ ba,pt %xcc, rtrap
stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC]
1:
@@ -1616,7 +1616,6 @@
sub %g0, %o0, %o0
or %g3, %g2, %g3
stx %o0, [%sp + PTREGS_OFF + PT_V9_I0]
- mov 1, %l6
stx %g3, [%sp + PTREGS_OFF + PT_V9_TSTATE]
bne,pn %icc, linux_syscall_trace2
add %l1, 0x4, %l2 ! npc = npc+4
diff --git a/arch/sparc64/kernel/entry.h b/arch/sparc64/kernel/entry.h
index 4a91e9c..32fbab6 100644
--- a/arch/sparc64/kernel/entry.h
+++ b/arch/sparc64/kernel/entry.h
@@ -20,7 +20,6 @@
extern void do_notify_resume(struct pt_regs *regs,
unsigned long orig_i0,
- int restart_syscall,
unsigned long thread_info_flags);
extern asmlinkage void syscall_trace(struct pt_regs *regs,
diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S
index 4b2bf9e..b49d3b6 100644
--- a/arch/sparc64/kernel/etrap.S
+++ b/arch/sparc64/kernel/etrap.S
@@ -53,7 +53,11 @@
stx %g3, [%g2 + STACKFRAME_SZ + PT_V9_TPC]
rd %y, %g3
stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC]
+ rdpr %tt, %g1
st %g3, [%g2 + STACKFRAME_SZ + PT_V9_Y]
+ sethi %hi(PT_REGS_MAGIC), %g3
+ or %g3, %g1, %g1
+ st %g1, [%g2 + STACKFRAME_SZ + PT_V9_MAGIC]
rdpr %cansave, %g1
brnz,pt %g1, etrap_save
diff --git a/arch/sparc64/kernel/iommu.c b/arch/sparc64/kernel/iommu.c
index 756fa24..2a37a6c 100644
--- a/arch/sparc64/kernel/iommu.c
+++ b/arch/sparc64/kernel/iommu.c
@@ -173,9 +173,11 @@
}
int iommu_table_init(struct iommu *iommu, int tsbsize,
- u32 dma_offset, u32 dma_addr_mask)
+ u32 dma_offset, u32 dma_addr_mask,
+ int numa_node)
{
- unsigned long i, tsbbase, order, sz, num_tsb_entries;
+ unsigned long i, order, sz, num_tsb_entries;
+ struct page *page;
num_tsb_entries = tsbsize / sizeof(iopte_t);
@@ -188,11 +190,12 @@
/* Allocate and initialize the free area map. */
sz = num_tsb_entries / 8;
sz = (sz + 7UL) & ~7UL;
- iommu->arena.map = kzalloc(sz, GFP_KERNEL);
+ iommu->arena.map = kmalloc_node(sz, GFP_KERNEL, numa_node);
if (!iommu->arena.map) {
printk(KERN_ERR "IOMMU: Error, kmalloc(arena.map) failed.\n");
return -ENOMEM;
}
+ memset(iommu->arena.map, 0, sz);
iommu->arena.limit = num_tsb_entries;
if (tlb_type != hypervisor)
@@ -201,21 +204,23 @@
/* Allocate and initialize the dummy page which we
* set inactive IO PTEs to point to.
*/
- iommu->dummy_page = get_zeroed_page(GFP_KERNEL);
- if (!iommu->dummy_page) {
+ page = alloc_pages_node(numa_node, GFP_KERNEL, 0);
+ if (!page) {
printk(KERN_ERR "IOMMU: Error, gfp(dummy_page) failed.\n");
goto out_free_map;
}
+ iommu->dummy_page = (unsigned long) page_address(page);
+ memset((void *)iommu->dummy_page, 0, PAGE_SIZE);
iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page);
/* Now allocate and setup the IOMMU page table itself. */
order = get_order(tsbsize);
- tsbbase = __get_free_pages(GFP_KERNEL, order);
- if (!tsbbase) {
+ page = alloc_pages_node(numa_node, GFP_KERNEL, order);
+ if (!page) {
printk(KERN_ERR "IOMMU: Error, gfp(tsb) failed.\n");
goto out_free_dummy_page;
}
- iommu->page_table = (iopte_t *)tsbbase;
+ iommu->page_table = (iopte_t *)page_address(page);
for (i = 0; i < num_tsb_entries; i++)
iopte_make_dummy(iommu, &iommu->page_table[i]);
@@ -276,20 +281,24 @@
static void *dma_4u_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_addrp, gfp_t gfp)
{
- struct iommu *iommu;
- iopte_t *iopte;
unsigned long flags, order, first_page;
+ struct iommu *iommu;
+ struct page *page;
+ int npages, nid;
+ iopte_t *iopte;
void *ret;
- int npages;
size = IO_PAGE_ALIGN(size);
order = get_order(size);
if (order >= 10)
return NULL;
- first_page = __get_free_pages(gfp, order);
- if (first_page == 0UL)
+ nid = dev->archdata.numa_node;
+ page = alloc_pages_node(nid, gfp, order);
+ if (unlikely(!page))
return NULL;
+
+ first_page = (unsigned long) page_address(page);
memset((char *)first_page, 0, PAGE_SIZE << order);
iommu = dev->archdata.iommu;
diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c
index b5f7b35..a2af5ed 100644
--- a/arch/sparc64/kernel/isa.c
+++ b/arch/sparc64/kernel/isa.c
@@ -92,6 +92,7 @@
sd->op = &isa_dev->ofdev;
sd->iommu = isa_br->ofdev.dev.parent->archdata.iommu;
sd->stc = isa_br->ofdev.dev.parent->archdata.stc;
+ sd->numa_node = isa_br->ofdev.dev.parent->archdata.numa_node;
isa_dev->ofdev.node = dp;
isa_dev->ofdev.dev.parent = &isa_br->ofdev.dev;
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c
index 9100835..dde52bc 100644
--- a/arch/sparc64/kernel/mdesc.c
+++ b/arch/sparc64/kernel/mdesc.c
@@ -1,10 +1,10 @@
/* mdesc.c: Sun4V machine description handling.
*
- * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/bootmem.h>
+#include <linux/lmb.h>
#include <linux/log2.h>
#include <linux/list.h>
#include <linux/slab.h>
@@ -84,24 +84,28 @@
hp->handle_size = handle_size;
}
-static struct mdesc_handle * __init mdesc_bootmem_alloc(unsigned int mdesc_size)
+static struct mdesc_handle * __init mdesc_lmb_alloc(unsigned int mdesc_size)
{
- struct mdesc_handle *hp;
unsigned int handle_size, alloc_size;
+ struct mdesc_handle *hp;
+ unsigned long paddr;
handle_size = (sizeof(struct mdesc_handle) -
sizeof(struct mdesc_hdr) +
mdesc_size);
alloc_size = PAGE_ALIGN(handle_size);
- hp = __alloc_bootmem(alloc_size, PAGE_SIZE, 0UL);
- if (hp)
- mdesc_handle_init(hp, handle_size, hp);
+ paddr = lmb_alloc(alloc_size, PAGE_SIZE);
+ hp = NULL;
+ if (paddr) {
+ hp = __va(paddr);
+ mdesc_handle_init(hp, handle_size, hp);
+ }
return hp;
}
-static void mdesc_bootmem_free(struct mdesc_handle *hp)
+static void mdesc_lmb_free(struct mdesc_handle *hp)
{
unsigned int alloc_size, handle_size = hp->handle_size;
unsigned long start, end;
@@ -124,9 +128,9 @@
}
}
-static struct mdesc_mem_ops bootmem_mdesc_ops = {
- .alloc = mdesc_bootmem_alloc,
- .free = mdesc_bootmem_free,
+static struct mdesc_mem_ops lmb_mdesc_ops = {
+ .alloc = mdesc_lmb_alloc,
+ .free = mdesc_lmb_free,
};
static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size)
@@ -888,7 +892,7 @@
printk("MDESC: Size is %lu bytes.\n", len);
- hp = mdesc_alloc(len, &bootmem_mdesc_ops);
+ hp = mdesc_alloc(len, &lmb_mdesc_ops);
if (hp == NULL) {
prom_printf("MDESC: alloc of %lu bytes failed.\n", len);
prom_halt();
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c
index 0fd9db9..9e58e8c 100644
--- a/arch/sparc64/kernel/of_device.c
+++ b/arch/sparc64/kernel/of_device.c
@@ -6,6 +6,7 @@
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/errno.h>
+#include <linux/irq.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
@@ -660,6 +661,7 @@
struct device_node *dp = op->node;
struct device_node *pp, *ip;
unsigned int orig_irq = irq;
+ int nid;
if (irq == 0xffffffff)
return irq;
@@ -672,7 +674,7 @@
printk("%s: direct translate %x --> %x\n",
dp->full_name, orig_irq, irq);
- return irq;
+ goto out;
}
/* Something more complicated. Walk up to the root, applying
@@ -744,6 +746,14 @@
printk("%s: Apply IRQ trans [%s] %x --> %x\n",
op->node->full_name, ip->full_name, orig_irq, irq);
+out:
+ nid = of_node_to_nid(dp);
+ if (nid != -1) {
+ cpumask_t numa_mask = node_to_cpumask(nid);
+
+ irq_set_affinity(irq, numa_mask);
+ }
+
return irq;
}
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index 545356b..49f9127 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -369,10 +369,12 @@
sd->host_controller = pbm;
sd->prom_node = node;
sd->op = of_find_device_by_node(node);
+ sd->numa_node = pbm->numa_node;
sd = &sd->op->dev.archdata;
sd->iommu = pbm->iommu;
sd->stc = &pbm->stc;
+ sd->numa_node = pbm->numa_node;
type = of_get_property(node, "device_type", NULL);
if (type == NULL)
@@ -1159,6 +1161,16 @@
return 0;
}
+#ifdef CONFIG_NUMA
+int pcibus_to_node(struct pci_bus *pbus)
+{
+ struct pci_pbm_info *pbm = pbus->sysdata;
+
+ return pbm->numa_node;
+}
+EXPORT_SYMBOL(pcibus_to_node);
+#endif
+
/* Return the domain nuber for this pci bus */
int pci_domain_nr(struct pci_bus *pbus)
diff --git a/arch/sparc64/kernel/pci_fire.c b/arch/sparc64/kernel/pci_fire.c
index 7571ed5..d23bb6f 100644
--- a/arch/sparc64/kernel/pci_fire.c
+++ b/arch/sparc64/kernel/pci_fire.c
@@ -71,7 +71,8 @@
*/
fire_write(iommu->iommu_flushinv, ~(u64)0);
- err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask);
+ err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask,
+ pbm->numa_node);
if (err)
return err;
@@ -449,6 +450,8 @@
pbm->next = pci_pbm_root;
pci_pbm_root = pbm;
+ pbm->numa_node = -1;
+
pbm->scan_bus = pci_fire_scan_bus;
pbm->pci_ops = &sun4u_pci_ops;
pbm->config_space_reg_bits = 12;
diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h
index 4a50da1..218bac4 100644
--- a/arch/sparc64/kernel/pci_impl.h
+++ b/arch/sparc64/kernel/pci_impl.h
@@ -148,6 +148,8 @@
struct pci_bus *pci_bus;
void (*scan_bus)(struct pci_pbm_info *);
struct pci_ops *pci_ops;
+
+ int numa_node;
};
struct pci_controller_info {
@@ -161,8 +163,6 @@
extern int pci_num_pbms;
/* PCI bus scanning and fixup support. */
-extern void pci_iommu_table_init(struct iommu *iommu, int tsbsize,
- u32 dma_offset, u32 dma_addr_mask);
extern void pci_get_pbm_props(struct pci_pbm_info *pbm);
extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm);
extern void pci_determine_mem_io_space(struct pci_pbm_info *pbm);
diff --git a/arch/sparc64/kernel/pci_msi.c b/arch/sparc64/kernel/pci_msi.c
index d6d64b4..db5e8fd 100644
--- a/arch/sparc64/kernel/pci_msi.c
+++ b/arch/sparc64/kernel/pci_msi.c
@@ -279,11 +279,17 @@
unsigned long devino)
{
int irq = ops->msiq_build_irq(pbm, msiqid, devino);
- int err;
+ int err, nid;
if (irq < 0)
return irq;
+ nid = pbm->numa_node;
+ if (nid != -1) {
+ cpumask_t numa_mask = node_to_cpumask(nid);
+
+ irq_set_affinity(irq, numa_mask);
+ }
err = request_irq(irq, sparc64_msiq_interrupt, 0,
"MSIQ",
&pbm->msiq_irq_cookies[msiqid - pbm->msiq_first]);
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c
index 0bad96e..994dbe0 100644
--- a/arch/sparc64/kernel/pci_psycho.c
+++ b/arch/sparc64/kernel/pci_psycho.c
@@ -848,7 +848,8 @@
/* Leave diag mode enabled for full-flushing done
* in pci_iommu.c
*/
- err = iommu_table_init(iommu, IO_TSB_SIZE, 0xc0000000, 0xffffffff);
+ err = iommu_table_init(iommu, IO_TSB_SIZE, 0xc0000000, 0xffffffff,
+ pbm->numa_node);
if (err)
return err;
@@ -979,6 +980,8 @@
pbm->next = pci_pbm_root;
pci_pbm_root = pbm;
+ pbm->numa_node = -1;
+
pbm->scan_bus = psycho_scan_bus;
pbm->pci_ops = &sun4u_pci_ops;
pbm->config_space_reg_bits = 8;
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
index 1c5f5fa..4c34195 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -704,7 +704,7 @@
* in pci_iommu.c
*/
err = iommu_table_init(iommu, tsbsize * 1024 * 8,
- dvma_offset, dma_mask);
+ dvma_offset, dma_mask, pbm->numa_node);
if (err)
return err;
@@ -737,6 +737,8 @@
pbm->name = dp->full_name;
printk("%s: SABRE PCI Bus Module\n", pbm->name);
+ pbm->numa_node = -1;
+
pbm->scan_bus = sabre_scan_bus;
pbm->pci_ops = &sun4u_pci_ops;
pbm->config_space_reg_bits = 8;
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c
index e306093..615edd9 100644
--- a/arch/sparc64/kernel/pci_schizo.c
+++ b/arch/sparc64/kernel/pci_schizo.c
@@ -1220,7 +1220,8 @@
/* Leave diag mode enabled for full-flushing done
* in pci_iommu.c
*/
- err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask);
+ err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask,
+ pbm->numa_node);
if (err)
return err;
@@ -1379,6 +1380,8 @@
pbm->next = pci_pbm_root;
pci_pbm_root = pbm;
+ pbm->numa_node = -1;
+
pbm->scan_bus = schizo_scan_bus;
pbm->pci_ops = &sun4u_pci_ops;
pbm->config_space_reg_bits = 8;
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c
index 0183970..e2bb979 100644
--- a/arch/sparc64/kernel/pci_sun4v.c
+++ b/arch/sparc64/kernel/pci_sun4v.c
@@ -127,10 +127,12 @@
static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_addrp, gfp_t gfp)
{
- struct iommu *iommu;
unsigned long flags, order, first_page, npages, n;
+ struct iommu *iommu;
+ struct page *page;
void *ret;
long entry;
+ int nid;
size = IO_PAGE_ALIGN(size);
order = get_order(size);
@@ -139,10 +141,12 @@
npages = size >> IO_PAGE_SHIFT;
- first_page = __get_free_pages(gfp, order);
- if (unlikely(first_page == 0UL))
+ nid = dev->archdata.numa_node;
+ page = alloc_pages_node(nid, gfp, order);
+ if (unlikely(!page))
return NULL;
+ first_page = (unsigned long) page_address(page);
memset((char *)first_page, 0, PAGE_SIZE << order);
iommu = dev->archdata.iommu;
@@ -899,6 +903,8 @@
pbm->next = pci_pbm_root;
pci_pbm_root = pbm;
+ pbm->numa_node = of_node_to_nid(dp);
+
pbm->scan_bus = pci_sun4v_scan_bus;
pbm->pci_ops = &sun4v_pci_ops;
pbm->config_space_reg_bits = 12;
@@ -913,6 +919,7 @@
pbm->name = dp->full_name;
printk("%s: SUN4V PCI Bus Module\n", pbm->name);
+ printk("%s: On NUMA node %d\n", pbm->name, pbm->numa_node);
pci_determine_mem_io_space(pbm);
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 68964dd..ed03a18 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -19,8 +19,8 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/mm.h>
-#include <linux/bootmem.h>
#include <linux/module.h>
+#include <linux/lmb.h>
#include <asm/prom.h>
#include <asm/of_device.h>
@@ -122,16 +122,20 @@
}
EXPORT_SYMBOL(of_find_in_proplist);
-static unsigned int prom_early_allocated;
+static unsigned int prom_early_allocated __initdata;
static void * __init prom_early_alloc(unsigned long size)
{
+ unsigned long paddr = lmb_alloc(size, SMP_CACHE_BYTES);
void *ret;
- ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
- if (ret != NULL)
- memset(ret, 0, size);
+ if (!paddr) {
+ prom_printf("prom_early_alloc(%lu) failed\n");
+ prom_halt();
+ }
+ ret = __va(paddr);
+ memset(ret, 0, size);
prom_early_allocated += size;
return ret;
diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S
index 079d18a..ecf6753 100644
--- a/arch/sparc64/kernel/rtrap.S
+++ b/arch/sparc64/kernel/rtrap.S
@@ -18,12 +18,6 @@
#define RTRAP_PSTATE_IRQOFF (PSTATE_RMO|PSTATE_PEF|PSTATE_PRIV)
#define RTRAP_PSTATE_AG_IRQOFF (PSTATE_RMO|PSTATE_PEF|PSTATE_PRIV|PSTATE_AG)
- /* Register %l6 keeps track of whether we are returning
- * from a system call or not. It is cleared if we call
- * do_notify_resume, and it must not be otherwise modified
- * until we fully commit to returning to userspace.
- */
-
.text
.align 32
__handle_softirq:
@@ -56,14 +50,12 @@
be,pt %xcc, __handle_user_windows_continue
nop
mov %l5, %o1
- mov %l6, %o2
add %sp, PTREGS_OFF, %o0
- mov %l0, %o3
+ mov %l0, %o2
call do_notify_resume
wrpr %g0, RTRAP_PSTATE, %pstate
wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- clr %l6
/* Signal delivery can modify pt_regs tstate, so we must
* reload it.
*/
@@ -99,14 +91,12 @@
be,pt %xcc, __handle_perfctrs_continue
sethi %hi(TSTATE_PEF), %o0
mov %l5, %o1
- mov %l6, %o2
add %sp, PTREGS_OFF, %o0
- mov %l0, %o3
+ mov %l0, %o2
call do_notify_resume
wrpr %g0, RTRAP_PSTATE, %pstate
wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- clr %l6
/* Signal delivery can modify pt_regs tstate, so we must
* reload it.
*/
@@ -127,13 +117,11 @@
__handle_signal:
mov %l5, %o1
- mov %l6, %o2
add %sp, PTREGS_OFF, %o0
- mov %l0, %o3
+ mov %l0, %o2
call do_notify_resume
wrpr %g0, RTRAP_PSTATE, %pstate
wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- clr %l6
/* Signal delivery can modify pt_regs tstate, so we must
* reload it.
@@ -145,9 +133,8 @@
andn %l1, %l4, %l1
.align 64
- .globl rtrap_irq, rtrap_clr_l6, rtrap, irqsz_patchme, rtrap_xcall
+ .globl rtrap_irq, rtrap, irqsz_patchme, rtrap_xcall
rtrap_irq:
-rtrap_clr_l6: clr %l6
rtrap:
#ifndef CONFIG_SMP
sethi %hi(per_cpu____cpu_data), %l0
diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c
index d1fb13b..fa2827c 100644
--- a/arch/sparc64/kernel/sbus.c
+++ b/arch/sparc64/kernel/sbus.c
@@ -544,6 +544,7 @@
sbus->ofdev.dev.archdata.iommu = iommu;
sbus->ofdev.dev.archdata.stc = strbuf;
+ sbus->ofdev.dev.archdata.numa_node = -1;
reg_base = regs + SYSIO_IOMMUREG_BASE;
iommu->iommu_control = reg_base + IOMMU_CONTROL;
@@ -575,7 +576,7 @@
sbus->portid, regs);
/* Setup for TSB_SIZE=7, TBW_SIZE=0, MMU_DE=1, MMU_EN=1 */
- if (iommu_table_init(iommu, IO_TSB_SIZE, MAP_BASE, 0xffffffff))
+ if (iommu_table_init(iommu, IO_TSB_SIZE, MAP_BASE, 0xffffffff, -1))
goto fatal_memory_error;
control = upa_readq(iommu->iommu_control);
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c
index 6acb4c5..da5e6ee 100644
--- a/arch/sparc64/kernel/setup.c
+++ b/arch/sparc64/kernel/setup.c
@@ -82,7 +82,7 @@
static struct console prom_early_console = {
.name = "earlyprom",
.write = prom_console_write,
- .flags = CON_PRINTBUFFER | CON_BOOT,
+ .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
.index = -1,
};
@@ -281,6 +281,7 @@
/* Initialize PROM console and command line. */
*cmdline_p = prom_getbootargs();
strcpy(boot_command_line, *cmdline_p);
+ parse_early_param();
boot_flags_init(*cmdline_p);
register_console(&prom_early_console);
diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c
index 1c47009..77a3e85 100644
--- a/arch/sparc64/kernel/signal.c
+++ b/arch/sparc64/kernel/signal.c
@@ -510,15 +510,20 @@
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
-static void do_signal(struct pt_regs *regs, unsigned long orig_i0, int restart_syscall)
+static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
{
- siginfo_t info;
struct signal_deliver_cookie cookie;
struct k_sigaction ka;
- int signr;
sigset_t *oldset;
+ siginfo_t info;
+ int signr, tt;
- cookie.restart_syscall = restart_syscall;
+ tt = regs->magic & 0x1ff;
+ if (tt == 0x110 || tt == 0x111 || tt == 0x16d) {
+ regs->magic &= ~0x1ff;
+ cookie.restart_syscall = 1;
+ } else
+ cookie.restart_syscall = 0;
cookie.orig_i0 = orig_i0;
if (test_thread_flag(TIF_RESTORE_SIGMASK))
@@ -529,9 +534,8 @@
#ifdef CONFIG_SPARC32_COMPAT
if (test_thread_flag(TIF_32BIT)) {
extern void do_signal32(sigset_t *, struct pt_regs *,
- unsigned long, int);
- do_signal32(oldset, regs, orig_i0,
- cookie.restart_syscall);
+ struct signal_deliver_cookie *);
+ do_signal32(oldset, regs, &cookie);
return;
}
#endif
@@ -539,7 +543,7 @@
signr = get_signal_to_deliver(&info, &ka, regs, &cookie);
if (signr > 0) {
if (cookie.restart_syscall)
- syscall_restart(orig_i0, regs, &ka.sa);
+ syscall_restart(cookie.orig_i0, regs, &ka.sa);
handle_signal(signr, &ka, &info, oldset, regs);
/* a signal was successfully delivered; the saved
@@ -576,11 +580,10 @@
}
}
-void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, int restart_syscall,
- unsigned long thread_info_flags)
+void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long thread_info_flags)
{
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
- do_signal(regs, orig_i0, restart_syscall);
+ do_signal(regs, orig_i0);
}
void ptrace_signal_deliver(struct pt_regs *regs, void *cookie)
diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c
index 74e0512..43cdec6 100644
--- a/arch/sparc64/kernel/signal32.c
+++ b/arch/sparc64/kernel/signal32.c
@@ -982,20 +982,16 @@
* mistake.
*/
void do_signal32(sigset_t *oldset, struct pt_regs * regs,
- unsigned long orig_i0, int restart_syscall)
+ struct signal_deliver_cookie *cookie)
{
- siginfo_t info;
- struct signal_deliver_cookie cookie;
struct k_sigaction ka;
+ siginfo_t info;
int signr;
- cookie.restart_syscall = restart_syscall;
- cookie.orig_i0 = orig_i0;
-
- signr = get_signal_to_deliver(&info, &ka, regs, &cookie);
+ signr = get_signal_to_deliver(&info, &ka, regs, cookie);
if (signr > 0) {
- if (cookie.restart_syscall)
- syscall_restart32(orig_i0, regs, &ka.sa);
+ if (cookie->restart_syscall)
+ syscall_restart32(cookie->orig_i0, regs, &ka.sa);
handle_signal32(signr, &ka, &info, oldset, regs);
/* a signal was successfully delivered; the saved
@@ -1007,16 +1003,16 @@
clear_thread_flag(TIF_RESTORE_SIGMASK);
return;
}
- if (cookie.restart_syscall &&
+ if (cookie->restart_syscall &&
(regs->u_regs[UREG_I0] == ERESTARTNOHAND ||
regs->u_regs[UREG_I0] == ERESTARTSYS ||
regs->u_regs[UREG_I0] == ERESTARTNOINTR)) {
/* replay the system call when we are done */
- regs->u_regs[UREG_I0] = cookie.orig_i0;
+ regs->u_regs[UREG_I0] = cookie->orig_i0;
regs->tpc -= 4;
regs->tnpc -= 4;
}
- if (cookie.restart_syscall &&
+ if (cookie->restart_syscall &&
regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) {
regs->u_regs[UREG_G1] = __NR_restart_syscall;
regs->tpc -= 4;
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index 59f020d..524b889 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -20,7 +20,7 @@
#include <linux/cache.h>
#include <linux/jiffies.h>
#include <linux/profile.h>
-#include <linux/bootmem.h>
+#include <linux/lmb.h>
#include <asm/head.h>
#include <asm/ptrace.h>
@@ -1431,7 +1431,7 @@
void __init real_setup_per_cpu_areas(void)
{
- unsigned long goal, size, i;
+ unsigned long paddr, goal, size, i;
char *ptr;
/* Copy section for each CPU (we discard the original) */
@@ -1441,8 +1441,13 @@
for (size = PAGE_SIZE; size < goal; size <<= 1UL)
__per_cpu_shift++;
- ptr = alloc_bootmem_pages(size * NR_CPUS);
+ paddr = lmb_alloc(size * NR_CPUS, PAGE_SIZE);
+ if (!paddr) {
+ prom_printf("Cannot allocate per-cpu memory.\n");
+ prom_halt();
+ }
+ ptr = __va(paddr);
__per_cpu_base = ptr - __per_cpu_start;
for (i = 0; i < NR_CPUS; i++, ptr += size)
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index 3873646..6633659 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -68,8 +68,6 @@
extern void *__memscan_generic(void *, int, size_t);
extern int __memcmp(const void *, const void *, __kernel_size_t);
extern __kernel_size_t strlen(const char *);
-extern void linux_sparc_syscall(void);
-extern void rtrap(void);
extern void show_regs(struct pt_regs *);
extern void syscall_trace(struct pt_regs *, int);
extern void sys_sigsuspend(void);
diff --git a/arch/sparc64/kernel/stacktrace.c b/arch/sparc64/kernel/stacktrace.c
index 84d39e8..01b52f5 100644
--- a/arch/sparc64/kernel/stacktrace.c
+++ b/arch/sparc64/kernel/stacktrace.c
@@ -20,6 +20,8 @@
thread_base = (unsigned long) tp;
do {
struct reg_window *rw;
+ struct pt_regs *regs;
+ unsigned long pc;
/* Bogus frame pointer? */
if (fp < (thread_base + sizeof(struct thread_info)) ||
@@ -27,11 +29,19 @@
break;
rw = (struct reg_window *) fp;
+ regs = (struct pt_regs *) (rw + 1);
+
+ if ((regs->magic & ~0x1ff) == PT_REGS_MAGIC) {
+ pc = regs->tpc;
+ fp = regs->u_regs[UREG_I6] + STACK_BIAS;
+ } else {
+ pc = rw->ins[7];
+ fp = rw->ins[6] + STACK_BIAS;
+ }
+
if (trace->skip > 0)
trace->skip--;
else
- trace->entries[trace->nr_entries++] = rw->ins[7];
-
- fp = rw->ins[6] + STACK_BIAS;
+ trace->entries[trace->nr_entries++] = pc;
} while (trace->nr_entries < trace->max_entries);
}
diff --git a/arch/sparc64/kernel/sun4v_tlb_miss.S b/arch/sparc64/kernel/sun4v_tlb_miss.S
index fd9430562..e1fbf8c 100644
--- a/arch/sparc64/kernel/sun4v_tlb_miss.S
+++ b/arch/sparc64/kernel/sun4v_tlb_miss.S
@@ -262,7 +262,7 @@
mov %l5, %o2
call sun4v_insn_access_exception
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
/* Instruction Access Exception, tl1. */
sun4v_iacc_tl1:
@@ -278,7 +278,7 @@
mov %l5, %o2
call sun4v_insn_access_exception_tl1
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
/* Data Access Exception, tl0. */
sun4v_dacc:
@@ -294,7 +294,7 @@
mov %l5, %o2
call sun4v_data_access_exception
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
/* Data Access Exception, tl1. */
sun4v_dacc_tl1:
@@ -310,7 +310,7 @@
mov %l5, %o2
call sun4v_data_access_exception_tl1
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
/* Memory Address Unaligned. */
sun4v_mna:
@@ -344,7 +344,7 @@
mov %l5, %o2
call sun4v_do_mna
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
/* Privileged Action. */
sun4v_privact:
@@ -352,7 +352,7 @@
rd %pc, %g7
call do_privact
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
/* Unaligned ldd float, tl0. */
sun4v_lddfmna:
@@ -368,7 +368,7 @@
mov %l5, %o2
call handle_lddfmna
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
/* Unaligned std float, tl0. */
sun4v_stdfmna:
@@ -384,7 +384,7 @@
mov %l5, %o2
call handle_stdfmna
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
#define BRANCH_ALWAYS 0x10680000
#define NOP 0x01000000
diff --git a/arch/sparc64/kernel/sysfs.c b/arch/sparc64/kernel/sysfs.c
index 52816c7..e885034 100644
--- a/arch/sparc64/kernel/sysfs.c
+++ b/arch/sparc64/kernel/sysfs.c
@@ -273,10 +273,22 @@
mmu_stats_supported = 1;
}
+static void register_nodes(void)
+{
+#ifdef CONFIG_NUMA
+ int i;
+
+ for (i = 0; i < MAX_NUMNODES; i++)
+ register_one_node(i);
+#endif
+}
+
static int __init topology_init(void)
{
int cpu;
+ register_nodes();
+
check_mmu_stats();
register_cpu_notifier(&sysfs_cpu_nb);
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
index 96da847..d9b8d46 100644
--- a/arch/sparc64/kernel/traps.c
+++ b/arch/sparc64/kernel/traps.c
@@ -2091,9 +2091,8 @@
void show_stack(struct task_struct *tsk, unsigned long *_ksp)
{
- unsigned long pc, fp, thread_base, ksp;
+ unsigned long fp, thread_base, ksp;
struct thread_info *tp;
- struct reg_window *rw;
int count = 0;
ksp = (unsigned long) _ksp;
@@ -2117,15 +2116,27 @@
printk("\n");
#endif
do {
+ struct reg_window *rw;
+ struct pt_regs *regs;
+ unsigned long pc;
+
/* Bogus frame pointer? */
if (fp < (thread_base + sizeof(struct thread_info)) ||
fp >= (thread_base + THREAD_SIZE))
break;
rw = (struct reg_window *)fp;
- pc = rw->ins[7];
+ regs = (struct pt_regs *) (rw + 1);
+
+ if ((regs->magic & ~0x1ff) == PT_REGS_MAGIC) {
+ pc = regs->tpc;
+ fp = regs->u_regs[UREG_I6] + STACK_BIAS;
+ } else {
+ pc = rw->ins[7];
+ fp = rw->ins[6] + STACK_BIAS;
+ }
+
printk(" [%016lx] ", pc);
print_symbol("%s\n", pc);
- fp = rw->ins[6] + STACK_BIAS;
} while (++count < 16);
#ifndef CONFIG_KALLSYMS
printk("\n");
diff --git a/arch/sparc64/kernel/tsb.S b/arch/sparc64/kernel/tsb.S
index 10adb2f..c499214 100644
--- a/arch/sparc64/kernel/tsb.S
+++ b/arch/sparc64/kernel/tsb.S
@@ -275,7 +275,7 @@
stx %l5, [%g6 + TI_FAULT_ADDR] ! Save fault address
call do_sparc64_fault ! Call fault handler
add %sp, PTREGS_OFF, %o0 ! Compute pt_regs arg
- ba,pt %xcc, rtrap_clr_l6 ! Restore cpu state
+ ba,pt %xcc, rtrap ! Restore cpu state
nop ! Delay slot (fill me)
winfix_trampoline:
diff --git a/arch/sparc64/kernel/winfixup.S b/arch/sparc64/kernel/winfixup.S
index c4aa110..a6b0863 100644
--- a/arch/sparc64/kernel/winfixup.S
+++ b/arch/sparc64/kernel/winfixup.S
@@ -32,7 +32,7 @@
rd %pc, %g7
call do_sparc64_fault
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap_clr_l6
+ ba,pt %xcc, rtrap
nop
/* Be very careful about usage of the trap globals here.
@@ -100,7 +100,7 @@
rd %pc, %g7
call do_sparc64_fault
add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
winfix_mna:
andn %g3, 0x7f, %g3
@@ -122,12 +122,12 @@
mov %l4, %o2
call sun4v_do_mna
mov %l5, %o1
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
1: mov %l4, %o1
mov %l5, %o2
call mem_address_unaligned
nop
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
winfix_dax:
andn %g3, 0x7f, %g3
@@ -150,7 +150,7 @@
add %sp, PTREGS_OFF, %o0
call sun4v_data_access_exception
nop
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
1: call spitfire_data_access_exception
nop
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index f37078d..177d8aa 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -24,6 +24,8 @@
#include <linux/cache.h>
#include <linux/sort.h>
#include <linux/percpu.h>
+#include <linux/lmb.h>
+#include <linux/mmzone.h>
#include <asm/head.h>
#include <asm/system.h>
@@ -72,9 +74,7 @@
#define MAX_BANKS 32
static struct linux_prom64_registers pavail[MAX_BANKS] __initdata;
-static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
static int pavail_ents __initdata;
-static int pavail_rescan_ents __initdata;
static int cmp_p64(const void *a, const void *b)
{
@@ -715,285 +715,684 @@
smp_new_mmu_context_version();
}
-/* Find a free area for the bootmem map, avoiding the kernel image
- * and the initial ramdisk.
- */
-static unsigned long __init choose_bootmap_pfn(unsigned long start_pfn,
- unsigned long end_pfn)
+static int numa_enabled = 1;
+static int numa_debug;
+
+static int __init early_numa(char *p)
{
- unsigned long avoid_start, avoid_end, bootmap_size;
- int i;
+ if (!p)
+ return 0;
- bootmap_size = bootmem_bootmap_pages(end_pfn - start_pfn);
- bootmap_size <<= PAGE_SHIFT;
+ if (strstr(p, "off"))
+ numa_enabled = 0;
- avoid_start = avoid_end = 0;
+ if (strstr(p, "debug"))
+ numa_debug = 1;
+
+ return 0;
+}
+early_param("numa", early_numa);
+
+#define numadbg(f, a...) \
+do { if (numa_debug) \
+ printk(KERN_INFO f, ## a); \
+} while (0)
+
+static void __init find_ramdisk(unsigned long phys_base)
+{
#ifdef CONFIG_BLK_DEV_INITRD
- avoid_start = initrd_start;
- avoid_end = PAGE_ALIGN(initrd_end);
-#endif
+ if (sparc_ramdisk_image || sparc_ramdisk_image64) {
+ unsigned long ramdisk_image;
- for (i = 0; i < pavail_ents; i++) {
- unsigned long start, end;
+ /* Older versions of the bootloader only supported a
+ * 32-bit physical address for the ramdisk image
+ * location, stored at sparc_ramdisk_image. Newer
+ * SILO versions set sparc_ramdisk_image to zero and
+ * provide a full 64-bit physical address at
+ * sparc_ramdisk_image64.
+ */
+ ramdisk_image = sparc_ramdisk_image;
+ if (!ramdisk_image)
+ ramdisk_image = sparc_ramdisk_image64;
- start = pavail[i].phys_addr;
- end = start + pavail[i].reg_size;
+ /* Another bootloader quirk. The bootloader normalizes
+ * the physical address to KERNBASE, so we have to
+ * factor that back out and add in the lowest valid
+ * physical page address to get the true physical address.
+ */
+ ramdisk_image -= KERNBASE;
+ ramdisk_image += phys_base;
- while (start < end) {
- if (start >= kern_base &&
- start < PAGE_ALIGN(kern_base + kern_size)) {
- start = PAGE_ALIGN(kern_base + kern_size);
- continue;
- }
- if (start >= avoid_start && start < avoid_end) {
- start = avoid_end;
- continue;
- }
+ numadbg("Found ramdisk at physical address 0x%lx, size %u\n",
+ ramdisk_image, sparc_ramdisk_size);
- if ((end - start) < bootmap_size)
- break;
+ initrd_start = ramdisk_image;
+ initrd_end = ramdisk_image + sparc_ramdisk_size;
- if (start < kern_base &&
- (start + bootmap_size) > kern_base) {
- start = PAGE_ALIGN(kern_base + kern_size);
- continue;
- }
-
- if (start < avoid_start &&
- (start + bootmap_size) > avoid_start) {
- start = avoid_end;
- continue;
- }
-
- /* OK, it doesn't overlap anything, use it. */
- return start >> PAGE_SHIFT;
- }
+ lmb_reserve(initrd_start, initrd_end);
}
-
- prom_printf("Cannot find free area for bootmap, aborting.\n");
- prom_halt();
+#endif
}
-static void __init trim_pavail(unsigned long *cur_size_p,
- unsigned long *end_of_phys_p)
+struct node_mem_mask {
+ unsigned long mask;
+ unsigned long val;
+ unsigned long bootmem_paddr;
+};
+static struct node_mem_mask node_masks[MAX_NUMNODES];
+static int num_node_masks;
+
+int numa_cpu_lookup_table[NR_CPUS];
+cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES];
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+static bootmem_data_t plat_node_bdata[MAX_NUMNODES];
+
+struct mdesc_mblock {
+ u64 base;
+ u64 size;
+ u64 offset; /* RA-to-PA */
+};
+static struct mdesc_mblock *mblocks;
+static int num_mblocks;
+
+static unsigned long ra_to_pa(unsigned long addr)
{
- unsigned long to_trim = *cur_size_p - cmdline_memory_size;
- unsigned long avoid_start, avoid_end;
int i;
- to_trim = PAGE_ALIGN(to_trim);
+ for (i = 0; i < num_mblocks; i++) {
+ struct mdesc_mblock *m = &mblocks[i];
- avoid_start = avoid_end = 0;
-#ifdef CONFIG_BLK_DEV_INITRD
- avoid_start = initrd_start;
- avoid_end = PAGE_ALIGN(initrd_end);
+ if (addr >= m->base &&
+ addr < (m->base + m->size)) {
+ addr += m->offset;
+ break;
+ }
+ }
+ return addr;
+}
+
+static int find_node(unsigned long addr)
+{
+ int i;
+
+ addr = ra_to_pa(addr);
+ for (i = 0; i < num_node_masks; i++) {
+ struct node_mem_mask *p = &node_masks[i];
+
+ if ((addr & p->mask) == p->val)
+ return i;
+ }
+ return -1;
+}
+
+static unsigned long nid_range(unsigned long start, unsigned long end,
+ int *nid)
+{
+ *nid = find_node(start);
+ start += PAGE_SIZE;
+ while (start < end) {
+ int n = find_node(start);
+
+ if (n != *nid)
+ break;
+ start += PAGE_SIZE;
+ }
+
+ return start;
+}
+#else
+static unsigned long nid_range(unsigned long start, unsigned long end,
+ int *nid)
+{
+ *nid = 0;
+ return end;
+}
#endif
- /* Trim some pavail[] entries in order to satisfy the
- * requested "mem=xxx" kernel command line specification.
- *
- * We must not trim off the kernel image area nor the
- * initial ramdisk range (if any). Also, we must not trim
- * any pavail[] entry down to zero in order to preserve
- * the invariant that all pavail[] entries have a non-zero
- * size which is assumed by all of the code in here.
- */
- for (i = 0; i < pavail_ents; i++) {
- unsigned long start, end, kern_end;
- unsigned long trim_low, trim_high, n;
+/* This must be invoked after performing all of the necessary
+ * add_active_range() calls for 'nid'. We need to be able to get
+ * correct data from get_pfn_range_for_nid().
+ */
+static void __init allocate_node_data(int nid)
+{
+ unsigned long paddr, num_pages, start_pfn, end_pfn;
+ struct pglist_data *p;
- kern_end = PAGE_ALIGN(kern_base + kern_size);
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+ paddr = lmb_alloc_nid(sizeof(struct pglist_data),
+ SMP_CACHE_BYTES, nid, nid_range);
+ if (!paddr) {
+ prom_printf("Cannot allocate pglist_data for nid[%d]\n", nid);
+ prom_halt();
+ }
+ NODE_DATA(nid) = __va(paddr);
+ memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));
- trim_low = start = pavail[i].phys_addr;
- trim_high = end = start + pavail[i].reg_size;
+ NODE_DATA(nid)->bdata = &plat_node_bdata[nid];
+#endif
- if (kern_base >= start &&
- kern_base < end) {
- trim_low = kern_base;
- if (kern_end >= end)
- continue;
- }
- if (kern_end >= start &&
- kern_end < end) {
- trim_high = kern_end;
- }
- if (avoid_start &&
- avoid_start >= start &&
- avoid_start < end) {
- if (trim_low > avoid_start)
- trim_low = avoid_start;
- if (avoid_end >= end)
- continue;
- }
- if (avoid_end &&
- avoid_end >= start &&
- avoid_end < end) {
- if (trim_high < avoid_end)
- trim_high = avoid_end;
- }
+ p = NODE_DATA(nid);
- if (trim_high <= trim_low)
+ get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
+ p->node_start_pfn = start_pfn;
+ p->node_spanned_pages = end_pfn - start_pfn;
+
+ if (p->node_spanned_pages) {
+ num_pages = bootmem_bootmap_pages(p->node_spanned_pages);
+
+ paddr = lmb_alloc_nid(num_pages << PAGE_SHIFT, PAGE_SIZE, nid,
+ nid_range);
+ if (!paddr) {
+ prom_printf("Cannot allocate bootmap for nid[%d]\n",
+ nid);
+ prom_halt();
+ }
+ node_masks[nid].bootmem_paddr = paddr;
+ }
+}
+
+static void init_node_masks_nonnuma(void)
+{
+ int i;
+
+ numadbg("Initializing tables for non-numa.\n");
+
+ node_masks[0].mask = node_masks[0].val = 0;
+ num_node_masks = 1;
+
+ for (i = 0; i < NR_CPUS; i++)
+ numa_cpu_lookup_table[i] = 0;
+
+ numa_cpumask_lookup_table[0] = CPU_MASK_ALL;
+}
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+struct pglist_data *node_data[MAX_NUMNODES];
+
+EXPORT_SYMBOL(numa_cpu_lookup_table);
+EXPORT_SYMBOL(numa_cpumask_lookup_table);
+EXPORT_SYMBOL(node_data);
+
+struct mdesc_mlgroup {
+ u64 node;
+ u64 latency;
+ u64 match;
+ u64 mask;
+};
+static struct mdesc_mlgroup *mlgroups;
+static int num_mlgroups;
+
+static int scan_pio_for_cfg_handle(struct mdesc_handle *md, u64 pio,
+ u32 cfg_handle)
+{
+ u64 arc;
+
+ mdesc_for_each_arc(arc, md, pio, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(md, arc);
+ const u64 *val;
+
+ val = mdesc_get_property(md, target,
+ "cfg-handle", NULL);
+ if (val && *val == cfg_handle)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int scan_arcs_for_cfg_handle(struct mdesc_handle *md, u64 grp,
+ u32 cfg_handle)
+{
+ u64 arc, candidate, best_latency = ~(u64)0;
+
+ candidate = MDESC_NODE_NULL;
+ mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(md, arc);
+ const char *name = mdesc_node_name(md, target);
+ const u64 *val;
+
+ if (strcmp(name, "pio-latency-group"))
continue;
- if (trim_low == start && trim_high == end) {
- /* Whole chunk is available for trimming.
- * Trim all except one page, in order to keep
- * entry non-empty.
- */
- n = (end - start) - PAGE_SIZE;
- if (n > to_trim)
- n = to_trim;
+ val = mdesc_get_property(md, target, "latency", NULL);
+ if (!val)
+ continue;
- if (n) {
- pavail[i].phys_addr += n;
- pavail[i].reg_size -= n;
- to_trim -= n;
- }
- } else {
- n = (trim_low - start);
- if (n > to_trim)
- n = to_trim;
-
- if (n) {
- pavail[i].phys_addr += n;
- pavail[i].reg_size -= n;
- to_trim -= n;
- }
- if (to_trim) {
- n = end - trim_high;
- if (n > to_trim)
- n = to_trim;
- if (n) {
- pavail[i].reg_size -= n;
- to_trim -= n;
- }
- }
+ if (*val < best_latency) {
+ candidate = target;
+ best_latency = *val;
}
-
- if (!to_trim)
- break;
}
- /* Recalculate. */
- *cur_size_p = 0UL;
- for (i = 0; i < pavail_ents; i++) {
- *end_of_phys_p = pavail[i].phys_addr +
- pavail[i].reg_size;
- *cur_size_p += pavail[i].reg_size;
+ if (candidate == MDESC_NODE_NULL)
+ return -ENODEV;
+
+ return scan_pio_for_cfg_handle(md, candidate, cfg_handle);
+}
+
+int of_node_to_nid(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+ struct mdesc_handle *md;
+ u32 cfg_handle;
+ int count, nid;
+ u64 grp;
+
+ if (!mlgroups)
+ return -1;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (!regs)
+ return -1;
+
+ cfg_handle = (regs->phys_addr >> 32UL) & 0x0fffffff;
+
+ md = mdesc_grab();
+
+ count = 0;
+ nid = -1;
+ mdesc_for_each_node_by_name(md, grp, "group") {
+ if (!scan_arcs_for_cfg_handle(md, grp, cfg_handle)) {
+ nid = count;
+ break;
+ }
+ count++;
+ }
+
+ mdesc_release(md);
+
+ return nid;
+}
+
+static void add_node_ranges(void)
+{
+ int i;
+
+ for (i = 0; i < lmb.memory.cnt; i++) {
+ unsigned long size = lmb_size_bytes(&lmb.memory, i);
+ unsigned long start, end;
+
+ start = lmb.memory.region[i].base;
+ end = start + size;
+ while (start < end) {
+ unsigned long this_end;
+ int nid;
+
+ this_end = nid_range(start, end, &nid);
+
+ numadbg("Adding active range nid[%d] "
+ "start[%lx] end[%lx]\n",
+ nid, start, this_end);
+
+ add_active_range(nid,
+ start >> PAGE_SHIFT,
+ this_end >> PAGE_SHIFT);
+
+ start = this_end;
+ }
}
}
-/* About pages_avail, this is the value we will use to calculate
- * the zholes_size[] argument given to free_area_init_node(). The
- * page allocator uses this to calculate nr_kernel_pages,
- * nr_all_pages and zone->present_pages. On NUMA it is used
- * to calculate zone->min_unmapped_pages and zone->min_slab_pages.
- *
- * So this number should really be set to what the page allocator
- * actually ends up with. This means:
- * 1) It should include bootmem map pages, we'll release those.
- * 2) It should not include the kernel image, except for the
- * __init sections which we will also release.
- * 3) It should include the initrd image, since we'll release
- * that too.
- */
-static unsigned long __init bootmem_init(unsigned long *pages_avail,
- unsigned long phys_base)
+static int __init grab_mlgroups(struct mdesc_handle *md)
{
- unsigned long bootmap_size, end_pfn;
- unsigned long end_of_phys_memory = 0UL;
- unsigned long bootmap_pfn, bytes_avail, size;
+ unsigned long paddr;
+ int count = 0;
+ u64 node;
+
+ mdesc_for_each_node_by_name(md, node, "memory-latency-group")
+ count++;
+ if (!count)
+ return -ENOENT;
+
+ paddr = lmb_alloc(count * sizeof(struct mdesc_mlgroup),
+ SMP_CACHE_BYTES);
+ if (!paddr)
+ return -ENOMEM;
+
+ mlgroups = __va(paddr);
+ num_mlgroups = count;
+
+ count = 0;
+ mdesc_for_each_node_by_name(md, node, "memory-latency-group") {
+ struct mdesc_mlgroup *m = &mlgroups[count++];
+ const u64 *val;
+
+ m->node = node;
+
+ val = mdesc_get_property(md, node, "latency", NULL);
+ m->latency = *val;
+ val = mdesc_get_property(md, node, "address-match", NULL);
+ m->match = *val;
+ val = mdesc_get_property(md, node, "address-mask", NULL);
+ m->mask = *val;
+
+ numadbg("MLGROUP[%d]: node[%lx] latency[%lx] "
+ "match[%lx] mask[%lx]\n",
+ count - 1, m->node, m->latency, m->match, m->mask);
+ }
+
+ return 0;
+}
+
+static int __init grab_mblocks(struct mdesc_handle *md)
+{
+ unsigned long paddr;
+ int count = 0;
+ u64 node;
+
+ mdesc_for_each_node_by_name(md, node, "mblock")
+ count++;
+ if (!count)
+ return -ENOENT;
+
+ paddr = lmb_alloc(count * sizeof(struct mdesc_mblock),
+ SMP_CACHE_BYTES);
+ if (!paddr)
+ return -ENOMEM;
+
+ mblocks = __va(paddr);
+ num_mblocks = count;
+
+ count = 0;
+ mdesc_for_each_node_by_name(md, node, "mblock") {
+ struct mdesc_mblock *m = &mblocks[count++];
+ const u64 *val;
+
+ val = mdesc_get_property(md, node, "base", NULL);
+ m->base = *val;
+ val = mdesc_get_property(md, node, "size", NULL);
+ m->size = *val;
+ val = mdesc_get_property(md, node,
+ "address-congruence-offset", NULL);
+ m->offset = *val;
+
+ numadbg("MBLOCK[%d]: base[%lx] size[%lx] offset[%lx]\n",
+ count - 1, m->base, m->size, m->offset);
+ }
+
+ return 0;
+}
+
+static void __init numa_parse_mdesc_group_cpus(struct mdesc_handle *md,
+ u64 grp, cpumask_t *mask)
+{
+ u64 arc;
+
+ cpus_clear(*mask);
+
+ mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_BACK) {
+ u64 target = mdesc_arc_target(md, arc);
+ const char *name = mdesc_node_name(md, target);
+ const u64 *id;
+
+ if (strcmp(name, "cpu"))
+ continue;
+ id = mdesc_get_property(md, target, "id", NULL);
+ if (*id < NR_CPUS)
+ cpu_set(*id, *mask);
+ }
+}
+
+static struct mdesc_mlgroup * __init find_mlgroup(u64 node)
+{
int i;
- bytes_avail = 0UL;
- for (i = 0; i < pavail_ents; i++) {
- end_of_phys_memory = pavail[i].phys_addr +
- pavail[i].reg_size;
- bytes_avail += pavail[i].reg_size;
+ for (i = 0; i < num_mlgroups; i++) {
+ struct mdesc_mlgroup *m = &mlgroups[i];
+ if (m->node == node)
+ return m;
}
+ return NULL;
+}
- /* Determine the location of the initial ramdisk before trying
- * to honor the "mem=xxx" command line argument. We must know
- * where the kernel image and the ramdisk image are so that we
- * do not trim those two areas from the physical memory map.
- */
+static int __init numa_attach_mlgroup(struct mdesc_handle *md, u64 grp,
+ int index)
+{
+ struct mdesc_mlgroup *candidate = NULL;
+ u64 arc, best_latency = ~(u64)0;
+ struct node_mem_mask *n;
-#ifdef CONFIG_BLK_DEV_INITRD
- /* Now have to check initial ramdisk, so that bootmap does not overwrite it */
- if (sparc_ramdisk_image || sparc_ramdisk_image64) {
- unsigned long ramdisk_image = sparc_ramdisk_image ?
- sparc_ramdisk_image : sparc_ramdisk_image64;
- ramdisk_image -= KERNBASE;
- initrd_start = ramdisk_image + phys_base;
- initrd_end = initrd_start + sparc_ramdisk_size;
- if (initrd_end > end_of_phys_memory) {
- printk(KERN_CRIT "initrd extends beyond end of memory "
- "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
- initrd_end, end_of_phys_memory);
- initrd_start = 0;
- initrd_end = 0;
+ mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(md, arc);
+ struct mdesc_mlgroup *m = find_mlgroup(target);
+ if (!m)
+ continue;
+ if (m->latency < best_latency) {
+ candidate = m;
+ best_latency = m->latency;
}
}
-#endif
+ if (!candidate)
+ return -ENOENT;
- if (cmdline_memory_size &&
- bytes_avail > cmdline_memory_size)
- trim_pavail(&bytes_avail,
- &end_of_phys_memory);
+ if (num_node_masks != index) {
+ printk(KERN_ERR "Inconsistent NUMA state, "
+ "index[%d] != num_node_masks[%d]\n",
+ index, num_node_masks);
+ return -EINVAL;
+ }
- *pages_avail = bytes_avail >> PAGE_SHIFT;
+ n = &node_masks[num_node_masks++];
- end_pfn = end_of_phys_memory >> PAGE_SHIFT;
+ n->mask = candidate->mask;
+ n->val = candidate->match;
- /* Initialize the boot-time allocator. */
+ numadbg("NUMA NODE[%d]: mask[%lx] val[%lx] (latency[%lx])\n",
+ index, n->mask, n->val, candidate->latency);
+
+ return 0;
+}
+
+static int __init numa_parse_mdesc_group(struct mdesc_handle *md, u64 grp,
+ int index)
+{
+ cpumask_t mask;
+ int cpu;
+
+ numa_parse_mdesc_group_cpus(md, grp, &mask);
+
+ for_each_cpu_mask(cpu, mask)
+ numa_cpu_lookup_table[cpu] = index;
+ numa_cpumask_lookup_table[index] = mask;
+
+ if (numa_debug) {
+ printk(KERN_INFO "NUMA GROUP[%d]: cpus [ ", index);
+ for_each_cpu_mask(cpu, mask)
+ printk("%d ", cpu);
+ printk("]\n");
+ }
+
+ return numa_attach_mlgroup(md, grp, index);
+}
+
+static int __init numa_parse_mdesc(void)
+{
+ struct mdesc_handle *md = mdesc_grab();
+ int i, err, count;
+ u64 node;
+
+ node = mdesc_node_by_name(md, MDESC_NODE_NULL, "latency-groups");
+ if (node == MDESC_NODE_NULL) {
+ mdesc_release(md);
+ return -ENOENT;
+ }
+
+ err = grab_mblocks(md);
+ if (err < 0)
+ goto out;
+
+ err = grab_mlgroups(md);
+ if (err < 0)
+ goto out;
+
+ count = 0;
+ mdesc_for_each_node_by_name(md, node, "group") {
+ err = numa_parse_mdesc_group(md, node, count);
+ if (err < 0)
+ break;
+ count++;
+ }
+
+ add_node_ranges();
+
+ for (i = 0; i < num_node_masks; i++) {
+ allocate_node_data(i);
+ node_set_online(i);
+ }
+
+ err = 0;
+out:
+ mdesc_release(md);
+ return err;
+}
+
+static int __init numa_parse_sun4u(void)
+{
+ return -1;
+}
+
+static int __init bootmem_init_numa(void)
+{
+ int err = -1;
+
+ numadbg("bootmem_init_numa()\n");
+
+ if (numa_enabled) {
+ if (tlb_type == hypervisor)
+ err = numa_parse_mdesc();
+ else
+ err = numa_parse_sun4u();
+ }
+ return err;
+}
+
+#else
+
+static int bootmem_init_numa(void)
+{
+ return -1;
+}
+
+#endif
+
+static void __init bootmem_init_nonnuma(void)
+{
+ unsigned long top_of_ram = lmb_end_of_DRAM();
+ unsigned long total_ram = lmb_phys_mem_size();
+ unsigned int i;
+
+ numadbg("bootmem_init_nonnuma()\n");
+
+ printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
+ top_of_ram, total_ram);
+ printk(KERN_INFO "Memory hole size: %ldMB\n",
+ (top_of_ram - total_ram) >> 20);
+
+ init_node_masks_nonnuma();
+
+ for (i = 0; i < lmb.memory.cnt; i++) {
+ unsigned long size = lmb_size_bytes(&lmb.memory, i);
+ unsigned long start_pfn, end_pfn;
+
+ if (!size)
+ continue;
+
+ start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT;
+ end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i);
+ add_active_range(0, start_pfn, end_pfn);
+ }
+
+ allocate_node_data(0);
+
+ node_set_online(0);
+}
+
+static void __init reserve_range_in_node(int nid, unsigned long start,
+ unsigned long end)
+{
+ numadbg(" reserve_range_in_node(nid[%d],start[%lx],end[%lx]\n",
+ nid, start, end);
+ while (start < end) {
+ unsigned long this_end;
+ int n;
+
+ this_end = nid_range(start, end, &n);
+ if (n == nid) {
+ numadbg(" MATCH reserving range [%lx:%lx]\n",
+ start, this_end);
+ reserve_bootmem_node(NODE_DATA(nid), start,
+ (this_end - start), BOOTMEM_DEFAULT);
+ } else
+ numadbg(" NO MATCH, advancing start to %lx\n",
+ this_end);
+
+ start = this_end;
+ }
+}
+
+static void __init trim_reserved_in_node(int nid)
+{
+ int i;
+
+ numadbg(" trim_reserved_in_node(%d)\n", nid);
+
+ for (i = 0; i < lmb.reserved.cnt; i++) {
+ unsigned long start = lmb.reserved.region[i].base;
+ unsigned long size = lmb_size_bytes(&lmb.reserved, i);
+ unsigned long end = start + size;
+
+ reserve_range_in_node(nid, start, end);
+ }
+}
+
+static void __init bootmem_init_one_node(int nid)
+{
+ struct pglist_data *p;
+
+ numadbg("bootmem_init_one_node(%d)\n", nid);
+
+ p = NODE_DATA(nid);
+
+ if (p->node_spanned_pages) {
+ unsigned long paddr = node_masks[nid].bootmem_paddr;
+ unsigned long end_pfn;
+
+ end_pfn = p->node_start_pfn + p->node_spanned_pages;
+
+ numadbg(" init_bootmem_node(%d, %lx, %lx, %lx)\n",
+ nid, paddr >> PAGE_SHIFT, p->node_start_pfn, end_pfn);
+
+ init_bootmem_node(p, paddr >> PAGE_SHIFT,
+ p->node_start_pfn, end_pfn);
+
+ numadbg(" free_bootmem_with_active_regions(%d, %lx)\n",
+ nid, end_pfn);
+ free_bootmem_with_active_regions(nid, end_pfn);
+
+ trim_reserved_in_node(nid);
+
+ numadbg(" sparse_memory_present_with_active_regions(%d)\n",
+ nid);
+ sparse_memory_present_with_active_regions(nid);
+ }
+}
+
+static unsigned long __init bootmem_init(unsigned long phys_base)
+{
+ unsigned long end_pfn;
+ int nid;
+
+ end_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
max_pfn = max_low_pfn = end_pfn;
min_low_pfn = (phys_base >> PAGE_SHIFT);
- bootmap_pfn = choose_bootmap_pfn(min_low_pfn, end_pfn);
+ if (bootmem_init_numa() < 0)
+ bootmem_init_nonnuma();
- bootmap_size = init_bootmem_node(NODE_DATA(0), bootmap_pfn,
- min_low_pfn, end_pfn);
+ /* XXX cpu notifier XXX */
- /* Now register the available physical memory with the
- * allocator.
- */
- for (i = 0; i < pavail_ents; i++)
- free_bootmem(pavail[i].phys_addr, pavail[i].reg_size);
-
-#ifdef CONFIG_BLK_DEV_INITRD
- if (initrd_start) {
- size = initrd_end - initrd_start;
-
- /* Reserve the initrd image area. */
- reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT);
-
- initrd_start += PAGE_OFFSET;
- initrd_end += PAGE_OFFSET;
- }
-#endif
- /* Reserve the kernel text/data/bss. */
- reserve_bootmem(kern_base, kern_size, BOOTMEM_DEFAULT);
- *pages_avail -= PAGE_ALIGN(kern_size) >> PAGE_SHIFT;
-
- /* Add back in the initmem pages. */
- size = ((unsigned long)(__init_end) & PAGE_MASK) -
- PAGE_ALIGN((unsigned long)__init_begin);
- *pages_avail += size >> PAGE_SHIFT;
-
- /* Reserve the bootmem map. We do not account for it
- * in pages_avail because we will release that memory
- * in free_all_bootmem.
- */
- size = bootmap_size;
- reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size, BOOTMEM_DEFAULT);
-
- for (i = 0; i < pavail_ents; i++) {
- unsigned long start_pfn, end_pfn;
-
- start_pfn = pavail[i].phys_addr >> PAGE_SHIFT;
- end_pfn = (start_pfn + (pavail[i].reg_size >> PAGE_SHIFT));
- memory_present(0, start_pfn, end_pfn);
- }
+ for_each_online_node(nid)
+ bootmem_init_one_node(nid);
sparse_init();
@@ -1289,7 +1688,7 @@
void __init paging_init(void)
{
- unsigned long end_pfn, pages_avail, shift, phys_base;
+ unsigned long end_pfn, shift, phys_base;
unsigned long real_end, i;
/* These build time checkes make sure that the dcache_dirty_cpu()
@@ -1330,12 +1729,26 @@
sun4v_ktsb_init();
}
+ lmb_init();
+
/* Find available physical memory... */
read_obp_memory("available", &pavail[0], &pavail_ents);
phys_base = 0xffffffffffffffffUL;
- for (i = 0; i < pavail_ents; i++)
+ for (i = 0; i < pavail_ents; i++) {
phys_base = min(phys_base, pavail[i].phys_addr);
+ lmb_add(pavail[i].phys_addr, pavail[i].reg_size);
+ }
+
+ lmb_reserve(kern_base, kern_size);
+
+ find_ramdisk(phys_base);
+
+ if (cmdline_memory_size)
+ lmb_enforce_memory_limit(phys_base + cmdline_memory_size);
+
+ lmb_analyze();
+ lmb_dump_all();
set_bit(0, mmu_context_bmap);
@@ -1371,14 +1784,10 @@
if (tlb_type == hypervisor)
sun4v_ktsb_register();
- /* Setup bootmem... */
- pages_avail = 0;
- last_valid_pfn = end_pfn = bootmem_init(&pages_avail, phys_base);
-
- max_mapnr = last_valid_pfn;
-
- kernel_physical_mapping_init();
-
+ /* We must setup the per-cpu areas before we pull in the
+ * PROM and the MDESC. The code there fills in cpu and
+ * other information into per-cpu data structures.
+ */
real_setup_per_cpu_areas();
prom_build_devicetree();
@@ -1386,20 +1795,22 @@
if (tlb_type == hypervisor)
sun4v_mdesc_init();
+ /* Setup bootmem... */
+ last_valid_pfn = end_pfn = bootmem_init(phys_base);
+
+#ifndef CONFIG_NEED_MULTIPLE_NODES
+ max_mapnr = last_valid_pfn;
+#endif
+ kernel_physical_mapping_init();
+
{
- unsigned long zones_size[MAX_NR_ZONES];
- unsigned long zholes_size[MAX_NR_ZONES];
- int znum;
+ unsigned long max_zone_pfns[MAX_NR_ZONES];
- for (znum = 0; znum < MAX_NR_ZONES; znum++)
- zones_size[znum] = zholes_size[znum] = 0;
+ memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
- zones_size[ZONE_NORMAL] = end_pfn;
- zholes_size[ZONE_NORMAL] = end_pfn - pages_avail;
+ max_zone_pfns[ZONE_NORMAL] = end_pfn;
- free_area_init_node(0, &contig_page_data, zones_size,
- __pa(PAGE_OFFSET) >> PAGE_SHIFT,
- zholes_size);
+ free_area_init_nodes(max_zone_pfns);
}
printk("Booting Linux...\n");
@@ -1408,21 +1819,52 @@
cpu_probe();
}
-static void __init taint_real_pages(void)
+int __init page_in_phys_avail(unsigned long paddr)
+{
+ int i;
+
+ paddr &= PAGE_MASK;
+
+ for (i = 0; i < pavail_ents; i++) {
+ unsigned long start, end;
+
+ start = pavail[i].phys_addr;
+ end = start + pavail[i].reg_size;
+
+ if (paddr >= start && paddr < end)
+ return 1;
+ }
+ if (paddr >= kern_base && paddr < (kern_base + kern_size))
+ return 1;
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (paddr >= __pa(initrd_start) &&
+ paddr < __pa(PAGE_ALIGN(initrd_end)))
+ return 1;
+#endif
+
+ return 0;
+}
+
+static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
+static int pavail_rescan_ents __initdata;
+
+/* Certain OBP calls, such as fetching "available" properties, can
+ * claim physical memory. So, along with initializing the valid
+ * address bitmap, what we do here is refetch the physical available
+ * memory list again, and make sure it provides at least as much
+ * memory as 'pavail' does.
+ */
+static void setup_valid_addr_bitmap_from_pavail(void)
{
int i;
read_obp_memory("available", &pavail_rescan[0], &pavail_rescan_ents);
- /* Find changes discovered in the physmem available rescan and
- * reserve the lost portions in the bootmem maps.
- */
for (i = 0; i < pavail_ents; i++) {
unsigned long old_start, old_end;
old_start = pavail[i].phys_addr;
- old_end = old_start +
- pavail[i].reg_size;
+ old_end = old_start + pavail[i].reg_size;
while (old_start < old_end) {
int n;
@@ -1440,7 +1882,16 @@
goto do_next_page;
}
}
- reserve_bootmem(old_start, PAGE_SIZE, BOOTMEM_DEFAULT);
+
+ prom_printf("mem_init: Lost memory in pavail\n");
+ prom_printf("mem_init: OLD start[%lx] size[%lx]\n",
+ pavail[i].phys_addr,
+ pavail[i].reg_size);
+ prom_printf("mem_init: NEW start[%lx] size[%lx]\n",
+ pavail_rescan[i].phys_addr,
+ pavail_rescan[i].reg_size);
+ prom_printf("mem_init: Cannot continue, aborting.\n");
+ prom_halt();
do_next_page:
old_start += PAGE_SIZE;
@@ -1448,32 +1899,6 @@
}
}
-int __init page_in_phys_avail(unsigned long paddr)
-{
- int i;
-
- paddr &= PAGE_MASK;
-
- for (i = 0; i < pavail_rescan_ents; i++) {
- unsigned long start, end;
-
- start = pavail_rescan[i].phys_addr;
- end = start + pavail_rescan[i].reg_size;
-
- if (paddr >= start && paddr < end)
- return 1;
- }
- if (paddr >= kern_base && paddr < (kern_base + kern_size))
- return 1;
-#ifdef CONFIG_BLK_DEV_INITRD
- if (paddr >= __pa(initrd_start) &&
- paddr < __pa(PAGE_ALIGN(initrd_end)))
- return 1;
-#endif
-
- return 0;
-}
-
void __init mem_init(void)
{
unsigned long codepages, datapages, initpages;
@@ -1496,14 +1921,26 @@
addr += PAGE_SIZE;
}
- taint_real_pages();
+ setup_valid_addr_bitmap_from_pavail();
high_memory = __va(last_valid_pfn << PAGE_SHIFT);
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+ for_each_online_node(i) {
+ if (NODE_DATA(i)->node_spanned_pages != 0) {
+ totalram_pages +=
+ free_all_bootmem_node(NODE_DATA(i));
+ }
+ }
+#else
+ totalram_pages = free_all_bootmem();
+#endif
+
/* We subtract one to account for the mem_map_zero page
* allocated below.
*/
- totalram_pages = num_physpages = free_all_bootmem() - 1;
+ totalram_pages -= 1;
+ num_physpages = totalram_pages;
/*
* Set up the zero page, mark it reserved, so that page count
diff --git a/arch/sparc64/mm/tsb.c b/arch/sparc64/mm/tsb.c
index a3e6e4b..fe70c8a 100644
--- a/arch/sparc64/mm/tsb.c
+++ b/arch/sparc64/mm/tsb.c
@@ -321,7 +321,8 @@
if (new_size > (PAGE_SIZE * 2))
gfp_flags = __GFP_NOWARN | __GFP_NORETRY;
- new_tsb = kmem_cache_alloc(tsb_caches[new_cache_index], gfp_flags);
+ new_tsb = kmem_cache_alloc_node(tsb_caches[new_cache_index],
+ gfp_flags, numa_node_id());
if (unlikely(!new_tsb)) {
/* Not being able to fork due to a high-order TSB
* allocation failure is very bad behavior. Just back
diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S
index 2865c10..e686a67 100644
--- a/arch/sparc64/mm/ultra.S
+++ b/arch/sparc64/mm/ultra.S
@@ -476,7 +476,6 @@
#endif
call smp_synchronize_tick_client
nop
- clr %l6
b rtrap_xcall
ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
@@ -511,7 +510,6 @@
#endif
call __show_regs
add %sp, PTREGS_OFF, %o0
- clr %l6
/* Has to be a non-v9 branch due to the large distance. */
b rtrap_xcall
ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
@@ -576,7 +574,7 @@
mov %l4, %o0
call hypervisor_tlbop_error_xcall
mov %l5, %o1
- ba,a,pt %xcc, rtrap_clr_l6
+ ba,a,pt %xcc, rtrap
.globl __hypervisor_xcall_flush_tlb_mm
__hypervisor_xcall_flush_tlb_mm: /* 21 insns */
diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c
index 37fe80d..f282976 100644
--- a/drivers/char/mwave/tp3780i.c
+++ b/drivers/char/mwave/tp3780i.c
@@ -97,24 +97,20 @@
static irqreturn_t UartInterrupt(int irq, void *dev_id)
{
- int irqno = (int)(unsigned long) dev_id;
-
PRINTK_3(TRACE_TP3780I,
- "tp3780i::UartInterrupt entry irq %x dev_id %p\n", irqno, dev_id);
+ "tp3780i::UartInterrupt entry irq %x dev_id %p\n", irq, dev_id);
return IRQ_HANDLED;
}
static irqreturn_t DspInterrupt(int irq, void *dev_id)
{
- int irqno = (int)(unsigned long) dev_id;
-
pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
DSP_3780I_CONFIG_SETTINGS *pSettings = &pDrvData->rBDData.rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
unsigned short usIPCSource = 0, usIsolationMask, usPCNum;
PRINTK_3(TRACE_TP3780I,
- "tp3780i::DspInterrupt entry irq %x dev_id %p\n", irqno, dev_id);
+ "tp3780i::DspInterrupt entry irq %x dev_id %p\n", irq, dev_id);
if (dsp3780I_GetIPCSource(usDspBaseIO, &usIPCSource) == 0) {
PRINTK_2(TRACE_TP3780I,
@@ -365,16 +361,14 @@
pSettings->bPllBypass = TP_CFG_PllBypass;
pSettings->usChipletEnable = TP_CFG_ChipletEnable;
- if (request_irq(pSettings->usUartIrq, &UartInterrupt, 0, "mwave_uart",
- (void *)(unsigned long) pSettings->usUartIrq)) {
+ if (request_irq(pSettings->usUartIrq, &UartInterrupt, 0, "mwave_uart", NULL)) {
PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: Could not get UART IRQ %x\n", pSettings->usUartIrq);
goto exit_cleanup;
} else { /* no conflict just release */
free_irq(pSettings->usUartIrq, NULL);
}
- if (request_irq(pSettings->usDspIrq, &DspInterrupt, 0, "mwave_3780i",
- (void *)(unsigned long) pSettings->usDspIrq)) {
+ if (request_irq(pSettings->usDspIrq, &DspInterrupt, 0, "mwave_3780i", NULL)) {
PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Error: Could not get 3780i IRQ %x\n", pSettings->usDspIrq);
goto exit_cleanup;
} else {
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c5263d6..92b6834 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -15,6 +15,7 @@
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on ALPHA || X86 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES
+ depends on SND_PCSP=n
help
Say Y here if you want the standard PC Speaker to be used for
bells and whistles.
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 316b106..03feb5b 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -690,10 +690,8 @@
static int snd_cx88_free(snd_cx88_card_t *chip)
{
- if (chip->irq >= 0){
- synchronize_irq(chip->irq);
+ if (chip->irq >= 0)
free_irq(chip->irq, chip);
- }
cx88_core_put(chip->core,chip->pci);
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index 047add8..ec6bdb9 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -954,10 +954,8 @@
if (chip->dev->dmasound.priv_data == NULL)
return;
- if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
+ if (chip->irq >= 0)
free_irq(chip->irq, &chip->dev->dmasound);
- }
chip->dev->dmasound.priv_data = NULL;
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
index af66f4f..4edc120 100644
--- a/drivers/mfd/htc-pasic3.c
+++ b/drivers/mfd/htc-pasic3.c
@@ -19,8 +19,6 @@
#include <linux/interrupt.h>
#include <linux/mfd/htc-pasic3.h>
-#include <asm/arch/pxa-regs.h>
-
struct pasic3_data {
void __iomem *mapping;
unsigned int bus_shift;
@@ -30,7 +28,6 @@
#define REG_ADDR 5
#define REG_DATA 6
-#define NUM_REGS 7
#define READ_MODE 0x80
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 4dc5b4b..d3207c0 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -123,7 +123,6 @@
u32 minseq; /* MP: min of most recent seqnos */
struct sk_buff_head mrq; /* MP: receive reconstruction queue */
#endif /* CONFIG_PPP_MULTILINK */
- struct net_device_stats stats; /* statistics */
#ifdef CONFIG_PPP_FILTER
struct sock_filter *pass_filter; /* filter for packets to pass */
struct sock_filter *active_filter;/* filter for pkts to reset idle */
@@ -914,18 +913,10 @@
outf:
kfree_skb(skb);
- ++ppp->stats.tx_dropped;
+ ++ppp->dev->stats.tx_dropped;
return 0;
}
-static struct net_device_stats *
-ppp_net_stats(struct net_device *dev)
-{
- struct ppp *ppp = (struct ppp *) dev->priv;
-
- return &ppp->stats;
-}
-
static int
ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
@@ -1095,8 +1086,8 @@
#endif /* CONFIG_PPP_FILTER */
}
- ++ppp->stats.tx_packets;
- ppp->stats.tx_bytes += skb->len - 2;
+ ++ppp->dev->stats.tx_packets;
+ ppp->dev->stats.tx_bytes += skb->len - 2;
switch (proto) {
case PPP_IP:
@@ -1171,7 +1162,7 @@
drop:
if (skb)
kfree_skb(skb);
- ++ppp->stats.tx_errors;
+ ++ppp->dev->stats.tx_errors;
}
/*
@@ -1409,7 +1400,7 @@
spin_unlock_bh(&pch->downl);
if (ppp->debug & 1)
printk(KERN_ERR "PPP: no memory (fragment)\n");
- ++ppp->stats.tx_errors;
+ ++ppp->dev->stats.tx_errors;
++ppp->nxseq;
return 1; /* abandon the frame */
}
@@ -1538,7 +1529,7 @@
if (skb->len > 0)
/* note: a 0-length skb is used as an error indication */
- ++ppp->stats.rx_length_errors;
+ ++ppp->dev->stats.rx_length_errors;
kfree_skb(skb);
ppp_receive_error(ppp);
@@ -1547,7 +1538,7 @@
static void
ppp_receive_error(struct ppp *ppp)
{
- ++ppp->stats.rx_errors;
+ ++ppp->dev->stats.rx_errors;
if (ppp->vj)
slhc_toss(ppp->vj);
}
@@ -1627,8 +1618,8 @@
break;
}
- ++ppp->stats.rx_packets;
- ppp->stats.rx_bytes += skb->len - 2;
+ ++ppp->dev->stats.rx_packets;
+ ppp->dev->stats.rx_bytes += skb->len - 2;
npi = proto_to_npindex(proto);
if (npi < 0) {
@@ -1806,7 +1797,7 @@
*/
if (seq_before(seq, ppp->nextseq)) {
kfree_skb(skb);
- ++ppp->stats.rx_dropped;
+ ++ppp->dev->stats.rx_dropped;
ppp_receive_error(ppp);
return;
}
@@ -1928,7 +1919,7 @@
/* Got a complete packet yet? */
if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
if (len > ppp->mrru + 2) {
- ++ppp->stats.rx_length_errors;
+ ++ppp->dev->stats.rx_length_errors;
printk(KERN_DEBUG "PPP: reconstructed packet"
" is too long (%d)\n", len);
} else if (p == head) {
@@ -1937,7 +1928,7 @@
skb = skb_get(p);
break;
} else if ((skb = dev_alloc_skb(len)) == NULL) {
- ++ppp->stats.rx_missed_errors;
+ ++ppp->dev->stats.rx_missed_errors;
printk(KERN_DEBUG "PPP: no memory for "
"reconstructed packet");
} else {
@@ -1966,7 +1957,7 @@
if (ppp->debug & 1)
printk(KERN_DEBUG " missed pkts %u..%u\n",
ppp->nextseq, head->sequence-1);
- ++ppp->stats.rx_dropped;
+ ++ppp->dev->stats.rx_dropped;
ppp_receive_error(ppp);
}
@@ -2377,12 +2368,12 @@
struct slcompress *vj = ppp->vj;
memset(st, 0, sizeof(*st));
- st->p.ppp_ipackets = ppp->stats.rx_packets;
- st->p.ppp_ierrors = ppp->stats.rx_errors;
- st->p.ppp_ibytes = ppp->stats.rx_bytes;
- st->p.ppp_opackets = ppp->stats.tx_packets;
- st->p.ppp_oerrors = ppp->stats.tx_errors;
- st->p.ppp_obytes = ppp->stats.tx_bytes;
+ st->p.ppp_ipackets = ppp->dev->stats.rx_packets;
+ st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
+ st->p.ppp_ibytes = ppp->dev->stats.rx_bytes;
+ st->p.ppp_opackets = ppp->dev->stats.tx_packets;
+ st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
+ st->p.ppp_obytes = ppp->dev->stats.tx_bytes;
if (!vj)
return;
st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
@@ -2436,7 +2427,6 @@
dev->priv = ppp;
dev->hard_start_xmit = ppp_start_xmit;
- dev->get_stats = ppp_net_stats;
dev->do_ioctl = ppp_net_ioctl;
ret = -EEXIST;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index d91856b..0ce07a3 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -668,16 +668,23 @@
break;
case TUNSETLINK:
+ {
+ int ret;
+
/* Only allow setting the type when the interface is down */
+ rtnl_lock();
if (tun->dev->flags & IFF_UP) {
DBG(KERN_INFO "%s: Linktype set failed because interface is up\n",
tun->dev->name);
- return -EBUSY;
+ ret = -EBUSY;
} else {
tun->dev->type = (int) arg;
DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type);
+ ret = 0;
}
- break;
+ rtnl_unlock();
+ return ret;
+ }
#ifdef TUN_DEBUG
case TUNSETDEBUG:
@@ -734,7 +741,12 @@
case SIOCADDMULTI:
/** Add the specified group to the character device's multicast filter
* list. */
+ rtnl_lock();
+ netif_tx_lock_bh(tun->dev);
add_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data);
+ netif_tx_unlock_bh(tun->dev);
+ rtnl_unlock();
+
DBG(KERN_DEBUG "%s: add multi: %s\n",
tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data));
return 0;
@@ -742,7 +754,12 @@
case SIOCDELMULTI:
/** Remove the specified group from the character device's multicast
* filter list. */
+ rtnl_lock();
+ netif_tx_lock_bh(tun->dev);
del_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data);
+ netif_tx_unlock_bh(tun->dev);
+ rtnl_unlock();
+
DBG(KERN_DEBUG "%s: del multi: %s\n",
tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data));
return 0;
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 7009219..c2642bc 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -56,8 +56,7 @@
obj-$(CONFIG_ADM8211) += adm8211.o
-obj-$(CONFIG_IWL3945) += iwlwifi/
-obj-$(CONFIG_IWL4965) += iwlwifi/
+obj-$(CONFIG_IWLCORE) += iwlwifi/
obj-$(CONFIG_RT2X00) += rt2x00/
obj-$(CONFIG_P54_COMMON) += p54/
diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c
index 87e7822..5fb1ae6 100644
--- a/drivers/net/wireless/ath5k/hw.c
+++ b/drivers/net/wireless/ath5k/hw.c
@@ -304,14 +304,20 @@
ah->ah_radio = AR5K_RF2413;
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC2) {
-
ah->ah_radio = AR5K_RF5413;
+ ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
+ } else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5133) {
- if (ah->ah_mac_srev <= AR5K_SREV_VER_AR5424 &&
- ah->ah_mac_srev >= AR5K_SREV_VER_AR2424)
+ /* AR5424 */
+ if (srev >= AR5K_SREV_VER_AR5424) {
+ ah->ah_radio = AR5K_RF5413;
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5424;
- else
+ /* AR2424 */
+ } else {
+ ah->ah_radio = AR5K_RF2413; /* For testing */
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
+ }
+
/*
* Register returns 0x4 for radio revision
* so ath5k_hw_radio_revision doesn't parse the value
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 21c886a..6dcbb3c 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -980,6 +980,42 @@
destroy_ring(dma, tx_ring_mcast);
}
+static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask)
+{
+ u64 orig_mask = mask;
+ bool fallback = 0;
+ int err;
+
+ /* Try to set the DMA mask. If it fails, try falling back to a
+ * lower mask, as we can always also support a lower one. */
+ while (1) {
+ err = ssb_dma_set_mask(dev->dev, mask);
+ if (!err)
+ break;
+ if (mask == DMA_64BIT_MASK) {
+ mask = DMA_32BIT_MASK;
+ fallback = 1;
+ continue;
+ }
+ if (mask == DMA_32BIT_MASK) {
+ mask = DMA_30BIT_MASK;
+ fallback = 1;
+ continue;
+ }
+ b43err(dev->wl, "The machine/kernel does not support "
+ "the required %u-bit DMA mask\n",
+ (unsigned int)dma_mask_to_engine_type(orig_mask));
+ return -EOPNOTSUPP;
+ }
+ if (fallback) {
+ b43info(dev->wl, "DMA mask fallback from %u-bit to %u-bit\n",
+ (unsigned int)dma_mask_to_engine_type(orig_mask),
+ (unsigned int)dma_mask_to_engine_type(mask));
+ }
+
+ return 0;
+}
+
int b43_dma_init(struct b43_wldev *dev)
{
struct b43_dma *dma = &dev->dma;
@@ -989,14 +1025,9 @@
dmamask = supported_dma_mask(dev);
type = dma_mask_to_engine_type(dmamask);
- err = ssb_dma_set_mask(dev->dev, dmamask);
- if (err) {
- b43err(dev->wl, "The machine/kernel does not support "
- "the required DMA mask (0x%08X%08X)\n",
- (unsigned int)((dmamask & 0xFFFFFFFF00000000ULL) >> 32),
- (unsigned int)(dmamask & 0x00000000FFFFFFFFULL));
- return -EOPNOTSUPP;
- }
+ err = b43_dma_set_mask(dev, dmamask);
+ if (err)
+ return err;
err = -ENOMEM;
/* setup TX DMA channels. */
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 943cc85..4bf8a99 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -84,6 +84,10 @@
module_param_named(qos, b43_modparam_qos, int, 0444);
MODULE_PARM_DESC(qos, "Enable QOS support (default on)");
+static int modparam_btcoex = 1;
+module_param_named(btcoex, modparam_btcoex, int, 0444);
+MODULE_PARM_DESC(btcoex, "Enable Bluetooth coexistance (default on)");
+
static const struct ssb_device_id b43_ssb_tbl[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
@@ -3706,8 +3710,10 @@
static void b43_bluetooth_coext_enable(struct b43_wldev *dev)
{
struct ssb_sprom *sprom = &dev->dev->bus->sprom;
- u32 hf;
+ u64 hf;
+ if (!modparam_btcoex)
+ return;
if (!(sprom->boardflags_lo & B43_BFL_BTCOEXIST))
return;
if (dev->phy.type != B43_PHYTYPE_B && !dev->phy.gmode)
@@ -3719,11 +3725,13 @@
else
hf |= B43_HF_BTCOEX;
b43_hf_write(dev, hf);
- //TODO
}
static void b43_bluetooth_coext_disable(struct b43_wldev *dev)
-{ //TODO
+{
+ if (!modparam_btcoex)
+ return;
+ //TODO
}
static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
@@ -3852,7 +3860,8 @@
struct ssb_sprom *sprom = &bus->sprom;
struct b43_phy *phy = &dev->phy;
int err;
- u32 hf, tmp;
+ u64 hf;
+ u32 tmp;
B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT);
@@ -4414,8 +4423,16 @@
return err;
}
+#define IS_PDEV(pdev, _vendor, _device, _subvendor, _subdevice) ( \
+ (pdev->vendor == PCI_VENDOR_ID_##_vendor) && \
+ (pdev->device == _device) && \
+ (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) && \
+ (pdev->subsystem_device == _subdevice) )
+
static void b43_sprom_fixup(struct ssb_bus *bus)
{
+ struct pci_dev *pdev;
+
/* boardflags workarounds */
if (bus->boardinfo.vendor == SSB_BOARDVENDOR_DELL &&
bus->chip_id == 0x4301 && bus->boardinfo.rev == 0x74)
@@ -4423,6 +4440,13 @@
if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE &&
bus->boardinfo.type == 0x4E && bus->boardinfo.rev > 0x40)
bus->sprom.boardflags_lo |= B43_BFL_PACTRL;
+ if (bus->bustype == SSB_BUSTYPE_PCI) {
+ pdev = bus->host_pci;
+ if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) ||
+ IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) ||
+ IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013))
+ bus->sprom.boardflags_lo &= ~B43_BFL_BTCOEXIST;
+ }
}
static void b43_wireless_exit(struct ssb_device *dev, struct b43_wl *wl)
diff --git a/drivers/net/wireless/b43/phy.c b/drivers/net/wireless/b43/phy.c
index 575c543..de024dc 100644
--- a/drivers/net/wireless/b43/phy.c
+++ b/drivers/net/wireless/b43/phy.c
@@ -2043,7 +2043,7 @@
void b43_set_rx_antenna(struct b43_wldev *dev, int antenna)
{
struct b43_phy *phy = &dev->phy;
- u32 hf;
+ u64 hf;
u16 tmp;
int autodiv = 0;
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 1a5678f..a1a0b3c 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -6907,7 +6907,6 @@
if (priv->vif != vif) {
IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
- mutex_unlock(&priv->mutex);
return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c
index d7e2358..d0bbcaa 100644
--- a/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -6473,7 +6473,6 @@
if (priv->vif != vif) {
IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
- mutex_unlock(&priv->mutex);
return 0;
}
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index e5b3c28..5b375b2 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -1186,7 +1186,7 @@
rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
devindex = r.u;
/* Now get the key, return it */
- if ((index < 0) || (index > 3))
+ if (index == -1 || index > 3)
/* no index provided, use the current one */
index = devindex;
rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r);
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 977751f..d0b1fb1 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2402,7 +2402,6 @@
priv->param_power_output = modparam_power_output;
priv->param_roamtrigger = modparam_roamtrigger;
priv->param_roamdelta = modparam_roamdelta;
- priv->param_workaround_interval = modparam_workaround_interval;
priv->param_country[0] = toupper(priv->param_country[0]);
priv->param_country[1] = toupper(priv->param_country[1]);
@@ -2425,8 +2424,10 @@
else if (priv->param_roamdelta > 2)
priv->param_roamdelta = 2;
- if (priv->param_workaround_interval < 0)
+ if (modparam_workaround_interval < 0)
priv->param_workaround_interval = 500;
+ else
+ priv->param_workaround_interval = modparam_workaround_interval;
rndis_set_config_parameter_str(dev, "Country", priv->param_country);
rndis_set_config_parameter_str(dev, "FrameBursting",
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index ed8c069..8d88526 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -200,7 +200,6 @@
config PCMCIA_SA1100
tristate "SA1100 support"
depends on ARM && ARCH_SA1100 && PCMCIA
- depends on ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL || MACH_ARMCORE
help
Say Y here to include support for SA11x0-based PCMCIA or CF
sockets, found on HP iPAQs, Yopy, and other StrongARM(R)/
@@ -221,6 +220,7 @@
config PCMCIA_PXA2XX
tristate "PXA2xx support"
depends on ARM && ARCH_PXA && PCMCIA
+ depends on ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL || MACH_ARMCORE
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
index cb2e405..3271379 100644
--- a/drivers/serial/sunzilog.c
+++ b/drivers/serial/sunzilog.c
@@ -1015,6 +1015,7 @@
.verify_port = sunzilog_verify_port,
};
+static int uart_chip_count;
static struct uart_sunzilog_port *sunzilog_port_table;
static struct zilog_layout __iomem **sunzilog_chip_regs;
@@ -1350,16 +1351,22 @@
static int __devinit zs_probe(struct of_device *op, const struct of_device_id *match)
{
- static int inst;
+ static int kbm_inst, uart_inst;
+ int inst;
struct uart_sunzilog_port *up;
struct zilog_layout __iomem *rp;
- int keyboard_mouse;
+ int keyboard_mouse = 0;
int err;
- keyboard_mouse = 0;
if (of_find_property(op->node, "keyboard", NULL))
keyboard_mouse = 1;
+ /* uarts must come before keyboards/mice */
+ if (keyboard_mouse)
+ inst = uart_chip_count + kbm_inst;
+ else
+ inst = uart_inst;
+
sunzilog_chip_regs[inst] = of_ioremap(&op->resource[0], 0,
sizeof(struct zilog_layout),
"zs");
@@ -1427,6 +1434,7 @@
rp, sizeof(struct zilog_layout));
return err;
}
+ uart_inst++;
} else {
printk(KERN_INFO "%s: Keyboard at MMIO 0x%llx (irq = %d) "
"is a %s\n",
@@ -1438,12 +1446,11 @@
op->dev.bus_id,
(unsigned long long) up[1].port.mapbase,
op->irqs[0], sunzilog_type(&up[1].port));
+ kbm_inst++;
}
dev_set_drvdata(&op->dev, &up[0]);
- inst++;
-
return 0;
}
@@ -1491,28 +1498,25 @@
static int __init sunzilog_init(void)
{
struct device_node *dp;
- int err, uart_count;
- int num_keybms;
+ int err;
+ int num_keybms = 0;
int num_sunzilog = 0;
- num_keybms = 0;
for_each_node_by_name(dp, "zs") {
num_sunzilog++;
if (of_find_property(dp, "keyboard", NULL))
num_keybms++;
}
- uart_count = 0;
if (num_sunzilog) {
- int uart_count;
-
err = sunzilog_alloc_tables(num_sunzilog);
if (err)
goto out;
- uart_count = (num_sunzilog * 2) - (2 * num_keybms);
+ uart_chip_count = num_sunzilog - num_keybms;
- err = sunserial_register_minors(&sunzilog_reg, uart_count);
+ err = sunserial_register_minors(&sunzilog_reg,
+ uart_chip_count * 2);
if (err)
goto out_free_tables;
}
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 904b1a8d..57c4ccf 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -484,6 +484,11 @@
goto unsupported;
}
+ if (out->boardflags_lo == 0xFFFF)
+ out->boardflags_lo = 0; /* per specs */
+ if (out->boardflags_hi == 0xFFFF)
+ out->boardflags_hi = 0; /* per specs */
+
return 0;
unsupported:
ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d "
diff --git a/include/asm-sparc/device.h b/include/asm-sparc/device.h
index 680e51d..19790eb 100644
--- a/include/asm-sparc/device.h
+++ b/include/asm-sparc/device.h
@@ -16,6 +16,8 @@
struct device_node *prom_node;
struct of_device *op;
+
+ int numa_node;
};
#endif /* _ASM_SPARC_DEVICE_H */
diff --git a/include/asm-sparc/prom.h b/include/asm-sparc/prom.h
index df5dc44..fd55522 100644
--- a/include/asm-sparc/prom.h
+++ b/include/asm-sparc/prom.h
@@ -77,6 +77,11 @@
const char *name,
int def);
extern int of_find_in_proplist(const char *list, const char *match, int len);
+#ifdef CONFIG_NUMA
+extern int of_node_to_nid(struct device_node *dp);
+#else
+#define of_node_to_nid(dp) (-1)
+#endif
extern void prom_build_devicetree(void);
diff --git a/include/asm-sparc64/iommu.h b/include/asm-sparc64/iommu.h
index 46325dd..d7b9afc 100644
--- a/include/asm-sparc64/iommu.h
+++ b/include/asm-sparc64/iommu.h
@@ -56,6 +56,7 @@
};
extern int iommu_table_init(struct iommu *iommu, int tsbsize,
- u32 dma_offset, u32 dma_addr_mask);
+ u32 dma_offset, u32 dma_addr_mask,
+ int numa_node);
#endif /* !(_SPARC64_IOMMU_H) */
diff --git a/include/asm-sparc64/mmzone.h b/include/asm-sparc64/mmzone.h
new file mode 100644
index 0000000..ebf5986
--- /dev/null
+++ b/include/asm-sparc64/mmzone.h
@@ -0,0 +1,17 @@
+#ifndef _SPARC64_MMZONE_H
+#define _SPARC64_MMZONE_H
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+
+extern struct pglist_data *node_data[];
+
+#define NODE_DATA(nid) (node_data[nid])
+#define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn)
+#define node_end_pfn(nid) (NODE_DATA(nid)->node_end_pfn)
+
+extern int numa_cpu_lookup_table[];
+extern cpumask_t numa_cpumask_lookup_table[];
+
+#endif /* CONFIG_NEED_MULTIPLE_NODES */
+
+#endif /* _SPARC64_MMZONE_H */
diff --git a/include/asm-sparc64/numnodes.h b/include/asm-sparc64/numnodes.h
deleted file mode 100644
index 017e7e7..0000000
--- a/include/asm-sparc64/numnodes.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SPARC64_NUMNODES_H
-#define _SPARC64_NUMNODES_H
-
-#define NODES_SHIFT 0
-
-#endif /* !(_SPARC64_NUMNODES_H) */
diff --git a/include/asm-sparc64/ptrace.h b/include/asm-sparc64/ptrace.h
index 6da1978..b4b951d 100644
--- a/include/asm-sparc64/ptrace.h
+++ b/include/asm-sparc64/ptrace.h
@@ -8,6 +8,8 @@
* stack during a system call and basically all traps.
*/
+#define PT_REGS_MAGIC 0x57ac6c00
+
#ifndef __ASSEMBLY__
struct pt_regs {
@@ -16,7 +18,19 @@
unsigned long tpc;
unsigned long tnpc;
unsigned int y;
- unsigned int fprs;
+
+ /* We encode a magic number, PT_REGS_MAGIC, along
+ * with the %tt (trap type) register value at trap
+ * entry time. The magic number allows us to identify
+ * accurately a trap stack frame in the stack
+ * unwinder, and the %tt value allows us to test
+ * things like "in a system call" etc. for an arbitray
+ * process.
+ *
+ * The PT_REGS_MAGIC is choosen such that it can be
+ * loaded completely using just a sethi instruction.
+ */
+ unsigned int magic;
};
struct pt_regs32 {
@@ -147,7 +161,7 @@
#define PT_V9_TPC 0x88
#define PT_V9_TNPC 0x90
#define PT_V9_Y 0x98
-#define PT_V9_FPRS 0x9c
+#define PT_V9_MAGIC 0x9c
#define PT_TSTATE PT_V9_TSTATE
#define PT_TPC PT_V9_TPC
#define PT_TNPC PT_V9_TNPC
diff --git a/include/asm-sparc64/sparsemem.h b/include/asm-sparc64/sparsemem.h
index 77bcd2b..b99d4e4 100644
--- a/include/asm-sparc64/sparsemem.h
+++ b/include/asm-sparc64/sparsemem.h
@@ -3,7 +3,7 @@
#ifdef __KERNEL__
-#define SECTION_SIZE_BITS 31
+#define SECTION_SIZE_BITS 30
#define MAX_PHYSADDR_BITS 42
#define MAX_PHYSMEM_BITS 42
diff --git a/include/asm-sparc64/topology.h b/include/asm-sparc64/topology.h
index c6b5570..001c040 100644
--- a/include/asm-sparc64/topology.h
+++ b/include/asm-sparc64/topology.h
@@ -1,6 +1,77 @@
#ifndef _ASM_SPARC64_TOPOLOGY_H
#define _ASM_SPARC64_TOPOLOGY_H
+#ifdef CONFIG_NUMA
+
+#include <asm/mmzone.h>
+
+static inline int cpu_to_node(int cpu)
+{
+ return numa_cpu_lookup_table[cpu];
+}
+
+#define parent_node(node) (node)
+
+static inline cpumask_t node_to_cpumask(int node)
+{
+ return numa_cpumask_lookup_table[node];
+}
+
+/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
+#define node_to_cpumask_ptr(v, node) \
+ cpumask_t *v = &(numa_cpumask_lookup_table[node])
+
+#define node_to_cpumask_ptr_next(v, node) \
+ v = &(numa_cpumask_lookup_table[node])
+
+static inline int node_to_first_cpu(int node)
+{
+ cpumask_t tmp;
+ tmp = node_to_cpumask(node);
+ return first_cpu(tmp);
+}
+
+struct pci_bus;
+#ifdef CONFIG_PCI
+extern int pcibus_to_node(struct pci_bus *pbus);
+#else
+static inline int pcibus_to_node(struct pci_bus *pbus)
+{
+ return -1;
+}
+#endif
+
+#define pcibus_to_cpumask(bus) \
+ (pcibus_to_node(bus) == -1 ? \
+ CPU_MASK_ALL : \
+ node_to_cpumask(pcibus_to_node(bus)))
+
+#define SD_NODE_INIT (struct sched_domain) { \
+ .min_interval = 8, \
+ .max_interval = 32, \
+ .busy_factor = 32, \
+ .imbalance_pct = 125, \
+ .cache_nice_tries = 2, \
+ .busy_idx = 3, \
+ .idle_idx = 2, \
+ .newidle_idx = 0, \
+ .wake_idx = 1, \
+ .forkexec_idx = 1, \
+ .flags = SD_LOAD_BALANCE \
+ | SD_BALANCE_FORK \
+ | SD_BALANCE_EXEC \
+ | SD_SERIALIZE \
+ | SD_WAKE_BALANCE, \
+ .last_balance = jiffies, \
+ .balance_interval = 1, \
+}
+
+#else /* CONFIG_NUMA */
+
+#include <asm-generic/topology.h>
+
+#endif /* !(CONFIG_NUMA) */
+
#ifdef CONFIG_SMP
#define topology_physical_package_id(cpu) (cpu_data(cpu).proc_id)
#define topology_core_id(cpu) (cpu_data(cpu).core_id)
@@ -10,8 +81,6 @@
#define smt_capable() (sparc64_multi_core)
#endif /* CONFIG_SMP */
-#include <asm-generic/topology.h>
-
#define cpu_coregroup_map(cpu) (cpu_core_map[cpu])
#endif /* _ASM_SPARC64_TOPOLOGY_H */
diff --git a/include/asm-sparc64/ttable.h b/include/asm-sparc64/ttable.h
index 7208a77..d3cc4ef 100644
--- a/include/asm-sparc64/ttable.h
+++ b/include/asm-sparc64/ttable.h
@@ -28,7 +28,7 @@
call routine; \
add %sp, PTREGS_OFF, %o0; \
ba,pt %xcc, rtrap; \
- clr %l6; \
+ nop; \
nop;
#define TRAP_7INSNS(routine) \
@@ -38,7 +38,7 @@
call routine; \
add %sp, PTREGS_OFF, %o0; \
ba,pt %xcc, rtrap; \
- clr %l6;
+ nop;
#define TRAP_SAVEFPU(routine) \
sethi %hi(109f), %g7; \
@@ -47,7 +47,7 @@
call routine; \
add %sp, PTREGS_OFF, %o0; \
ba,pt %xcc, rtrap; \
- clr %l6; \
+ nop; \
nop;
#define TRAP_NOSAVE(routine) \
@@ -67,7 +67,7 @@
call routine; \
add %sp, PTREGS_OFF, %o0; \
ba,pt %xcc, rtrap; \
- clr %l6; \
+ nop; \
nop;
#define TRAP_ARG(routine, arg) \
@@ -78,7 +78,7 @@
call routine; \
mov arg, %o1; \
ba,pt %xcc, rtrap; \
- clr %l6;
+ nop;
#define TRAPTL1_ARG(routine, arg) \
sethi %hi(109f), %g7; \
@@ -88,7 +88,7 @@
call routine; \
mov arg, %o1; \
ba,pt %xcc, rtrap; \
- clr %l6;
+ nop;
#define SYSCALL_TRAP(routine, systbl) \
sethi %hi(109f), %g7; \
@@ -166,7 +166,7 @@
ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %l1; \
add %l1, 4, %l2; \
stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC]; \
- ba,pt %xcc, rtrap_clr_l6; \
+ ba,pt %xcc, rtrap; \
stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC];
#ifdef CONFIG_KPROBES
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index f27d11a..529f301 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -465,13 +465,19 @@
WLAN_EID_TS_DELAY = 43,
WLAN_EID_TCLAS_PROCESSING = 44,
WLAN_EID_QOS_CAPA = 46,
- /* 802.11s */
- WLAN_EID_MESH_CONFIG = 36, /* Pending IEEE 802.11 ANA approval */
- WLAN_EID_MESH_ID = 37, /* Pending IEEE 802.11 ANA approval */
- WLAN_EID_PEER_LINK = 40, /* Pending IEEE 802.11 ANA approval */
- WLAN_EID_PREQ = 53, /* Pending IEEE 802.11 ANA approval */
- WLAN_EID_PREP = 54, /* Pending IEEE 802.11 ANA approval */
- WLAN_EID_PERR = 55, /* Pending IEEE 802.11 ANA approval */
+ /* 802.11s
+ *
+ * All mesh EID numbers are pending IEEE 802.11 ANA approval.
+ * The numbers have been incremented from those suggested in
+ * 802.11s/D2.0 so that MESH_CONFIG does not conflict with
+ * EXT_SUPP_RATES.
+ */
+ WLAN_EID_MESH_CONFIG = 51,
+ WLAN_EID_MESH_ID = 52,
+ WLAN_EID_PEER_LINK = 55,
+ WLAN_EID_PREQ = 68,
+ WLAN_EID_PREP = 69,
+ WLAN_EID_PERR = 70,
/* 802.11h */
WLAN_EID_PWR_CONSTRAINT = 32,
WLAN_EID_PWR_CAPABILITY = 33,
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index b9e1740..44c81c7 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -740,13 +740,13 @@
extern void rtnl_lock(void);
extern void rtnl_unlock(void);
extern int rtnl_trylock(void);
+extern int rtnl_is_locked(void);
extern void rtnetlink_init(void);
extern void __rtnl_unlock(void);
#define ASSERT_RTNL() do { \
- if (unlikely(rtnl_trylock())) { \
- rtnl_unlock(); \
+ if (unlikely(!rtnl_is_locked())) { \
printk(KERN_ERR "RTNL: assertion failed at %s (%d)\n", \
__FILE__, __LINE__); \
dump_stack(); \
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index 0148058..049edc5 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -397,6 +397,7 @@
#define AC97_HAS_NO_TONE (1<<16) /* no Tone volume */
#define AC97_HAS_NO_STD_PCM (1<<17) /* no standard AC97 PCM volume and mute */
#define AC97_HAS_NO_AUX (1<<18) /* no standard AC97 AUX volume and mute */
+#define AC97_HAS_8CH (1<<19) /* supports 8-channel output */
/* rates indexes */
#define AC97_RATES_FRONT_DAC 0
diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h
index 4e80d3f..d293d36 100644
--- a/include/sound/ak4114.h
+++ b/include/sound/ak4114.h
@@ -182,6 +182,7 @@
unsigned char rcs0;
unsigned char rcs1;
struct delayed_work work;
+ unsigned int check_flags;
void *change_callback_private;
void (*change_callback)(struct ak4114 *ak4114, unsigned char c0, unsigned char c1);
};
diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h
index 6153b91..891cf1a 100644
--- a/include/sound/ak4xxx-adda.h
+++ b/include/sound/ak4xxx-adda.h
@@ -68,7 +68,7 @@
enum {
SND_AK4524, SND_AK4528, SND_AK4529,
SND_AK4355, SND_AK4358, SND_AK4381,
- SND_AK5365, NON_AKM
+ SND_AK5365
} type;
/* (array) information of combined codecs */
diff --git a/include/sound/asoundef.h b/include/sound/asoundef.h
index 024ce62..a6e0fac 100644
--- a/include/sound/asoundef.h
+++ b/include/sound/asoundef.h
@@ -112,6 +112,14 @@
#define IEC958_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */
#define IEC958_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */
#define IEC958_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */
+#define IEC958_AES4_CON_MAX_WORDLEN_24 (1<<0) /* 0 = 20-bit, 1 = 24-bit */
+#define IEC958_AES4_CON_WORDLEN (7<<1) /* mask - sample word length */
+#define IEC958_AES4_CON_WORDLEN_NOTID (0<<1) /* not indicated */
+#define IEC958_AES4_CON_WORDLEN_20_16 (1<<1) /* 20-bit or 16-bit */
+#define IEC958_AES4_CON_WORDLEN_22_18 (2<<1) /* 22-bit or 18-bit */
+#define IEC958_AES4_CON_WORDLEN_23_19 (4<<1) /* 23-bit or 19-bit */
+#define IEC958_AES4_CON_WORDLEN_24_20 (5<<1) /* 24-bit or 20-bit */
+#define IEC958_AES4_CON_WORDLEN_21_17 (6<<1) /* 21-bit or 17-bit */
/*****************************************************************************
* *
diff --git a/include/sound/control.h b/include/sound/control.h
index e79baa6..3dc1291 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -169,4 +169,11 @@
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
+/*
+ * virtual master control
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+ const unsigned int *tlv);
+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
+
#endif /* __SOUND_CONTROL_H */
diff --git a/include/sound/core.h b/include/sound/core.h
index 4fc0235..695ee53 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -277,8 +277,8 @@
int snd_minor_info_oss_init(void);
int snd_minor_info_oss_done(void);
#else
-#define snd_minor_info_oss_init() /*NOP*/
-#define snd_minor_info_oss_done() /*NOP*/
+static inline int snd_minor_info_oss_init(void) { return 0; }
+static inline int snd_minor_info_oss_done(void) { return 0; }
#endif
/* memory.c */
@@ -310,7 +310,7 @@
int snd_card_file_remove(struct snd_card *card, struct file *file);
#ifndef snd_card_set_dev
-#define snd_card_set_dev(card,devptr) ((card)->dev = (devptr))
+#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
#endif
/* device.c */
@@ -373,7 +373,7 @@
* snd_printd - debug printk
* @fmt: format string
*
- * Compiled only when Works like snd_printk() for debugging purpose.
+ * Works like snd_printk() for debugging purposes.
* Ignored when CONFIG_SND_DEBUG is not set.
*/
#define snd_printd(fmt, args...) \
@@ -417,7 +417,7 @@
* snd_printdd - debug printk
* @format: format string
*
- * Compiled only when Works like snd_printk() for debugging purpose.
+ * Works like snd_printk() for debugging purposes.
* Ignored when CONFIG_SND_DEBUG_DETECT is not set.
*/
#define snd_printdd(format, args...) snd_printk(format, ##args)
diff --git a/include/sound/mpu401.h b/include/sound/mpu401.h
index d45218b..68b634b 100644
--- a/include/sound/mpu401.h
+++ b/include/sound/mpu401.h
@@ -103,6 +103,21 @@
#define MPU401D(mpu) (mpu)->port
/*
+ * control register bits
+ */
+/* read MPU401C() */
+#define MPU401_RX_EMPTY 0x80
+#define MPU401_TX_FULL 0x40
+
+/* write MPU401C() */
+#define MPU401_RESET 0xff
+#define MPU401_ENTER_UART 0x3f
+
+/* read MPU401D() */
+#define MPU401_ACK 0xfe
+
+
+/*
*/
diff --git a/include/sound/version.h b/include/sound/version.h
index fac66c4..ed6fb2e 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
/* include/version.h. Generated by alsa/ksync script. */
-#define CONFIG_SND_VERSION "1.0.16rc2"
-#define CONFIG_SND_DATE " (Thu Jan 31 16:40:16 2008 UTC)"
+#define CONFIG_SND_VERSION "1.0.16"
+#define CONFIG_SND_DATE ""
diff --git a/lib/lmb.c b/lib/lmb.c
index 896e283..207147a 100644
--- a/lib/lmb.c
+++ b/lib/lmb.c
@@ -346,7 +346,7 @@
if (j < 0) {
/* this area isn't reserved, take it */
if (lmb_add_region(&lmb.reserved, base,
- size) < 0)
+ lmb_align_up(size, align)) < 0)
return 0;
return base;
}
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index bc39e41..cf857c4 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -82,6 +82,11 @@
return mutex_trylock(&rtnl_mutex);
}
+int rtnl_is_locked(void)
+{
+ return mutex_is_locked(&rtnl_mutex);
+}
+
static struct rtnl_link *rtnl_msg_handlers[NPROTO];
static inline int rtm_msgindex(int msgtype)
@@ -1402,6 +1407,7 @@
EXPORT_SYMBOL(rtnl_lock);
EXPORT_SYMBOL(rtnl_trylock);
EXPORT_SYMBOL(rtnl_unlock);
+EXPORT_SYMBOL(rtnl_is_locked);
EXPORT_SYMBOL(rtnl_unicast);
EXPORT_SYMBOL(rtnl_notify);
EXPORT_SYMBOL(rtnl_set_sk_err);
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
index 02088de..2e2fc33 100644
--- a/net/ipv4/fib_hash.c
+++ b/net/ipv4/fib_hash.c
@@ -1003,7 +1003,7 @@
static int fib_seq_show(struct seq_file *seq, void *v)
{
struct fib_iter_state *iter;
- char bf[128];
+ int len;
__be32 prefix, mask;
unsigned flags;
struct fib_node *f;
@@ -1025,18 +1025,19 @@
mask = FZ_MASK(iter->zone);
flags = fib_flag_trans(fa->fa_type, mask, fi);
if (fi)
- snprintf(bf, sizeof(bf),
- "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
+ seq_printf(seq,
+ "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u%n",
fi->fib_dev ? fi->fib_dev->name : "*", prefix,
fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority,
mask, (fi->fib_advmss ? fi->fib_advmss + 40 : 0),
fi->fib_window,
- fi->fib_rtt >> 3);
+ fi->fib_rtt >> 3, &len);
else
- snprintf(bf, sizeof(bf),
- "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
- prefix, 0, flags, 0, 0, 0, mask, 0, 0, 0);
- seq_printf(seq, "%-127s\n", bf);
+ seq_printf(seq,
+ "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u%n",
+ prefix, 0, flags, 0, 0, 0, mask, 0, 0, 0, &len);
+
+ seq_printf(seq, "%*s\n", 127 - len, "");
out:
return 0;
}
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index ea294ff..4b02d14 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -2602,15 +2602,16 @@
list_for_each_entry_rcu(fa, &li->falh, fa_list) {
const struct fib_info *fi = fa->fa_info;
unsigned flags = fib_flag_trans(fa->fa_type, mask, fi);
- char bf[128];
+ int len;
if (fa->fa_type == RTN_BROADCAST
|| fa->fa_type == RTN_MULTICAST)
continue;
if (fi)
- snprintf(bf, sizeof(bf),
- "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
+ seq_printf(seq,
+ "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
+ "%d\t%08X\t%d\t%u\t%u%n",
fi->fib_dev ? fi->fib_dev->name : "*",
prefix,
fi->fib_nh->nh_gw, flags, 0, 0,
@@ -2619,14 +2620,15 @@
(fi->fib_advmss ?
fi->fib_advmss + 40 : 0),
fi->fib_window,
- fi->fib_rtt >> 3);
+ fi->fib_rtt >> 3, &len);
else
- snprintf(bf, sizeof(bf),
- "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
+ seq_printf(seq,
+ "*\t%08X\t%08X\t%04X\t%d\t%u\t"
+ "%d\t%08X\t%d\t%u\t%u%n",
prefix, 0, flags, 0, 0, 0,
- mask, 0, 0, 0);
+ mask, 0, 0, 0, &len);
- seq_printf(seq, "%-127s\n", bf);
+ seq_printf(seq, "%*s\n", 127 - len, "");
}
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 780e948..ce25a13 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -367,10 +367,10 @@
"HHUptod\tSpecDst");
else {
struct rtable *r = v;
- char temp[256];
+ int len;
- sprintf(temp, "%s\t%08lX\t%08lX\t%8X\t%d\t%u\t%d\t"
- "%08lX\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X",
+ seq_printf(seq, "%s\t%08lX\t%08lX\t%8X\t%d\t%u\t%d\t"
+ "%08lX\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X%n",
r->u.dst.dev ? r->u.dst.dev->name : "*",
(unsigned long)r->rt_dst, (unsigned long)r->rt_gateway,
r->rt_flags, atomic_read(&r->u.dst.__refcnt),
@@ -384,8 +384,9 @@
r->u.dst.hh ? atomic_read(&r->u.dst.hh->hh_refcnt) : -1,
r->u.dst.hh ? (r->u.dst.hh->hh_output ==
dev_queue_xmit) : 0,
- r->rt_spec_dst);
- seq_printf(seq, "%-127s\n", temp);
+ r->rt_spec_dst, &len);
+
+ seq_printf(seq, "%*s\n", 127 - len, "");
}
return 0;
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 7766151..0e9bc12 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2255,13 +2255,13 @@
}
static void get_openreq4(struct sock *sk, struct request_sock *req,
- char *tmpbuf, int i, int uid)
+ struct seq_file *f, int i, int uid, int *len)
{
const struct inet_request_sock *ireq = inet_rsk(req);
int ttd = req->expires - jiffies;
- sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %p",
+ seq_printf(f, "%4d: %08X:%04X %08X:%04X"
+ " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %p%n",
i,
ireq->loc_addr,
ntohs(inet_sk(sk)->sport),
@@ -2276,10 +2276,11 @@
0, /* non standard timer */
0, /* open_requests have no inode */
atomic_read(&sk->sk_refcnt),
- req);
+ req,
+ len);
}
-static void get_tcp4_sock(struct sock *sk, char *tmpbuf, int i)
+static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
{
int timer_active;
unsigned long timer_expires;
@@ -2305,8 +2306,8 @@
timer_expires = jiffies;
}
- sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
- "%08X %5d %8d %lu %d %p %u %u %u %u %d",
+ seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
+ "%08X %5d %8d %lu %d %p %u %u %u %u %d%n",
i, src, srcp, dest, destp, sk->sk_state,
tp->write_seq - tp->snd_una,
sk->sk_state == TCP_LISTEN ? sk->sk_ack_backlog :
@@ -2322,11 +2323,12 @@
icsk->icsk_ack.ato,
(icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
tp->snd_cwnd,
- tp->snd_ssthresh >= 0xFFFF ? -1 : tp->snd_ssthresh);
+ tp->snd_ssthresh >= 0xFFFF ? -1 : tp->snd_ssthresh,
+ len);
}
static void get_timewait4_sock(struct inet_timewait_sock *tw,
- char *tmpbuf, int i)
+ struct seq_file *f, int i, int *len)
{
__be32 dest, src;
__u16 destp, srcp;
@@ -2340,11 +2342,11 @@
destp = ntohs(tw->tw_dport);
srcp = ntohs(tw->tw_sport);
- sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p",
+ seq_printf(f, "%4d: %08X:%04X %08X:%04X"
+ " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p%n",
i, src, srcp, dest, destp, tw->tw_substate, 0, 0,
3, jiffies_to_clock_t(ttd), 0, 0, 0, 0,
- atomic_read(&tw->tw_refcnt), tw);
+ atomic_read(&tw->tw_refcnt), tw, len);
}
#define TMPSZ 150
@@ -2352,7 +2354,7 @@
static int tcp4_seq_show(struct seq_file *seq, void *v)
{
struct tcp_iter_state* st;
- char tmpbuf[TMPSZ + 1];
+ int len;
if (v == SEQ_START_TOKEN) {
seq_printf(seq, "%-*s\n", TMPSZ - 1,
@@ -2366,16 +2368,16 @@
switch (st->state) {
case TCP_SEQ_STATE_LISTENING:
case TCP_SEQ_STATE_ESTABLISHED:
- get_tcp4_sock(v, tmpbuf, st->num);
+ get_tcp4_sock(v, seq, st->num, &len);
break;
case TCP_SEQ_STATE_OPENREQ:
- get_openreq4(st->syn_wait_sk, v, tmpbuf, st->num, st->uid);
+ get_openreq4(st->syn_wait_sk, v, seq, st->num, st->uid, &len);
break;
case TCP_SEQ_STATE_TIME_WAIT:
- get_timewait4_sock(v, tmpbuf, st->num);
+ get_timewait4_sock(v, seq, st->num, &len);
break;
}
- seq_printf(seq, "%-*s\n", TMPSZ - 1, tmpbuf);
+ seq_printf(seq, "%*s\n", TMPSZ - 1 - len, "");
out:
return 0;
}
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index b053ac7..1f535e31 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1619,7 +1619,8 @@
}
/* ------------------------------------------------------------------------ */
-static void udp4_format_sock(struct sock *sp, char *tmpbuf, int bucket)
+static void udp4_format_sock(struct sock *sp, struct seq_file *f,
+ int bucket, int *len)
{
struct inet_sock *inet = inet_sk(sp);
__be32 dest = inet->daddr;
@@ -1627,13 +1628,13 @@
__u16 destp = ntohs(inet->dport);
__u16 srcp = ntohs(inet->sport);
- sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p",
+ seq_printf(f, "%4d: %08X:%04X %08X:%04X"
+ " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p%n",
bucket, src, srcp, dest, destp, sp->sk_state,
atomic_read(&sp->sk_wmem_alloc),
atomic_read(&sp->sk_rmem_alloc),
0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp),
- atomic_read(&sp->sk_refcnt), sp);
+ atomic_read(&sp->sk_refcnt), sp, len);
}
int udp4_seq_show(struct seq_file *seq, void *v)
@@ -1644,11 +1645,11 @@
"rx_queue tr tm->when retrnsmt uid timeout "
"inode");
else {
- char tmpbuf[129];
struct udp_iter_state *state = seq->private;
+ int len;
- udp4_format_sock(v, tmpbuf, state->bucket);
- seq_printf(seq, "%-127s\n", tmpbuf);
+ udp4_format_sock(v, seq, state->bucket, &len);
+ seq_printf(seq, "%*s\n", 127 - len ,"");
}
return 0;
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 6b75cb6..a5e5c31 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2248,10 +2248,13 @@
struct ieee80211_sta_bss *bss)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- if (!atomic_dec_and_test(&bss->users))
- return;
- spin_lock_bh(&local->sta_bss_lock);
+ local_bh_disable();
+ if (!atomic_dec_and_lock(&bss->users, &local->sta_bss_lock)) {
+ local_bh_enable();
+ return;
+ }
+
__ieee80211_rx_bss_hash_del(dev, bss);
list_del(&bss->list);
spin_unlock_bh(&local->sta_bss_lock);
@@ -2709,7 +2712,26 @@
bss->wmm_ie_len = elems.wmm_param_len + 2;
} else
bss->wmm_ie_len = 0;
- } else if (!elems.wmm_param && bss->wmm_ie) {
+ } else if (elems.wmm_info &&
+ (!bss->wmm_ie || bss->wmm_ie_len != elems.wmm_info_len ||
+ memcmp(bss->wmm_ie, elems.wmm_info, elems.wmm_info_len))) {
+ /* As for certain AP's Fifth bit is not set in WMM IE in
+ * beacon frames.So while parsing the beacon frame the
+ * wmm_info structure is used instead of wmm_param.
+ * wmm_info structure was never used to set bss->wmm_ie.
+ * This code fixes this problem by copying the WME
+ * information from wmm_info to bss->wmm_ie and enabling
+ * n-band association.
+ */
+ kfree(bss->wmm_ie);
+ bss->wmm_ie = kmalloc(elems.wmm_info_len + 2, GFP_ATOMIC);
+ if (bss->wmm_ie) {
+ memcpy(bss->wmm_ie, elems.wmm_info - 2,
+ elems.wmm_info_len + 2);
+ bss->wmm_ie_len = elems.wmm_info_len + 2;
+ } else
+ bss->wmm_ie_len = 0;
+ } else if (!elems.wmm_param && !elems.wmm_info && bss->wmm_ie) {
kfree(bss->wmm_ie);
bss->wmm_ie = NULL;
bss->wmm_ie_len = 0;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 52e4554..02f436a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2170,7 +2170,7 @@
struct ieee80211_supported_band *sband;
if (status->band < 0 ||
- status->band > IEEE80211_NUM_BANDS) {
+ status->band >= IEEE80211_NUM_BANDS) {
WARN_ON(1);
return;
}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 4e94e40..64faa3d 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -709,7 +709,7 @@
struct ieee80211_sched_data *q = qdisc_priv(root_qd);
struct Qdisc *qdisc = q->queues[queue];
struct sk_buff *skb = NULL;
- u32 len = qdisc->q.qlen;
+ u32 len;
if (!qdisc || !qdisc->dequeue)
return;
diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c
index cfeb07e..f73ec0e 100644
--- a/net/sctp/objcnt.c
+++ b/net/sctp/objcnt.c
@@ -83,13 +83,12 @@
*/
static int sctp_objcnt_seq_show(struct seq_file *seq, void *v)
{
- int i;
- char temp[128];
+ int i, len;
i = (int)*(loff_t *)v;
- sprintf(temp, "%s: %d", sctp_dbg_objcnt[i].label,
- atomic_read(sctp_dbg_objcnt[i].counter));
- seq_printf(seq, "%-127s\n", temp);
+ seq_printf(seq, "%s: %d%n", sctp_dbg_objcnt[i].label,
+ atomic_read(sctp_dbg_objcnt[i].counter), &len);
+ seq_printf(seq, "%*s\n", 127 - len, "");
return 0;
}
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 1454afc..e18cd36 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2197,7 +2197,11 @@
unregister_pernet_subsys(&unix_net_ops);
}
-module_init(af_unix_init);
+/* Earlier than device_initcall() so that other drivers invoking
+ request_module() don't end up in a loop when modprobe tries
+ to use a UNIX socket. But later than subsys_initcall() because
+ we depend on stuff initialised there */
+fs_initcall(af_unix_init);
module_exit(af_unix_exit);
MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 8704e28..5b3274b 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -72,7 +72,7 @@
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
!((GSR | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
- __FUNCTION__, reg, GSR | gsr_bits);
+ __func__, reg, GSR | gsr_bits);
val = -1;
goto out;
}
@@ -104,7 +104,7 @@
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
!((GSR | gsr_bits) & GSR_CDONE))
printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
- __FUNCTION__, reg, GSR | gsr_bits);
+ __func__, reg, GSR | gsr_bits);
mutex_unlock(&car_mutex);
}
@@ -112,6 +112,16 @@
static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
{
/* First, try cold reset */
+#ifdef CONFIG_PXA3xx
+ int timeout;
+
+ /* Hold CLKBPB for 100us */
+ GCR = 0;
+ GCR = GCR_CLKBPB;
+ udelay(100);
+ GCR = 0;
+#endif
+
GCR &= GCR_COLD_RST; /* clear everything but nCRST */
GCR &= ~GCR_COLD_RST; /* then assert nCRST */
@@ -123,6 +133,14 @@
clk_disable(ac97conf_clk);
GCR = GCR_COLD_RST;
udelay(50);
+#elif defined(CONFIG_PXA3xx)
+ timeout = 1000;
+ /* Can't use interrupts on PXA3xx */
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+
+ GCR = GCR_WARM_RST | GCR_COLD_RST;
+ while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(10);
#else
GCR = GCR_COLD_RST;
GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
@@ -131,7 +149,7 @@
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
- __FUNCTION__, gsr_bits);
+ __func__, gsr_bits);
/* let's try warm reset */
gsr_bits = 0;
@@ -143,6 +161,12 @@
GCR |= GCR_WARM_RST;
pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
udelay(500);
+#elif defined(CONFIG_PXA3xx)
+ timeout = 100;
+ /* Can't use interrupts */
+ GCR |= GCR_WARM_RST;
+ while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(1);
#else
GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;
wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
@@ -150,7 +174,7 @@
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
- __FUNCTION__, gsr_bits);
+ __func__, gsr_bits);
}
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
@@ -424,6 +448,7 @@
.resume = pxa2xx_ac97_resume,
.driver = {
.name = "pxa2xx-ac97",
+ .owner = THIS_MODULE,
},
};
@@ -443,3 +468,4 @@
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-ac97");
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 829ca38..a8d71c6 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -181,3 +181,7 @@
It is usually not required, but if you have trouble with
sound clicking when system is loaded, it may help to determine
the process or driver which causes the scheduling gaps.
+
+config SND_VMASTER
+ bool
+ depends on SND
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 267039a9..da8e685 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -6,6 +6,7 @@
snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
snd-$(CONFIG_ISA_DMA_API) += isadma.o
snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
+snd-$(CONFIG_SND_VMASTER) += vmaster.o
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o
diff --git a/sound/core/init.c b/sound/core/init.c
index e3338d6..ac05734 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -254,7 +254,7 @@
if (likely(df))
return df->disconnected_f_op->release(inode, file);
- panic("%s(%p, %p) failed!", __FUNCTION__, inode, file);
+ panic("%s(%p, %p) failed!", __func__, inode, file);
}
static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
@@ -311,6 +311,9 @@
struct file *file;
int err;
+ if (!card)
+ return -EINVAL;
+
spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
@@ -322,6 +325,7 @@
/* phase 1: disable fops (user space) operations for ALSA API */
mutex_lock(&snd_card_mutex);
snd_cards[card->number] = NULL;
+ snd_cards_lock &= ~(1 << card->number);
mutex_unlock(&snd_card_mutex);
/* phase 2: replace file->f_op with special dummy operations */
@@ -360,6 +364,15 @@
snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
snd_info_card_disconnect(card);
+#ifndef CONFIG_SYSFS_DEPRECATED
+ if (card->card_dev) {
+ device_unregister(card->card_dev);
+ card->card_dev = NULL;
+ }
+#endif
+#ifdef CONFIG_PM
+ wake_up(&card->power_sleep);
+#endif
return 0;
}
@@ -401,33 +414,14 @@
snd_printk(KERN_WARNING "unable to free card info\n");
/* Not fatal error */
}
-#ifndef CONFIG_SYSFS_DEPRECATED
- if (card->card_dev)
- device_unregister(card->card_dev);
-#endif
kfree(card);
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);
+ int ret = snd_card_disconnect(card);
if (ret)
return ret;
@@ -447,7 +441,7 @@
int snd_card_free(struct snd_card *card)
{
- int ret = snd_card_free_prepare(card);
+ int ret = snd_card_disconnect(card);
if (ret)
return ret;
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 102d1c3..38524f6 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -39,7 +39,7 @@
{
va_list args;
- if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
+ if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
char tmp[] = "<0>";
tmp[1] = format[1];
printk("%sALSA %s:%d: ", tmp, file, line);
@@ -60,7 +60,7 @@
{
va_list args;
- if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
+ if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
char tmp[] = "<0>";
tmp[1] = format[1];
printk("%sALSA %s:%d: ", tmp, file, line);
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 75daed2..581aa2c 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1257,6 +1257,8 @@
{ SOUND_MIXER_DIGITAL3, "Digital", 2 },
{ SOUND_MIXER_PHONEIN, "Phone", 0 },
{ SOUND_MIXER_PHONEOUT, "Master Mono", 0 },
+ { SOUND_MIXER_PHONEOUT, "Speaker", 0 }, /*fallback*/
+ { SOUND_MIXER_PHONEOUT, "Mono", 0 }, /*fallback*/
{ SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */
{ SOUND_MIXER_VIDEO, "Video", 0 },
{ SOUND_MIXER_RADIO, "Radio", 0 },
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index ab570a0..558dadb 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -245,8 +245,13 @@
info->nr_voices = rec->nr_voices;
if (info->nr_voices > 0) {
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
- if (!info->ch)
- BUG();
+ if (!info->ch) {
+ snd_printk(KERN_ERR "Cannot malloc\n");
+ rec->oper.close(&info->arg);
+ module_put(rec->oper.owner);
+ snd_use_lock_free(&rec->use_lock);
+ continue;
+ }
reset_channels(info);
}
debug_printk(("synth %d assigned\n", i));
diff --git a/sound/pci/hda/vmaster.c b/sound/core/vmaster.c
similarity index 96%
rename from sound/pci/hda/vmaster.c
rename to sound/core/vmaster.c
index 2da49d2..4cc57f9 100644
--- a/sound/pci/hda/vmaster.c
+++ b/sound/core/vmaster.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
/*
* a subset of information returned via ctl info callback
@@ -34,6 +35,7 @@
struct list_head slaves;
struct link_ctl_info info;
int val; /* the master value */
+ unsigned int tlv[4];
};
/*
@@ -253,6 +255,8 @@
return 0;
}
+EXPORT_SYMBOL(snd_ctl_add_slave);
+
/*
* ctl callbacks for master controls
*/
@@ -355,10 +359,13 @@
kctl->private_free = master_free;
/* additional (constant) TLV read */
- if (tlv) {
- /* FIXME: this assumes that the max volume is 0 dB */
+ if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- kctl->tlv.p = tlv;
+ memcpy(master->tlv, tlv, sizeof(master->tlv));
+ kctl->tlv.p = master->tlv;
}
+
return kctl;
}
+
+EXPORT_SYMBOL(snd_ctl_make_virtual_master);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 75d4fe0..fe85af1 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -4,6 +4,24 @@
depends on SND!=n
+config SND_PCSP
+ tristate "Internal PC speaker support"
+ depends on X86_PC && HIGH_RES_TIMERS
+ depends on INPUT
+ help
+ If you don't have a sound card in your computer, you can include a
+ driver for the PC speaker which allows it to act like a primitive
+ sound card.
+ This driver also replaces the pcspkr driver for beeps.
+
+ You can compile this as a module which will be called snd-pcsp.
+
+ You don't need this driver if you only want your pc-speaker to beep.
+ You don't need this driver if you have a tablet piezo beeper
+ in your PC instead of the real speaker.
+
+ It should not hurt to say Y or M here in all other cases.
+
config SND_MPU401_UART
tristate
select SND_RAWMIDI
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 8e55300..d4a07f9 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -20,4 +20,4 @@
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
-obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
+obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index a240eae..4e4c69e 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -181,10 +181,10 @@
struct snd_dummy *dummy;
spinlock_t lock;
struct timer_list timer;
- unsigned int pcm_size;
- unsigned int pcm_count;
+ unsigned int pcm_buffer_size;
+ unsigned int pcm_period_size;
unsigned int pcm_bps; /* bytes per second */
- unsigned int pcm_jiffie; /* bytes per one jiffie */
+ unsigned int pcm_hz; /* HZ */
unsigned int pcm_irq_pos; /* IRQ position */
unsigned int pcm_buf_pos; /* position in buffer */
struct snd_pcm_substream *substream;
@@ -230,19 +230,24 @@
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dummy_pcm *dpcm = runtime->private_data;
- unsigned int bps;
+ int bps;
- bps = runtime->rate * runtime->channels;
- bps *= snd_pcm_format_width(runtime->format);
- bps /= 8;
+ bps = snd_pcm_format_width(runtime->format) * runtime->rate *
+ runtime->channels / 8;
+
if (bps <= 0)
return -EINVAL;
+
dpcm->pcm_bps = bps;
- dpcm->pcm_jiffie = bps / HZ;
- dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream);
- dpcm->pcm_count = snd_pcm_lib_period_bytes(substream);
+ dpcm->pcm_hz = HZ;
+ dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream);
dpcm->pcm_irq_pos = 0;
dpcm->pcm_buf_pos = 0;
+
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+ bytes_to_samples(runtime, runtime->dma_bytes));
+
return 0;
}
@@ -254,11 +259,11 @@
spin_lock_irqsave(&dpcm->lock, flags);
dpcm->timer.expires = 1 + jiffies;
add_timer(&dpcm->timer);
- dpcm->pcm_irq_pos += dpcm->pcm_jiffie;
- dpcm->pcm_buf_pos += dpcm->pcm_jiffie;
- dpcm->pcm_buf_pos %= dpcm->pcm_size;
- if (dpcm->pcm_irq_pos >= dpcm->pcm_count) {
- dpcm->pcm_irq_pos %= dpcm->pcm_count;
+ dpcm->pcm_irq_pos += dpcm->pcm_bps;
+ dpcm->pcm_buf_pos += dpcm->pcm_bps;
+ dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz;
+ if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) {
+ dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz;
spin_unlock_irqrestore(&dpcm->lock, flags);
snd_pcm_period_elapsed(dpcm->substream);
} else
@@ -270,7 +275,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dummy_pcm *dpcm = runtime->private_data;
- return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
+ return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz);
}
static struct snd_pcm_hardware snd_card_dummy_playback =
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
index 05a871a..ecdbeb6 100644
--- a/sound/drivers/ml403-ac97cr.c
+++ b/sound/drivers/ml403-ac97cr.c
@@ -1191,8 +1191,6 @@
return err;
}
- snd_card_set_dev(card, &pfdev->dev);
-
*rml403_ac97cr = ml403_ac97cr;
return 0;
}
@@ -1330,11 +1328,15 @@
return 0;
}
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:" SND_ML403_AC97CR_DRIVER);
+
static struct platform_driver snd_ml403_ac97cr_driver = {
.probe = snd_ml403_ac97cr_probe,
.remove = snd_ml403_ac97cr_remove,
.driver = {
.name = SND_ML403_AC97CR_DRIVER,
+ .owner = THIS_MODULE,
},
};
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index 5993864a..18cca24 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.c
@@ -49,12 +49,10 @@
*/
-#define snd_mpu401_input_avail(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x80))
-#define snd_mpu401_output_ready(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x40))
-
-#define MPU401_RESET 0xff
-#define MPU401_ENTER_UART 0x3f
-#define MPU401_ACK 0xfe
+#define snd_mpu401_input_avail(mpu) \
+ (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY))
+#define snd_mpu401_output_ready(mpu) \
+ (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL))
/* Build in lowlevel io */
static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data,
@@ -425,16 +423,17 @@
static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
{
unsigned char byte;
- int max = 256, timeout;
+ int max = 256;
do {
if (snd_rawmidi_transmit_peek(mpu->substream_output,
&byte, 1) == 1) {
- for (timeout = 100; timeout > 0; timeout--) {
- if (snd_mpu401_output_ready(mpu))
- break;
- }
- if (timeout == 0)
+ /*
+ * Try twice because there is hardware that insists on
+ * setting the output busy bit after each write.
+ */
+ if (!snd_mpu401_output_ready(mpu) &&
+ !snd_mpu401_output_ready(mpu))
break; /* Tx FIFO full - try again later */
mpu->write(mpu, byte, MPU401D(mpu));
snd_rawmidi_transmit_ack(mpu->substream_output, 1);
diff --git a/sound/drivers/pcsp/Makefile b/sound/drivers/pcsp/Makefile
new file mode 100644
index 0000000..b19555b
--- /dev/null
+++ b/sound/drivers/pcsp/Makefile
@@ -0,0 +1,2 @@
+snd-pcsp-objs := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
+obj-$(CONFIG_SND_PCSP) += snd-pcsp.o
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
new file mode 100644
index 0000000..5920351
--- /dev/null
+++ b/sound/drivers/pcsp/pcsp.c
@@ -0,0 +1,235 @@
+/*
+ * PC-Speaker driver for Linux
+ *
+ * Copyright (C) 1997-2001 David Woodhouse
+ * Copyright (C) 2001-2008 Stas Sergeev
+ */
+
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include "pcsp_input.h"
+#include "pcsp.h"
+
+MODULE_AUTHOR("Stas Sergeev <stsp@users.sourceforge.net>");
+MODULE_DESCRIPTION("PC-Speaker driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{PC-Speaker, pcsp}}");
+MODULE_ALIAS("platform:pcspkr");
+
+static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
+static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
+module_param(enable, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
+
+struct snd_pcsp pcsp_chip;
+
+static int __devinit snd_pcsp_create(struct snd_card *card)
+{
+ static struct snd_device_ops ops = { };
+ struct timespec tp;
+ int err;
+ int div, min_div, order;
+
+ hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+ if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
+ printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
+ "(%linS)\n", tp.tv_nsec);
+ printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
+ "enabled.\n");
+ return -EIO;
+ }
+
+ if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
+ min_div = MIN_DIV;
+ else
+ min_div = MAX_DIV;
+#if PCSP_DEBUG
+ printk("PCSP: lpj=%li, min_div=%i, res=%li\n",
+ loops_per_jiffy, min_div, tp.tv_nsec);
+#endif
+
+ div = MAX_DIV / min_div;
+ order = fls(div) - 1;
+
+ pcsp_chip.max_treble = min(order, PCSP_MAX_TREBLE);
+ pcsp_chip.treble = min(pcsp_chip.max_treble, PCSP_DEFAULT_TREBLE);
+ pcsp_chip.playback_ptr = 0;
+ pcsp_chip.period_ptr = 0;
+ atomic_set(&pcsp_chip.timer_active, 0);
+ pcsp_chip.enable = 1;
+ pcsp_chip.pcspkr = 1;
+
+ spin_lock_init(&pcsp_chip.substream_lock);
+
+ pcsp_chip.card = card;
+ pcsp_chip.port = 0x61;
+ pcsp_chip.irq = -1;
+ pcsp_chip.dma = -1;
+
+ /* Register device */
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, &pcsp_chip, &ops);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
+{
+ struct snd_card *card;
+ int err;
+
+ if (devnum != 0)
+ return -EINVAL;
+
+ hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE;
+ pcsp_chip.timer.function = pcsp_do_timer;
+
+ card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (!card)
+ return -ENOMEM;
+
+ err = snd_pcsp_create(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ err = snd_pcsp_new_pcm(&pcsp_chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ err = snd_pcsp_new_mixer(&pcsp_chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_card_set_dev(pcsp_chip.card, dev);
+
+ strcpy(card->driver, "PC-Speaker");
+ strcpy(card->shortname, "pcsp");
+ sprintf(card->longname, "Internal PC-Speaker at port 0x%x",
+ pcsp_chip.port);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __devinit alsa_card_pcsp_init(struct device *dev)
+{
+ int err;
+
+ err = snd_card_pcsp_probe(0, dev);
+ if (err) {
+ printk(KERN_ERR "PC-Speaker initialization failed.\n");
+ return err;
+ }
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ /* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */
+ printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
+ "which may make the sound noisy.\n");
+#endif
+
+ return 0;
+}
+
+static void __devexit alsa_card_pcsp_exit(struct snd_pcsp *chip)
+{
+ snd_card_free(chip->card);
+}
+
+static int __devinit pcsp_probe(struct platform_device *dev)
+{
+ int err;
+
+ err = pcspkr_input_init(&pcsp_chip.input_dev, &dev->dev);
+ if (err < 0)
+ return err;
+
+ err = alsa_card_pcsp_init(&dev->dev);
+ if (err < 0) {
+ pcspkr_input_remove(pcsp_chip.input_dev);
+ return err;
+ }
+
+ platform_set_drvdata(dev, &pcsp_chip);
+ return 0;
+}
+
+static int __devexit pcsp_remove(struct platform_device *dev)
+{
+ struct snd_pcsp *chip = platform_get_drvdata(dev);
+ alsa_card_pcsp_exit(chip);
+ pcspkr_input_remove(chip->input_dev);
+ platform_set_drvdata(dev, NULL);
+ return 0;
+}
+
+static void pcsp_stop_beep(struct snd_pcsp *chip)
+{
+ spin_lock_irq(&chip->substream_lock);
+ if (!chip->playback_substream)
+ pcspkr_stop_sound();
+ spin_unlock_irq(&chip->substream_lock);
+}
+
+static int pcsp_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct snd_pcsp *chip = platform_get_drvdata(dev);
+ pcsp_stop_beep(chip);
+ snd_pcm_suspend_all(chip->pcm);
+ return 0;
+}
+
+static void pcsp_shutdown(struct platform_device *dev)
+{
+ struct snd_pcsp *chip = platform_get_drvdata(dev);
+ pcsp_stop_beep(chip);
+}
+
+static struct platform_driver pcsp_platform_driver = {
+ .driver = {
+ .name = "pcspkr",
+ .owner = THIS_MODULE,
+ },
+ .probe = pcsp_probe,
+ .remove = __devexit_p(pcsp_remove),
+ .suspend = pcsp_suspend,
+ .shutdown = pcsp_shutdown,
+};
+
+static int __init pcsp_init(void)
+{
+ if (!enable)
+ return -ENODEV;
+ return platform_driver_register(&pcsp_platform_driver);
+}
+
+static void __exit pcsp_exit(void)
+{
+ platform_driver_unregister(&pcsp_platform_driver);
+}
+
+module_init(pcsp_init);
+module_exit(pcsp_exit);
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
new file mode 100644
index 0000000..f07cc1e
--- /dev/null
+++ b/sound/drivers/pcsp/pcsp.h
@@ -0,0 +1,82 @@
+/*
+ * PC-Speaker driver for Linux
+ *
+ * Copyright (C) 1993-1997 Michael Beck
+ * Copyright (C) 1997-2001 David Woodhouse
+ * Copyright (C) 2001-2008 Stas Sergeev
+ */
+
+#ifndef __PCSP_H__
+#define __PCSP_H__
+
+#include <linux/hrtimer.h>
+#if defined(CONFIG_MIPS) || defined(CONFIG_X86)
+/* Use the global PIT lock ! */
+#include <asm/i8253.h>
+#else
+#include <asm/8253pit.h>
+static DEFINE_SPINLOCK(i8253_lock);
+#endif
+
+#define PCSP_SOUND_VERSION 0x400 /* read 4.00 */
+#define PCSP_DEBUG 0
+
+/* default timer freq for PC-Speaker: 18643 Hz */
+#define DIV_18KHZ 64
+#define MAX_DIV DIV_18KHZ
+#define CUR_DIV() (MAX_DIV >> chip->treble)
+#define PCSP_MAX_TREBLE 1
+
+/* unfortunately, with hrtimers 37KHz does not work very well :( */
+#define PCSP_DEFAULT_TREBLE 0
+#define MIN_DIV (MAX_DIV >> PCSP_MAX_TREBLE)
+
+/* wild guess */
+#define PCSP_MIN_LPJ 1000000
+#define PCSP_DEFAULT_SDIV (DIV_18KHZ >> 1)
+#define PCSP_DEFAULT_SRATE (PIT_TICK_RATE / PCSP_DEFAULT_SDIV)
+#define PCSP_INDEX_INC() (1 << (PCSP_MAX_TREBLE - chip->treble))
+#define PCSP_RATE() (PIT_TICK_RATE / CUR_DIV())
+#define PCSP_MIN_RATE__1 MAX_DIV/PIT_TICK_RATE
+#define PCSP_MAX_RATE__1 MIN_DIV/PIT_TICK_RATE
+#define PCSP_MAX_PERIOD_NS (1000000000ULL * PCSP_MIN_RATE__1)
+#define PCSP_MIN_PERIOD_NS (1000000000ULL * PCSP_MAX_RATE__1)
+#define PCSP_CALC_NS(div) ({ \
+ u64 __val = 1000000000ULL * (div); \
+ do_div(__val, PIT_TICK_RATE); \
+ __val; \
+})
+#define PCSP_PERIOD_NS() PCSP_CALC_NS(CUR_DIV())
+
+#define PCSP_MAX_PERIOD_SIZE (64*1024)
+#define PCSP_MAX_PERIODS 512
+#define PCSP_BUFFER_SIZE (128*1024)
+
+struct snd_pcsp {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct input_dev *input_dev;
+ struct hrtimer timer;
+ unsigned short port, irq, dma;
+ spinlock_t substream_lock;
+ struct snd_pcm_substream *playback_substream;
+ size_t playback_ptr;
+ size_t period_ptr;
+ atomic_t timer_active;
+ int thalf;
+ u64 ns_rem;
+ unsigned char val61;
+ int enable;
+ int max_treble;
+ int treble;
+ int pcspkr;
+};
+
+extern struct snd_pcsp pcsp_chip;
+
+extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
+
+extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
+extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
+
+#endif
diff --git a/sound/drivers/pcsp/pcsp_input.c b/sound/drivers/pcsp/pcsp_input.c
new file mode 100644
index 0000000..cd9b83e
--- /dev/null
+++ b/sound/drivers/pcsp/pcsp_input.c
@@ -0,0 +1,116 @@
+/*
+ * PC Speaker beeper driver for Linux
+ *
+ * Copyright (c) 2002 Vojtech Pavlik
+ * Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <asm/io.h>
+#include "pcsp.h"
+
+static void pcspkr_do_sound(unsigned int count)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8253_lock, flags);
+
+ if (count) {
+ /* enable counter 2 */
+ outb_p(inb_p(0x61) | 3, 0x61);
+ /* set command for counter 2, 2 byte write */
+ outb_p(0xB6, 0x43);
+ /* select desired HZ */
+ outb_p(count & 0xff, 0x42);
+ outb((count >> 8) & 0xff, 0x42);
+ } else {
+ /* disable counter 2 */
+ outb(inb_p(0x61) & 0xFC, 0x61);
+ }
+
+ spin_unlock_irqrestore(&i8253_lock, flags);
+}
+
+void pcspkr_stop_sound(void)
+{
+ pcspkr_do_sound(0);
+}
+
+static int pcspkr_input_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ unsigned int count = 0;
+
+ if (atomic_read(&pcsp_chip.timer_active) || !pcsp_chip.pcspkr)
+ return 0;
+
+ switch (type) {
+ case EV_SND:
+ switch (code) {
+ case SND_BELL:
+ if (value)
+ value = 1000;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = PIT_TICK_RATE / value;
+
+ pcspkr_do_sound(count);
+
+ return 0;
+}
+
+int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev)
+{
+ int err;
+
+ struct input_dev *input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "PC Speaker";
+ input_dev->phys = "isa0061/input0";
+ input_dev->id.bustype = BUS_ISA;
+ input_dev->id.vendor = 0x001f;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = dev;
+
+ input_dev->evbit[0] = BIT(EV_SND);
+ input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
+ input_dev->event = pcspkr_input_event;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+ *rdev = input_dev;
+ return 0;
+}
+
+int pcspkr_input_remove(struct input_dev *dev)
+{
+ pcspkr_stop_sound();
+ input_unregister_device(dev); /* this also does kfree() */
+
+ return 0;
+}
diff --git a/sound/drivers/pcsp/pcsp_input.h b/sound/drivers/pcsp/pcsp_input.h
new file mode 100644
index 0000000..e66738c
--- /dev/null
+++ b/sound/drivers/pcsp/pcsp_input.h
@@ -0,0 +1,14 @@
+/*
+ * PC-Speaker driver for Linux
+ *
+ * Copyright (C) 2001-2008 Stas Sergeev
+ */
+
+#ifndef __PCSP_INPUT_H__
+#define __PCSP_INPUT_H__
+
+int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev);
+int pcspkr_input_remove(struct input_dev *dev);
+void pcspkr_stop_sound(void);
+
+#endif
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
new file mode 100644
index 0000000..ac6238e
--- /dev/null
+++ b/sound/drivers/pcsp/pcsp_lib.c
@@ -0,0 +1,338 @@
+/*
+ * PC-Speaker driver for Linux
+ *
+ * Copyright (C) 1993-1997 Michael Beck
+ * Copyright (C) 1997-2001 David Woodhouse
+ * Copyright (C) 2001-2008 Stas Sergeev
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include "pcsp.h"
+
+static int nforce_wa;
+module_param(nforce_wa, bool, 0444);
+MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
+ "(expect bad sound)");
+
+static void pcsp_start_timer(unsigned long dummy)
+{
+ hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+}
+
+/*
+ * We need the hrtimer_start as a tasklet to avoid
+ * the nasty locking problem. :(
+ * The problem:
+ * - The timer handler is called with the cpu_base->lock
+ * already held by hrtimer code.
+ * - snd_pcm_period_elapsed() takes the
+ * substream->self_group.lock.
+ * So far so good.
+ * But the snd_pcsp_trigger() is called with the
+ * substream->self_group.lock held, and it calls
+ * hrtimer_start(), which takes the cpu_base->lock.
+ * You see the problem. We have the code pathes
+ * which take two locks in a reverse order. This
+ * can deadlock and the lock validator complains.
+ * The only solution I could find was to move the
+ * hrtimer_start() into a tasklet. -stsp
+ */
+static DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0);
+
+enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
+{
+ unsigned long flags;
+ unsigned char timer_cnt, val;
+ int periods_elapsed;
+ u64 ns;
+ size_t period_bytes, buffer_bytes;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
+
+ if (chip->thalf) {
+ outb(chip->val61, 0x61);
+ chip->thalf = 0;
+ if (!atomic_read(&chip->timer_active))
+ return HRTIMER_NORESTART;
+ hrtimer_forward(&chip->timer, chip->timer.expires,
+ ktime_set(0, chip->ns_rem));
+ return HRTIMER_RESTART;
+ }
+
+ /* hrtimer calls us from both hardirq and softirq contexts,
+ * so irqsave :( */
+ spin_lock_irqsave(&chip->substream_lock, flags);
+ /* Takashi Iwai says regarding this extra lock:
+
+ If the irq handler handles some data on the DMA buffer, it should
+ do snd_pcm_stream_lock().
+ That protects basically against all races among PCM callbacks, yes.
+ However, there are two remaining issues:
+ 1. The substream pointer you try to lock isn't protected _before_
+ this lock yet.
+ 2. snd_pcm_period_elapsed() itself acquires the lock.
+ The requirement of another lock is because of 1. When you get
+ chip->playback_substream, it's not protected.
+ Keeping this lock while snd_pcm_period_elapsed() assures the substream
+ is still protected (at least, not released). And the other status is
+ handled properly inside snd_pcm_stream_lock() in
+ snd_pcm_period_elapsed().
+
+ */
+ if (!chip->playback_substream)
+ goto exit_nr_unlock1;
+ substream = chip->playback_substream;
+ snd_pcm_stream_lock(substream);
+ if (!atomic_read(&chip->timer_active))
+ goto exit_nr_unlock2;
+
+ runtime = substream->runtime;
+ /* assume it is u8 mono */
+ val = runtime->dma_area[chip->playback_ptr];
+ timer_cnt = val * CUR_DIV() / 256;
+
+ if (timer_cnt && chip->enable) {
+ spin_lock(&i8253_lock);
+ if (!nforce_wa) {
+ outb_p(chip->val61, 0x61);
+ outb_p(timer_cnt, 0x42);
+ outb(chip->val61 ^ 1, 0x61);
+ } else {
+ outb(chip->val61 ^ 2, 0x61);
+ chip->thalf = 1;
+ }
+ spin_unlock(&i8253_lock);
+ }
+
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+ chip->playback_ptr += PCSP_INDEX_INC();
+ periods_elapsed = chip->playback_ptr - chip->period_ptr;
+ if (periods_elapsed < 0) {
+ printk(KERN_WARNING "PCSP: playback_ptr inconsistent "
+ "(%zi %zi %zi)\n",
+ chip->playback_ptr, period_bytes, buffer_bytes);
+ periods_elapsed += buffer_bytes;
+ }
+ periods_elapsed /= period_bytes;
+ /* wrap the pointer _before_ calling snd_pcm_period_elapsed(),
+ * or ALSA will BUG on us. */
+ chip->playback_ptr %= buffer_bytes;
+
+ snd_pcm_stream_unlock(substream);
+
+ if (periods_elapsed) {
+ snd_pcm_period_elapsed(substream);
+ chip->period_ptr += periods_elapsed * period_bytes;
+ chip->period_ptr %= buffer_bytes;
+ }
+
+ spin_unlock_irqrestore(&chip->substream_lock, flags);
+
+ if (!atomic_read(&chip->timer_active))
+ return HRTIMER_NORESTART;
+
+ chip->ns_rem = PCSP_PERIOD_NS();
+ ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
+ chip->ns_rem -= ns;
+ hrtimer_forward(&chip->timer, chip->timer.expires, ktime_set(0, ns));
+ return HRTIMER_RESTART;
+
+exit_nr_unlock2:
+ snd_pcm_stream_unlock(substream);
+exit_nr_unlock1:
+ spin_unlock_irqrestore(&chip->substream_lock, flags);
+ return HRTIMER_NORESTART;
+}
+
+static void pcsp_start_playing(struct snd_pcsp *chip)
+{
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: start_playing called\n");
+#endif
+ if (atomic_read(&chip->timer_active)) {
+ printk(KERN_ERR "PCSP: Timer already active\n");
+ return;
+ }
+
+ spin_lock(&i8253_lock);
+ chip->val61 = inb(0x61) | 0x03;
+ outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */
+ spin_unlock(&i8253_lock);
+ atomic_set(&chip->timer_active, 1);
+ chip->thalf = 0;
+
+ tasklet_schedule(&pcsp_start_timer_tasklet);
+}
+
+static void pcsp_stop_playing(struct snd_pcsp *chip)
+{
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: stop_playing called\n");
+#endif
+ if (!atomic_read(&chip->timer_active))
+ return;
+
+ atomic_set(&chip->timer_active, 0);
+ spin_lock(&i8253_lock);
+ /* restore the timer */
+ outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */
+ outb(chip->val61 & 0xFC, 0x61);
+ spin_unlock(&i8253_lock);
+}
+
+static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: close called\n");
+#endif
+ if (atomic_read(&chip->timer_active)) {
+ printk(KERN_ERR "PCSP: timer still active\n");
+ pcsp_stop_playing(chip);
+ }
+ spin_lock_irq(&chip->substream_lock);
+ chip->playback_substream = NULL;
+ spin_unlock_irq(&chip->substream_lock);
+ return 0;
+}
+
+static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int err;
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
+{
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: hw_free called\n");
+#endif
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: prepare called, "
+ "size=%zi psize=%zi f=%zi f1=%i\n",
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream),
+ snd_pcm_lib_buffer_bytes(substream) /
+ snd_pcm_lib_period_bytes(substream),
+ substream->runtime->periods);
+#endif
+ chip->playback_ptr = 0;
+ chip->period_ptr = 0;
+ return 0;
+}
+
+static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: trigger called\n");
+#endif
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ pcsp_start_playing(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ pcsp_stop_playing(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
+ return bytes_to_frames(substream->runtime, chip->playback_ptr);
+}
+
+static struct snd_pcm_hardware snd_pcsp_playback = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_HALF_DUPLEX |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = PCSP_DEFAULT_SRATE,
+ .rate_max = PCSP_DEFAULT_SRATE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = PCSP_BUFFER_SIZE,
+ .period_bytes_min = 64,
+ .period_bytes_max = PCSP_MAX_PERIOD_SIZE,
+ .periods_min = 2,
+ .periods_max = PCSP_MAX_PERIODS,
+ .fifo_size = 0,
+};
+
+static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: open called\n");
+#endif
+ if (atomic_read(&chip->timer_active)) {
+ printk(KERN_ERR "PCSP: still active!!\n");
+ return -EBUSY;
+ }
+ runtime->hw = snd_pcsp_playback;
+ spin_lock_irq(&chip->substream_lock);
+ chip->playback_substream = substream;
+ spin_unlock_irq(&chip->substream_lock);
+ return 0;
+}
+
+static struct snd_pcm_ops snd_pcsp_playback_ops = {
+ .open = snd_pcsp_playback_open,
+ .close = snd_pcsp_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_pcsp_playback_hw_params,
+ .hw_free = snd_pcsp_playback_hw_free,
+ .prepare = snd_pcsp_playback_prepare,
+ .trigger = snd_pcsp_trigger,
+ .pointer = snd_pcsp_playback_pointer,
+};
+
+int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
+{
+ int err;
+
+ err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_pcsp_playback_ops);
+
+ chip->pcm->private_data = chip;
+ chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+ strcpy(chip->pcm->name, "pcsp");
+
+ snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL), PCSP_BUFFER_SIZE,
+ PCSP_BUFFER_SIZE);
+
+ return 0;
+}
diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c
new file mode 100644
index 0000000..64a695f
--- /dev/null
+++ b/sound/drivers/pcsp/pcsp_mixer.c
@@ -0,0 +1,143 @@
+/*
+ * PC-Speaker driver for Linux
+ *
+ * Mixer implementation.
+ * Copyright (C) 2001-2008 Stas Sergeev
+ */
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include "pcsp.h"
+
+
+static int pcsp_enable_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 pcsp_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = chip->enable;
+ return 0;
+}
+
+static int pcsp_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int enab = ucontrol->value.integer.value[0];
+ if (enab != chip->enable) {
+ chip->enable = enab;
+ changed = 1;
+ }
+ return changed;
+}
+
+static int pcsp_treble_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = chip->max_treble + 1;
+ if (uinfo->value.enumerated.item > chip->max_treble)
+ uinfo->value.enumerated.item = chip->max_treble;
+ sprintf(uinfo->value.enumerated.name, "%d", PCSP_RATE());
+ return 0;
+}
+
+static int pcsp_treble_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = chip->treble;
+ return 0;
+}
+
+static int pcsp_treble_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int treble = ucontrol->value.enumerated.item[0];
+ if (treble != chip->treble) {
+ chip->treble = treble;
+#if PCSP_DEBUG
+ printk(KERN_INFO "PCSP: rate set to %i\n", PCSP_RATE());
+#endif
+ changed = 1;
+ }
+ return changed;
+}
+
+static int pcsp_pcspkr_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 pcsp_pcspkr_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = chip->pcspkr;
+ return 0;
+}
+
+static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int spkr = ucontrol->value.integer.value[0];
+ if (spkr != chip->pcspkr) {
+ chip->pcspkr = spkr;
+ changed = 1;
+ }
+ return changed;
+}
+
+#define PCSP_MIXER_CONTROL(ctl_type, ctl_name) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = ctl_name, \
+ .info = pcsp_##ctl_type##_info, \
+ .get = pcsp_##ctl_type##_get, \
+ .put = pcsp_##ctl_type##_put, \
+}
+
+static struct snd_kcontrol_new __devinitdata snd_pcsp_controls[] = {
+ PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
+ PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
+ PCSP_MIXER_CONTROL(pcspkr, "PC Speaker Playback Switch"),
+};
+
+int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip)
+{
+ struct snd_card *card = chip->card;
+ int i, err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_pcsp_controls); i++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(snd_pcsp_controls + i,
+ chip));
+ if (err < 0)
+ return err;
+ }
+
+ strcpy(card->mixername, "PC-Speaker");
+
+ return 0;
+}
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 15061bd..d20d893b 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -27,6 +27,7 @@
#include <sound/pcm.h>
#include <sound/ak4114.h>
#include <sound/asoundef.h>
+#include <sound/info.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei");
@@ -446,6 +447,26 @@
}
};
+
+static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct ak4114 *ak4114 = entry->private_data;
+ int reg, val;
+ /* all ak4114 registers 0x00 - 0x1f */
+ for (reg = 0; reg < 0x20; reg++) {
+ val = reg_read(ak4114, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+static void snd_ak4114_proc_init(struct ak4114 *ak4114)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ak4114->card, "ak4114", &entry))
+ snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read);
+}
+
int snd_ak4114_build(struct ak4114 *ak4114,
struct snd_pcm_substream *ply_substream,
struct snd_pcm_substream *cap_substream)
@@ -478,6 +499,7 @@
return err;
ak4114->kctls[idx] = kctl;
}
+ snd_ak4114_proc_init(ak4114);
/* trigger workq */
schedule_delayed_work(&ak4114->work, HZ / 10);
return 0;
@@ -590,7 +612,7 @@
struct ak4114 *chip = container_of(work, struct ak4114, work.work);
if (!chip->init)
- snd_ak4114_check_rate_and_errors(chip, 0);
+ snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
schedule_delayed_work(&chip->work, HZ / 10);
}
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 35fbbf2..288926d 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -70,7 +70,8 @@
}
/* reset procedure for AK4355 and AK4358 */
-static void ak4355_reset(struct snd_akm4xxx *ak, int state)
+static void ak435X_reset(struct snd_akm4xxx *ak, int state,
+ unsigned char total_regs)
{
unsigned char reg;
@@ -78,7 +79,7 @@
snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
return;
}
- for (reg = 0x00; reg < 0x0b; reg++)
+ for (reg = 0x00; reg < total_regs; reg++)
if (reg != 0x01)
snd_akm4xxx_write(ak, 0, reg,
snd_akm4xxx_get(ak, 0, reg));
@@ -118,8 +119,10 @@
/* FIXME: needed for ak4529? */
break;
case SND_AK4355:
+ ak435X_reset(ak, state, 0x0b);
+ break;
case SND_AK4358:
- ak4355_reset(ak, state);
+ ak435X_reset(ak, state, 0x10);
break;
case SND_AK4381:
ak4381_reset(ak, state);
@@ -292,11 +295,6 @@
case SND_AK5365:
/* FIXME: any init sequence? */
return;
- case NON_AKM:
- /* fake value for non-akm codecs using akm infrastructure
- * (e.g. of ice1724) - certainly FIXME
- */
- return;
default:
snd_BUG();
return;
@@ -374,6 +372,8 @@
nval = mask - nval;
if (AK_GET_NEEDSMSB(kcontrol->private_value))
nval |= 0x80;
+ /* printk(KERN_DEBUG "DEBUG - AK writing reg: chip %x addr %x,
+ nval %x\n", chip, addr, nval); */
snd_akm4xxx_write(ak, chip, addr, nval);
return 1;
}
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index bed29ca..f3fd7b4 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -331,7 +331,7 @@
return -EFAULT;
if ((file_h.name != RIFF_HEADER) ||
(le32_to_cpu(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) {
- snd_printd("%s: Invalid RIFF header\n", __FUNCTION__);
+ snd_printd("%s: Invalid RIFF header\n", __func__);
return -EINVAL;
}
data_ptr += sizeof(file_h);
@@ -340,7 +340,7 @@
if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
return -EFAULT;
if (item_type != CSP__HEADER) {
- snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__);
+ snd_printd("%s: Invalid RIFF file type\n", __func__);
return -EINVAL;
}
data_ptr += sizeof (item_type);
@@ -395,7 +395,7 @@
return -EFAULT;
if (code_h.name != MAIN_HEADER) {
- snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__);
+ snd_printd("%s: Missing 'main' microcode\n", __func__);
return -EINVAL;
}
data_ptr += sizeof(code_h);
@@ -439,7 +439,7 @@
p->acc_format = p->acc_width = p->acc_rates = 0;
p->mode = 0;
snd_printd("%s: Unsupported CSP codec type: 0x%04x\n",
- __FUNCTION__,
+ __func__,
le16_to_cpu(funcdesc_h.VOC_type));
return -EINVAL;
}
@@ -458,7 +458,7 @@
return 0;
}
}
- snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req);
+ snd_printd("%s: Function #%d not found\n", __func__, info.func_req);
return -EINVAL;
}
@@ -612,7 +612,7 @@
static int snd_sb_csp_check_version(struct snd_sb_csp * p)
{
if (p->version < 0x10 || p->version > 0x1f) {
- snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version);
+ snd_printd("%s: Invalid CSP version: 0x%x\n", __func__, p->version);
return 1;
}
return 0;
@@ -631,7 +631,7 @@
spin_lock_irqsave(&p->chip->reg_lock, flags);
snd_sbdsp_command(p->chip, 0x01); /* CSP download command */
if (snd_sbdsp_get_byte(p->chip)) {
- snd_printd("%s: Download command failed\n", __FUNCTION__);
+ snd_printd("%s: Download command failed\n", __func__);
goto __fail;
}
/* Send CSP low byte (size - 1) */
@@ -658,7 +658,7 @@
udelay (10);
}
if (status != 0x55) {
- snd_printd("%s: Microcode initialization failed\n", __FUNCTION__);
+ snd_printd("%s: Microcode initialization failed\n", __func__);
goto __fail;
}
} else {
@@ -824,19 +824,19 @@
unsigned long flags;
if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) {
- snd_printd("%s: Microcode not loaded\n", __FUNCTION__);
+ snd_printd("%s: Microcode not loaded\n", __func__);
return -ENXIO;
}
if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
- snd_printd("%s: CSP already running\n", __FUNCTION__);
+ snd_printd("%s: CSP already running\n", __func__);
return -EBUSY;
}
if (!(sample_width & p->acc_width)) {
- snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__);
+ snd_printd("%s: Unsupported PCM sample width\n", __func__);
return -EINVAL;
}
if (!(channels & p->acc_channels)) {
- snd_printd("%s: Invalid number of channels\n", __FUNCTION__);
+ snd_printd("%s: Invalid number of channels\n", __func__);
return -EINVAL;
}
@@ -858,11 +858,11 @@
s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */
if (set_codec_parameter(p->chip, 0x81, s_type)) {
- snd_printd("%s: Set sample type command failed\n", __FUNCTION__);
+ snd_printd("%s: Set sample type command failed\n", __func__);
goto __fail;
}
if (set_codec_parameter(p->chip, 0x80, 0x00)) {
- snd_printd("%s: Codec start command failed\n", __FUNCTION__);
+ snd_printd("%s: Codec start command failed\n", __func__);
goto __fail;
}
p->run_width = sample_width;
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index d63c1af..b432d9a 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -51,7 +51,7 @@
outb(val, SBP(chip, COMMAND));
return 1;
}
- snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val);
+ snd_printd("%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
return 0;
}
@@ -68,7 +68,7 @@
return val;
}
}
- snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port);
+ snd_printd("%s [0x%lx]: timeout\n", __func__, chip->port);
return -ENODEV;
}
@@ -87,7 +87,7 @@
else
break;
}
- snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port);
+ snd_printdd("%s [0x%lx] failed...\n", __func__, chip->port);
return -ENODEV;
}
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
index eaf6997..1e90d76 100644
--- a/sound/oss/dmabuf.c
+++ b/sound/oss/dmabuf.c
@@ -795,9 +795,9 @@
#ifdef BE_CONSERVATIVE
active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
#else
- active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT);
+ active_offs = max(DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT), 0);
/* Check for pointer wrapping situation */
- if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
+ if (active_offs >= dmap->bytes_in_use)
active_offs = 0;
active_offs += dmap->byte_counter;
#endif
diff --git a/sound/oss/trident.c b/sound/oss/trident.c
index d6af906..f43f91e 100644
--- a/sound/oss/trident.c
+++ b/sound/oss/trident.c
@@ -3076,8 +3076,7 @@
u16 wcontrol;
unsigned long flags;
- if (!card)
- BUG();
+ BUG_ON(!card);
address = ALI_AC97_READ;
if (card->revision == ALI_5451_V02) {
@@ -3148,8 +3147,7 @@
data = ((u32) val) << 16;
- if (!card)
- BUG();
+ BUG_ON(!card);
address = ALI_AC97_WRITE;
mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY;
@@ -3213,8 +3211,7 @@
struct trident_card *card = NULL;
/* Added by Matt Wu */
- if (!codec)
- BUG();
+ BUG_ON(!codec);
card = (struct trident_card *) codec->private_data;
@@ -3240,8 +3237,7 @@
struct trident_card *card;
/* Added by Matt Wu */
- if (!codec)
- BUG();
+ BUG_ON(!codec);
card = (struct trident_card *) codec->private_data;
diff --git a/sound/oss/trident.h b/sound/oss/trident.h
index 4713b49..ff30a1d 100644
--- a/sound/oss/trident.h
+++ b/sound/oss/trident.h
@@ -322,7 +322,7 @@
#define VALIDATE_MAGIC(FOO,MAG) \
({ \
if (!(FOO) || (FOO)->magic != MAG) { \
- printk(invalid_magic,__FUNCTION__); \
+ printk(invalid_magic,__func__); \
return -ENXIO; \
} \
})
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
index d25249a..2c5aaa5 100644
--- a/sound/oss/vwsnd.c
+++ b/sound/oss/vwsnd.c
@@ -194,11 +194,11 @@
* DBGRV - debug print function return when verbose
*/
-#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e))
+#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__func__, __LINE__, #e))
#define DBGDO(x) x
#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args))
-#define DBGP(fmt, args...) (DBGX("%s: " fmt, __FUNCTION__ , ##args))
-#define DBGE(fmt, args...) (DBGX("%s" fmt, __FUNCTION__ , ##args))
+#define DBGP(fmt, args...) (DBGX("%s: " fmt, __func__ , ##args))
+#define DBGE(fmt, args...) (DBGX("%s" fmt, __func__ , ##args))
#define DBGC(rtn) (DBGP("calling %s\n", rtn))
#define DBGR() (DBGP("returning\n"))
#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args))
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 812085d..581debf 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -122,6 +122,21 @@
To compile this driver as a module, choose M here: the module
will be called snd-au8830.
+config SND_AW2
+ tristate "Emagic Audiowerk 2"
+ depends on SND
+ help
+ Say Y here to include support for Emagic Audiowerk 2 soundcards.
+
+ Supported features: Analog and SPDIF output. Analog or SPDIF input.
+ Note: Switch between analog and digital input does not always work.
+ It can produce continuous noise. The workaround is to switch again
+ (and again) between digital and analog input until it works.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-aw2.
+
+
config SND_AZT3328
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
depends on SND && EXPERIMENTAL
@@ -162,6 +177,7 @@
depends on SND
select SND_AC97_CODEC
select SND_RAWMIDI
+ select SND_VMASTER
help
Say Y here to include support for the Sound Blaster Audigy LS
and Live 24bit.
@@ -517,6 +533,7 @@
tristate "Intel HD Audio"
depends on SND
select SND_PCM
+ select SND_VMASTER
help
Say Y here to include support for Intel "High Definition
Audio" (Azalia) motherboard devices.
@@ -680,6 +697,7 @@
depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
+ select SND_VMASTER
help
Say Y here to include support for soundcards based on
ICE/VT1724/1720 (Envy24HT/PT) chips.
@@ -896,12 +914,12 @@
will be called snd-via82xx-modem.
config SND_VIRTUOSO
- tristate "Asus Virtuoso 200 (Xonar)"
+ tristate "Asus Virtuoso 100/200 (Xonar)"
depends on SND
select SND_OXYGEN_LIB
help
Say Y here to include support for sound cards based on the
- Asus AV200 chip, i.e., Xonar D2 and Xonar D2X.
+ Asus AV100/AV200 chips, i.e., Xonar D2, DX and D2X.
To compile this driver as a module, choose M here: the module
will be called snd-virtuoso.
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 2d42fd2..85ef14b 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -58,6 +58,7 @@
ac97/ \
ali5451/ \
au88x0/ \
+ aw2/ \
ca0106/ \
cs46xx/ \
cs5535audio/ \
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 50c637e..39198e5 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -114,10 +114,9 @@
static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[] = { "2ch", "4ch", "6ch" };
- if (kcontrol->private_value)
- return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */
- return ac97_enum_text_info(kcontrol, uinfo, texts, 3);
+ static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" };
+ return ac97_enum_text_info(kcontrol, uinfo, texts,
+ kcontrol->private_value);
}
static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -133,13 +132,8 @@
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
unsigned char mode = ucontrol->value.enumerated.item[0];
- if (kcontrol->private_value) {
- if (mode >= 2)
- return -EINVAL;
- } else {
- if (mode >= 3)
- return -EINVAL;
- }
+ if (mode >= kcontrol->private_value)
+ return -EINVAL;
if (mode != ac97->channel_mode) {
ac97->channel_mode = mode;
@@ -158,6 +152,7 @@
.get = ac97_surround_jack_mode_get, \
.put = ac97_surround_jack_mode_put, \
}
+/* 6ch */
#define AC97_CHANNEL_MODE_CTL \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -165,7 +160,9 @@
.info = ac97_channel_mode_info, \
.get = ac97_channel_mode_get, \
.put = ac97_channel_mode_put, \
+ .private_value = 3, \
}
+/* 4ch */
#define AC97_CHANNEL_MODE_4CH_CTL \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -173,7 +170,17 @@
.info = ac97_channel_mode_info, \
.get = ac97_channel_mode_get, \
.put = ac97_channel_mode_put, \
- .private_value = 1, \
+ .private_value = 2, \
+ }
+/* 8ch */
+#define AC97_CHANNEL_MODE_8CH_CTL \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Channel Mode", \
+ .info = ac97_channel_mode_info, \
+ .get = ac97_channel_mode_get, \
+ .put = ac97_channel_mode_put, \
+ .private_value = 4, \
}
static inline int is_surround_on(struct snd_ac97 *ac97)
@@ -210,6 +217,10 @@
return !ac97->indep_surround && !is_clfe_on(ac97);
}
+static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97)
+{
+ return is_surround_on(ac97);
+}
/* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */
/* Modified for YMF743 by Keita Maehara <maehara@debian.org> */
@@ -2816,10 +2827,12 @@
#define AC97_ALC850_JACK_SELECT 0x76
#define AC97_ALC850_MISC1 0x7a
+#define AC97_ALC850_MULTICH 0x6a
static void alc850_update_jacks(struct snd_ac97 *ac97)
{
int shared;
+ int aux_is_back_surround;
/* shared Line-In / Surround Out */
shared = is_shared_surrout(ac97);
@@ -2837,13 +2850,18 @@
/* MIC-IN = 1, CENTER-LFE = 5 */
snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
shared ? (5<<4) : (1<<4));
+
+ aux_is_back_surround = alc850_is_aux_back_surround(ac97);
+ /* Aux is Back Surround */
+ snd_ac97_update_bits(ac97, AC97_ALC850_MULTICH, 1 << 10,
+ aux_is_back_surround ? (1<<10) : (0<<10));
}
static const struct snd_kcontrol_new snd_ac97_controls_alc850[] = {
AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1),
AC97_SURROUND_JACK_MODE_CTL,
- AC97_CHANNEL_MODE_CTL,
+ AC97_CHANNEL_MODE_8CH_CTL,
};
static int patch_alc850_specific(struct snd_ac97 *ac97)
@@ -2869,6 +2887,7 @@
ac97->build_ops = &patch_alc850_ops;
ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */
+ ac97->flags |= AC97_HAS_8CH;
/* assume only page 0 for writing cache */
snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
@@ -2878,6 +2897,7 @@
spdif-in monitor off, spdif-in PCM off
center on mic off, surround on line-in off
duplicate front off
+ NB default bit 10=0 = Aux is Capture, not Back Surround
*/
snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
/* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index 3674f35..48cbda9 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -574,7 +574,6 @@
r = rate > 48000;
bus = pcm->bus;
if (cfg == AC97_PCM_CFG_SPDIF) {
- int err;
for (cidx = 0; cidx < 4; cidx++)
if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) {
err = set_spdif_rate(bus->codec[cidx], rate);
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index a66d515..39ec55b 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -264,10 +264,10 @@
mdelay(1);
if (!retry) {
snd_printk(KERN_ERR PFX "[%s] Link is not ready.\n",
- __FUNCTION__);
+ __func__);
return -EIO;
}
- ad1889_debug("[%s] ready after %d ms\n", __FUNCTION__, 400 - retry);
+ ad1889_debug("[%s] ready after %d ms\n", __func__, 400 - retry);
return 0;
}
@@ -854,8 +854,6 @@
spin_unlock_irq(&chip->lock);
- synchronize_irq(chip->irq);
-
if (chip->irq >= 0)
free_irq(chip->irq, chip);
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 6a905ed..1a0fd65 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -1809,26 +1809,26 @@
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ali *codec = kcontrol->private_data;
- unsigned int enable;
+ unsigned int spdif_enable;
- enable = ucontrol->value.integer.value[0] ? 1 : 0;
+ spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
spin_lock_irq(&codec->reg_lock);
switch (kcontrol->private_value) {
case 0:
- enable = (codec->spdif_mask & 0x02) ? 1 : 0;
+ spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0;
break;
case 1:
- enable = ((codec->spdif_mask & 0x02) &&
+ spdif_enable = ((codec->spdif_mask & 0x02) &&
(codec->spdif_mask & 0x04)) ? 1 : 0;
break;
case 2:
- enable = (codec->spdif_mask & 0x01) ? 1 : 0;
+ spdif_enable = (codec->spdif_mask & 0x01) ? 1 : 0;
break;
default:
break;
}
- ucontrol->value.integer.value[0] = enable;
+ ucontrol->value.integer.value[0] = spdif_enable;
spin_unlock_irq(&codec->reg_lock);
return 0;
}
@@ -1837,17 +1837,17 @@
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ali *codec = kcontrol->private_data;
- unsigned int change = 0, enable = 0;
+ unsigned int change = 0, spdif_enable = 0;
- enable = ucontrol->value.integer.value[0] ? 1 : 0;
+ spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
spin_lock_irq(&codec->reg_lock);
switch (kcontrol->private_value) {
case 0:
change = (codec->spdif_mask & 0x02) ? 1 : 0;
- change = change ^ enable;
+ change = change ^ spdif_enable;
if (change) {
- if (enable) {
+ if (spdif_enable) {
codec->spdif_mask |= 0x02;
snd_ali_enable_spdif_out(codec);
} else {
@@ -1859,9 +1859,9 @@
break;
case 1:
change = (codec->spdif_mask & 0x04) ? 1 : 0;
- change = change ^ enable;
+ change = change ^ spdif_enable;
if (change && (codec->spdif_mask & 0x02)) {
- if (enable) {
+ if (spdif_enable) {
codec->spdif_mask |= 0x04;
snd_ali_enable_spdif_chnout(codec);
} else {
@@ -1872,9 +1872,9 @@
break;
case 2:
change = (codec->spdif_mask & 0x01) ? 1 : 0;
- change = change ^ enable;
+ change = change ^ spdif_enable;
if (change) {
- if (enable) {
+ if (spdif_enable) {
codec->spdif_mask |= 0x01;
snd_ali_enable_spdif_in(codec);
} else {
@@ -2047,10 +2047,8 @@
{
if (codec->hw_initialized)
snd_ali_disable_address_interrupt(codec);
- if (codec->irq >= 0) {
- synchronize_irq(codec->irq);
+ if (codec->irq >= 0)
free_irq(codec->irq, codec);
- }
if (codec->port)
pci_release_regions(codec->pci);
pci_disable_device(codec->pci);
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 0e990a7..8df6824 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -92,8 +92,8 @@
#if DEBUG_CALLS
#define snd_als300_dbgcalls(format, args...) printk(format, ##args)
-#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__)
-#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__)
+#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
+#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
#else
#define snd_als300_dbgcalls(format, args...)
#define snd_als300_dbgcallenter()
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 4594186..457228f 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1553,7 +1553,7 @@
if (chip->irq < 0)
goto __hw_end;
snd_atiixp_chip_stop(chip);
- synchronize_irq(chip->irq);
+
__hw_end:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index a67a869..d457a32 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1197,7 +1197,7 @@
if (chip->irq < 0)
goto __hw_end;
snd_atiixp_chip_stop(chip);
- synchronize_irq(chip->irq);
+
__hw_end:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 26819e2..68368e4 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -126,7 +126,6 @@
vortex_gameport_unregister(vortex);
vortex_core_shutdown(vortex);
// Take down PCI interface.
- synchronize_irq(vortex->irq);
free_irq(vortex->irq, vortex);
iounmap(vortex->mmio);
pci_release_regions(vortex->pci_dev);
@@ -220,7 +219,6 @@
return 0;
alloc_out:
- synchronize_irq(chip->irq);
free_irq(chip->irq, chip);
irq_out:
vortex_core_shutdown(chip);
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index 526c6c5..f9a58b4 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -498,14 +498,14 @@
};
/* create a pcm device */
-static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr)
+static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
{
struct snd_pcm *pcm;
struct snd_kcontrol *kctl;
int i;
int err, nr_capt;
- if ((chip == 0) || (idx < 0) || (idx >= VORTEX_PCM_LAST))
+ if (!chip || idx < 0 || idx >= VORTEX_PCM_LAST)
return -ENODEV;
/* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the
@@ -514,9 +514,9 @@
nr_capt = nr;
else
nr_capt = 0;
- if ((err =
- snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
- nr_capt, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
+ nr_capt, &pcm);
+ if (err < 0)
return err;
strcpy(pcm->name, vortex_pcm_name[idx]);
chip->pcm[idx] = pcm;
diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile
new file mode 100644
index 0000000..842335d
--- /dev/null
+++ b/sound/pci/aw2/Makefile
@@ -0,0 +1,3 @@
+snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
+
+obj-$(CONFIG_SND_AW2) += snd-aw2.o
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
new file mode 100644
index 0000000..56f87cd
--- /dev/null
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -0,0 +1,794 @@
+/*****************************************************************************
+ *
+ * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
+ * Jean-Christian Hassler <jhassler@free.fr>
+ *
+ * This file is part of the Audiowerk2 ALSA driver
+ *
+ * The Audiowerk2 ALSA driver 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; version 2.
+ *
+ * The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *****************************************************************************/
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+
+#include "saa7146.h"
+#include "aw2-saa7146.h"
+
+MODULE_AUTHOR("Cedric Bregardis <cedric.bregardis@free.fr>, "
+ "Jean-Christian Hassler <jhassler@free.fr>");
+MODULE_DESCRIPTION("Emagic Audiowerk 2 sound driver");
+MODULE_LICENSE("GPL");
+
+/*********************************
+ * DEFINES
+ ********************************/
+#define PCI_VENDOR_ID_SAA7146 0x1131
+#define PCI_DEVICE_ID_SAA7146 0x7146
+
+#define CTL_ROUTE_ANALOG 0
+#define CTL_ROUTE_DIGITAL 1
+
+/*********************************
+ * TYPEDEFS
+ ********************************/
+ /* hardware definition */
+static struct snd_pcm_hardware snd_aw2_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .channels_min = 2,
+ .channels_max = 4,
+ .buffer_bytes_max = 32768,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+static struct snd_pcm_hardware snd_aw2_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 32768,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+struct aw2_pcm_device {
+ struct snd_pcm *pcm;
+ unsigned int stream_number;
+ struct aw2 *chip;
+};
+
+struct aw2 {
+ struct snd_aw2_saa7146 saa7146;
+
+ struct pci_dev *pci;
+ int irq;
+ spinlock_t reg_lock;
+ struct mutex mtx;
+
+ unsigned long iobase_phys;
+ void __iomem *iobase_virt;
+
+ struct snd_card *card;
+
+ struct aw2_pcm_device device_playback[NB_STREAM_PLAYBACK];
+ struct aw2_pcm_device device_capture[NB_STREAM_CAPTURE];
+};
+
+/*********************************
+ * FUNCTION DECLARATIONS
+ ********************************/
+static int __init alsa_card_aw2_init(void);
+static void __exit alsa_card_aw2_exit(void);
+static int snd_aw2_dev_free(struct snd_device *device);
+static int __devinit snd_aw2_create(struct snd_card *card,
+ struct pci_dev *pci, struct aw2 **rchip);
+static int __devinit snd_aw2_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id);
+static void __devexit snd_aw2_remove(struct pci_dev *pci);
+static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream);
+static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream);
+static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream);
+static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream);
+static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params);
+static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream);
+static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream);
+static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream);
+static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
+ int cmd);
+static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
+ int cmd);
+static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
+ *substream);
+static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
+ *substream);
+static int __devinit snd_aw2_new_pcm(struct aw2 *chip);
+
+static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value
+ *ucontrol);
+static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value
+ *ucontrol);
+
+/*********************************
+ * VARIABLES
+ ********************************/
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Audiowerk2 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard.");
+
+static struct pci_device_id snd_aw2_ids[] = {
+ {PCI_VENDOR_ID_SAA7146, PCI_DEVICE_ID_SAA7146, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+ .name = "Emagic Audiowerk 2",
+ .id_table = snd_aw2_ids,
+ .probe = snd_aw2_probe,
+ .remove = __devexit_p(snd_aw2_remove),
+};
+
+/* operators for playback PCM alsa interface */
+static struct snd_pcm_ops snd_aw2_playback_ops = {
+ .open = snd_aw2_pcm_playback_open,
+ .close = snd_aw2_pcm_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_aw2_pcm_hw_params,
+ .hw_free = snd_aw2_pcm_hw_free,
+ .prepare = snd_aw2_pcm_prepare_playback,
+ .trigger = snd_aw2_pcm_trigger_playback,
+ .pointer = snd_aw2_pcm_pointer_playback,
+};
+
+/* operators for capture PCM alsa interface */
+static struct snd_pcm_ops snd_aw2_capture_ops = {
+ .open = snd_aw2_pcm_capture_open,
+ .close = snd_aw2_pcm_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_aw2_pcm_hw_params,
+ .hw_free = snd_aw2_pcm_hw_free,
+ .prepare = snd_aw2_pcm_prepare_capture,
+ .trigger = snd_aw2_pcm_trigger_capture,
+ .pointer = snd_aw2_pcm_pointer_capture,
+};
+
+static struct snd_kcontrol_new aw2_control __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Capture Route",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 0xffff,
+ .info = snd_aw2_control_switch_capture_info,
+ .get = snd_aw2_control_switch_capture_get,
+ .put = snd_aw2_control_switch_capture_put
+};
+
+/*********************************
+ * FUNCTION IMPLEMENTATIONS
+ ********************************/
+
+/* initialization of the module */
+static int __init alsa_card_aw2_init(void)
+{
+ snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
+ return pci_register_driver(&driver);
+}
+
+/* clean up the module */
+static void __exit alsa_card_aw2_exit(void)
+{
+ snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_aw2_init);
+module_exit(alsa_card_aw2_exit);
+
+/* component-destructor */
+static int snd_aw2_dev_free(struct snd_device *device)
+{
+ struct aw2 *chip = device->device_data;
+
+ /* Free hardware */
+ snd_aw2_saa7146_free(&chip->saa7146);
+
+ /* release the irq */
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ /* release the i/o ports & memory */
+ if (chip->iobase_virt)
+ iounmap(chip->iobase_virt);
+
+ pci_release_regions(chip->pci);
+ /* disable the PCI entry */
+ pci_disable_device(chip->pci);
+ /* release the data */
+ kfree(chip);
+
+ return 0;
+}
+
+/* chip-specific constructor */
+static int __devinit snd_aw2_create(struct snd_card *card,
+ struct pci_dev *pci, struct aw2 **rchip)
+{
+ struct aw2 *chip;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_aw2_dev_free,
+ };
+
+ *rchip = NULL;
+
+ /* initialize the PCI entry */
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+ pci_set_master(pci);
+
+ /* check PCI availability (32bit DMA) */
+ if ((pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) ||
+ (pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0)) {
+ printk(KERN_ERR "aw2: Impossible to set 32bit mask DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ /* initialize the stuff */
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ /* (1) PCI resource allocation */
+ err = pci_request_regions(pci, "Audiowerk2");
+ if (err < 0) {
+ pci_disable_device(pci);
+ kfree(chip);
+ return err;
+ }
+ chip->iobase_phys = pci_resource_start(pci, 0);
+ chip->iobase_virt =
+ ioremap_nocache(chip->iobase_phys,
+ pci_resource_len(pci, 0));
+
+ if (chip->iobase_virt == NULL) {
+ printk(KERN_ERR "aw2: unable to remap memory region");
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+ kfree(chip);
+ return -ENOMEM;
+ }
+
+
+ if (request_irq(pci->irq, snd_aw2_saa7146_interrupt,
+ IRQF_SHARED, "Audiowerk2", chip)) {
+ printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq);
+
+ iounmap(chip->iobase_virt);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ /* (2) initialization of the chip hardware */
+ snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ free_irq(chip->irq, (void *)chip);
+ iounmap(chip->iobase_virt);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+ *rchip = chip;
+
+ printk(KERN_INFO
+ "Audiowerk 2 sound card (saa7146 chipset) detected and "
+ "managed\n");
+ return 0;
+}
+
+/* constructor */
+static int __devinit snd_aw2_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ struct aw2 *chip;
+ int err;
+
+ /* (1) Continue if device is not enabled, else inc dev */
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ /* (2) Create card instance */
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ /* (3) Create main component */
+ err = snd_aw2_create(card, pci, &chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* initialize mutex */
+ mutex_init(&chip->mtx);
+ /* init spinlock */
+ spin_lock_init(&chip->reg_lock);
+ /* (4) Define driver ID and name string */
+ strcpy(card->driver, "aw2");
+ strcpy(card->shortname, "Audiowerk2");
+
+ sprintf(card->longname, "%s with SAA7146 irq %i",
+ card->shortname, chip->irq);
+
+ /* (5) Create other components */
+ snd_aw2_new_pcm(chip);
+
+ /* (6) Register card instance */
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* (7) Set PCI driver data */
+ pci_set_drvdata(pci, card);
+
+ dev++;
+ return 0;
+}
+
+/* destructor */
+static void __devexit snd_aw2_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/* open callback */
+static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_printdd(KERN_DEBUG "aw2: Playback_open \n");
+ runtime->hw = snd_aw2_playback_hw;
+ return 0;
+}
+
+/* close callback */
+static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+
+}
+
+static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_printdd(KERN_DEBUG "aw2: Capture_open \n");
+ runtime->hw = snd_aw2_capture_hw;
+ return 0;
+}
+
+/* close callback */
+static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ /* TODO: something to do ? */
+ return 0;
+}
+
+ /* hw_params callback */
+static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback for playback */
+static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
+{
+ struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
+ struct aw2 *chip = pcm_device->chip;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long period_size, buffer_size;
+
+ mutex_lock(&chip->mtx);
+
+ period_size = snd_pcm_lib_period_bytes(substream);
+ buffer_size = snd_pcm_lib_buffer_bytes(substream);
+
+ snd_aw2_saa7146_pcm_init_playback(&chip->saa7146,
+ pcm_device->stream_number,
+ runtime->dma_addr, period_size,
+ buffer_size);
+
+ /* Define Interrupt callback */
+ snd_aw2_saa7146_define_it_playback_callback(pcm_device->stream_number,
+ (snd_aw2_saa7146_it_cb)
+ snd_pcm_period_elapsed,
+ (void *)substream);
+
+ mutex_unlock(&chip->mtx);
+
+ return 0;
+}
+
+/* prepare callback for capture */
+static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+ struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
+ struct aw2 *chip = pcm_device->chip;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long period_size, buffer_size;
+
+ mutex_lock(&chip->mtx);
+
+ period_size = snd_pcm_lib_period_bytes(substream);
+ buffer_size = snd_pcm_lib_buffer_bytes(substream);
+
+ snd_aw2_saa7146_pcm_init_capture(&chip->saa7146,
+ pcm_device->stream_number,
+ runtime->dma_addr, period_size,
+ buffer_size);
+
+ /* Define Interrupt callback */
+ snd_aw2_saa7146_define_it_capture_callback(pcm_device->stream_number,
+ (snd_aw2_saa7146_it_cb)
+ snd_pcm_period_elapsed,
+ (void *)substream);
+
+ mutex_unlock(&chip->mtx);
+
+ return 0;
+}
+
+/* playback trigger callback */
+static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ int status = 0;
+ struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
+ struct aw2 *chip = pcm_device->chip;
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146,
+ pcm_device->
+ stream_number);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_aw2_saa7146_pcm_trigger_stop_playback(&chip->saa7146,
+ pcm_device->
+ stream_number);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ spin_unlock(&chip->reg_lock);
+ return status;
+}
+
+/* capture trigger callback */
+static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ int status = 0;
+ struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
+ struct aw2 *chip = pcm_device->chip;
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146,
+ pcm_device->
+ stream_number);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_aw2_saa7146_pcm_trigger_stop_capture(&chip->saa7146,
+ pcm_device->
+ stream_number);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ spin_unlock(&chip->reg_lock);
+ return status;
+}
+
+/* playback pointer callback */
+static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
+ *substream)
+{
+ struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
+ struct aw2 *chip = pcm_device->chip;
+ unsigned int current_ptr;
+
+ /* get the current hardware pointer */
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ current_ptr =
+ snd_aw2_saa7146_get_hw_ptr_playback(&chip->saa7146,
+ pcm_device->stream_number,
+ runtime->dma_area,
+ runtime->buffer_size);
+
+ return bytes_to_frames(substream->runtime, current_ptr);
+}
+
+/* capture pointer callback */
+static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
+ *substream)
+{
+ struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
+ struct aw2 *chip = pcm_device->chip;
+ unsigned int current_ptr;
+
+ /* get the current hardware pointer */
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ current_ptr =
+ snd_aw2_saa7146_get_hw_ptr_capture(&chip->saa7146,
+ pcm_device->stream_number,
+ runtime->dma_area,
+ runtime->buffer_size);
+
+ return bytes_to_frames(substream->runtime, current_ptr);
+}
+
+/* create a pcm device */
+static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
+{
+ struct snd_pcm *pcm_playback_ana;
+ struct snd_pcm *pcm_playback_num;
+ struct snd_pcm *pcm_capture;
+ struct aw2_pcm_device *pcm_device;
+ int err = 0;
+
+ /* Create new Alsa PCM device */
+
+ err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0,
+ &pcm_playback_ana);
+ if (err < 0) {
+ printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ return err;
+ }
+
+ /* Creation ok */
+ pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA];
+
+ /* Set PCM device name */
+ strcpy(pcm_playback_ana->name, "Analog playback");
+ /* Associate private data to PCM device */
+ pcm_playback_ana->private_data = pcm_device;
+ /* set operators of PCM device */
+ snd_pcm_set_ops(pcm_playback_ana, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_aw2_playback_ops);
+ /* store PCM device */
+ pcm_device->pcm = pcm_playback_ana;
+ /* give base chip pointer to our internal pcm device
+ structure */
+ pcm_device->chip = chip;
+ /* Give stream number to PCM device */
+ pcm_device->stream_number = NUM_STREAM_PLAYBACK_ANA;
+
+ /* pre-allocation of buffers */
+ /* Preallocate continuous pages. */
+ err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data
+ (chip->pci),
+ 64 * 1024, 64 * 1024);
+ if (err)
+ printk(KERN_ERR "aw2: snd_pcm_lib_preallocate_pages_for_all "
+ "error (0x%X)\n", err);
+
+ err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0,
+ &pcm_playback_num);
+
+ if (err < 0) {
+ printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ return err;
+ }
+ /* Creation ok */
+ pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG];
+
+ /* Set PCM device name */
+ strcpy(pcm_playback_num->name, "Digital playback");
+ /* Associate private data to PCM device */
+ pcm_playback_num->private_data = pcm_device;
+ /* set operators of PCM device */
+ snd_pcm_set_ops(pcm_playback_num, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_aw2_playback_ops);
+ /* store PCM device */
+ pcm_device->pcm = pcm_playback_num;
+ /* give base chip pointer to our internal pcm device
+ structure */
+ pcm_device->chip = chip;
+ /* Give stream number to PCM device */
+ pcm_device->stream_number = NUM_STREAM_PLAYBACK_DIG;
+
+ /* pre-allocation of buffers */
+ /* Preallocate continuous pages. */
+ err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data
+ (chip->pci),
+ 64 * 1024, 64 * 1024);
+ if (err)
+ printk(KERN_ERR
+ "aw2: snd_pcm_lib_preallocate_pages_for_all error "
+ "(0x%X)\n", err);
+
+
+
+ err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1,
+ &pcm_capture);
+
+ if (err < 0) {
+ printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ return err;
+ }
+
+ /* Creation ok */
+ pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA];
+
+ /* Set PCM device name */
+ strcpy(pcm_capture->name, "Capture");
+ /* Associate private data to PCM device */
+ pcm_capture->private_data = pcm_device;
+ /* set operators of PCM device */
+ snd_pcm_set_ops(pcm_capture, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_aw2_capture_ops);
+ /* store PCM device */
+ pcm_device->pcm = pcm_capture;
+ /* give base chip pointer to our internal pcm device
+ structure */
+ pcm_device->chip = chip;
+ /* Give stream number to PCM device */
+ pcm_device->stream_number = NUM_STREAM_CAPTURE_ANA;
+
+ /* pre-allocation of buffers */
+ /* Preallocate continuous pages. */
+ err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data
+ (chip->pci),
+ 64 * 1024, 64 * 1024);
+ if (err)
+ printk(KERN_ERR
+ "aw2: snd_pcm_lib_preallocate_pages_for_all error "
+ "(0x%X)\n", err);
+
+
+ /* Create control */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip));
+ if (err < 0) {
+ printk(KERN_ERR "aw2: snd_ctl_add error (0x%X)\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[2] = {
+ "Analog", "Digital"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) {
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ }
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value
+ *ucontrol)
+{
+ struct aw2 *chip = snd_kcontrol_chip(kcontrol);
+ if (snd_aw2_saa7146_is_using_digital_input(&chip->saa7146))
+ ucontrol->value.enumerated.item[0] = CTL_ROUTE_DIGITAL;
+ else
+ ucontrol->value.enumerated.item[0] = CTL_ROUTE_ANALOG;
+ return 0;
+}
+
+static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value
+ *ucontrol)
+{
+ struct aw2 *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int is_disgital =
+ snd_aw2_saa7146_is_using_digital_input(&chip->saa7146);
+
+ if (((ucontrol->value.integer.value[0] == CTL_ROUTE_DIGITAL)
+ && !is_disgital)
+ || ((ucontrol->value.integer.value[0] == CTL_ROUTE_ANALOG)
+ && is_disgital)) {
+ snd_aw2_saa7146_use_digital_input(&chip->saa7146, !is_disgital);
+ changed = 1;
+ }
+ return changed;
+}
diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c
new file mode 100644
index 0000000..6a3891a
--- /dev/null
+++ b/sound/pci/aw2/aw2-saa7146.c
@@ -0,0 +1,465 @@
+/*****************************************************************************
+ *
+ * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
+ * Jean-Christian Hassler <jhassler@free.fr>
+ *
+ * This file is part of the Audiowerk2 ALSA driver
+ *
+ * The Audiowerk2 ALSA driver 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; version 2.
+ *
+ * The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *****************************************************************************/
+
+#define AW2_SAA7146_M
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "saa7146.h"
+#include "aw2-saa7146.h"
+
+#include "aw2-tsl.c"
+
+#define WRITEREG(value, addr) writel((value), chip->base_addr + (addr))
+#define READREG(addr) readl(chip->base_addr + (addr))
+
+static struct snd_aw2_saa7146_cb_param
+ arr_substream_it_playback_cb[NB_STREAM_PLAYBACK];
+static struct snd_aw2_saa7146_cb_param
+ arr_substream_it_capture_cb[NB_STREAM_CAPTURE];
+
+static int snd_aw2_saa7146_get_limit(int size);
+
+/* chip-specific destructor */
+int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip)
+{
+ /* disable all irqs */
+ WRITEREG(0, IER);
+
+ /* reset saa7146 */
+ WRITEREG((MRST_N << 16), MC1);
+
+ /* Unset base addr */
+ chip->base_addr = NULL;
+
+ return 0;
+}
+
+void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip,
+ void __iomem *pci_base_addr)
+{
+ /* set PCI burst/threshold
+
+ Burst length definition
+ VALUE BURST LENGTH
+ 000 1 Dword
+ 001 2 Dwords
+ 010 4 Dwords
+ 011 8 Dwords
+ 100 16 Dwords
+ 101 32 Dwords
+ 110 64 Dwords
+ 111 128 Dwords
+
+ Threshold definition
+ VALUE WRITE MODE READ MODE
+ 00 1 Dword of valid data 1 empty Dword
+ 01 4 Dwords of valid data 4 empty Dwords
+ 10 8 Dwords of valid data 8 empty Dwords
+ 11 16 Dwords of valid data 16 empty Dwords */
+
+ unsigned int acon2;
+ unsigned int acon1 = 0;
+ int i;
+
+ /* Set base addr */
+ chip->base_addr = pci_base_addr;
+
+ /* disable all irqs */
+ WRITEREG(0, IER);
+
+ /* reset saa7146 */
+ WRITEREG((MRST_N << 16), MC1);
+
+ /* enable audio interface */
+#ifdef __BIG_ENDIAN
+ acon1 |= A1_SWAP;
+ acon1 |= A2_SWAP;
+#endif
+ /* WS0_CTRL, WS0_SYNC: input TSL1, I2S */
+
+ /* At initialization WS1 and WS2 are disbaled (configured as input */
+ acon1 |= 0 * WS1_CTRL;
+ acon1 |= 0 * WS2_CTRL;
+
+ /* WS4 is not used. So it must not restart A2.
+ This is why it is configured as output (force to low) */
+ acon1 |= 3 * WS4_CTRL;
+
+ /* WS3_CTRL, WS3_SYNC: output TSL2, I2S */
+ acon1 |= 2 * WS3_CTRL;
+
+ /* A1 and A2 are active and asynchronous */
+ acon1 |= 3 * AUDIO_MODE;
+ WRITEREG(acon1, ACON1);
+
+ /* The following comes from original windows driver.
+ It is needed to have a correct behavior of input and output
+ simultenously, but I don't know why ! */
+ WRITEREG(3 * (BurstA1_in) + 3 * (ThreshA1_in) +
+ 3 * (BurstA1_out) + 3 * (ThreshA1_out) +
+ 3 * (BurstA2_out) + 3 * (ThreshA2_out), PCI_BT_A);
+
+ /* enable audio port pins */
+ WRITEREG((EAP << 16) | EAP, MC1);
+
+ /* enable I2C */
+ WRITEREG((EI2C << 16) | EI2C, MC1);
+ /* enable interrupts */
+ WRITEREG(A1_out | A2_out | A1_in | IIC_S | IIC_E, IER);
+
+ /* audio configuration */
+ acon2 = A2_CLKSRC | BCLK1_OEN;
+ WRITEREG(acon2, ACON2);
+
+ /* By default use analog input */
+ snd_aw2_saa7146_use_digital_input(chip, 0);
+
+ /* TSL setup */
+ for (i = 0; i < 8; ++i) {
+ WRITEREG(tsl1[i], TSL1 + (i * 4));
+ WRITEREG(tsl2[i], TSL2 + (i * 4));
+ }
+
+}
+
+void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
+ int stream_number,
+ unsigned long dma_addr,
+ unsigned long period_size,
+ unsigned long buffer_size)
+{
+ unsigned long dw_page, dw_limit;
+
+ /* Configure DMA for substream
+ Configuration informations: ALSA has allocated continuous memory
+ pages. So we don't need to use MMU of saa7146.
+ */
+
+ /* No MMU -> nothing to do with PageA1, we only configure the limit of
+ PageAx_out register */
+ /* Disable MMU */
+ dw_page = (0L << 11);
+
+ /* Configure Limit for DMA access.
+ The limit register defines an address limit, which generates
+ an interrupt if passed by the actual PCI address pointer.
+ '0001' means an interrupt will be generated if the lower
+ 6 bits (64 bytes) of the PCI address are zero. '0010'
+ defines a limit of 128 bytes, '0011' one of 256 bytes, and
+ so on up to 1 Mbyte defined by '1111'. This interrupt range
+ can be calculated as follows:
+ Range = 2^(5 + Limit) bytes.
+ */
+ dw_limit = snd_aw2_saa7146_get_limit(period_size);
+ dw_page |= (dw_limit << 4);
+
+ if (stream_number == 0) {
+ WRITEREG(dw_page, PageA2_out);
+
+ /* Base address for DMA transfert. */
+ /* This address has been reserved by ALSA. */
+ /* This is a physical address */
+ WRITEREG(dma_addr, BaseA2_out);
+
+ /* Define upper limit for DMA access */
+ WRITEREG(dma_addr + buffer_size, ProtA2_out);
+
+ } else if (stream_number == 1) {
+ WRITEREG(dw_page, PageA1_out);
+
+ /* Base address for DMA transfert. */
+ /* This address has been reserved by ALSA. */
+ /* This is a physical address */
+ WRITEREG(dma_addr, BaseA1_out);
+
+ /* Define upper limit for DMA access */
+ WRITEREG(dma_addr + buffer_size, ProtA1_out);
+ } else {
+ printk(KERN_ERR
+ "aw2: snd_aw2_saa7146_pcm_init_playback: "
+ "Substream number is not 0 or 1 -> not managed\n");
+ }
+}
+
+void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
+ int stream_number, unsigned long dma_addr,
+ unsigned long period_size,
+ unsigned long buffer_size)
+{
+ unsigned long dw_page, dw_limit;
+
+ /* Configure DMA for substream
+ Configuration informations: ALSA has allocated continuous memory
+ pages. So we don't need to use MMU of saa7146.
+ */
+
+ /* No MMU -> nothing to do with PageA1, we only configure the limit of
+ PageAx_out register */
+ /* Disable MMU */
+ dw_page = (0L << 11);
+
+ /* Configure Limit for DMA access.
+ The limit register defines an address limit, which generates
+ an interrupt if passed by the actual PCI address pointer.
+ '0001' means an interrupt will be generated if the lower
+ 6 bits (64 bytes) of the PCI address are zero. '0010'
+ defines a limit of 128 bytes, '0011' one of 256 bytes, and
+ so on up to 1 Mbyte defined by '1111'. This interrupt range
+ can be calculated as follows:
+ Range = 2^(5 + Limit) bytes.
+ */
+ dw_limit = snd_aw2_saa7146_get_limit(period_size);
+ dw_page |= (dw_limit << 4);
+
+ if (stream_number == 0) {
+ WRITEREG(dw_page, PageA1_in);
+
+ /* Base address for DMA transfert. */
+ /* This address has been reserved by ALSA. */
+ /* This is a physical address */
+ WRITEREG(dma_addr, BaseA1_in);
+
+ /* Define upper limit for DMA access */
+ WRITEREG(dma_addr + buffer_size, ProtA1_in);
+ } else {
+ printk(KERN_ERR
+ "aw2: snd_aw2_saa7146_pcm_init_capture: "
+ "Substream number is not 0 -> not managed\n");
+ }
+}
+
+void snd_aw2_saa7146_define_it_playback_callback(unsigned int stream_number,
+ snd_aw2_saa7146_it_cb
+ p_it_callback,
+ void *p_callback_param)
+{
+ if (stream_number < NB_STREAM_PLAYBACK) {
+ arr_substream_it_playback_cb[stream_number].p_it_callback =
+ (snd_aw2_saa7146_it_cb) p_it_callback;
+ arr_substream_it_playback_cb[stream_number].p_callback_param =
+ (void *)p_callback_param;
+ }
+}
+
+void snd_aw2_saa7146_define_it_capture_callback(unsigned int stream_number,
+ snd_aw2_saa7146_it_cb
+ p_it_callback,
+ void *p_callback_param)
+{
+ if (stream_number < NB_STREAM_CAPTURE) {
+ arr_substream_it_capture_cb[stream_number].p_it_callback =
+ (snd_aw2_saa7146_it_cb) p_it_callback;
+ arr_substream_it_capture_cb[stream_number].p_callback_param =
+ (void *)p_callback_param;
+ }
+}
+
+void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 *chip,
+ int stream_number)
+{
+ unsigned int acon1 = 0;
+ /* In aw8 driver, dma transfert is always active. It is
+ started and stopped in a larger "space" */
+ acon1 = READREG(ACON1);
+ if (stream_number == 0) {
+ WRITEREG((TR_E_A2_OUT << 16) | TR_E_A2_OUT, MC1);
+
+ /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */
+ acon1 |= 2 * WS2_CTRL;
+ WRITEREG(acon1, ACON1);
+
+ } else if (stream_number == 1) {
+ WRITEREG((TR_E_A1_OUT << 16) | TR_E_A1_OUT, MC1);
+
+ /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */
+ acon1 |= 1 * WS1_CTRL;
+ WRITEREG(acon1, ACON1);
+ }
+}
+
+void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 *chip,
+ int stream_number)
+{
+ unsigned int acon1 = 0;
+ acon1 = READREG(ACON1);
+ if (stream_number == 0) {
+ /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */
+ acon1 &= ~(3 * WS2_CTRL);
+ WRITEREG(acon1, ACON1);
+
+ WRITEREG((TR_E_A2_OUT << 16), MC1);
+ } else if (stream_number == 1) {
+ /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */
+ acon1 &= ~(3 * WS1_CTRL);
+ WRITEREG(acon1, ACON1);
+
+ WRITEREG((TR_E_A1_OUT << 16), MC1);
+ }
+}
+
+void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 *chip,
+ int stream_number)
+{
+ /* In aw8 driver, dma transfert is always active. It is
+ started and stopped in a larger "space" */
+ if (stream_number == 0)
+ WRITEREG((TR_E_A1_IN << 16) | TR_E_A1_IN, MC1);
+}
+
+void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip,
+ int stream_number)
+{
+ if (stream_number == 0)
+ WRITEREG((TR_E_A1_IN << 16), MC1);
+}
+
+irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id)
+{
+ unsigned int isr;
+ unsigned int iicsta;
+ struct snd_aw2_saa7146 *chip = dev_id;
+
+ isr = READREG(ISR);
+ if (!isr)
+ return IRQ_NONE;
+
+ WRITEREG(isr, ISR);
+
+ if (isr & (IIC_S | IIC_E)) {
+ iicsta = READREG(IICSTA);
+ WRITEREG(0x100, IICSTA);
+ }
+
+ if (isr & A1_out) {
+ if (arr_substream_it_playback_cb[1].p_it_callback != NULL) {
+ arr_substream_it_playback_cb[1].
+ p_it_callback(arr_substream_it_playback_cb[1].
+ p_callback_param);
+ }
+ }
+ if (isr & A2_out) {
+ if (arr_substream_it_playback_cb[0].p_it_callback != NULL) {
+ arr_substream_it_playback_cb[0].
+ p_it_callback(arr_substream_it_playback_cb[0].
+ p_callback_param);
+ }
+
+ }
+ if (isr & A1_in) {
+ if (arr_substream_it_capture_cb[0].p_it_callback != NULL) {
+ arr_substream_it_capture_cb[0].
+ p_it_callback(arr_substream_it_capture_cb[0].
+ p_callback_param);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 *chip,
+ int stream_number,
+ unsigned char *start_addr,
+ unsigned int buffer_size)
+{
+ long pci_adp = 0;
+ size_t ptr = 0;
+
+ if (stream_number == 0) {
+ pci_adp = READREG(PCI_ADP3);
+ ptr = pci_adp - (long)start_addr;
+
+ if (ptr == buffer_size)
+ ptr = 0;
+ }
+ if (stream_number == 1) {
+ pci_adp = READREG(PCI_ADP1);
+ ptr = pci_adp - (size_t) start_addr;
+
+ if (ptr == buffer_size)
+ ptr = 0;
+ }
+ return ptr;
+}
+
+unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 *chip,
+ int stream_number,
+ unsigned char *start_addr,
+ unsigned int buffer_size)
+{
+ size_t pci_adp = 0;
+ size_t ptr = 0;
+ if (stream_number == 0) {
+ pci_adp = READREG(PCI_ADP2);
+ ptr = pci_adp - (size_t) start_addr;
+
+ if (ptr == buffer_size)
+ ptr = 0;
+ }
+ return ptr;
+}
+
+void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip,
+ int use_digital)
+{
+ /* FIXME: switch between analog and digital input does not always work.
+ It can produce a kind of white noise. It seams that received data
+ are inverted sometime (endian inversion). Why ? I don't know, maybe
+ a problem of synchronization... However for the time being I have
+ not found the problem. Workaround: switch again (and again) between
+ digital and analog input until it works. */
+ if (use_digital)
+ WRITEREG(0x40, GPIO_CTRL);
+ else
+ WRITEREG(0x50, GPIO_CTRL);
+}
+
+int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 *chip)
+{
+ unsigned int reg_val = READREG(GPIO_CTRL);
+ if ((reg_val & 0xFF) == 0x40)
+ return 1;
+ else
+ return 0;
+}
+
+
+static int snd_aw2_saa7146_get_limit(int size)
+{
+ int limitsize = 32;
+ int limit = 0;
+ while (limitsize < size) {
+ limitsize *= 2;
+ limit++;
+ }
+ return limit;
+}
diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h
new file mode 100644
index 0000000..5b35e35
--- /dev/null
+++ b/sound/pci/aw2/aw2-saa7146.h
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ *
+ * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
+ * Jean-Christian Hassler <jhassler@free.fr>
+ *
+ * This file is part of the Audiowerk2 ALSA driver
+ *
+ * The Audiowerk2 ALSA driver 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; version 2.
+ *
+ * The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *****************************************************************************/
+
+#ifndef AW2_SAA7146_H
+#define AW2_SAA7146_H
+
+#define NB_STREAM_PLAYBACK 2
+#define NB_STREAM_CAPTURE 1
+
+#define NUM_STREAM_PLAYBACK_ANA 0
+#define NUM_STREAM_PLAYBACK_DIG 1
+
+#define NUM_STREAM_CAPTURE_ANA 0
+
+typedef void (*snd_aw2_saa7146_it_cb) (void *);
+
+struct snd_aw2_saa7146_cb_param {
+ snd_aw2_saa7146_it_cb p_it_callback;
+ void *p_callback_param;
+};
+
+/* definition of the chip-specific record */
+
+struct snd_aw2_saa7146 {
+ void __iomem *base_addr;
+};
+
+extern void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip,
+ void __iomem *pci_base_addr);
+extern int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip);
+
+extern void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
+ int stream_number,
+ unsigned long dma_addr,
+ unsigned long period_size,
+ unsigned long buffer_size);
+extern void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
+ int stream_number,
+ unsigned long dma_addr,
+ unsigned long period_size,
+ unsigned long buffer_size);
+extern void snd_aw2_saa7146_define_it_playback_callback(unsigned int
+ stream_number,
+ snd_aw2_saa7146_it_cb
+ p_it_callback,
+ void *p_callback_param);
+extern void snd_aw2_saa7146_define_it_capture_callback(unsigned int
+ stream_number,
+ snd_aw2_saa7146_it_cb
+ p_it_callback,
+ void *p_callback_param);
+extern void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146
+ *chip, int stream_number);
+extern void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146
+ *chip, int stream_number);
+
+extern void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146
+ *chip,
+ int stream_number);
+extern void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146
+ *chip, int stream_number);
+
+extern irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id);
+extern unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146
+ *chip,
+ int stream_number,
+ unsigned char
+ *start_addr,
+ unsigned int
+ buffer_size);
+extern unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146
+ *chip,
+ int stream_number,
+ unsigned char
+ *start_addr,
+ unsigned int
+ buffer_size);
+
+extern void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip,
+ int use_digital);
+
+extern int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146
+ *chip);
+
+#endif
diff --git a/sound/pci/aw2/aw2-tsl.c b/sound/pci/aw2/aw2-tsl.c
new file mode 100644
index 0000000..459b031
--- /dev/null
+++ b/sound/pci/aw2/aw2-tsl.c
@@ -0,0 +1,110 @@
+/*****************************************************************************
+ *
+ * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
+ * Jean-Christian Hassler <jhassler@free.fr>
+ * Copyright 1998 Emagic Soft- und Hardware GmbH
+ * Copyright 2002 Martijn Sipkema
+ *
+ * This file is part of the Audiowerk2 ALSA driver
+ *
+ * The Audiowerk2 ALSA driver 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; version 2.
+ *
+ * The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *****************************************************************************/
+
+#define TSL_WS0 (1UL << 31)
+#define TSL_WS1 (1UL << 30)
+#define TSL_WS2 (1UL << 29)
+#define TSL_WS3 (1UL << 28)
+#define TSL_WS4 (1UL << 27)
+#define TSL_DIS_A1 (1UL << 24)
+#define TSL_SDW_A1 (1UL << 23)
+#define TSL_SIB_A1 (1UL << 22)
+#define TSL_SF_A1 (1UL << 21)
+#define TSL_LF_A1 (1UL << 20)
+#define TSL_BSEL_A1 (1UL << 17)
+#define TSL_DOD_A1 (1UL << 15)
+#define TSL_LOW_A1 (1UL << 14)
+#define TSL_DIS_A2 (1UL << 11)
+#define TSL_SDW_A2 (1UL << 10)
+#define TSL_SIB_A2 (1UL << 9)
+#define TSL_SF_A2 (1UL << 8)
+#define TSL_LF_A2 (1UL << 7)
+#define TSL_BSEL_A2 (1UL << 4)
+#define TSL_DOD_A2 (1UL << 2)
+#define TSL_LOW_A2 (1UL << 1)
+#define TSL_EOS (1UL << 0)
+
+ /* Audiowerk8 hardware setup: */
+ /* WS0, SD4, TSL1 - Analog/ digital in */
+ /* WS1, SD0, TSL1 - Analog out #1, digital out */
+ /* WS2, SD2, TSL1 - Analog out #2 */
+ /* WS3, SD1, TSL2 - Analog out #3 */
+ /* WS4, SD3, TSL2 - Analog out #4 */
+
+ /* Audiowerk8 timing: */
+ /* Timeslot: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... */
+
+ /* A1_INPUT: */
+ /* SD4: <_ADC-L_>-------<_ADC-R_>-------< */
+ /* WS0: _______________/---------------\_ */
+
+ /* A1_OUTPUT: */
+ /* SD0: <_1-L___>-------<_1-R___>-------< */
+ /* WS1: _______________/---------------\_ */
+ /* SD2: >-------<_2-L___>-------<_2-R___> */
+ /* WS2: -------\_______________/--------- */
+
+ /* A2_OUTPUT: */
+ /* SD1: <_3-L___>-------<_3-R___>-------< */
+ /* WS3: _______________/---------------\_ */
+ /* SD3: >-------<_4-L___>-------<_4-R___> */
+ /* WS4: -------\_______________/--------- */
+
+static int tsl1[8] = {
+ 1 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
+ 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_LF_A1,
+
+ 1 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 |
+ 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
+
+ 0 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
+ 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
+
+ 0 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 |
+ 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
+
+ 1 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 |
+ 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
+
+ 1 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 |
+ 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
+
+ 0 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 |
+ 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
+
+ 0 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 | 0 * TSL_DIS_A1 |
+ 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0 | TSL_SF_A1 | TSL_EOS,
+};
+
+static int tsl2[8] = {
+ 0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_LF_A2,
+ 0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
+ 0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
+ 0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
+ 0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
+ 0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
+ 0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
+ 0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2 | TSL_EOS
+};
diff --git a/sound/pci/aw2/saa7146.h b/sound/pci/aw2/saa7146.h
new file mode 100644
index 0000000..ce0ab5f
--- /dev/null
+++ b/sound/pci/aw2/saa7146.h
@@ -0,0 +1,168 @@
+/*****************************************************************************
+ *
+ * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
+ * Jean-Christian Hassler <jhassler@free.fr>
+ *
+ * This file is part of the Audiowerk2 ALSA driver
+ *
+ * The Audiowerk2 ALSA driver 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; version 2.
+ *
+ * The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *****************************************************************************/
+
+/* SAA7146 registers */
+#define PCI_BT_A 0x4C
+#define IICTFR 0x8C
+#define IICSTA 0x90
+#define BaseA1_in 0x94
+#define ProtA1_in 0x98
+#define PageA1_in 0x9C
+#define BaseA1_out 0xA0
+#define ProtA1_out 0xA4
+#define PageA1_out 0xA8
+#define BaseA2_in 0xAC
+#define ProtA2_in 0xB0
+#define PageA2_in 0xB4
+#define BaseA2_out 0xB8
+#define ProtA2_out 0xBC
+#define PageA2_out 0xC0
+#define IER 0xDC
+#define GPIO_CTRL 0xE0
+#define ACON1 0xF4
+#define ACON2 0xF8
+#define MC1 0xFC
+#define MC2 0x100
+#define ISR 0x10C
+#define PSR 0x110
+#define SSR 0x114
+#define PCI_ADP1 0x12C
+#define PCI_ADP2 0x130
+#define PCI_ADP3 0x134
+#define PCI_ADP4 0x138
+#define LEVEL_REP 0x140
+#define FB_BUFFER1 0x144
+#define FB_BUFFER2 0x148
+#define TSL1 0x180
+#define TSL2 0x1C0
+
+#define ME (1UL << 11)
+#define LIMIT (1UL << 4)
+#define PV (1UL << 3)
+
+/* PSR/ISR/IER */
+#define PPEF (1UL << 31)
+#define PABO (1UL << 30)
+#define IIC_S (1UL << 17)
+#define IIC_E (1UL << 16)
+#define A2_in (1UL << 15)
+#define A2_out (1UL << 14)
+#define A1_in (1UL << 13)
+#define A1_out (1UL << 12)
+#define AFOU (1UL << 11)
+#define PIN3 (1UL << 6)
+#define PIN2 (1UL << 5)
+#define PIN1 (1UL << 4)
+#define PIN0 (1UL << 3)
+#define ECS (1UL << 2)
+#define EC3S (1UL << 1)
+#define EC0S (1UL << 0)
+
+/* SSR */
+#define PRQ (1UL << 31)
+#define PMA (1UL << 30)
+#define IIC_EA (1UL << 21)
+#define IIC_EW (1UL << 20)
+#define IIC_ER (1UL << 19)
+#define IIC_EL (1UL << 18)
+#define IIC_EF (1UL << 17)
+#define AF2_in (1UL << 10)
+#define AF2_out (1UL << 9)
+#define AF1_in (1UL << 8)
+#define AF1_out (1UL << 7)
+#define EC5S (1UL << 3)
+#define EC4S (1UL << 2)
+#define EC2S (1UL << 1)
+#define EC1S (1UL << 0)
+
+/* PCI_BT_A */
+#define BurstA1_in (1UL << 26)
+#define ThreshA1_in (1UL << 24)
+#define BurstA1_out (1UL << 18)
+#define ThreshA1_out (1UL << 16)
+#define BurstA2_in (1UL << 10)
+#define ThreshA2_in (1UL << 8)
+#define BurstA2_out (1UL << 2)
+#define ThreshA2_out (1UL << 0)
+
+/* MC1 */
+#define MRST_N (1UL << 15)
+#define EAP (1UL << 9)
+#define EI2C (1UL << 8)
+#define TR_E_A2_OUT (1UL << 3)
+#define TR_E_A2_IN (1UL << 2)
+#define TR_E_A1_OUT (1UL << 1)
+#define TR_E_A1_IN (1UL << 0)
+
+/* MC2 */
+#define UPLD_IIC (1UL << 0)
+
+/* ACON1 */
+#define AUDIO_MODE (1UL << 29)
+#define MAXLEVEL (1UL << 22)
+#define A1_SWAP (1UL << 21)
+#define A2_SWAP (1UL << 20)
+#define WS0_CTRL (1UL << 18)
+#define WS0_SYNC (1UL << 16)
+#define WS1_CTRL (1UL << 14)
+#define WS1_SYNC (1UL << 12)
+#define WS2_CTRL (1UL << 10)
+#define WS2_SYNC (1UL << 8)
+#define WS3_CTRL (1UL << 6)
+#define WS3_SYNC (1UL << 4)
+#define WS4_CTRL (1UL << 2)
+#define WS4_SYNC (1UL << 0)
+
+/* ACON2 */
+#define A1_CLKSRC (1UL << 27)
+#define A2_CLKSRC (1UL << 22)
+#define INVERT_BCLK1 (1UL << 21)
+#define INVERT_BCLK2 (1UL << 20)
+#define BCLK1_OEN (1UL << 19)
+#define BCLK2_OEN (1UL << 18)
+
+/* IICSTA */
+#define IICCC (1UL << 8)
+#define ABORT (1UL << 7)
+#define SPERR (1UL << 6)
+#define APERR (1UL << 5)
+#define DTERR (1UL << 4)
+#define DRERR (1UL << 3)
+#define AL (1UL << 2)
+#define ERR (1UL << 1)
+#define BUSY (1UL << 0)
+
+/* IICTFR */
+#define BYTE2 (1UL << 24)
+#define BYTE1 (1UL << 16)
+#define BYTE0 (1UL << 8)
+#define ATRR2 (1UL << 6)
+#define ATRR1 (1UL << 4)
+#define ATRR0 (1UL << 2)
+#define ERR (1UL << 1)
+#define BUSY (1UL << 0)
+
+#define START 3
+#define CONT 2
+#define STOP 1
+#define NOP 0
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 4e71a55..5f63af6 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -157,8 +157,8 @@
#if DEBUG_CALLS
#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
-#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__)
-#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__)
+#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
+#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
#else
#define snd_azf3328_dbgcalls(format, args...)
#define snd_azf3328_dbgcallenter()
@@ -1514,7 +1514,8 @@
/* well, at least we know how to disable the timer IRQ */
snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00);
- synchronize_irq(chip->irq);
+ if (chip->irq >= 0)
+ synchronize_irq(chip->irq);
__end_hw:
snd_azf3328_free_joystick(chip);
if (chip->irq >= 0)
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 176e0f0..ecbe79b 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -435,22 +435,22 @@
static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
{
unsigned long flags;
- unsigned int enable;
-
+ unsigned int intr_enable;
+
spin_lock_irqsave(&emu->emu_lock, flags);
- enable = inl(emu->port + INTE) | intrenb;
- outl(enable, emu->port + INTE);
+ intr_enable = inl(emu->port + INTE) | intrenb;
+ outl(intr_enable, emu->port + INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
{
unsigned long flags;
- unsigned int enable;
-
+ unsigned int intr_enable;
+
spin_lock_irqsave(&emu->emu_lock, flags);
- enable = inl(emu->port + INTE) & ~intrenb;
- outl(enable, emu->port + INTE);
+ intr_enable = inl(emu->port + INTE) & ~intrenb;
+ outl(intr_enable, emu->port + INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -1114,6 +1114,8 @@
* So we can fix: snd-malloc: Memory leak? pages not freed = 8
*/
}
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
// release the data
#if 1
if (chip->buffer.area)
@@ -1123,9 +1125,6 @@
// release the i/o port
release_and_free_resource(chip->res_port);
- // release the irq
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
pci_disable_device(chip->pci);
kfree(chip);
return 0;
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index af73686..3025ed1 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -650,19 +650,55 @@
#define ADD_CTLS(emu, ctls) \
do { \
- int i, err; \
+ int i, _err; \
for (i = 0; i < ARRAY_SIZE(ctls); i++) { \
- err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
- if (err < 0) \
- return err; \
+ _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
+ if (_err < 0) \
+ return _err; \
} \
} while (0)
+static __devinitdata
+DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
+
+static char *slave_vols[] __devinitdata = {
+ "Analog Front Playback Volume",
+ "Analog Rear Playback Volume",
+ "Analog Center/LFE Playback Volume",
+ "Analog Side Playback Volume",
+ "IEC958 Front Playback Volume",
+ "IEC958 Rear Playback Volume",
+ "IEC958 Center/LFE Playback Volume",
+ "IEC958 Unknown Playback Volume",
+ "CAPTURE feedback Playback Volume",
+ NULL
+};
+
+static char *slave_sws[] __devinitdata = {
+ "Analog Front Playback Switch",
+ "Analog Rear Playback Switch",
+ "Analog Center/LFE Playback Switch",
+ "Analog Side Playback Switch",
+ "IEC958 Playback Switch",
+ NULL
+};
+
+static void __devinit add_slaves(struct snd_card *card,
+ struct snd_kcontrol *master, char **list)
+{
+ for (; *list; list++) {
+ struct snd_kcontrol *slave = ctl_find(card, *list);
+ if (slave)
+ snd_ctl_add_slave(master, slave);
+ }
+}
+
int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
{
int err;
struct snd_card *card = emu->card;
char **c;
+ struct snd_kcontrol *vmaster;
static char *ca0106_remove_ctls[] = {
"Master Mono Playback Switch",
"Master Mono Playback Volume",
@@ -719,6 +755,21 @@
}
if (emu->details->spi_dac == 1)
ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
+
+ /* Create virtual master controls */
+ vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
+ snd_ca0106_master_db_scale);
+ if (!vmaster)
+ return -ENOMEM;
+ add_slaves(card, vmaster, slave_vols);
+
+ if (emu->details->spi_dac == 1) {
+ vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
+ NULL);
+ if (!vmaster)
+ return -ENOMEM;
+ add_slaves(card, vmaster, slave_sws);
+ }
return 0;
}
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 135f308..9971b5b 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2744,12 +2744,13 @@
}
for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
- struct snd_ctl_elem_id id;
+ struct snd_ctl_elem_id elem_id;
struct snd_kcontrol *ctl;
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, cm_saved_mixer[idx].name);
- if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL)
+ memset(&elem_id, 0, sizeof(elem_id));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(elem_id.name, cm_saved_mixer[idx].name);
+ ctl = snd_ctl_find_id(cm->card, &elem_id);
+ if (ctl)
cm->mixer_res_ctl[idx] = ctl;
}
@@ -2932,8 +2933,6 @@
/* reset mixer */
snd_cmipci_mixer_write(cm, 0, 0);
- synchronize_irq(cm->irq);
-
free_irq(cm->irq, cm);
}
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 87ddffc..e214e56 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2772,6 +2772,9 @@
if (chip->irq >= 0)
free_irq(chip->irq, chip);
+ if (chip->active_ctrl)
+ chip->active_ctrl(chip, -chip->amplifier);
+
for (idx = 0; idx < 5; idx++) {
struct snd_cs46xx_region *region = &chip->region.idx[idx];
if (region->remap_addr)
@@ -2779,9 +2782,6 @@
release_and_free_resource(region->resource);
}
- if (chip->active_ctrl)
- chip->active_ctrl(chip, -chip->amplifier);
-
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->dsp_spos_instance) {
cs46xx_dsp_spos_destroy(chip);
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 90ec090..e16dc92 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1852,15 +1852,16 @@
static int snd_echo_free(struct echoaudio *chip)
{
DE_INIT(("Stop DSP...\n"));
- if (chip->comm_page) {
+ if (chip->comm_page)
rest_in_peace(chip);
- snd_dma_free_pages(&chip->commpage_dma_buf);
- }
DE_INIT(("Stopped.\n"));
if (chip->irq >= 0)
free_irq(chip->irq, chip);
+ if (chip->comm_page)
+ snd_dma_free_pages(&chip->commpage_dma_buf);
+
if (chip->dsp_registers)
iounmap(chip->dsp_registers);
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 9a9b977..abde5b9 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1249,11 +1249,6 @@
if (emu->port) { /* avoid access to already used hardware */
snd_emu10k1_fx8010_tram_setup(emu, 0);
snd_emu10k1_done(emu);
- /* remove reserved page */
- if (emu->reserved_page) {
- snd_emu10k1_synth_free(emu, (struct snd_util_memblk *)emu->reserved_page);
- emu->reserved_page = NULL;
- }
snd_emu10k1_free_efx(emu);
}
if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
@@ -1262,6 +1257,14 @@
}
if (emu->emu1010.firmware_thread)
kthread_stop(emu->emu1010.firmware_thread);
+ if (emu->irq >= 0)
+ free_irq(emu->irq, emu);
+ /* remove reserved page */
+ if (emu->reserved_page) {
+ snd_emu10k1_synth_free(emu,
+ (struct snd_util_memblk *)emu->reserved_page);
+ emu->reserved_page = NULL;
+ }
if (emu->memhdr)
snd_util_memhdr_free(emu->memhdr);
if (emu->silent_page.area)
@@ -1273,8 +1276,6 @@
#ifdef CONFIG_PM
free_pm_buffer(emu);
#endif
- if (emu->irq >= 0)
- free_irq(emu->irq, emu);
if (emu->port)
pci_release_regions(emu->pci);
if (emu->card_capabilities->ca0151_chip) /* P16V */
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 5512abd..491a4a5 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -327,22 +327,22 @@
static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb)
{
unsigned long flags;
- unsigned int enable;
-
+ unsigned int intr_enable;
+
spin_lock_irqsave(&emu->emu_lock, flags);
- enable = inl(emu->port + INTE) | intrenb;
- outl(enable, emu->port + INTE);
+ intr_enable = inl(emu->port + INTE) | intrenb;
+ outl(intr_enable, emu->port + INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb)
{
unsigned long flags;
- unsigned int enable;
-
+ unsigned int intr_enable;
+
spin_lock_irqsave(&emu->emu_lock, flags);
- enable = inl(emu->port + INTE) & ~intrenb;
- outl(enable, emu->port + INTE);
+ intr_enable = inl(emu->port + INTE) & ~intrenb;
+ outl(intr_enable, emu->port + INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -754,13 +754,13 @@
// disable audio
outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
- // release the i/o port
- release_and_free_resource(chip->res_port);
-
- // release the irq
+ /* release the irq */
if (chip->irq >= 0)
free_irq(chip->irq, chip);
+ // release the i/o port
+ release_and_free_resource(chip->res_port);
+
// release the DMA
if (chip->dma_buffer.area) {
snd_dma_free_pages(&chip->dma_buffer);
@@ -795,9 +795,9 @@
// capture interrupt
if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) {
- struct emu10k1x_voice *pvoice = &chip->capture_voice;
- if (pvoice->use)
- snd_emu10k1x_pcm_interrupt(chip, pvoice);
+ struct emu10k1x_voice *cap_voice = &chip->capture_voice;
+ if (cap_voice->use)
+ snd_emu10k1x_pcm_interrupt(chip, cap_voice);
else
snd_emu10k1x_intr_disable(chip,
INTE_CAP_0_LOOP |
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index f3caa3f..216f974 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -412,7 +412,7 @@
struct snd_info_buffer *buffer)
{
struct snd_emu10k1 *emu = entry->private_data;
- int value;
+ u32 value;
unsigned long flags;
int i;
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 72d85a5..fbf1124 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1635,20 +1635,20 @@
if (has_spdif > 0 ||
(!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) {
struct snd_kcontrol *kctl;
- int i, index = 0;
+ int i, is_spdif = 0;
ensoniq->spdif_default = ensoniq->spdif_stream =
SNDRV_PCM_DEFAULT_CON_SPDIF;
outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS));
if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
- index++;
+ is_spdif++;
for (i = 0; i < ARRAY_SIZE(snd_es1371_mixer_spdif); i++) {
kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq);
if (!kctl)
return -ENOMEM;
- kctl->id.index = index;
+ kctl->id.index = is_spdif;
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
@@ -1910,7 +1910,8 @@
outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */
outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
#endif
- synchronize_irq(ensoniq->irq);
+ if (ensoniq->irq >= 0)
+ synchronize_irq(ensoniq->irq);
pci_set_power_state(ensoniq->pci, 3);
__hw_end:
#ifdef CHIP1370
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 1a314fa..84fac1f 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1488,7 +1488,6 @@
outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */
if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
free_irq(chip->irq, chip);
chip->irq = -1;
}
@@ -1578,10 +1577,8 @@
snd_es1938_free_gameport(chip);
- if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
+ if (chip->irq >= 0)
free_irq(chip->irq, chip);
- }
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 7d911a1..1bf298d 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -1827,6 +1827,22 @@
return 0;
}
+/*
+ * suppress jitter on some maestros when playing stereo
+ */
+static void snd_es1968_suppress_jitter(struct es1968 *chip, struct esschan *es)
+{
+ unsigned int cp1;
+ unsigned int cp2;
+ unsigned int diff;
+
+ cp1 = __apu_get_register(chip, 0, 5);
+ cp2 = __apu_get_register(chip, 1, 5);
+ diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
+
+ if (diff > 1)
+ __maestro_write(chip, IDR0_DATA_PORT, cp1);
+}
/*
* update pointer
@@ -1948,8 +1964,11 @@
struct esschan *es;
spin_lock(&chip->substream_lock);
list_for_each_entry(es, &chip->substream_list, list) {
- if (es->running)
+ if (es->running) {
snd_es1968_update_pcm(chip, es);
+ if (es->fmt & ESS_FMT_STEREO)
+ snd_es1968_suppress_jitter(chip, es);
+ }
}
spin_unlock(&chip->substream_lock);
if (chip->in_measurement) {
@@ -1972,7 +1991,7 @@
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
- struct snd_ctl_elem_id id;
+ struct snd_ctl_elem_id elem_id;
int err;
static struct snd_ac97_bus_ops ops = {
.write = snd_es1968_ac97_write,
@@ -1989,14 +2008,14 @@
return err;
/* attach master switch / volumes for h/w volume control */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Master Playback Switch");
- chip->master_switch = snd_ctl_find_id(chip->card, &id);
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Master Playback Volume");
- chip->master_volume = snd_ctl_find_id(chip->card, &id);
+ memset(&elem_id, 0, sizeof(elem_id));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(elem_id.name, "Master Playback Switch");
+ chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
+ memset(&elem_id, 0, sizeof(elem_id));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(elem_id.name, "Master Playback Volume");
+ chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
return 0;
}
@@ -2456,7 +2475,8 @@
static int snd_es1968_free(struct es1968 *chip)
{
if (chip->io_port) {
- synchronize_irq(chip->irq);
+ if (chip->irq >= 0)
+ synchronize_irq(chip->irq);
outw(1, chip->io_port + 0x04); /* clear WP interrupts */
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
}
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 4c300e6..c129f9e 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1285,7 +1285,6 @@
static int snd_fm801_chip_init(struct fm801 *chip, int resume)
{
- int id;
unsigned short cmdw;
if (chip->tea575x_tuner & 0x0010)
@@ -1310,13 +1309,14 @@
} else {
/* my card has the secondary codec */
/* at address #3, so the loop is inverted */
- for (id = 3; id > 0; id--) {
- if (! wait_for_codec(chip, id, AC97_VENDOR_ID1,
+ int i;
+ for (i = 3; i > 0; i--) {
+ if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
msecs_to_jiffies(50))) {
cmdw = inw(FM801_REG(chip, AC97_DATA));
if (cmdw != 0xffff && cmdw != 0) {
chip->secondary = 1;
- chip->secondary_addr = id;
+ chip->secondary_addr = i;
break;
}
}
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 9e0d8a1..ab0c726 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -2,7 +2,7 @@
# since snd-hda-intel is the only driver using hda-codec,
# merge it into a single module although it was originally
# designed to be individual modules
-snd-hda-intel-y += hda_codec.o vmaster.o
+snd-hda-intel-y += hda_codec.o
snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 37c4139..a6be6e3 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -31,6 +31,7 @@
#include <sound/initval.h>
#include "hda_local.h"
#include <sound/hda_hwdep.h>
+#include "hda_patch.h" /* codec presets */
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* define this option here to hide as static */
@@ -51,21 +52,50 @@
/* codec vendor labels */
static struct hda_vendor_id hda_vendor_ids[] = {
- { 0x10ec, "Realtek" },
+ { 0x1002, "ATI" },
{ 0x1057, "Motorola" },
+ { 0x1095, "Silicon Image" },
+ { 0x10ec, "Realtek" },
{ 0x1106, "VIA" },
{ 0x111d, "IDT" },
+ { 0x11c1, "LSI" },
{ 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" },
{ 0x14f1, "Conexant" },
+ { 0x17e8, "Chrontel" },
+ { 0x1854, "LG" },
{ 0x434d, "C-Media" },
{ 0x8384, "SigmaTel" },
{} /* terminator */
};
-/* codec presets */
-#include "hda_patch.h"
-
+static const struct hda_codec_preset *hda_preset_tables[] = {
+#ifdef CONFIG_SND_HDA_CODEC_REALTEK
+ snd_hda_preset_realtek,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
+ snd_hda_preset_cmedia,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_ANALOG
+ snd_hda_preset_analog,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
+ snd_hda_preset_sigmatel,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_SI3054
+ snd_hda_preset_si3054,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
+ snd_hda_preset_atihdmi,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
+ snd_hda_preset_conexant,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_VIA
+ snd_hda_preset_via,
+#endif
+ NULL
+};
#ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_power_work(struct work_struct *work);
@@ -690,6 +720,19 @@
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
}
+void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return;
+
+ snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+#if 0 /* keep the format */
+ msleep(1);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+#endif
+}
+
/*
* amp access functions
*/
@@ -1037,16 +1080,24 @@
}
/* find a mixer control element with the given name */
-struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
- const char *name)
+static struct snd_kcontrol *
+_snd_hda_find_mixer_ctl(struct hda_codec *codec,
+ const char *name, int idx)
{
struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ id.index = idx;
strcpy(id.name, name);
return snd_ctl_find_id(codec->bus->card, &id);
}
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+ const char *name)
+{
+ return _snd_hda_find_mixer_ctl(codec, name, 0);
+}
+
/* create a virtual master control and add slaves */
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves)
@@ -1481,6 +1532,8 @@
{ } /* end */
};
+#define SPDIF_MAX_IDX 4 /* 4 instances should be enough to probe */
+
/**
* snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
* @codec: the HDA codec
@@ -1496,9 +1549,20 @@
int err;
struct snd_kcontrol *kctl;
struct snd_kcontrol_new *dig_mix;
+ int idx;
+ for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
+ if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch",
+ idx))
+ break;
+ }
+ if (idx >= SPDIF_MAX_IDX) {
+ printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
+ return -EBUSY;
+ }
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
+ kctl->id.index = idx;
kctl->private_value = nid;
err = snd_ctl_add(codec->bus->card, kctl);
if (err < 0)
@@ -1512,6 +1576,43 @@
}
/*
+ * SPDIF sharing with analog output
+ */
+static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = mout->share_spdif;
+ return 0;
+}
+
+static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+ mout->share_spdif = !!ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static struct snd_kcontrol_new spdif_share_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Default PCM Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = spdif_share_sw_get,
+ .put = spdif_share_sw_put,
+};
+
+int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
+ struct hda_multi_out *mout)
+{
+ if (!mout->dig_out_nid)
+ return 0;
+ /* ATTENTION: here mout is passed as private_data, instead of codec */
+ return snd_ctl_add(codec->bus->card,
+ snd_ctl_new1(&spdif_share_sw, mout));
+}
+
+/*
* SPDIF input
*/
@@ -1595,7 +1696,17 @@
int err;
struct snd_kcontrol *kctl;
struct snd_kcontrol_new *dig_mix;
+ int idx;
+ for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
+ if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch",
+ idx))
+ break;
+ }
+ if (idx >= SPDIF_MAX_IDX) {
+ printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
+ return -EBUSY;
+ }
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
kctl->private_value = nid;
@@ -2106,7 +2217,7 @@
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
- snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
return 0;
}
@@ -2491,7 +2602,7 @@
mutex_lock(&codec->spdif_mutex);
if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
/* already opened as analog dup; reset it once */
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
mout->dig_out_used = HDA_DIG_EXCLUSIVE;
mutex_unlock(&codec->spdif_mutex);
return 0;
@@ -2526,9 +2637,36 @@
*/
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
struct hda_multi_out *mout,
- struct snd_pcm_substream *substream)
+ struct snd_pcm_substream *substream,
+ struct hda_pcm_stream *hinfo)
{
- substream->runtime->hw.channels_max = mout->max_channels;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ runtime->hw.channels_max = mout->max_channels;
+ if (mout->dig_out_nid) {
+ if (!mout->analog_rates) {
+ mout->analog_rates = hinfo->rates;
+ mout->analog_formats = hinfo->formats;
+ mout->analog_maxbps = hinfo->maxbps;
+ } else {
+ runtime->hw.rates = mout->analog_rates;
+ runtime->hw.formats = mout->analog_formats;
+ hinfo->maxbps = mout->analog_maxbps;
+ }
+ if (!mout->spdif_rates) {
+ snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
+ &mout->spdif_rates,
+ &mout->spdif_formats,
+ &mout->spdif_maxbps);
+ }
+ mutex_lock(&codec->spdif_mutex);
+ if (mout->share_spdif) {
+ runtime->hw.rates &= mout->spdif_rates;
+ runtime->hw.formats &= mout->spdif_formats;
+ if (mout->spdif_maxbps < hinfo->maxbps)
+ hinfo->maxbps = mout->spdif_maxbps;
+ }
+ mutex_unlock(&codec->spdif_mutex);
+ }
return snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
}
@@ -2548,7 +2686,8 @@
int i;
mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+ if (mout->dig_out_nid && mout->share_spdif &&
+ mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
if (chs == 2 &&
snd_hda_is_supported_format(codec, mout->dig_out_nid,
format) &&
@@ -2558,8 +2697,7 @@
stream_tag, format);
} else {
mout->dig_out_used = 0;
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
}
}
mutex_unlock(&codec->spdif_mutex);
@@ -2601,17 +2739,16 @@
int i;
for (i = 0; i < mout->num_dacs; i++)
- snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, nids[i]);
if (mout->hp_nid)
- snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
if (mout->extra_out_nid[i])
- snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
- 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec,
+ mout->extra_out_nid[i]);
mutex_lock(&codec->spdif_mutex);
if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
mout->dig_out_used = 0;
}
mutex_unlock(&codec->spdif_mutex);
@@ -2790,6 +2927,30 @@
}
}
+ /* FIX-UP:
+ * If no line-out is defined but multiple HPs are found,
+ * some of them might be the real line-outs.
+ */
+ if (!cfg->line_outs && cfg->hp_outs > 1) {
+ int i = 0;
+ while (i < cfg->hp_outs) {
+ /* The real HPs should have the sequence 0x0f */
+ if ((sequences_hp[i] & 0x0f) == 0x0f) {
+ i++;
+ continue;
+ }
+ /* Move it to the line-out table */
+ cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
+ sequences_line_out[cfg->line_outs] = sequences_hp[i];
+ cfg->line_outs++;
+ cfg->hp_outs--;
+ memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
+ sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
+ memmove(sequences_hp + i - 1, sequences_hp + i,
+ sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
+ }
+ }
+
/* sort by sequence */
sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
cfg->line_outs);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index f148711..dcd390b 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -590,11 +590,21 @@
struct hda_pcm_ops ops;
};
+/* PCM types */
+enum {
+ HDA_PCM_TYPE_AUDIO,
+ HDA_PCM_TYPE_SPDIF,
+ HDA_PCM_TYPE_HDMI,
+ HDA_PCM_TYPE_MODEM,
+ HDA_PCM_NTYPES
+};
+
/* for PCM creation */
struct hda_pcm {
char *name;
struct hda_pcm_stream stream[2];
- unsigned int is_modem; /* modem codec? */
+ unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
+ int device; /* assigned device number */
};
/* codec information */
@@ -712,6 +722,7 @@
void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
u32 stream_tag,
int channel_id, int format);
+void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid);
unsigned int snd_hda_calc_stream_format(unsigned int rate,
unsigned int channels,
unsigned int format,
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f9de7c4..59e4389 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1007,8 +1007,8 @@
{
struct hda_gspec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
- snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid);
return 0;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 4be36c8..b3a618e 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -39,6 +39,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/dma-mapping.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -185,35 +186,28 @@
/* max number of SDs */
/* ICH, ATI and VIA have 4 playback and 4 capture */
-#define ICH6_CAPTURE_INDEX 0
#define ICH6_NUM_CAPTURE 4
-#define ICH6_PLAYBACK_INDEX 4
#define ICH6_NUM_PLAYBACK 4
/* ULI has 6 playback and 5 capture */
-#define ULI_CAPTURE_INDEX 0
#define ULI_NUM_CAPTURE 5
-#define ULI_PLAYBACK_INDEX 5
#define ULI_NUM_PLAYBACK 6
/* ATI HDMI has 1 playback and 0 capture */
-#define ATIHDMI_CAPTURE_INDEX 0
#define ATIHDMI_NUM_CAPTURE 0
-#define ATIHDMI_PLAYBACK_INDEX 0
#define ATIHDMI_NUM_PLAYBACK 1
/* this number is statically defined for simplicity */
#define MAX_AZX_DEV 16
/* max number of fragments - we may use more if allocating more pages for BDL */
-#define BDL_SIZE PAGE_ALIGN(8192)
-#define AZX_MAX_FRAG (BDL_SIZE / (MAX_AZX_DEV * 16))
+#define BDL_SIZE 4096
+#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
+#define AZX_MAX_FRAG 32
/* max buffer size - no h/w limit, you can increase as you like */
#define AZX_MAX_BUF_SIZE (1024*1024*1024)
/* max number of PCM devics per card */
-#define AZX_MAX_AUDIO_PCMS 6
-#define AZX_MAX_MODEM_PCMS 2
-#define AZX_MAX_PCMS (AZX_MAX_AUDIO_PCMS + AZX_MAX_MODEM_PCMS)
+#define AZX_MAX_PCMS 8
/* RIRB int mask: overrun[2], response[0] */
#define RIRB_INT_RESPONSE 0x01
@@ -227,6 +221,9 @@
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
+#define SD_CTL_STRIPE (3 << 16) /* stripe control */
+#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
+#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
#define SD_CTL_STREAM_TAG_SHIFT 20
@@ -284,12 +281,10 @@
*/
struct azx_dev {
- u32 *bdl; /* virtual address of the BDL */
- dma_addr_t bdl_addr; /* physical address of the BDL */
+ struct snd_dma_buffer bdl; /* BDL buffer */
u32 *posbuf; /* position buffer pointer */
unsigned int bufsize; /* size of the play buffer in bytes */
- unsigned int fragsize; /* size of each period in bytes */
unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */
@@ -350,7 +345,6 @@
struct azx_dev *azx_dev;
/* PCM */
- unsigned int pcm_devs;
struct snd_pcm *pcm[AZX_MAX_PCMS];
/* HD codec */
@@ -361,8 +355,7 @@
struct azx_rb corb;
struct azx_rb rirb;
- /* BDL, CORB/RIRB and position buffers */
- struct snd_dma_buffer bdl;
+ /* CORB/RIRB and position buffers */
struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf;
@@ -546,8 +539,9 @@
if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
snd_hda_queue_unsol_event(chip->bus, res, res_ex);
else if (chip->rirb.cmds) {
- chip->rirb.cmds--;
chip->rirb.res = res;
+ smp_wmb();
+ chip->rirb.cmds--;
}
}
}
@@ -566,8 +560,10 @@
azx_update_rirb(chip);
spin_unlock_irq(&chip->reg_lock);
}
- if (!chip->rirb.cmds)
+ if (!chip->rirb.cmds) {
+ smp_rmb();
return chip->rirb.res; /* the last value */
+ }
if (time_after(jiffies, timeout))
break;
if (codec->bus->needs_damn_long_delay)
@@ -965,30 +961,57 @@
/*
* set up BDL entries
*/
-static void azx_setup_periods(struct azx_dev *azx_dev)
+static int azx_setup_periods(struct snd_pcm_substream *substream,
+ struct azx_dev *azx_dev)
{
- u32 *bdl = azx_dev->bdl;
- dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;
- int idx;
+ struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+ u32 *bdl;
+ int i, ofs, periods, period_bytes;
/* reset BDL address */
azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ periods = azx_dev->bufsize / period_bytes;
+
/* program the initial BDL entries */
- for (idx = 0; idx < azx_dev->frags; idx++) {
- unsigned int off = idx << 2; /* 4 dword step */
- dma_addr_t addr = dma_addr + idx * azx_dev->fragsize;
- /* program the address field of the BDL entry */
- bdl[off] = cpu_to_le32((u32)addr);
- bdl[off+1] = cpu_to_le32(upper_32bit(addr));
-
- /* program the size field of the BDL entry */
- bdl[off+2] = cpu_to_le32(azx_dev->fragsize);
-
- /* program the IOC to enable interrupt when buffer completes */
- bdl[off+3] = cpu_to_le32(0x01);
+ bdl = (u32 *)azx_dev->bdl.area;
+ ofs = 0;
+ azx_dev->frags = 0;
+ for (i = 0; i < periods; i++) {
+ int size, rest;
+ if (i >= AZX_MAX_BDL_ENTRIES) {
+ snd_printk(KERN_ERR "Too many BDL entries: "
+ "buffer=%d, period=%d\n",
+ azx_dev->bufsize, period_bytes);
+ /* reset */
+ azx_sd_writel(azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(azx_dev, SD_BDLPU, 0);
+ return -EINVAL;
+ }
+ rest = period_bytes;
+ do {
+ dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32bit(addr));
+ /* program the size field of the BDL entry */
+ size = PAGE_SIZE - (ofs % PAGE_SIZE);
+ if (rest < size)
+ size = rest;
+ bdl[2] = cpu_to_le32(size);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ rest -= size;
+ bdl[3] = rest ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ azx_dev->frags++;
+ ofs += size;
+ } while (rest > 0);
}
+ return 0;
}
/*
@@ -1037,14 +1060,17 @@
/* program the BDL address */
/* lower BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr);
+ azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
/* upper BDL address */
- azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
+ azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr));
/* enable the position buffer */
- if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
- azx_writel(chip, DPLBASE,
- (u32)chip->posbuf.addr |ICH6_DPLBASE_ENABLE);
+ if (chip->position_fix == POS_FIX_POSBUF ||
+ chip->position_fix == POS_FIX_AUTO) {
+ if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+ azx_writel(chip, DPLBASE,
+ (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+ }
/* set the interrupt enable bits in the descriptor control register */
azx_sd_writel(azx_dev, SD_CTL,
@@ -1157,7 +1183,8 @@
SNDRV_PCM_INFO_MMAP_VALID |
/* No full-resume yet implemented */
/* SNDRV_PCM_INFO_RESUME |*/
- SNDRV_PCM_INFO_PAUSE),
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
@@ -1219,6 +1246,7 @@
spin_unlock_irqrestore(&chip->reg_lock, flags);
runtime->private_data = azx_dev;
+ snd_pcm_set_sync(substream);
mutex_unlock(&chip->open_mutex);
return 0;
}
@@ -1275,8 +1303,6 @@
struct snd_pcm_runtime *runtime = substream->runtime;
azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
- azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);
- azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize;
azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
runtime->channels,
runtime->format,
@@ -1288,10 +1314,10 @@
return -EINVAL;
}
- snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, "
- "format=0x%x\n",
- azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);
- azx_setup_periods(azx_dev);
+ snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
+ azx_dev->bufsize, azx_dev->format_val);
+ if (azx_setup_periods(substream, azx_dev) < 0)
+ return -EINVAL;
azx_setup_controller(chip, azx_dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
@@ -1305,37 +1331,94 @@
static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx_dev *azx_dev = get_azx_dev(substream);
struct azx *chip = apcm->chip;
- int err = 0;
+ struct azx_dev *azx_dev;
+ struct snd_pcm_substream *s;
+ int start, nsync = 0, sbits = 0;
+ int nwait, timeout;
- spin_lock(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
- azx_stream_start(chip, azx_dev);
- azx_dev->running = 1;
+ start = 1;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- azx_stream_stop(chip, azx_dev);
- azx_dev->running = 0;
+ start = 0;
break;
default:
- err = -EINVAL;
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ sbits |= 1 << azx_dev->index;
+ nsync++;
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ spin_lock(&chip->reg_lock);
+ if (nsync > 1) {
+ /* first, set SYNC bits of corresponding streams */
+ azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits);
+ }
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (start)
+ azx_stream_start(chip, azx_dev);
+ else
+ azx_stream_stop(chip, azx_dev);
+ azx_dev->running = start;
}
spin_unlock(&chip->reg_lock);
- if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH ||
- cmd == SNDRV_PCM_TRIGGER_SUSPEND ||
- cmd == SNDRV_PCM_TRIGGER_STOP) {
- int timeout = 5000;
- while ((azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START) &&
- --timeout)
- ;
+ if (start) {
+ if (nsync == 1)
+ return 0;
+ /* wait until all FIFOs get ready */
+ for (timeout = 5000; timeout; timeout--) {
+ nwait = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (!(azx_sd_readb(azx_dev, SD_STS) &
+ SD_STS_FIFO_READY))
+ nwait++;
+ }
+ if (!nwait)
+ break;
+ cpu_relax();
+ }
+ } else {
+ /* wait until all RUN bits are cleared */
+ for (timeout = 5000; timeout; timeout--) {
+ nwait = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (azx_sd_readb(azx_dev, SD_CTL) &
+ SD_CTL_DMA_START)
+ nwait++;
+ }
+ if (!nwait)
+ break;
+ cpu_relax();
+ }
}
- return err;
+ if (nsync > 1) {
+ spin_lock(&chip->reg_lock);
+ /* reset SYNC bits */
+ azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits);
+ spin_unlock(&chip->reg_lock);
+ }
+ return 0;
}
static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
@@ -1378,6 +1461,7 @@
.prepare = azx_pcm_prepare,
.trigger = azx_pcm_trigger,
.pointer = azx_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
};
static void azx_pcm_free(struct snd_pcm *pcm)
@@ -1386,7 +1470,7 @@
}
static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
- struct hda_pcm *cpcm, int pcm_dev)
+ struct hda_pcm *cpcm)
{
int err;
struct snd_pcm *pcm;
@@ -1400,7 +1484,7 @@
snd_assert(cpcm->name, return -EINVAL);
- err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+ err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
cpcm->stream[0].substreams,
cpcm->stream[1].substreams,
&pcm);
@@ -1420,62 +1504,70 @@
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
if (cpcm->stream[1].substreams)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci),
1024 * 64, 1024 * 1024);
- chip->pcm[pcm_dev] = pcm;
- if (chip->pcm_devs < pcm_dev + 1)
- chip->pcm_devs = pcm_dev + 1;
-
+ chip->pcm[cpcm->device] = pcm;
return 0;
}
static int __devinit azx_pcm_create(struct azx *chip)
{
+ static const char *dev_name[HDA_PCM_NTYPES] = {
+ "Audio", "SPDIF", "HDMI", "Modem"
+ };
+ /* starting device index for each PCM type */
+ static int dev_idx[HDA_PCM_NTYPES] = {
+ [HDA_PCM_TYPE_AUDIO] = 0,
+ [HDA_PCM_TYPE_SPDIF] = 1,
+ [HDA_PCM_TYPE_HDMI] = 3,
+ [HDA_PCM_TYPE_MODEM] = 6
+ };
+ /* normal audio device indices; not linear to keep compatibility */
+ static int audio_idx[4] = { 0, 2, 4, 5 };
struct hda_codec *codec;
int c, err;
- int pcm_dev;
+ int num_devs[HDA_PCM_NTYPES];
err = snd_hda_build_pcms(chip->bus);
if (err < 0)
return err;
/* create audio PCMs */
- pcm_dev = 0;
+ memset(num_devs, 0, sizeof(num_devs));
list_for_each_entry(codec, &chip->bus->codec_list, list) {
for (c = 0; c < codec->num_pcms; c++) {
- if (codec->pcm_info[c].is_modem)
- continue; /* create later */
- if (pcm_dev >= AZX_MAX_AUDIO_PCMS) {
- snd_printk(KERN_ERR SFX
- "Too many audio PCMs\n");
- return -EINVAL;
+ struct hda_pcm *cpcm = &codec->pcm_info[c];
+ int type = cpcm->pcm_type;
+ switch (type) {
+ case HDA_PCM_TYPE_AUDIO:
+ if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
+ snd_printk(KERN_WARNING
+ "Too many audio devices\n");
+ continue;
+ }
+ cpcm->device = audio_idx[num_devs[type]];
+ break;
+ case HDA_PCM_TYPE_SPDIF:
+ case HDA_PCM_TYPE_HDMI:
+ case HDA_PCM_TYPE_MODEM:
+ if (num_devs[type]) {
+ snd_printk(KERN_WARNING
+ "%s already defined\n",
+ dev_name[type]);
+ continue;
+ }
+ cpcm->device = dev_idx[type];
+ break;
+ default:
+ snd_printk(KERN_WARNING
+ "Invalid PCM type %d\n", type);
+ continue;
}
- err = create_codec_pcm(chip, codec,
- &codec->pcm_info[c], pcm_dev);
+ num_devs[type]++;
+ err = create_codec_pcm(chip, codec, cpcm);
if (err < 0)
return err;
- pcm_dev++;
- }
- }
-
- /* create modem PCMs */
- pcm_dev = AZX_MAX_AUDIO_PCMS;
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- for (c = 0; c < codec->num_pcms; c++) {
- if (!codec->pcm_info[c].is_modem)
- continue; /* already created */
- if (pcm_dev >= AZX_MAX_PCMS) {
- snd_printk(KERN_ERR SFX
- "Too many modem PCMs\n");
- return -EINVAL;
- }
- err = create_codec_pcm(chip, codec,
- &codec->pcm_info[c], pcm_dev);
- if (err < 0)
- return err;
- chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM;
- pcm_dev++;
}
}
return 0;
@@ -1502,10 +1594,7 @@
* and initialize
*/
for (i = 0; i < chip->num_streams; i++) {
- unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_dev->bdl = (u32 *)(chip->bdl.area + off);
- azx_dev->bdl_addr = chip->bdl.addr + off;
azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
@@ -1587,13 +1676,12 @@
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < chip->pcm_devs; i++)
+ for (i = 0; i < AZX_MAX_PCMS; i++)
snd_pcm_suspend_all(chip->pcm[i]);
if (chip->initialized)
snd_hda_suspend(chip->bus, state);
azx_stop_chip(chip);
if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
free_irq(chip->irq, chip);
chip->irq = -1;
}
@@ -1641,24 +1729,26 @@
*/
static int azx_free(struct azx *chip)
{
+ int i;
+
if (chip->initialized) {
- int i;
for (i = 0; i < chip->num_streams; i++)
azx_stream_stop(chip, &chip->azx_dev[i]);
azx_stop_chip(chip);
}
- if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
+ if (chip->irq >= 0)
free_irq(chip->irq, (void*)chip);
- }
if (chip->msi)
pci_disable_msi(chip->pci);
if (chip->remap_addr)
iounmap(chip->remap_addr);
- if (chip->bdl.area)
- snd_dma_free_pages(&chip->bdl);
+ if (chip->azx_dev) {
+ for (i = 0; i < chip->num_streams; i++)
+ if (chip->azx_dev[i].bdl.area)
+ snd_dma_free_pages(&chip->azx_dev[i].bdl);
+ }
if (chip->rb.area)
snd_dma_free_pages(&chip->rb);
if (chip->posbuf.area)
@@ -1682,6 +1772,7 @@
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE),
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE),
+ SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE),
{}
};
@@ -1740,7 +1831,7 @@
struct azx **rchip)
{
struct azx *chip;
- int err;
+ int i, err;
unsigned short gcap;
static struct snd_device_ops ops = {
.dev_free = azx_dev_free,
@@ -1812,38 +1903,35 @@
gcap = azx_readw(chip, GCAP);
snd_printdd("chipset global capabilities = 0x%x\n", gcap);
- if (gcap) {
- /* read number of streams from GCAP register instead of using
- * hardcoded value
- */
- chip->playback_streams = (gcap & (0xF << 12)) >> 12;
- chip->capture_streams = (gcap & (0xF << 8)) >> 8;
- chip->playback_index_offset = chip->capture_streams;
- chip->capture_index_offset = 0;
- } else {
+ /* allow 64bit DMA address if supported by H/W */
+ if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_64BIT_MASK))
+ pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK);
+
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->capture_streams = (gcap >> 8) & 0x0f;
+ chip->playback_streams = (gcap >> 12) & 0x0f;
+ if (!chip->playback_streams && !chip->capture_streams) {
/* gcap didn't give any info, switching to old method */
switch (chip->driver_type) {
case AZX_DRIVER_ULI:
chip->playback_streams = ULI_NUM_PLAYBACK;
chip->capture_streams = ULI_NUM_CAPTURE;
- chip->playback_index_offset = ULI_PLAYBACK_INDEX;
- chip->capture_index_offset = ULI_CAPTURE_INDEX;
break;
case AZX_DRIVER_ATIHDMI:
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
- chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
- chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
break;
default:
chip->playback_streams = ICH6_NUM_PLAYBACK;
chip->capture_streams = ICH6_NUM_CAPTURE;
- chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
- chip->capture_index_offset = ICH6_CAPTURE_INDEX;
break;
}
}
+ chip->capture_index_offset = 0;
+ chip->playback_index_offset = chip->capture_streams;
chip->num_streams = chip->playback_streams + chip->capture_streams;
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
GFP_KERNEL);
@@ -1852,13 +1940,15 @@
goto errout;
}
- /* allocate memory for the BDL for each stream */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- BDL_SIZE, &chip->bdl);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
- goto errout;
+ for (i = 0; i < chip->num_streams; i++) {
+ /* allocate memory for the BDL for each stream */
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ BDL_SIZE, &chip->azx_dev[i].bdl);
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
+ goto errout;
+ }
}
/* allocate memory for the position buffer */
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
@@ -1994,48 +2084,63 @@
/* PCI IDs */
static struct pci_device_id azx_ids[] = {
- { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */
- { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */
- { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
- { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
- { 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
- { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
- { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
- { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
- { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/
- { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
- { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
- { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
- { 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
- { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
- { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */
- { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */
- { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */
- { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */
- { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */
- { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */
- { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */
- { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
- { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
- { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
- { 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP51 */
- { 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP55 */
- { 0x10de, 0x03e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */
- { 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */
- { 0x10de, 0x044a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
- { 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
- { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
- { 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
- { 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
- { 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
- { 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
- { 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
- { 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
- { 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
- { 0x10de, 0x0ac0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
- { 0x10de, 0x0ac1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
- { 0x10de, 0x0ac2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
- { 0x10de, 0x0ac3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
+ /* ICH 6..10 */
+ { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
+ { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
+ /* SCH */
+ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+ /* ATI SB 450/600 */
+ { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
+ { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
+ /* ATI HDMI */
+ { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI },
+ /* VIA VT8251/VT8237A */
+ { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
+ /* SIS966 */
+ { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
+ /* ULI M5461 */
+ { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI },
+ /* NVIDIA MCP */
+ { PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index ad0014a..5c9e578 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -228,8 +228,18 @@
int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */
+ int share_spdif; /* share SPDIF pin */
+ /* PCM information for both analog and SPDIF DACs */
+ unsigned int analog_rates;
+ unsigned int analog_maxbps;
+ u64 analog_formats;
+ unsigned int spdif_rates;
+ unsigned int spdif_maxbps;
+ u64 spdif_formats;
};
+int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
+ struct hda_multi_out *mout);
int snd_hda_multi_out_dig_open(struct hda_codec *codec,
struct hda_multi_out *mout);
int snd_hda_multi_out_dig_close(struct hda_codec *codec,
@@ -241,7 +251,8 @@
struct snd_pcm_substream *substream);
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
struct hda_multi_out *mout,
- struct snd_pcm_substream *substream);
+ struct snd_pcm_substream *substream,
+ struct hda_pcm_stream *hinfo);
int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
unsigned int stream_tag,
@@ -407,11 +418,4 @@
hda_nid_t nid);
#endif /* CONFIG_SND_HDA_POWER_SAVE */
-/*
- * virtual master control
- */
-struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
- const unsigned int *tlv);
-int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
-
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
index f5c23bb..2fdf235 100644
--- a/sound/pci/hda/hda_patch.h
+++ b/sound/pci/hda/hda_patch.h
@@ -18,31 +18,3 @@
extern struct hda_codec_preset snd_hda_preset_conexant[];
/* VIA codecs */
extern struct hda_codec_preset snd_hda_preset_via[];
-
-static const struct hda_codec_preset *hda_preset_tables[] = {
-#ifdef CONFIG_SND_HDA_CODEC_REALTEK
- snd_hda_preset_realtek,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
- snd_hda_preset_cmedia,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ANALOG
- snd_hda_preset_analog,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
- snd_hda_preset_sigmatel,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SI3054
- snd_hda_preset_si3054,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
- snd_hda_preset_atihdmi,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
- snd_hda_preset_conexant,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_VIA
- snd_hda_preset_via,
-#endif
- NULL
-};
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index c864928..e0a605a 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -28,6 +28,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_patch.h"
struct ad198x_spec {
struct snd_kcontrol_new *mixers[5];
@@ -80,7 +81,6 @@
#endif
/* for virtual master */
hda_nid_t vmaster_nid;
- u32 vmaster_tlv[4];
const char **slave_vols;
const char **slave_sws;
};
@@ -171,6 +171,11 @@
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0)
return err;
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -180,10 +185,11 @@
/* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ unsigned int vmaster_tlv[4];
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, spec->vmaster_tlv);
+ HDA_OUTPUT, vmaster_tlv);
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- spec->vmaster_tlv,
+ vmaster_tlv,
(spec->slave_vols ?
spec->slave_vols : ad_slave_vols));
if (err < 0)
@@ -217,7 +223,8 @@
struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -289,8 +296,7 @@
struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
return 0;
}
@@ -359,6 +365,7 @@
info++;
codec->num_pcms++;
info->name = "AD198x Digital";
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
if (spec->dig_in_nid) {
@@ -611,13 +618,19 @@
},
};
+static struct hda_input_mux ad1986a_automic_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ { "Mix", 0x5 },
+ },
+};
+
static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
@@ -641,6 +654,33 @@
{ } /* end */
};
+/* re-connect the mic boost input according to the jack sensing */
+static void ad1986a_automic(struct hda_codec *codec)
+{
+ unsigned int present;
+ present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
+ /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
+ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
+ (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
+}
+
+#define AD1986A_MIC_EVENT 0x36
+
+static void ad1986a_automic_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != AD1986A_MIC_EVENT)
+ return;
+ ad1986a_automic(codec);
+}
+
+static int ad1986a_automic_init(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ ad1986a_automic(codec);
+ return 0;
+}
+
/* laptop-automute - 2ch only */
static void ad1986a_update_hp(struct hda_codec *codec)
@@ -844,6 +884,15 @@
{}
};
+static struct hda_verb ad1986a_automic_verbs[] = {
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
+ {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
+ {}
+};
+
/* Ultra initialization */
static struct hda_verb ad1986a_ultra_init[] = {
/* eapd initialization */
@@ -986,14 +1035,17 @@
break;
case AD1986A_LAPTOP_EAPD:
spec->mixers[0] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 2;
+ spec->num_init_verbs = 3;
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+ spec->init_verbs[2] = ad1986a_automic_verbs;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
if (!is_jack_available(codec, 0x25))
spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+ spec->input_mux = &ad1986a_automic_capture_source;
+ codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
+ codec->patch_ops.init = ad1986a_automic_init;
break;
case AD1986A_LAPTOP_AUTOMUTE:
spec->mixers[0] = ad1986a_laptop_automute_mixers;
@@ -1365,7 +1417,10 @@
if (! ad198x_eapd_put(kcontrol, ucontrol))
return 0;
-
+ /* change speaker pin appropriately */
+ snd_hda_codec_write(codec, 0x05, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ spec->cur_eapd ? PIN_OUT : 0);
/* toggle HP mute appropriately */
snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
HDA_AMP_MUTE,
@@ -2087,6 +2142,10 @@
{ } /* end */
};
+static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
+ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
/*
* initialization verbs
@@ -2187,6 +2246,13 @@
{ }
};
+/* AD1989 has no ADC -> SPDIF route */
+static struct hda_verb ad1989_spdif_init_verbs[] = {
+ /* SPDIF out pin */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+ { }
+};
+
/*
* verbs for 3stack (+dig)
*/
@@ -2894,10 +2960,19 @@
spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
if (spec->multiout.dig_out_nid) {
- spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;
+ if (codec->vendor_id >= 0x11d4989a) {
+ spec->mixers[spec->num_mixers++] =
+ ad1989_spdif_out_mixers;
+ spec->init_verbs[spec->num_init_verbs++] =
+ ad1989_spdif_init_verbs;
+ } else {
+ spec->mixers[spec->num_mixers++] =
+ ad1988_spdif_out_mixers;
+ spec->init_verbs[spec->num_init_verbs++] =
+ ad1988_spdif_init_verbs;
+ }
}
- if (spec->dig_in_nid)
+ if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
codec->patch_ops = ad198x_patch_ops;
@@ -3133,11 +3208,12 @@
* Lenovo Thinkpad T61/X61
*/
static struct hda_input_mux ad1984_thinkpad_capture_source = {
- .num_items = 3,
+ .num_items = 4,
.items = {
{ "Mic", 0x0 },
{ "Internal Mic", 0x1 },
{ "Mix", 0x3 },
+ { "Docking-Station", 0x4 },
},
};
@@ -3268,8 +3344,7 @@
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
- snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
- 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
return 0;
}
@@ -3356,6 +3431,472 @@
/*
+ * AD1883 / AD1884A / AD1984A / AD1984B
+ *
+ * port-B (0x14) - front mic-in
+ * port-E (0x1c) - rear mic-in
+ * port-F (0x16) - CD / ext out
+ * port-C (0x15) - rear line-in
+ * port-D (0x12) - rear line-out
+ * port-A (0x11) - front hp-out
+ *
+ * AD1984A = AD1884A + digital-mic
+ * AD1883 = equivalent with AD1984A
+ * AD1984B = AD1984A + extra SPDIF-out
+ *
+ * FIXME:
+ * We share the single DAC for both HP and line-outs (see AD1884/1984).
+ */
+
+static hda_nid_t ad1884a_dac_nids[1] = {
+ 0x03,
+};
+
+#define ad1884a_adc_nids ad1884_adc_nids
+#define ad1884a_capsrc_nids ad1884_capsrc_nids
+
+#define AD1884A_SPDIF_OUT 0x02
+
+static struct hda_input_mux ad1884a_capture_source = {
+ .num_items = 5,
+ .items = {
+ { "Front Mic", 0x0 },
+ { "Mic", 0x4 },
+ { "Line", 0x1 },
+ { "CD", 0x2 },
+ { "Mix", 0x3 },
+ },
+};
+
+static struct snd_kcontrol_new ad1884a_base_mixers[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ /* SPDIF controls */
+ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+ /* identical with ad1983 */
+ .info = ad1983_spdif_route_info,
+ .get = ad1983_spdif_route_get,
+ .put = ad1983_spdif_route_put,
+ },
+ { } /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1884a_init_verbs[] = {
+ /* DACs; unmute as default */
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+ /* Port-A (HP) mixer - route only from analog mixer */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ /* Port-A pin */
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Port-D (Line-out) mixer - route only from analog mixer */
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ /* Port-D pin */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Mono-out mixer - route only from analog mixer */
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ /* Mono-out pin */
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Port-B (front mic) pin */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Port-C (rear line-in) pin */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Port-E (rear mic) pin */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
+ /* Port-F (CD) pin */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Analog mixer; mute as default */
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+ /* Analog Mix output amp */
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* capture sources */
+ {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* SPDIF output amp */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+ { } /* end */
+};
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1884a_loopbacks[] = {
+ { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+ { 0x20, HDA_INPUT, 1 }, /* Mic */
+ { 0x20, HDA_INPUT, 2 }, /* CD */
+ { 0x20, HDA_INPUT, 4 }, /* Docking */
+ { } /* end */
+};
+#endif
+
+/*
+ * Laptop model
+ *
+ * Port A: Headphone jack
+ * Port B: MIC jack
+ * Port C: Internal MIC
+ * Port D: Dock Line Out (if enabled)
+ * Port E: Dock Line In (if enabled)
+ * Port F: Internal speakers
+ */
+
+static struct hda_input_mux ad1884a_laptop_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 }, /* port-B */
+ { "Internal Mic", 0x1 }, /* port-C */
+ { "Dock Mic", 0x4 }, /* port-E */
+ { "Mix", 0x3 },
+ },
+};
+
+static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_input_mux ad1884a_mobile_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x1 }, /* port-C */
+ { "Mix", 0x3 },
+ },
+};
+
+static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/* mute internal speaker if HP is plugged */
+static void ad1884a_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x11, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ present ? 0x00 : 0x02);
+}
+
+#define AD1884A_HP_EVENT 0x37
+
+/* unsolicited event for HP jack sensing */
+static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ if ((res >> 26) != AD1884A_HP_EVENT)
+ return;
+ ad1884a_hp_automute(codec);
+}
+
+/* initialize jack-sensing, too */
+static int ad1884a_hp_init(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ ad1884a_hp_automute(codec);
+ return 0;
+}
+
+/* additional verbs for laptop model */
+static struct hda_verb ad1884a_laptop_verbs[] = {
+ /* Port-A (HP) pin - always unmuted */
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Port-F (int speaker) mixer - route only from analog mixer */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ /* Port-F pin */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* analog mix */
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* unsolicited event for pin-sense */
+ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+ { } /* end */
+};
+
+/*
+ * Thinkpad X300
+ * 0x11 - HP
+ * 0x12 - speaker
+ * 0x14 - mic-in
+ * 0x17 - built-in mic
+ */
+
+static struct hda_verb ad1984a_thinkpad_verbs[] = {
+ /* HP unmute */
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* analog mix */
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* turn on EAPD */
+ {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+ /* unsolicited event for pin-sense */
+ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+ /* internal mic - dmic */
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ /* set magic COEFs for dmic */
+ {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
+ {0x01, AC_VERB_SET_PROC_COEF, 0x08},
+ { } /* end */
+};
+
+static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct hda_input_mux ad1984a_thinkpad_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Internal Mic", 0x5 },
+ { "Mix", 0x3 },
+ },
+};
+
+/* mute internal speaker if HP is plugged */
+static void ad1984a_thinkpad_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+/* unsolicited event for HP jack sensing */
+static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != AD1884A_HP_EVENT)
+ return;
+ ad1984a_thinkpad_automute(codec);
+}
+
+/* initialize jack-sensing, too */
+static int ad1984a_thinkpad_init(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ ad1984a_thinkpad_automute(codec);
+ return 0;
+}
+
+/*
+ */
+
+enum {
+ AD1884A_DESKTOP,
+ AD1884A_LAPTOP,
+ AD1884A_MOBILE,
+ AD1884A_THINKPAD,
+ AD1884A_MODELS
+};
+
+static const char *ad1884a_models[AD1884A_MODELS] = {
+ [AD1884A_DESKTOP] = "desktop",
+ [AD1884A_LAPTOP] = "laptop",
+ [AD1884A_MOBILE] = "mobile",
+ [AD1884A_THINKPAD] = "thinkpad",
+};
+
+static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
+ SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
+ {}
+};
+
+static int patch_ad1884a(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec;
+ int board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ mutex_init(&spec->amp_mutex);
+ codec->spec = spec;
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
+ spec->multiout.dac_nids = ad1884a_dac_nids;
+ spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
+ spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
+ spec->adc_nids = ad1884a_adc_nids;
+ spec->capsrc_nids = ad1884a_capsrc_nids;
+ spec->input_mux = &ad1884a_capture_source;
+ spec->num_mixers = 1;
+ spec->mixers[0] = ad1884a_base_mixers;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = ad1884a_init_verbs;
+ spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = ad1884a_loopbacks;
+#endif
+ codec->patch_ops = ad198x_patch_ops;
+
+ /* override some parameters */
+ board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
+ ad1884a_models,
+ ad1884a_cfg_tbl);
+ switch (board_config) {
+ case AD1884A_LAPTOP:
+ spec->mixers[0] = ad1884a_laptop_mixers;
+ spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
+ spec->multiout.dig_out_nid = 0;
+ spec->input_mux = &ad1884a_laptop_capture_source;
+ codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
+ codec->patch_ops.init = ad1884a_hp_init;
+ break;
+ case AD1884A_MOBILE:
+ spec->mixers[0] = ad1884a_mobile_mixers;
+ spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
+ spec->multiout.dig_out_nid = 0;
+ spec->input_mux = &ad1884a_mobile_capture_source;
+ codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
+ codec->patch_ops.init = ad1884a_hp_init;
+ break;
+ case AD1884A_THINKPAD:
+ spec->mixers[0] = ad1984a_thinkpad_mixers;
+ spec->init_verbs[spec->num_init_verbs++] =
+ ad1984a_thinkpad_verbs;
+ spec->multiout.dig_out_nid = 0;
+ spec->input_mux = &ad1984a_thinkpad_capture_source;
+ codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
+ codec->patch_ops.init = ad1984a_thinkpad_init;
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
* AD1882
*
* port-A - front hp-out
@@ -3654,13 +4195,19 @@
* patch entries
*/
struct hda_codec_preset snd_hda_preset_analog[] = {
+ { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
+ { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
+ { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
+ { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
+ { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
+ { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index 9a8bb4c..1227250 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -27,6 +27,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_patch.h"
struct atihdmi_spec {
struct hda_multi_out multiout;
@@ -58,6 +59,10 @@
static int atihdmi_init(struct hda_codec *codec)
{
snd_hda_sequence_write(codec, atihdmi_basic_init);
+ /* SI codec requires to unmute the pin */
+ if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
return 0;
}
@@ -112,6 +117,7 @@
codec->pcm_info = info;
info->name = "ATI HDMI";
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
return 0;
@@ -158,5 +164,7 @@
{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
+ { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
+ { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 3d6097b..c73ce07 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -28,6 +28,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_patch.h"
#define NUM_PINS 11
@@ -329,6 +330,11 @@
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0)
return err;
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -432,7 +438,8 @@
struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -506,7 +513,7 @@
{
struct cmi_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
return 0;
}
@@ -571,6 +578,7 @@
codec->num_pcms++;
info++;
info->name = "CMI9880 Digital";
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->multiout.dig_out_nid) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
@@ -603,6 +611,7 @@
static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
+ SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG),
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 7206b30..36fd852 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -27,6 +27,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_patch.h"
#define CXT_PIN_DIR_IN 0x00
#define CXT_PIN_DIR_OUT 0x01
@@ -98,7 +99,8 @@
struct snd_pcm_substream *substream)
{
struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -172,8 +174,7 @@
struct snd_pcm_substream *substream)
{
struct conexant_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
return 0;
}
@@ -241,7 +242,7 @@
struct snd_pcm_substream *substream)
{
struct conexant_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
spec->cur_adc = 0;
return 0;
}
@@ -284,6 +285,7 @@
info++;
codec->num_pcms++;
info->name = "Conexant Digital";
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
conexant_pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
@@ -371,6 +373,11 @@
spec->multiout.dig_out_nid);
if (err < 0)
return err;
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
@@ -511,6 +518,14 @@
}
};
+static struct hda_input_mux cxt5045_capture_source_hp530 = {
+ .num_items = 2,
+ .items = {
+ { "ExtMic", 0x1 },
+ { "IntMic", 0x2 },
+ }
+};
+
/* turn on/off EAPD (+ mute HP) as a master switch */
static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -639,6 +654,37 @@
{}
};
+static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = conexant_mux_enum_info,
+ .get = conexant_mux_enum_get,
+ .put = conexant_mux_enum_put
+ },
+ HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
+ HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5045_hp_master_sw_put,
+ .private_value = 0x10,
+ },
+
+ {}
+};
+
static struct hda_verb cxt5045_init_verbs[] = {
/* Line in, Mic */
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
@@ -833,6 +879,7 @@
CXT5045_LAPTOP_MICSENSE,
CXT5045_LAPTOP_HPMICSENSE,
CXT5045_BENQ,
+ CXT5045_LAPTOP_HP530,
#ifdef CONFIG_SND_DEBUG
CXT5045_TEST,
#endif
@@ -844,6 +891,7 @@
[CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
[CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
[CXT5045_BENQ] = "benq",
+ [CXT5045_LAPTOP_HP530] = "laptop-hp530",
#ifdef CONFIG_SND_DEBUG
[CXT5045_TEST] = "test",
#endif
@@ -857,7 +905,7 @@
SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
@@ -941,6 +989,14 @@
spec->num_mixers = 2;
codec->patch_ops.init = cxt5045_init;
break;
+ case CXT5045_LAPTOP_HP530:
+ codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+ spec->input_mux = &cxt5045_capture_source_hp530;
+ spec->num_init_verbs = 2;
+ spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
+ spec->mixers[0] = cxt5045_mixers_hp530;
+ codec->patch_ops.init = cxt5045_init;
+ break;
#ifdef CONFIG_SND_DEBUG
case CXT5045_TEST:
spec->input_mux = &cxt5045_test_capture_source;
@@ -1537,7 +1593,7 @@
new_adc = spec->adc_nids[spec->cur_adc_idx];
if (spec->cur_adc && spec->cur_adc != new_adc) {
/* stream is running, let's swap the current ADC */
- snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
spec->cur_adc = new_adc;
snd_hda_codec_setup_stream(codec, new_adc,
spec->cur_adc_stream_tag, 0,
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 33282f9..cdda64b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -30,6 +30,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_patch.h"
#define ALC880_FRONT_EVENT 0x01
#define ALC880_DCVOL_EVENT 0x02
@@ -97,16 +98,19 @@
ALC262_SONY_ASSAMD,
ALC262_BENQ_T31,
ALC262_ULTRA,
+ ALC262_LENOVO_3000,
ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */
};
/* ALC268 models */
enum {
+ ALC267_QUANTA_IL1,
ALC268_3ST,
ALC268_TOSHIBA,
ALC268_ACER,
ALC268_DELL,
+ ALC268_ZEPTO,
#ifdef CONFIG_SND_DEBUG
ALC268_TEST,
#endif
@@ -195,10 +199,11 @@
ALC883_LENOVO_NB0763,
ALC888_LENOVO_MS7195_DIG,
ALC883_HAIER_W66,
- ALC888_6ST_HP,
ALC888_3ST_HP,
ALC888_6ST_DELL,
ALC883_MITAC,
+ ALC883_CLEVO_M720,
+ ALC883_FUJITSU_PI2515,
ALC883_AUTO,
ALC883_MODEL_LAST,
};
@@ -237,6 +242,7 @@
/* capture */
unsigned int num_adc_nids;
hda_nid_t *adc_nids;
+ hda_nid_t *capsrc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
/* capture source */
@@ -270,7 +276,6 @@
/* for virtual master */
hda_nid_t vmaster_nid;
- u32 vmaster_tlv[4];
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
#endif
@@ -290,6 +295,7 @@
hda_nid_t hp_nid; /* optional */
unsigned int num_adc_nids;
hda_nid_t *adc_nids;
+ hda_nid_t *capsrc_nids;
hda_nid_t dig_in_nid;
unsigned int num_channel_mode;
const struct hda_channel_mode *channel_mode;
@@ -336,9 +342,10 @@
struct alc_spec *spec = codec->spec;
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+ hda_nid_t nid = spec->capsrc_nids ?
+ spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
- spec->adc_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
+ nid, &spec->cur_mux[adc_idx]);
}
@@ -707,6 +714,7 @@
spec->num_adc_nids = preset->num_adc_nids;
spec->adc_nids = preset->adc_nids;
+ spec->capsrc_nids = preset->capsrc_nids;
spec->dig_in_nid = preset->dig_in_nid;
spec->unsol_event = preset->unsol_event;
@@ -741,7 +749,6 @@
static void alc_sku_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int mute;
unsigned int present;
unsigned int hp_nid = spec->autocfg.hp_pins[0];
unsigned int sp_nid = spec->autocfg.speaker_pins[0];
@@ -751,16 +758,8 @@
present = snd_hda_codec_read(codec, hp_nid, 0,
AC_VERB_GET_PIN_SENSE, 0);
spec->jack_present = (present & 0x80000000) != 0;
- if (spec->jack_present) {
- /* mute internal speaker */
- snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, hp_nid, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
+ snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ spec->jack_present ? 0 : PIN_OUT);
}
/* unsolicited event for HP jack sensing */
@@ -1319,11 +1318,19 @@
HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
{ } /* end */
};
+static struct hda_input_mux alc880_f1734_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x1 },
+ { "CD", 0x4 },
+ },
+};
+
/*
* ALC880 ASUS model
@@ -1516,6 +1523,11 @@
spec->multiout.dig_out_nid);
if (err < 0)
return err;
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -1525,10 +1537,11 @@
/* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ unsigned int vmaster_tlv[4];
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, spec->vmaster_tlv);
+ HDA_OUTPUT, vmaster_tlv);
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- spec->vmaster_tlv, alc_slave_vols);
+ vmaster_tlv, alc_slave_vols);
if (err < 0)
return err;
}
@@ -1882,7 +1895,7 @@
present = snd_hda_codec_read(codec, 0x14, 0,
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_INPUT, 0, HDA_AMP_MUTE, bits);
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits);
}
static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -1915,6 +1928,7 @@
* HP = 0x14, speaker-out = 0x15, mic = 0x18
*/
static struct hda_verb alc880_pin_f1734_init_verbs[] = {
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x01},
{0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
{0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
@@ -1927,7 +1941,7 @@
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
@@ -1935,6 +1949,9 @@
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT},
+
{ }
};
@@ -2318,7 +2335,8 @@
struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -2392,8 +2410,8 @@
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
- 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec,
+ spec->adc_nids[substream->number + 1]);
return 0;
}
@@ -2498,6 +2516,7 @@
codec->num_pcms = 2;
info = spec->pcm_rec + 1;
info->name = spec->stream_name_digital;
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->multiout.dig_out_nid &&
spec->stream_digital_playback) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
@@ -2560,6 +2579,7 @@
kfree(spec->kctl_alloc);
}
kfree(spec);
+ codec->spec = NULL; /* to be sure */
}
/*
@@ -3057,7 +3077,9 @@
.hp_nid = 0x02,
.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
.channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
+ .input_mux = &alc880_f1734_capture_source,
+ .unsol_event = alc880_uniwill_p53_unsol_event,
+ .init_hook = alc880_uniwill_p53_hp_automute,
},
[ALC880_ASUS] = {
.mixers = { alc880_asus_mixer },
@@ -3467,15 +3489,21 @@
return 0;
}
+static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int pin_type)
+{
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_type);
+ /* unmute pin */
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+}
+
static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid, int pin_type,
int dac_idx)
{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
+ alc_set_pin_output(codec, nid, pin_type);
/* need the manual connection? */
if (alc880_is_multi_pin(nid)) {
struct alc_spec *spec = codec->spec;
@@ -3597,9 +3625,12 @@
/* additional initialization for auto-configuration model */
static void alc880_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc880_auto_init_multi_out(codec);
alc880_auto_init_extra_out(codec);
alc880_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
/*
@@ -4795,11 +4826,7 @@
hda_nid_t nid, int pin_type,
int sel_idx)
{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
+ alc_set_pin_output(codec, nid, pin_type);
/* need the manual connection? */
if (nid >= 0x12) {
int idx = nid - 0x12;
@@ -4929,7 +4956,7 @@
/* check whether NID 0x04 is valid */
wcap = get_wcaps(codec, 0x04);
wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
- if (wcap != AC_WID_AUD_IN) {
+ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
spec->adc_nids = alc260_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
@@ -4946,8 +4973,11 @@
/* additional initialization for auto-configuration model */
static void alc260_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc260_auto_init_multi_out(codec);
alc260_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -5204,6 +5234,9 @@
#define alc882_adc_nids alc880_adc_nids
#define alc882_adc_nids_alt alc880_adc_nids_alt
+static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
+static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
+
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
@@ -5226,15 +5259,11 @@
struct alc_spec *spec = codec->spec;
const struct hda_input_mux *imux = spec->input_mux;
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
- hda_nid_t nid;
+ hda_nid_t nid = spec->capsrc_nids ?
+ spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
unsigned int *cur_val = &spec->cur_mux[adc_idx];
unsigned int i, idx;
- if (spec->num_adc_nids < 3)
- nid = capture_mixers[adc_idx + 1];
- else
- nid = capture_mixers[adc_idx];
idx = ucontrol->value.enumerated.item[0];
if (idx >= imux->num_items)
idx = imux->num_items - 1;
@@ -6111,6 +6140,7 @@
.dig_out_nid = ALC882_DIGOUT_NID,
.num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
.adc_nids = alc882_adc_nids,
+ .capsrc_nids = alc882_capsrc_nids,
.num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
.channel_mode = alc882_3ST_6ch_modes,
.need_dac_fix = 1,
@@ -6127,6 +6157,7 @@
.dig_out_nid = ALC882_DIGOUT_NID,
.num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
.adc_nids = alc882_adc_nids,
+ .capsrc_nids = alc882_capsrc_nids,
.num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
.channel_mode = alc882_3ST_6ch_modes,
.need_dac_fix = 1,
@@ -6182,15 +6213,11 @@
struct alc_spec *spec = codec->spec;
int idx;
+ alc_set_pin_output(codec, nid, pin_type);
if (spec->multiout.dac_nids[dac_idx] == 0x25)
idx = 4;
else
idx = spec->multiout.dac_nids[dac_idx] - 2;
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
}
@@ -6219,6 +6246,9 @@
if (pin) /* connect to front */
/* use dac 0 */
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin)
+ alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
#define alc882_is_input_pin(nid) alc880_is_input_pin(nid)
@@ -6231,16 +6261,21 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (alc882_is_input_pin(nid)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ?
- PIN_VREF80 : PIN_IN);
- if (nid != ALC882_PIN_CD_NID)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
+ unsigned int vref;
+ if (!nid)
+ continue;
+ vref = PIN_IN;
+ if (1 /*i <= AUTO_PIN_FRONT_MIC*/) {
+ if (snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) &
+ AC_PINCAP_VREF_80)
+ vref = PIN_VREF80;
}
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, vref);
+ if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
}
}
@@ -6294,11 +6329,16 @@
/* additional initialization for auto-configuration model */
static void alc882_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc882_auto_init_multi_out(codec);
alc882_auto_init_hp_out(codec);
alc882_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
+static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */
+
static int patch_alc882(struct hda_codec *codec)
{
struct alc_spec *spec;
@@ -6328,6 +6368,11 @@
board_config = ALC885_MBP3;
break;
default:
+ /* ALC889A is handled better as ALC888-compatible */
+ if (codec->revision_id == 0x100103) {
+ alc_free(codec);
+ return patch_alc883(codec);
+ }
printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
"trying auto-probe from BIOS...\n");
board_config = ALC882_AUTO;
@@ -6372,12 +6417,14 @@
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc882_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
+ spec->capsrc_nids = alc882_capsrc_nids_alt;
spec->mixers[spec->num_mixers] =
alc882_capture_alt_mixer;
spec->num_mixers++;
} else {
spec->adc_nids = alc882_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
+ spec->capsrc_nids = alc882_capsrc_nids;
spec->mixers[spec->num_mixers] = alc882_capture_mixer;
spec->num_mixers++;
}
@@ -6412,7 +6459,7 @@
static hda_nid_t alc883_dac_nids[4] = {
/* front, rear, clfe, rear_surr */
- 0x02, 0x04, 0x03, 0x05
+ 0x02, 0x03, 0x04, 0x05
};
static hda_nid_t alc883_adc_nids[2] = {
@@ -6420,6 +6467,8 @@
0x08, 0x09,
};
+static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
+
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
@@ -6451,35 +6500,18 @@
},
};
+static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ { "Int Mic", 0x1 },
+ },
+};
+
#define alc883_mux_enum_info alc_mux_enum_info
#define alc883_mux_enum_get alc_mux_enum_get
-
-static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- static hda_nid_t capture_mixers[2] = { 0x23, 0x22 };
- hda_nid_t nid = capture_mixers[adc_idx];
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
-}
+/* ALC883 has the ALC882-type input selection */
+#define alc883_mux_enum_put alc882_mux_enum_put
/*
* 2ch mode
@@ -6638,6 +6670,60 @@
{ } /* end */
};
+static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -6787,6 +6873,9 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
@@ -6878,124 +6967,6 @@
{ } /* end */
};
-static struct snd_kcontrol_new alc888_6st_hp_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_6st_dell_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
- { } /* end */
-};
-
static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -7171,6 +7142,35 @@
{ } /* end */
};
+static struct hda_verb alc883_clevo_m720_verbs[] = {
+ /* HP */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Int speaker */
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* enable unsolicited event */
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+
+ { } /* end */
+};
+
+static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = {
+ /* HP */
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Subwoofer */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* enable unsolicited event */
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
+ { } /* end */
+};
+
static struct hda_verb alc883_tagra_verbs[] = {
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -7227,26 +7227,14 @@
{ } /* end */
};
-static struct hda_verb alc888_6st_hp_verbs[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 2 (0x0e) */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 1 (0x0d) */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */
- { }
-};
-
static struct hda_verb alc888_3st_hp_verbs[] = {
{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
{ }
};
static struct hda_verb alc888_6st_dell_verbs[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */
{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
{ }
};
@@ -7354,6 +7342,68 @@
alc883_tagra_automute(codec);
}
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_clevo_m720_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+}
+
+static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+static void alc883_clevo_m720_automute(struct hda_codec *codec)
+{
+ alc883_clevo_m720_hp_automute(codec);
+ alc883_clevo_m720_mic_automute(codec);
+}
+
+static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc883_clevo_m720_hp_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc883_clevo_m720_mic_automute(codec);
+ break;
+ }
+}
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+}
+
+static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc883_2ch_fujitsu_pi2515_automute(codec);
+}
+
static void alc883_haier_w66_automute(struct hda_codec *codec)
{
unsigned int present;
@@ -7587,10 +7637,11 @@
[ALC883_LENOVO_NB0763] = "lenovo-nb0763",
[ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
[ALC883_HAIER_W66] = "haier-w66",
- [ALC888_6ST_HP] = "6stack-hp",
[ALC888_3ST_HP] = "3stack-hp",
[ALC888_6ST_DELL] = "6stack-dell",
[ALC883_MITAC] = "mitac",
+ [ALC883_CLEVO_M720] = "clevo-m720",
+ [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
[ALC883_AUTO] = "auto",
};
@@ -7604,7 +7655,7 @@
SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
+ SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
@@ -7614,7 +7665,9 @@
SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
@@ -7627,13 +7680,17 @@
SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
+ SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
+ SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515),
SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
@@ -7652,8 +7709,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.dig_in_nid = ALC883_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
@@ -7665,8 +7720,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.dig_in_nid = ALC883_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
.channel_mode = alc883_3ST_6ch_modes,
@@ -7678,8 +7731,6 @@
.init_verbs = { alc883_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_6ch_modes),
.channel_mode = alc883_3ST_6ch_modes,
.need_dac_fix = 1,
@@ -7691,8 +7742,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.dig_in_nid = ALC883_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
@@ -7704,8 +7753,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
.channel_mode = alc883_3ST_6ch_modes,
.need_dac_fix = 1,
@@ -7719,8 +7766,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .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,
@@ -7737,8 +7782,6 @@
.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,
@@ -7749,8 +7792,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .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,
@@ -7764,8 +7805,6 @@
alc883_medion_eapd_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_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source,
@@ -7776,8 +7815,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .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,
@@ -7789,19 +7826,27 @@
.init_verbs = { alc883_init_verbs, alc882_eapd_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,
},
+ [ALC883_CLEVO_M720] = {
+ .mixers = { alc883_clevo_m720_mixer },
+ .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .input_mux = &alc883_capture_source,
+ .unsol_event = alc883_clevo_m720_unsol_event,
+ .init_hook = alc883_clevo_m720_automute,
+ },
[ALC883_LENOVO_101E_2ch] = {
.mixers = { alc883_lenovo_101e_2ch_mixer},
.init_verbs = { alc883_init_verbs, alc883_lenovo_101e_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_lenovo_101e_capture_source,
@@ -7813,8 +7858,6 @@
.init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_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,
.need_dac_fix = 1,
@@ -7828,8 +7871,6 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
.channel_mode = alc883_3ST_6ch_modes,
.need_dac_fix = 1,
@@ -7843,47 +7884,28 @@
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .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,
.unsol_event = alc883_haier_w66_unsol_event,
.init_hook = alc883_haier_w66_automute,
- },
- [ALC888_6ST_HP] = {
- .mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_6st_hp_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
},
[ALC888_3ST_HP] = {
- .mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer },
+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
.init_verbs = { alc883_init_verbs, alc888_3st_hp_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(alc888_3st_hp_modes),
.channel_mode = alc888_3st_hp_modes,
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
},
[ALC888_6ST_DELL] = {
- .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer },
+ .mixers = { alc883_base_mixer, alc883_chmode_mixer },
.init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs },
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.dig_in_nid = ALC883_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
@@ -7896,14 +7918,25 @@
.init_verbs = { alc883_init_verbs, alc883_mitac_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,
.unsol_event = alc883_mitac_unsol_event,
.init_hook = alc883_mitac_automute,
},
+ [ALC883_FUJITSU_PI2515] = {
+ .mixers = { alc883_2ch_fujitsu_pi2515_mixer },
+ .init_verbs = { alc883_init_verbs,
+ alc883_2ch_fujitsu_pi2515_verbs},
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .input_mux = &alc883_fujitsu_pi2515_capture_source,
+ .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
+ .init_hook = alc883_2ch_fujitsu_pi2515_automute,
+ },
};
@@ -7918,15 +7951,11 @@
struct alc_spec *spec = codec->spec;
int idx;
+ alc_set_pin_output(codec, nid, pin_type);
if (spec->multiout.dac_nids[dac_idx] == 0x25)
idx = 4;
else
idx = spec->multiout.dac_nids[dac_idx] - 2;
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
}
@@ -7955,6 +7984,9 @@
if (pin) /* connect to front */
/* use dac 0 */
alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin)
+ alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
#define alc883_is_input_pin(nid) alc880_is_input_pin(nid)
@@ -8006,9 +8038,12 @@
/* additional initialization for auto-configuration model */
static void alc883_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc883_auto_init_multi_out(codec);
alc883_auto_init_hp_out(codec);
alc883_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
static int patch_alc883(struct hda_codec *codec)
@@ -8057,10 +8092,9 @@
spec->stream_digital_playback = &alc883_pcm_digital_playback;
spec->stream_digital_capture = &alc883_pcm_digital_capture;
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = alc883_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
- }
+ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+ spec->adc_nids = alc883_adc_nids;
+ spec->capsrc_nids = alc883_capsrc_nids;
spec->vmaster_nid = 0x0c;
@@ -8085,6 +8119,8 @@
#define alc262_dac_nids alc260_dac_nids
#define alc262_adc_nids alc882_adc_nids
#define alc262_adc_nids_alt alc882_adc_nids_alt
+#define alc262_capsrc_nids alc882_capsrc_nids
+#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt
#define alc262_modes alc260_modes
#define alc262_capture_source alc882_capture_source
@@ -8585,7 +8621,8 @@
/*
* fujitsu model
- * 0x14 = headphone/spdif-out, 0x15 = internal speaker
+ * 0x14 = headphone/spdif-out, 0x15 = internal speaker,
+ * 0x1b = port replicator headphone out
*/
#define ALC_HP_EVENT 0x37
@@ -8593,6 +8630,14 @@
static struct hda_verb alc262_fujitsu_unsol_verbs[] = {
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {}
+};
+
+static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = {
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{}
};
@@ -8633,12 +8678,16 @@
unsigned int mute;
if (force || !spec->sense_updated) {
- unsigned int present;
+ unsigned int present_int_hp, present_dock_hp;
/* need to execute and sync at first */
snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & 0x80000000) != 0;
+ present_int_hp = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ snd_hda_codec_read(codec, 0x1B, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present_dock_hp = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present_int_hp & 0x80000000) != 0;
+ spec->jack_present |= (present_dock_hp & 0x80000000) != 0;
spec->sense_updated = 1;
}
if (spec->jack_present) {
@@ -8672,6 +8721,46 @@
},
};
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute;
+
+ if (force || !spec->sense_updated) {
+ unsigned int present_int_hp;
+ /* need to execute and sync at first */
+ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present_int_hp = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present_int_hp & 0x80000000) != 0;
+ spec->sense_updated = 1;
+ }
+ if (spec->jack_present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC_HP_EVENT)
+ return;
+ alc262_lenovo_3000_automute(codec, 1);
+}
+
/* bind hp and internal speaker mute (with plug check) */
static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -8680,12 +8769,13 @@
long *valp = ucontrol->value.integer.value;
int change;
- change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[0] ? 0 : HDA_AMP_MUTE);
- change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[1] ? 0 : HDA_AMP_MUTE);
+ change = snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE,
+ valp ? 0 : HDA_AMP_MUTE);
+ change |= snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE,
+ valp ? 0 : HDA_AMP_MUTE);
+
if (change)
alc262_fujitsu_automute(codec, 0);
return change;
@@ -8703,6 +8793,46 @@
},
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ { } /* end */
+};
+
+/* bind hp and internal speaker mute (with plug check) */
+static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int change;
+
+ change = snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE,
+ valp ? 0 : HDA_AMP_MUTE);
+
+ if (change)
+ alc262_lenovo_3000_automute(codec, 0);
+ return change;
+}
+
+static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
+ HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc262_lenovo_3000_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+ },
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
@@ -8730,59 +8860,72 @@
/* Samsung Q1 Ultra Vista model setup */
static struct snd_kcontrol_new alc262_ultra_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Mic Boost", 0x15, 0, HDA_INPUT),
{ } /* end */
};
static struct hda_verb alc262_ultra_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ /* output mixer */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* speaker */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* HP */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Mic is on Node 0x19 */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x24, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ /* internal mic */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* ADC, choose mic */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)},
{}
};
-static struct hda_input_mux alc262_ultra_capture_source = {
- .num_items = 1,
- .items = {
- { "Mic", 0x1 },
- },
-};
-
/* mute/unmute internal speaker according to the hp jack and mute state */
static void alc262_ultra_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
unsigned int mute;
- unsigned int present;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & 0x80000000) != 0;
- if (spec->jack_present) {
- /* mute internal speaker */
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
+ mute = 0;
+ /* auto-mute only when HP is used as HP */
+ if (!spec->cur_mux[0]) {
+ unsigned int present;
+ /* need to execute and sync at first */
+ snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ if (spec->jack_present)
+ mute = HDA_AMP_MUTE;
}
+ /* mute/unmute internal speaker */
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ /* mute/unmute HP */
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE);
}
/* unsolicited event for HP jack sensing */
@@ -8794,6 +8937,45 @@
alc262_ultra_automute(codec);
}
+static struct hda_input_mux alc262_ultra_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x1 },
+ { "Headphone", 0x7 },
+ },
+};
+
+static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int ret;
+
+ ret = alc882_mux_enum_put(kcontrol, ucontrol);
+ if (!ret)
+ return 0;
+ /* reprogram the HP pin as mic or HP according to the input source */
+ snd_hda_codec_write_cache(codec, 0x15, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ spec->cur_mux[0] ? PIN_VREF80 : PIN_HP);
+ alc262_ultra_automute(codec); /* mute/unmute HP */
+ return ret;
+}
+
+static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = alc882_mux_enum_info,
+ .get = alc882_mux_enum_get,
+ .put = alc262_ultra_mux_enum_put,
+ },
+ { } /* end */
+};
+
/* 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)
@@ -9185,9 +9367,12 @@
/* init callback for auto-configuration model -- overriding the default init */
static void alc262_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc262_auto_init_multi_out(codec);
alc262_auto_init_hp_out(codec);
alc262_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
/*
@@ -9206,6 +9391,7 @@
[ALC262_BENQ_T31] = "benq-t31",
[ALC262_SONY_ASSAMD] = "sony-assamd",
[ALC262_ULTRA] = "ultra",
+ [ALC262_LENOVO_3000] = "lenovo-3000",
[ALC262_AUTO] = "auto",
};
@@ -9241,6 +9427,8 @@
SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
+ SND_PCI_QUIRK(0x144d, 0xc039, "Samsung Q1U EL", ALC262_ULTRA),
+ SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000),
SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
@@ -9390,17 +9578,31 @@
.init_hook = alc262_hippo_automute,
},
[ALC262_ULTRA] = {
- .mixers = { alc262_ultra_mixer },
- .init_verbs = { alc262_init_verbs, alc262_ultra_verbs },
+ .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },
+ .init_verbs = { alc262_ultra_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_ultra_capture_source,
+ .adc_nids = alc262_adc_nids, /* ADC0 */
+ .capsrc_nids = alc262_capsrc_nids,
+ .num_adc_nids = 1, /* single ADC */
+ .unsol_event = alc262_ultra_unsol_event,
+ .init_hook = alc262_ultra_automute,
+ },
+ [ALC262_LENOVO_3000] = {
+ .mixers = { alc262_lenovo_3000_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
+ alc262_lenovo_3000_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc262_dac_nids),
.dac_nids = alc262_dac_nids,
.hp_nid = 0x03,
.dig_out_nid = ALC262_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
- .input_mux = &alc262_ultra_capture_source,
- .unsol_event = alc262_ultra_unsol_event,
- .init_hook = alc262_ultra_automute,
+ .input_mux = &alc262_fujitsu_capture_source,
+ .unsol_event = alc262_lenovo_3000_unsol_event,
},
};
@@ -9472,12 +9674,14 @@
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc262_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
+ spec->capsrc_nids = alc262_capsrc_nids_alt;
spec->mixers[spec->num_mixers] =
alc262_capture_alt_mixer;
spec->num_mixers++;
} else {
spec->adc_nids = alc262_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
+ spec->capsrc_nids = alc262_capsrc_nids;
spec->mixers[spec->num_mixers] = alc262_capture_mixer;
spec->num_mixers++;
}
@@ -9517,6 +9721,8 @@
0x08
};
+static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 };
+
static struct snd_kcontrol_new alc268_base_mixer[] = {
/* output mixer control */
HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
@@ -9529,6 +9735,22 @@
{ }
};
+/* bind Beep switches of both NID 0x0f and 0x10 */
+static struct hda_bind_ctls alc268_bind_beep_sw = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc268_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT),
+ HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw),
+ { }
+};
+
static struct hda_verb alc268_eapd_verbs[] = {
{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
@@ -9613,8 +9835,12 @@
};
static struct hda_verb alc268_acer_verbs[] = {
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
{ }
@@ -9685,6 +9911,64 @@
#define alc268_dell_init_hook alc268_dell_automute
+static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+ { }
+};
+
+static struct hda_verb alc267_quanta_il1_verbs[] = {
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+ { }
+};
+
+static void alc267_quanta_il1_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ present ? 0 : PIN_OUT);
+}
+
+static void alc267_quanta_il1_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ present ? 0x00 : 0x01);
+}
+
+static void alc267_quanta_il1_automute(struct hda_codec *codec)
+{
+ alc267_quanta_il1_hp_automute(codec);
+ alc267_quanta_il1_mic_automute(codec);
+}
+
+static void alc267_quanta_il1_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc267_quanta_il1_hp_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc267_quanta_il1_mic_automute(codec);
+ break;
+ }
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -9725,7 +10009,11 @@
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+
+ /* set PCBEEP vol = 0, mute connections */
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
/* Unmute Selector 23h,24h and set the default input to mic-in */
@@ -9764,29 +10052,17 @@
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* set PCBEEP vol = 0 */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))},
+ /* set PCBEEP vol = 0, mute connections */
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{ }
};
#define alc268_mux_enum_info alc_mux_enum_info
#define alc268_mux_enum_get alc_mux_enum_get
-
-static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
-
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- static hda_nid_t capture_mixers[3] = { 0x23, 0x24 };
- hda_nid_t nid = capture_mixers[adc_idx];
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- nid,
- &spec->cur_mux[adc_idx]);
-}
+#define alc268_mux_enum_put alc_mux_enum_put
static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
@@ -9836,13 +10112,17 @@
},
};
+static struct hda_input_mux alc268_acer_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Internal Mic", 0x6 },
+ { "Line", 0x2 },
+ },
+};
+
#ifdef CONFIG_SND_DEBUG
static struct snd_kcontrol_new alc268_test_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-
/* Volume widgets */
HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
@@ -9981,6 +10261,10 @@
case 0x1c:
idx1 = 3; /* CD */
break;
+ case 0x12:
+ case 0x13:
+ idx1 = 6; /* digital mics */
+ break;
default:
continue;
}
@@ -10073,6 +10357,9 @@
if (spec->kctl_alloc)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->autocfg.speaker_pins[0] != 0x1d)
+ spec->mixers[spec->num_mixers++] = alc268_beep_mixer;
+
spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -10091,20 +10378,25 @@
/* init callback for auto-configuration model -- overriding the default init */
static void alc268_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc268_auto_init_multi_out(codec);
alc268_auto_init_hp_out(codec);
alc268_auto_init_mono_speaker_out(codec);
alc268_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
/*
* configuration and preset
*/
static const char *alc268_models[ALC268_MODEL_LAST] = {
+ [ALC267_QUANTA_IL1] = "quanta-il1",
[ALC268_3ST] = "3stack",
[ALC268_TOSHIBA] = "toshiba",
[ALC268_ACER] = "acer",
[ALC268_DELL] = "dell",
+ [ALC268_ZEPTO] = "zepto",
#ifdef CONFIG_SND_DEBUG
[ALC268_TEST] = "test",
#endif
@@ -10112,6 +10404,7 @@
};
static struct snd_pci_quirk alc268_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER),
SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
@@ -10122,17 +10415,36 @@
SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
+ SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
+ SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
{}
};
static struct alc_config_preset alc268_presets[] = {
+ [ALC267_QUANTA_IL1] = {
+ .mixers = { alc267_quanta_il1_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc267_quanta_il1_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ .unsol_event = alc267_quanta_il1_unsol_event,
+ .init_hook = alc267_quanta_il1_automute,
+ },
[ALC268_3ST] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
+ alc268_beep_mixer },
.init_verbs = { alc268_base_init_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
.dac_nids = alc268_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
.adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
.hp_nid = 0x03,
.dig_out_nid = ALC268_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc268_modes),
@@ -10140,13 +10452,15 @@
.input_mux = &alc268_capture_source,
},
[ALC268_TOSHIBA] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
+ alc268_beep_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc268_toshiba_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
.dac_nids = alc268_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
.adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc268_modes),
.channel_mode = alc268_modes,
@@ -10155,22 +10469,24 @@
.init_hook = alc268_toshiba_automute,
},
[ALC268_ACER] = {
- .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer },
+ .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
+ alc268_beep_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc268_acer_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
.dac_nids = alc268_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
.adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
.hp_nid = 0x02,
.num_channel_mode = ARRAY_SIZE(alc268_modes),
.channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
+ .input_mux = &alc268_acer_capture_source,
.unsol_event = alc268_acer_unsol_event,
.init_hook = alc268_acer_init_hook,
},
[ALC268_DELL] = {
- .mixers = { alc268_dell_mixer },
+ .mixers = { alc268_dell_mixer, alc268_beep_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc268_dell_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
@@ -10182,6 +10498,24 @@
.init_hook = alc268_dell_init_hook,
.input_mux = &alc268_capture_source,
},
+ [ALC268_ZEPTO] = {
+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
+ alc268_beep_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_toshiba_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC268_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ .unsol_event = alc268_toshiba_unsol_event,
+ .init_hook = alc268_toshiba_automute
+ },
#ifdef CONFIG_SND_DEBUG
[ALC268_TEST] = {
.mixers = { alc268_test_mixer, alc268_capture_mixer },
@@ -10191,6 +10525,7 @@
.dac_nids = alc268_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
.adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
.hp_nid = 0x03,
.dig_out_nid = ALC268_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc268_modes),
@@ -10247,13 +10582,22 @@
spec->stream_name_digital = "ALC268 Digital";
spec->stream_digital_playback = &alc268_pcm_digital_playback;
+ if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
+ /* override the amp caps for beep generator */
+ snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
+ (0x0c << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
+ int i;
/* get type */
wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- if (wcap != AC_WID_AUD_IN) {
+ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
spec->adc_nids = alc268_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
spec->mixers[spec->num_mixers] =
@@ -10266,6 +10610,12 @@
alc268_capture_mixer;
spec->num_mixers++;
}
+ spec->capsrc_nids = alc268_capsrc_nids;
+ /* set default input source */
+ for (i = 0; i < spec->num_adc_nids; i++)
+ snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
+ 0, AC_VERB_SET_CONNECT_SEL,
+ spec->input_mux->items[0].index);
}
spec->vmaster_nid = 0x02;
@@ -10539,9 +10889,12 @@
/* init callback for auto-configuration model -- overriding the default init */
static void alc269_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc269_auto_init_multi_out(codec);
alc269_auto_init_hp_out(codec);
alc269_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
/*
@@ -11463,13 +11816,7 @@
hda_nid_t nid,
int pin_type, int dac_idx)
{
- /* set as output */
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
-
+ alc_set_pin_output(codec, nid, pin_type);
}
static void alc861_auto_init_multi_out(struct hda_codec *codec)
@@ -11496,6 +11843,9 @@
if (pin) /* connect to front */
alc861_auto_set_output_and_unmute(codec, pin, PIN_HP,
spec->multiout.dac_nids[0]);
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin)
+ alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
static void alc861_auto_init_analog_input(struct hda_codec *codec)
@@ -11568,9 +11918,12 @@
/* additional initialization for auto-configuration model */
static void alc861_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc861_auto_init_multi_out(codec);
alc861_auto_init_hp_out(codec);
alc861_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -11822,6 +12175,8 @@
0x09,
};
+static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 };
+
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
static struct hda_input_mux alc861vd_capture_source = {
@@ -11835,11 +12190,10 @@
};
static struct hda_input_mux alc861vd_dallas_capture_source = {
- .num_items = 3,
+ .num_items = 2,
.items = {
- { "Front Mic", 0x0 },
- { "ATAPI Mic", 0x1 },
- { "Line In", 0x5 },
+ { "Ext Mic", 0x0 },
+ { "Int Mic", 0x1 },
},
};
@@ -11853,33 +12207,8 @@
#define alc861vd_mux_enum_info alc_mux_enum_info
#define alc861vd_mux_enum_get alc_mux_enum_get
-
-static int alc861vd_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- static hda_nid_t capture_mixers[1] = { 0x22 };
- hda_nid_t nid = capture_mixers[adc_idx];
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
-}
+/* ALC861VD has the ALC882-type input selection (but has only one ADC) */
+#define alc861vd_mux_enum_put alc882_mux_enum_put
/*
* 2ch mode
@@ -12034,20 +12363,22 @@
{ } /* end */
};
-/* Pin assignment: Front=0x14, HP = 0x15,
- * Front Mic=0x18, ATAPI Mic = 0x19, Line In = 0x1d
+/* Pin assignment: Speaker=0x14, HP = 0x15,
+ * Ext Mic=0x18, Int Mic = 0x19, CD = 0x1c, PC Beep = 0x1d
*/
static struct snd_kcontrol_new alc861vd_dallas_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Beep Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Beep Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -12348,6 +12679,7 @@
/*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS),
SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
@@ -12362,8 +12694,6 @@
alc861vd_3stack_init_verbs },
.num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
.dac_nids = alc660vd_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
- .adc_nids = alc861vd_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_capture_source,
@@ -12375,8 +12705,6 @@
.num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
.dac_nids = alc660vd_dac_nids,
.dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
- .adc_nids = alc861vd_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_capture_source,
@@ -12421,8 +12749,6 @@
alc861vd_lenovo_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
.dac_nids = alc660vd_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
- .adc_nids = alc861vd_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_capture_source,
@@ -12434,8 +12760,6 @@
.init_verbs = { alc861vd_dallas_verbs },
.num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
.dac_nids = alc861vd_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
- .adc_nids = alc861vd_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_dallas_capture_source,
@@ -12447,9 +12771,7 @@
.init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs },
.num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
.dac_nids = alc861vd_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
.dig_out_nid = ALC861VD_DIGOUT_NID,
- .adc_nids = alc861vd_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_hp_capture_source,
@@ -12464,11 +12786,7 @@
static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid, int pin_type, int dac_idx)
{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ alc_set_pin_output(codec, nid, pin_type);
}
static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
@@ -12495,6 +12813,9 @@
pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front and use dac 0 */
alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin)
+ alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
#define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid)
@@ -12698,9 +13019,12 @@
/* additional initialization for auto-configuration model */
static void alc861vd_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc861vd_auto_init_multi_out(codec);
alc861vd_auto_init_hp_out(codec);
alc861vd_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
static int patch_alc861vd(struct hda_codec *codec)
@@ -12751,6 +13075,7 @@
spec->adc_nids = alc861vd_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
+ spec->capsrc_nids = alc861vd_capsrc_nids;
spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
spec->num_mixers++;
@@ -12792,9 +13117,11 @@
/* ADC1-2 */
0x09,
};
+
+static hda_nid_t alc662_capsrc_nids[1] = { 0x22 };
+
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
-
static struct hda_input_mux alc662_capture_source = {
.num_items = 4,
.items = {
@@ -12823,33 +13150,8 @@
#define alc662_mux_enum_info alc_mux_enum_info
#define alc662_mux_enum_get alc_mux_enum_get
+#define alc662_mux_enum_put alc882_mux_enum_put
-static int alc662_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- static hda_nid_t capture_mixers[2] = { 0x23, 0x22 };
- hda_nid_t nid = capture_mixers[adc_idx];
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
-}
/*
* 2ch mode
*/
@@ -12918,13 +13220,13 @@
static struct snd_kcontrol_new alc662_base_mixer[] = {
/* output mixer control */
HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
/*Input mixer control */
@@ -12941,7 +13243,7 @@
static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
@@ -12958,13 +13260,13 @@
static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
@@ -13313,6 +13615,7 @@
};
static struct snd_pci_quirk alc662_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
@@ -13326,8 +13629,6 @@
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
.dig_out_nid = ALC662_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
- .adc_nids = alc662_adc_nids,
.dig_in_nid = ALC662_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
@@ -13340,8 +13641,6 @@
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
.dig_out_nid = ALC662_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
- .adc_nids = alc662_adc_nids,
.dig_in_nid = ALC662_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
.channel_mode = alc662_3ST_6ch_modes,
@@ -13354,8 +13653,6 @@
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
- .adc_nids = alc662_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
.channel_mode = alc662_3ST_6ch_modes,
.need_dac_fix = 1,
@@ -13368,8 +13665,6 @@
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
.dig_out_nid = ALC662_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
- .adc_nids = alc662_adc_nids,
.dig_in_nid = ALC662_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc662_5stack_modes),
.channel_mode = alc662_5stack_modes,
@@ -13380,8 +13675,6 @@
.init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
- .adc_nids = alc662_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
.input_mux = &alc662_lenovo_101e_capture_source,
@@ -13394,8 +13687,6 @@
alc662_eeepc_sue_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
- .adc_nids = alc662_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
.input_mux = &alc662_eeepc_capture_source,
@@ -13409,8 +13700,6 @@
alc662_eeepc_ep20_sue_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
- .adc_nids = alc662_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
.channel_mode = alc662_3ST_6ch_modes,
.input_mux = &alc662_lenovo_101e_capture_source,
@@ -13556,11 +13845,7 @@
hda_nid_t nid, int pin_type,
int dac_idx)
{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ alc_set_pin_output(codec, nid, pin_type);
/* need the manual connection? */
if (alc880_is_multi_pin(nid)) {
struct alc_spec *spec = codec->spec;
@@ -13595,6 +13880,9 @@
if (pin) /* connect to front */
/* use dac 0 */
alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin)
+ alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
#define alc662_is_input_pin(nid) alc880_is_input_pin(nid)
@@ -13672,9 +13960,12 @@
/* additional initialization for auto-configuration model */
static void alc662_auto_init(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
alc662_auto_init_multi_out(codec);
alc662_auto_init_hp_out(codec);
alc662_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_sku_automute(codec);
}
static int patch_alc662(struct hda_codec *codec)
@@ -13722,10 +14013,9 @@
spec->stream_digital_playback = &alc662_pcm_digital_playback;
spec->stream_digital_capture = &alc662_pcm_digital_capture;
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = alc662_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
- }
+ spec->adc_nids = alc662_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
+ spec->capsrc_nids = alc662_capsrc_nids;
spec->vmaster_nid = 0x02;
@@ -13761,6 +14051,8 @@
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
+ { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
+ .patch = patch_alc882 }, /* should be patch_alc883() in future */
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index d22f5a6..9332b63 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -28,7 +28,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-
+#include "hda_patch.h"
/* si3054 verbs */
#define SI3054_VERB_READ_NODE 0x900
@@ -206,7 +206,7 @@
info->name = "Si3054 Modem";
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
- info->is_modem = 1;
+ info->pcm_type = HDA_PCM_TYPE_MODEM;
return 0;
}
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index caf48ed..b3a15d6 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -32,6 +32,7 @@
#include <sound/asoundef.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_patch.h"
#define NUM_CONTROL_ALLOC 32
#define STAC_PWR_EVENT 0x20
@@ -39,6 +40,7 @@
enum {
STAC_REF,
+ STAC_9200_OQO,
STAC_9200_DELL_D21,
STAC_9200_DELL_D22,
STAC_9200_DELL_D23,
@@ -50,6 +52,7 @@
STAC_9200_DELL_M26,
STAC_9200_DELL_M27,
STAC_9200_GATEWAY,
+ STAC_9200_PANASONIC,
STAC_9200_MODELS
};
@@ -63,11 +66,14 @@
enum {
STAC_92HD73XX_REF,
+ STAC_DELL_M6,
STAC_92HD73XX_MODELS
};
enum {
STAC_92HD71BXX_REF,
+ STAC_DELL_M4_1,
+ STAC_DELL_M4_2,
STAC_92HD71BXX_MODELS
};
@@ -123,6 +129,7 @@
unsigned int hp_detect: 1;
/* gpio lines */
+ unsigned int eapd_mask;
unsigned int gpio_mask;
unsigned int gpio_dir;
unsigned int gpio_data;
@@ -135,6 +142,7 @@
/* power management */
unsigned int num_pwrs;
hda_nid_t *pwr_nids;
+ hda_nid_t *dac_list;
/* playback */
struct hda_input_mux *mono_mux;
@@ -173,6 +181,7 @@
/* i/o switches */
unsigned int io_switch[2];
unsigned int clfe_swap;
+ unsigned int hp_switch;
unsigned int aloopback;
struct hda_pcm pcm_rec[2]; /* PCM information */
@@ -184,9 +193,6 @@
struct hda_input_mux private_dimux;
struct hda_input_mux private_imux;
struct hda_input_mux private_mono_mux;
-
- /* virtual master */
- unsigned int vmaster_tlv[4];
};
static hda_nid_t stac9200_adc_nids[1] = {
@@ -244,7 +250,7 @@
0x1c,
};
-static hda_nid_t stac92hd71bxx_dac_nids[2] = {
+static hda_nid_t stac92hd71bxx_dac_nids[1] = {
0x10, /*0x11, */
};
@@ -290,6 +296,10 @@
0x15, 0x16, 0x17
};
+static hda_nid_t stac927x_dac_nids[6] = {
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0
+};
+
static hda_nid_t stac927x_dmux_nids[1] = {
0x1b,
};
@@ -331,10 +341,10 @@
0x0f, 0x10, 0x11, 0x15, 0x1b,
};
-static hda_nid_t stac92hd73xx_pin_nids[12] = {
+static hda_nid_t stac92hd73xx_pin_nids[13] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x22
+ 0x14, 0x1e, 0x22
};
static hda_nid_t stac92hd71bxx_pin_nids[10] = {
@@ -527,6 +537,43 @@
{}
};
+static struct hda_verb dell_eq_core_init[] = {
+ /* set master volume to max value without distortion
+ * and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
+ /* setup audio connections */
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* setup adcs to point to mixer */
+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ /* setup import muxs */
+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {}
+};
+
+static struct hda_verb dell_m6_core_init[] = {
+ /* set master volume and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* setup audio connections */
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* setup adcs to point to mixer */
+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ /* setup import muxs */
+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {}
+};
+
static struct hda_verb stac92hd73xx_8ch_core_init[] = {
/* set master volume and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
@@ -910,6 +957,11 @@
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0)
return err;
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -919,10 +971,11 @@
/* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ unsigned int vmaster_tlv[4];
snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
- HDA_OUTPUT, spec->vmaster_tlv);
+ HDA_OUTPUT, vmaster_tlv);
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- spec->vmaster_tlv, slave_vols);
+ vmaster_tlv, slave_vols);
if (err < 0)
return err;
}
@@ -1052,9 +1105,15 @@
0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
};
+static unsigned int oqo9200_pin_configs[8] = {
+ 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
+ 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
+};
+
static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
[STAC_REF] = ref9200_pin_configs,
+ [STAC_9200_OQO] = oqo9200_pin_configs,
[STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
[STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
[STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
@@ -1065,10 +1124,12 @@
[STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
[STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
[STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
+ [STAC_9200_PANASONIC] = ref9200_pin_configs,
};
static const char *stac9200_models[STAC_9200_MODELS] = {
[STAC_REF] = "ref",
+ [STAC_9200_OQO] = "oqo",
[STAC_9200_DELL_D21] = "dell-d21",
[STAC_9200_DELL_D22] = "dell-d22",
[STAC_9200_DELL_D23] = "dell-d23",
@@ -1080,6 +1141,7 @@
[STAC_9200_DELL_M26] = "dell-m26",
[STAC_9200_DELL_M27] = "dell-m27",
[STAC_9200_GATEWAY] = "gateway",
+ [STAC_9200_PANASONIC] = "panasonic",
};
static struct snd_pci_quirk stac9200_cfg_tbl[] = {
@@ -1146,13 +1208,15 @@
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
"unknown Dell", STAC_9200_DELL_M26),
/* Panasonic */
- SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF),
+ SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC),
/* Gateway machines needs EAPD to be set on resume */
SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_GATEWAY),
SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*",
STAC_9200_GATEWAY),
SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707",
STAC_9200_GATEWAY),
+ /* OQO Mobile */
+ SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO),
{} /* terminator */
};
@@ -1202,24 +1266,48 @@
{} /* terminator */
};
-static unsigned int ref92hd73xx_pin_configs[12] = {
+static unsigned int ref92hd73xx_pin_configs[13] = {
0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
0x0181302e, 0x01014010, 0x01014020, 0x01014030,
0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
+ 0x01452050,
+};
+
+static unsigned int dell_m6_pin_configs[13] = {
+ 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
+ 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0,
+ 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
+ 0x4f0000f0,
};
static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
- [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
+ [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
+ [STAC_DELL_M6] = dell_m6_pin_configs,
};
static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
[STAC_92HD73XX_REF] = "ref",
+ [STAC_DELL_M6] = "dell-m6",
};
static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_92HD73XX_REF),
+ "DFI LanParty", STAC_92HD73XX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
+ "unknown Dell", STAC_DELL_M6),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
+ "unknown Dell", STAC_DELL_M6),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256,
+ "unknown Dell", STAC_DELL_M6),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257,
+ "unknown Dell", STAC_DELL_M6),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e,
+ "unknown Dell", STAC_DELL_M6),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f,
+ "unknown Dell", STAC_DELL_M6),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271,
+ "unknown Dell", STAC_DELL_M6),
{} /* terminator */
};
@@ -1229,18 +1317,56 @@
0x90a000f0, 0x01452050,
};
+static unsigned int dell_m4_1_pin_configs[13] = {
+ 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
+ 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
+ 0x40f000f0, 0x4f0000f0,
+};
+
+static unsigned int dell_m4_2_pin_configs[13] = {
+ 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
+ 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
+ 0x40f000f0, 0x044413b0,
+};
+
static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
[STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
+ [STAC_DELL_M4_1] = dell_m4_1_pin_configs,
+ [STAC_DELL_M4_2] = dell_m4_2_pin_configs,
};
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
[STAC_92HD71BXX_REF] = "ref",
+ [STAC_DELL_M4_1] = "dell-m4-1",
+ [STAC_DELL_M4_2] = "dell-m4-2",
};
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
+ "unknown Dell", STAC_DELL_M4_2),
{} /* terminator */
};
@@ -1733,7 +1859,8 @@
struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -1807,7 +1934,7 @@
{
struct sigmatel_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
return 0;
}
@@ -1889,6 +2016,7 @@
codec->num_pcms++;
info++;
info->name = "STAC92xx Digital";
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->multiout.dig_out_nid) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
@@ -1925,6 +2053,34 @@
AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
}
+#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info
+
+static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = spec->hp_switch;
+ return 0;
+}
+
+static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ spec->hp_switch = ucontrol->value.integer.value[0];
+
+ /* check to be sure that the ports are upto date with
+ * switch changes
+ */
+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+
+ return 1;
+}
+
#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1996,6 +2152,15 @@
return 1;
}
+#define STAC_CODEC_HP_SWITCH(xname) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = 0, \
+ .info = stac92xx_hp_switch_info, \
+ .get = stac92xx_hp_switch_get, \
+ .put = stac92xx_hp_switch_put, \
+ }
+
#define STAC_CODEC_IO_SWITCH(xname, xpval) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
@@ -2020,6 +2185,7 @@
STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE,
STAC_CTL_WIDGET_MONO_MUX,
+ STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH,
STAC_CTL_WIDGET_CLFE_SWITCH
};
@@ -2028,6 +2194,7 @@
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
STAC_MONO_MUX,
+ STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0),
STAC_CODEC_CLFE_SWITCH(NULL, 0),
};
@@ -2222,6 +2389,29 @@
return 0;
}
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = nid;
+ else if (spec->multiout.num_dacs > 4) {
+ printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
+ return 1;
+ } else {
+ spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
+ spec->multiout.num_dacs++;
+ }
+ return 0;
+}
+
+static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ if (is_in_dac_nids(spec, nid))
+ return 1;
+ if (spec->multiout.hp_nid == nid)
+ return 1;
+ return 0;
+}
+
/* add playback controls from the parsed DAC table */
static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
@@ -2236,7 +2426,7 @@
unsigned int wid_caps, pincap;
- for (i = 0; i < cfg->line_outs; i++) {
+ for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) {
if (!spec->multiout.dac_nids[i])
continue;
@@ -2269,6 +2459,14 @@
}
}
+ if (cfg->hp_outs > 1) {
+ err = stac92xx_add_control(spec,
+ STAC_CTL_WIDGET_HP_SWITCH,
+ "Headphone as Line Out Switch", 0);
+ if (err < 0)
+ return err;
+ }
+
if (spec->line_switch) {
nid = cfg->input_pins[AUTO_PIN_LINE];
pincap = snd_hda_param_read(codec, nid,
@@ -2284,10 +2482,11 @@
if (spec->mic_switch) {
unsigned int def_conf;
- nid = cfg->input_pins[AUTO_PIN_MIC];
+ unsigned int mic_pin = AUTO_PIN_MIC;
+again:
+ nid = cfg->input_pins[mic_pin];
def_conf = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
-
/* some laptops have an internal analog microphone
* which can't be used as a output */
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
@@ -2297,38 +2496,22 @@
err = stac92xx_add_control(spec,
STAC_CTL_WIDGET_IO_SWITCH,
"Mic as Output Switch", (nid << 8) | 1);
+ nid = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+ if (!check_in_dac_nids(spec, nid))
+ add_spec_dacs(spec, nid);
if (err < 0)
return err;
}
+ } else if (mic_pin == AUTO_PIN_MIC) {
+ mic_pin = AUTO_PIN_FRONT_MIC;
+ goto again;
}
}
return 0;
}
-static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- if (is_in_dac_nids(spec, nid))
- return 1;
- if (spec->multiout.hp_nid == nid)
- return 1;
- return 0;
-}
-
-static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else if (spec->multiout.num_dacs > 4) {
- printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
- return 1;
- } else {
- spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
- spec->multiout.num_dacs++;
- }
- return 0;
-}
-
/* add playback controls for Speaker and HP outputs */
static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
struct auto_pin_cfg *cfg)
@@ -2378,12 +2561,8 @@
return err;
}
if (spec->multiout.hp_nid) {
- const char *pfx;
- if (old_num_dacs == spec->multiout.num_dacs)
- pfx = "Master";
- else
- pfx = "Headphone";
- err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
+ err = create_controls(spec, "Headphone",
+ spec->multiout.hp_nid, 3);
if (err < 0)
return err;
}
@@ -2745,7 +2924,7 @@
*/
for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
hda_nid_t pin = spec->autocfg.speaker_pins[i];
- unsigned long wcaps = get_wcaps(codec, pin);
+ unsigned int wcaps = get_wcaps(codec, pin);
wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
if (wcaps == AC_WCAP_OUT_AMP)
/* found a mono speaker with an amp, must be lfe */
@@ -2756,12 +2935,12 @@
if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
hda_nid_t pin = spec->autocfg.line_out_pins[i];
- unsigned long cfg;
- cfg = snd_hda_codec_read(codec, pin, 0,
+ unsigned int defcfg;
+ defcfg = snd_hda_codec_read(codec, pin, 0,
AC_VERB_GET_CONFIG_DEFAULT,
0x00);
- if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) {
- unsigned long wcaps = get_wcaps(codec, pin);
+ if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
+ unsigned int wcaps = get_wcaps(codec, pin);
wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
if (wcaps == AC_WCAP_OUT_AMP)
/* found a mono speaker with an amp,
@@ -2866,6 +3045,19 @@
return 0; /* nid is not a HP-Out */
};
+static void stac92xx_power_down(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ /* power down inactive DACs */
+ hda_nid_t *dac;
+ for (dac = spec->dac_list; *dac; dac++)
+ if (!is_in_dac_nids(spec, *dac) &&
+ spec->multiout.hp_nid != *dac)
+ snd_hda_codec_write_cache(codec, *dac, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+}
+
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -2909,16 +3101,21 @@
? STAC_HP_EVENT : STAC_PWR_EVENT;
int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i],
+ 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
/* outputs are only ports capable of power management
* any attempts on powering down a input port cause the
* referenced VREF to act quirky.
*/
if (pinctl & AC_PINCTL_IN_EN)
continue;
+ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED)
+ continue;
enable_pin_detect(codec, spec->pwr_nids[i], event | i);
codec->patch_ops.unsol_event(codec, (event | i) << 26);
}
-
+ if (spec->dac_list)
+ stac92xx_power_down(codec);
if (cfg->dig_out_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
AC_PINCTL_OUT_EN);
@@ -3014,6 +3211,7 @@
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
+ int nid = cfg->hp_pins[cfg->hp_outs - 1];
int i, presence;
presence = 0;
@@ -3024,26 +3222,42 @@
for (i = 0; i < cfg->hp_outs; i++) {
if (presence)
break;
+ if (spec->hp_switch && cfg->hp_pins[i] == nid)
+ break;
presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
}
if (presence) {
/* disable lineouts, enable hp */
+ if (spec->hp_switch)
+ stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN);
for (i = 0; i < cfg->line_outs; i++)
stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
for (i = 0; i < cfg->speaker_outs; i++)
stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
AC_PINCTL_OUT_EN);
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
} else {
/* enable lineouts, disable hp */
+ if (spec->hp_switch)
+ stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
for (i = 0; i < cfg->line_outs; i++)
stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
for (i = 0; i < cfg->speaker_outs; i++)
stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
AC_PINCTL_OUT_EN);
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data |
+ spec->eapd_mask);
}
+ if (!spec->hp_switch && cfg->hp_outs > 1 && presence)
+ stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
}
static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
@@ -3091,6 +3305,9 @@
spec->gpio_dir, spec->gpio_data);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
+ /* power down inactive DACs */
+ if (spec->dac_list)
+ stac92xx_power_down(codec);
/* invoke unsolicited event to reset the HP state */
if (spec->hp_detect)
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
@@ -3147,12 +3364,18 @@
spec->num_adcs = 1;
spec->num_pwrs = 0;
- if (spec->board_config == STAC_9200_GATEWAY)
+ if (spec->board_config == STAC_9200_GATEWAY ||
+ spec->board_config == STAC_9200_OQO)
spec->init = stac9200_eapd_init;
else
spec->init = stac9200_core_init;
spec->mixer = stac9200_mixer;
+ if (spec->board_config == STAC_9200_PANASONIC) {
+ spec->gpio_mask = spec->gpio_dir = 0x09;
+ spec->gpio_data = 0x00;
+ }
+
err = stac9200_parse_auto_config(codec);
if (err < 0) {
stac92xx_free(codec);
@@ -3293,6 +3516,7 @@
switch (spec->multiout.num_dacs) {
case 0x3: /* 6 Channel */
+ spec->multiout.hp_nid = 0x17;
spec->mixer = stac92hd73xx_6ch_mixer;
spec->init = stac92hd73xx_6ch_core_init;
break;
@@ -3318,13 +3542,42 @@
spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
- spec->num_dmics = STAC92HD73XX_NUM_DMICS;
spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
spec->dinput_mux = &stac92hd73xx_dmux;
/* GPIO0 High = Enable EAPD */
- spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
spec->gpio_data = 0x01;
+ switch (spec->board_config) {
+ case STAC_DELL_M6:
+ spec->init = dell_eq_core_init;
+ switch (codec->subsystem_id) {
+ case 0x1028025e: /* Analog Mics */
+ case 0x1028025f:
+ stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
+ spec->num_dmics = 0;
+ break;
+ case 0x10280271: /* Digital Mics */
+ case 0x10280272:
+ spec->init = dell_m6_core_init;
+ /* fall-through */
+ case 0x10280254:
+ case 0x10280255:
+ stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+ spec->num_dmics = 1;
+ break;
+ case 0x10280256: /* Both */
+ case 0x10280057:
+ stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
+ stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+ spec->num_dmics = 1;
+ break;
+ }
+ break;
+ default:
+ spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+ }
+
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
spec->pwr_nids = stac92hd73xx_pwr_nids;
@@ -3398,7 +3651,10 @@
spec->aloopback_shift = 0;
/* GPIO0 High = EAPD */
- spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1;
+ spec->gpio_mask = 0x01;
+ spec->gpio_dir = 0x01;
+ spec->gpio_mask = 0x01;
+ spec->gpio_data = 0x01;
spec->mux_nids = stac92hd71bxx_mux_nids;
spec->adc_nids = stac92hd71bxx_adc_nids;
@@ -3413,7 +3669,7 @@
spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
spec->pwr_nids = stac92hd71bxx_pwr_nids;
- spec->multiout.num_dacs = 2;
+ spec->multiout.num_dacs = 1;
spec->multiout.hp_nid = 0x11;
spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
@@ -3577,13 +3833,14 @@
spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
spec->mux_nids = stac927x_mux_nids;
spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+ spec->dac_list = stac927x_dac_nids;
spec->multiout.dac_nids = spec->dac_nids;
switch (spec->board_config) {
case STAC_D965_3ST:
case STAC_D965_5ST:
/* GPIO0 High = Enable EAPD */
- spec->gpio_mask = spec->gpio_dir = 0x01;
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x01;
spec->gpio_data = 0x01;
spec->num_dmics = 0;
@@ -3591,14 +3848,23 @@
spec->mixer = stac927x_mixer;
break;
case STAC_DELL_BIOS:
+ switch (codec->subsystem_id) {
+ case 0x10280209:
+ case 0x1028022e:
+ /* correct the device field to SPDIF out */
+ stac92xx_set_config_reg(codec, 0x21, 0x01442070);
+ break;
+ };
+ /* configure the analog microphone on some laptops */
+ stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
/* correct the front output jack as a hp out */
- stac92xx_set_config_reg(codec, 0x0f, 0x02270110);
+ stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
/* correct the front input jack as a mic */
stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
/* fallthru */
case STAC_DELL_3ST:
/* GPIO2 High = Enable EAPD */
- spec->gpio_mask = spec->gpio_dir = 0x04;
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04;
spec->gpio_data = 0x04;
spec->dmic_nids = stac927x_dmic_nids;
spec->num_dmics = STAC927X_NUM_DMICS;
@@ -3610,7 +3876,7 @@
break;
default:
/* GPIO0 High = Enable EAPD */
- spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
spec->gpio_data = 0x01;
spec->num_dmics = 0;
@@ -3714,6 +3980,7 @@
(AC_USRSP_EN | STAC_HP_EVENT));
spec->gpio_dir = 0x0b;
+ spec->eapd_mask = 0x01;
spec->gpio_mask = 0x1b;
spec->gpio_mute = 0x10;
/* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
@@ -3723,7 +3990,7 @@
break;
default:
/* GPIO0 High = EAPD */
- spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
spec->gpio_data = 0x01;
break;
}
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 4e5dd4c..52b1d81 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -39,7 +39,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-
+#include "hda_patch.h"
/* amp values */
#define AMP_VAL_IDX_SHIFT 19
@@ -357,7 +357,8 @@
struct snd_pcm_substream *substream)
{
struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -430,8 +431,7 @@
struct snd_pcm_substream *substream)
{
struct via_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- 0, 0, 0);
+ snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
return 0;
}
@@ -493,6 +493,11 @@
spec->multiout.dig_out_nid);
if (err < 0)
return err;
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -523,6 +528,7 @@
codec->num_pcms++;
info++;
info->name = spec->stream_name_digital;
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->multiout.dig_out_nid) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
*(spec->stream_digital_playback);
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index efd180b..0ed96c1 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -1,8 +1,8 @@
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
- * Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile
- * Digigram VX442
+ * Lowlevel functions for M-Audio Delta 1010, 1010E, 44, 66, 66E, Dio2496,
+ * Audiophile, Digigram VX442
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
@@ -86,6 +86,7 @@
unsigned char tmp;
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
tmp &= ~ICE1712_DELTA_1010LT_CS;
tmp |= ICE1712_DELTA_1010LT_CCLK | ICE1712_DELTA_1010LT_CS_CS8427;
@@ -109,6 +110,7 @@
static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
{
switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
tmp &= ~ICE1712_DELTA_1010LT_CS;
tmp |= ICE1712_DELTA_1010LT_CS_NONE;
@@ -534,6 +536,14 @@
int err;
struct snd_akm4xxx *ak;
+ if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 &&
+ ice->eeprom.gpiodir == 0x7b)
+ ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA1010E;
+
+ if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA66 &&
+ ice->eeprom.gpiodir == 0xfb)
+ ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA66E;
+
/* determine I2C, DACs and ADCs */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_AUDIOPHILE:
@@ -550,6 +560,7 @@
ice->num_total_adcs = ice->omni ? 8 : 4;
break;
case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_MEDIASTATION:
ice->num_total_dacs = 8;
@@ -559,6 +570,7 @@
ice->num_total_dacs = 4; /* two AK4324 codecs */
break;
case ICE1712_SUBDEVICE_VX442:
+ case ICE1712_SUBDEVICE_DELTA66E: /* omni not suported yet */
ice->num_total_dacs = 4;
ice->num_total_adcs = 4;
break;
@@ -568,8 +580,10 @@
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_AUDIOPHILE:
case ICE1712_SUBDEVICE_DELTA410:
+ case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_VX442:
+ case ICE1712_SUBDEVICE_DELTA66E:
if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
snd_printk(KERN_ERR "unable to create I2C bus\n");
return err;
@@ -601,6 +615,7 @@
/* no analog? */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTADIO2496:
case ICE1712_SUBDEVICE_MEDIASTATION:
return 0;
@@ -627,6 +642,7 @@
err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
break;
case ICE1712_SUBDEVICE_VX442:
+ case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
break;
default:
@@ -674,6 +690,7 @@
if (err < 0)
return err;
break;
+ case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_select, ice));
if (err < 0)
@@ -716,6 +733,7 @@
case ICE1712_SUBDEVICE_DELTA44:
case ICE1712_SUBDEVICE_DELTA66:
case ICE1712_SUBDEVICE_VX442:
+ case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index 26ea05a..ea7116c 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -36,8 +36,10 @@
"{Lionstracs,Mediastation},"
#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6
+#define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6
#define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6
#define ICE1712_SUBDEVICE_DELTA66 0x121432d6
+#define ICE1712_SUBDEVICE_DELTA66E 0xff1432d6
#define ICE1712_SUBDEVICE_DELTA44 0x121433d6
#define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6
#define ICE1712_SUBDEVICE_DELTA410 0x121438d6
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 064760d..013fc4f 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -238,6 +238,7 @@
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_TS88:
if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1)
!= 1)
goto _error;
@@ -433,6 +434,7 @@
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_TS88:
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
break;
@@ -475,6 +477,8 @@
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_TS88:
+
err = snd_i2c_device_create(ice->i2c, "CS8404",
ICE1712_EWS88MT_CS8404_ADDR,
&spec->i2cdevs[EWS_I2C_CS8404]);
@@ -518,6 +522,7 @@
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_TS88:
case ICE1712_SUBDEVICE_EWS88D:
/* set up CS8404 */
ice->spdif.ops.open = ews88_open_spdif;
@@ -547,6 +552,7 @@
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_TS88:
err = snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, &akm_ews88mt_priv, ice);
break;
case ICE1712_SUBDEVICE_EWX2496:
@@ -973,6 +979,7 @@
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_TS88:
case ICE1712_SUBDEVICE_DMX6FIRE:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
@@ -992,6 +999,7 @@
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
+ case ICE1712_SUBDEVICE_TS88:
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice));
if (err < 0)
return err;
@@ -1049,6 +1057,13 @@
.build_controls = snd_ice1712_ews_add_controls,
},
{
+ .subvendor = ICE1712_SUBDEVICE_TS88,
+ .name = "terrasoniq TS88",
+ .model = "phase88",
+ .chip_init = snd_ice1712_ews_init,
+ .build_controls = snd_ice1712_ews_add_controls,
+ },
+ {
.subvendor = ICE1712_SUBDEVICE_EWS88D,
.name = "TerraTec EWS88D",
.model = "ews88d",
diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h
index e4ed1b4..1c44371 100644
--- a/sound/pci/ice1712/ews.h
+++ b/sound/pci/ice1712/ews.h
@@ -30,7 +30,8 @@
"{TerraTec,EWS 88MT},"\
"{TerraTec,EWS 88D},"\
"{TerraTec,DMX 6Fire},"\
- "{TerraTec,Phase 88},"
+ "{TerraTec,Phase 88}," \
+ "{terrasoniq,TS 88},"
#define ICE1712_SUBDEVICE_EWX2496 0x3b153011
#define ICE1712_SUBDEVICE_EWS88MT 0x3b151511
@@ -38,6 +39,7 @@
#define ICE1712_SUBDEVICE_EWS88D 0x3b152b11
#define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811
#define ICE1712_SUBDEVICE_PHASE88 0x3b155111
+#define ICE1712_SUBDEVICE_TS88 0x3b157c11
/* entry point */
extern struct snd_ice1712_card_info snd_ice1712_ews_cards[];
diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
index cf5c7c08..6914189 100644
--- a/sound/pci/ice1712/hoontech.c
+++ b/sound/pci/ice1712/hoontech.c
@@ -208,6 +208,19 @@
/* ICE1712_STDSP24_MUTE |
ICE1712_STDSP24_INSEL |
ICE1712_STDSP24_DAREAR; */
+ /* These boxconfigs have caused problems in the past.
+ * The code is not optimal, but should now enable a working config to
+ * be achieved.
+ * ** MIDI IN can only be configured on one box **
+ * ICE1712_STDSP24_BOX_MIDI1 needs to be set for that box.
+ * Tests on a ADAC2000 box suggest the box config flags do not
+ * work as would be expected, and the inputs are crossed.
+ * Setting ICE1712_STDSP24_BOX_MIDI1 and ICE1712_STDSP24_BOX_MIDI2
+ * on the same box connects MIDI-In to both 401 uarts; both outputs
+ * are then active on all boxes.
+ * The default config here sets up everything on the first box.
+ * Alan Horstmann 5.2.2008
+ */
spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
ICE1712_STDSP24_BOX_CHN2 |
ICE1712_STDSP24_BOX_CHN3 |
@@ -223,14 +236,14 @@
(spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0);
snd_ice1712_stdsp24_insel(ice,
(spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0);
- for (box = 0; box < 1; box++) {
+ for (box = 0; box < 4; box++) {
if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
snd_ice1712_stdsp24_midi2(ice, 1);
for (chn = 0; chn < 4; chn++)
snd_ice1712_stdsp24_box_channel(ice, box, chn,
(spec->boxconfig[box] & (1 << chn)) ? 1 : 0);
- snd_ice1712_stdsp24_box_midi(ice, box,
- (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
+ if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1)
+ snd_ice1712_stdsp24_box_midi(ice, box, 1);
}
return 0;
@@ -322,6 +335,8 @@
.name = "Hoontech SoundTrack Audio DSP24",
.model = "dsp24",
.chip_init = snd_ice1712_hoontech_init,
+ .mpu401_1_name = "MIDI-1 Hoontech/STA DSP24",
+ .mpu401_2_name = "MIDI-2 Hoontech/STA DSP24",
},
{
.subvendor = ICE1712_SUBDEVICE_STDSP24_VALUE, /* a dummy id */
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index df292af..29d449d 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -1297,11 +1297,14 @@
static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+ int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
+ kcontrol->private_value;
spin_lock_irq(&ice->reg_lock);
- ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
- ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1);
+ ucontrol->value.integer.value[0] =
+ !((ice->pro_volumes[priv_idx] >> 15) & 1);
+ ucontrol->value.integer.value[1] =
+ !((ice->pro_volumes[priv_idx] >> 31) & 1);
spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1309,16 +1312,17 @@
static int snd_ice1712_pro_mixer_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+ int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
+ kcontrol->private_value;
unsigned int nval, change;
nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
(ucontrol->value.integer.value[1] ? 0 : 0x80000000);
spin_lock_irq(&ice->reg_lock);
- nval |= ice->pro_volumes[index] & ~0x80008000;
- change = nval != ice->pro_volumes[index];
- ice->pro_volumes[index] = nval;
- snd_ice1712_update_volume(ice, index);
+ nval |= ice->pro_volumes[priv_idx] & ~0x80008000;
+ change = nval != ice->pro_volumes[priv_idx];
+ ice->pro_volumes[priv_idx] = nval;
+ snd_ice1712_update_volume(ice, priv_idx);
spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -1335,11 +1339,14 @@
static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+ int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
+ kcontrol->private_value;
spin_lock_irq(&ice->reg_lock);
- ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
- ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127;
+ ucontrol->value.integer.value[0] =
+ (ice->pro_volumes[priv_idx] >> 0) & 127;
+ ucontrol->value.integer.value[1] =
+ (ice->pro_volumes[priv_idx] >> 16) & 127;
spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1347,16 +1354,17 @@
static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
+ int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
+ kcontrol->private_value;
unsigned int nval, change;
nval = (ucontrol->value.integer.value[0] & 127) |
((ucontrol->value.integer.value[1] & 127) << 16);
spin_lock_irq(&ice->reg_lock);
- nval |= ice->pro_volumes[index] & ~0x007f007f;
- change = nval != ice->pro_volumes[index];
- ice->pro_volumes[index] = nval;
- snd_ice1712_update_volume(ice, index);
+ nval |= ice->pro_volumes[priv_idx] & ~0x007f007f;
+ change = nval != ice->pro_volumes[priv_idx];
+ ice->pro_volumes[priv_idx] = nval;
+ snd_ice1712_update_volume(ice, priv_idx);
spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2482,10 +2490,9 @@
outb(0xff, ICEREG(ice, IRQMASK));
/* --- */
__hw_end:
- if (ice->irq >= 0) {
- synchronize_irq(ice->irq);
+ if (ice->irq >= 0)
free_irq(ice->irq, ice);
- }
+
if (ice->port)
pci_release_regions(ice->pci);
snd_ice1712_akm4xxx_free(ice);
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 303cffe..3208901 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -367,6 +367,15 @@
/* other board-specific data */
void *spec;
+
+ /* VT172x specific */
+ int pro_rate_default;
+ int (*is_spdif_master)(struct snd_ice1712 *ice);
+ unsigned int (*get_rate)(struct snd_ice1712 *ice);
+ void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate);
+ unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
+ void (*set_spdif_clock)(struct snd_ice1712 *ice);
+
};
@@ -429,10 +438,14 @@
static inline void snd_ice1712_gpio_write_bits(struct snd_ice1712 *ice,
unsigned int mask, unsigned int bits)
{
+ unsigned val;
+
ice->gpio.direction |= mask;
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
- snd_ice1712_gpio_set_mask(ice, ~mask);
- snd_ice1712_gpio_write(ice, mask & bits);
+ val = snd_ice1712_gpio_read(ice);
+ val &= ~mask;
+ val |= mask & bits;
+ snd_ice1712_gpio_write(ice, val);
}
static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index f533850..4490422 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -106,15 +106,19 @@
* Basic I/O
*/
+/*
+ * default rates, default clock routines
+ */
+
/* check whether the clock mode is spdif-in */
-static inline int is_spdif_master(struct snd_ice1712 *ice)
+static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice)
{
return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;
}
static inline int is_pro_rate_locked(struct snd_ice1712 *ice)
{
- return is_spdif_master(ice) || PRO_RATE_LOCKED;
+ return ice->is_spdif_master(ice) || PRO_RATE_LOCKED;
}
/*
@@ -219,6 +223,32 @@
}
/*
+ * MPU401 accessor
+ */
+static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu,
+ unsigned long addr)
+{
+ /* fix status bits to the standard position */
+ /* only RX_EMPTY and TX_FULL are checked */
+ if (addr == MPU401C(mpu))
+ return (inb(addr) & 0x0c) << 4;
+ else
+ return inb(addr);
+}
+
+static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu,
+ unsigned char data, unsigned long addr)
+{
+ if (addr == MPU401C(mpu)) {
+ if (data == MPU401_ENTER_UART)
+ outb(0x01, addr);
+ /* what else? */
+ } else
+ outb(data, addr);
+}
+
+
+/*
* Interrupt handler
*/
@@ -226,24 +256,53 @@
{
struct snd_ice1712 *ice = dev_id;
unsigned char status;
+ unsigned char status_mask =
+ VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX | VT1724_IRQ_MTPCM;
int handled = 0;
+#ifdef CONFIG_SND_DEBUG
+ int timeout = 0;
+#endif
while (1) {
status = inb(ICEREG1724(ice, IRQSTAT));
+ status &= status_mask;
if (status == 0)
break;
-
- handled = 1;
- /* these should probably be separated at some point,
- * but as we don't currently have MPU support on the board
- * I will leave it
- */
- if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) {
- if (ice->rmidi[0])
- snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data);
- outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT));
- status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX);
+#ifdef CONFIG_SND_DEBUG
+ if (++timeout > 10) {
+ printk(KERN_ERR
+ "ice1724: Too long irq loop, status = 0x%x\n",
+ status);
+ break;
}
+#endif
+ handled = 1;
+ if (status & VT1724_IRQ_MPU_TX) {
+ if (ice->rmidi[0])
+ snd_mpu401_uart_interrupt_tx(irq,
+ ice->rmidi[0]->private_data);
+ else /* disable TX to be sure */
+ outb(inb(ICEREG1724(ice, IRQMASK)) |
+ VT1724_IRQ_MPU_TX,
+ ICEREG1724(ice, IRQMASK));
+ /* Due to mysterical reasons, MPU_TX is always
+ * generated (and can't be cleared) when a PCM
+ * playback is going. So let's ignore at the
+ * next loop.
+ */
+ status_mask &= ~VT1724_IRQ_MPU_TX;
+ }
+ if (status & VT1724_IRQ_MPU_RX) {
+ if (ice->rmidi[0])
+ snd_mpu401_uart_interrupt(irq,
+ ice->rmidi[0]->private_data);
+ else /* disable RX to be sure */
+ outb(inb(ICEREG1724(ice, IRQMASK)) |
+ VT1724_IRQ_MPU_RX,
+ ICEREG1724(ice, IRQMASK));
+ }
+ /* ack MPU irq */
+ outb(status, ICEREG1724(ice, IRQSTAT));
if (status & VT1724_IRQ_MTPCM) {
/*
* Multi-track PCM
@@ -391,51 +450,61 @@
#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\
VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)
-static int get_max_rate(struct snd_ice1712 *ice)
+static const unsigned int stdclock_rate_list[16] = {
+ 48000, 24000, 12000, 9600, 32000, 16000, 8000, 96000, 44100,
+ 22050, 11025, 88200, 176400, 0, 192000, 64000
+};
+
+static unsigned int stdclock_get_rate(struct snd_ice1712 *ice)
{
+ unsigned int rate;
+ rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
+ return rate;
+}
+
+static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(stdclock_rate_list); i++) {
+ if (stdclock_rate_list[i] == rate) {
+ outb(i, ICEMT1724(ice, RATE));
+ return;
+ }
+ }
+}
+
+static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
+ unsigned int rate)
+{
+ unsigned char val, old;
+ /* check MT02 */
if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
- if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)
- return 192000;
+ val = old = inb(ICEMT1724(ice, I2S_FORMAT));
+ if (rate > 96000)
+ val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */
else
- return 96000;
- } else
- return 48000;
+ val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */
+ if (val != old) {
+ outb(val, ICEMT1724(ice, I2S_FORMAT));
+ /* master clock changed */
+ return 1;
+ }
+ }
+ /* no change in master clock */
+ return 0;
}
static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
int force)
{
unsigned long flags;
- unsigned char val, old;
- unsigned int i, mclk_change;
+ unsigned char mclk_change;
+ unsigned int i, old_rate;
- if (rate > get_max_rate(ice))
+ if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
return;
-
- switch (rate) {
- case 8000: val = 6; break;
- case 9600: val = 3; break;
- case 11025: val = 10; break;
- case 12000: val = 2; break;
- case 16000: val = 5; break;
- case 22050: val = 9; break;
- case 24000: val = 1; break;
- case 32000: val = 4; break;
- case 44100: val = 8; break;
- case 48000: val = 0; break;
- case 64000: val = 15; break;
- case 88200: val = 11; break;
- case 96000: val = 7; break;
- case 176400: val = 12; break;
- case 192000: val = 14; break;
- default:
- snd_BUG();
- val = 0;
- break;
- }
-
spin_lock_irqsave(&ice->reg_lock, flags);
- if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
+ if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
(inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
/* running? we cannot change the rate now... */
spin_unlock_irqrestore(&ice->reg_lock, flags);
@@ -446,9 +515,9 @@
return;
}
- old = inb(ICEMT1724(ice, RATE));
- if (force || old != val)
- outb(val, ICEMT1724(ice, RATE));
+ old_rate = ice->get_rate(ice);
+ if (force || (old_rate != rate))
+ ice->set_rate(ice, rate);
else if (rate == ice->cur_rate) {
spin_unlock_irqrestore(&ice->reg_lock, flags);
return;
@@ -456,19 +525,9 @@
ice->cur_rate = rate;
- /* check MT02 */
- mclk_change = 0;
- if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
- val = old = inb(ICEMT1724(ice, I2S_FORMAT));
- if (rate > 96000)
- val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */
- else
- val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */
- if (val != old) {
- outb(val, ICEMT1724(ice, I2S_FORMAT));
- mclk_change = 1;
- }
- }
+ /* setting master clock */
+ mclk_change = ice->set_mclk(ice, rate);
+
spin_unlock_irqrestore(&ice->reg_lock, flags);
if (mclk_change && ice->gpio.i2s_mclk_changed)
@@ -727,43 +786,32 @@
/*
* set rate constraints
*/
-static int set_rate_constraints(struct snd_ice1712 *ice,
- struct snd_pcm_substream *substream)
+static void set_std_hw_rates(struct snd_ice1712 *ice)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- if (ice->hw_rates) {
- /* hardware specific */
- runtime->hw.rate_min = ice->hw_rates->list[0];
- runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1];
- runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
- return snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- ice->hw_rates);
- }
if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
/* I2S */
/* VT1720 doesn't support more than 96kHz */
if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)
- return snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_constraints_rates_192);
- else {
- runtime->hw.rates = SNDRV_PCM_RATE_KNOT |
- SNDRV_PCM_RATE_8000_96000;
- runtime->hw.rate_max = 96000;
- return snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_constraints_rates_96);
- }
- } else if (ice->ac97) {
+ ice->hw_rates = &hw_constraints_rates_192;
+ else
+ ice->hw_rates = &hw_constraints_rates_96;
+ } else {
/* ACLINK */
- runtime->hw.rate_max = 48000;
- runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000;
- return snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_constraints_rates_48);
+ ice->hw_rates = &hw_constraints_rates_48;
}
- return 0;
+}
+
+static int set_rate_constraints(struct snd_ice1712 *ice,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.rate_min = ice->hw_rates->list[0];
+ runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1];
+ runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+ return snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ ice->hw_rates);
}
/* multi-channel playback needs alignment 8x32bit regardless of the channels
@@ -824,7 +872,7 @@
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
if (PRO_RATE_RESET)
- snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
ice->playback_pro_substream = NULL;
return 0;
@@ -835,7 +883,7 @@
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
if (PRO_RATE_RESET)
- snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
ice->capture_pro_substream = NULL;
return 0;
}
@@ -970,6 +1018,8 @@
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ if (ice->spdif.ops.open)
+ ice->spdif.ops.open(ice, substream);
return 0;
}
@@ -978,8 +1028,10 @@
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
if (PRO_RATE_RESET)
- snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
ice->playback_con_substream = NULL;
+ if (ice->spdif.ops.close)
+ ice->spdif.ops.close(ice, substream);
return 0;
}
@@ -1002,6 +1054,8 @@
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ if (ice->spdif.ops.open)
+ ice->spdif.ops.open(ice, substream);
return 0;
}
@@ -1010,8 +1064,10 @@
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
if (PRO_RATE_RESET)
- snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
ice->capture_con_substream = NULL;
+ if (ice->spdif.ops.close)
+ ice->spdif.ops.close(ice, substream);
return 0;
}
@@ -1154,7 +1210,7 @@
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
if (PRO_RATE_RESET)
- snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
ice->playback_con_substream_ds[substream->number] = NULL;
ice->pcm_reserved[substream->number] = NULL;
@@ -1572,50 +1628,18 @@
static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char * const texts_1724[] = {
- "8000", /* 0: 6 */
- "9600", /* 1: 3 */
- "11025", /* 2: 10 */
- "12000", /* 3: 2 */
- "16000", /* 4: 5 */
- "22050", /* 5: 9 */
- "24000", /* 6: 1 */
- "32000", /* 7: 4 */
- "44100", /* 8: 8 */
- "48000", /* 9: 0 */
- "64000", /* 10: 15 */
- "88200", /* 11: 11 */
- "96000", /* 12: 7 */
- "176400", /* 13: 12 */
- "192000", /* 14: 14 */
- "IEC958 Input", /* 15: -- */
- };
- static const char * const texts_1720[] = {
- "8000", /* 0: 6 */
- "9600", /* 1: 3 */
- "11025", /* 2: 10 */
- "12000", /* 3: 2 */
- "16000", /* 4: 5 */
- "22050", /* 5: 9 */
- "24000", /* 6: 1 */
- "32000", /* 7: 4 */
- "44100", /* 8: 8 */
- "48000", /* 9: 0 */
- "64000", /* 10: 15 */
- "88200", /* 11: 11 */
- "96000", /* 12: 7 */
- "IEC958 Input", /* 13: -- */
- };
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
- uinfo->value.enumerated.items = ice->vt1720 ? 14 : 16;
+ uinfo->value.enumerated.items = ice->hw_rates->count + 1;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- ice->vt1720 ? texts_1720[uinfo->value.enumerated.item] :
- texts_1724[uinfo->value.enumerated.item]);
+ if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1)
+ strcpy(uinfo->value.enumerated.name, "IEC958 Input");
+ else
+ sprintf(uinfo->value.enumerated.name, "%d",
+ ice->hw_rates->list[uinfo->value.enumerated.item]);
return 0;
}
@@ -1623,68 +1647,79 @@
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- static const unsigned char xlate[16] = {
- 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10
- };
- unsigned char val;
+ unsigned int i, rate;
spin_lock_irq(&ice->reg_lock);
- if (is_spdif_master(ice)) {
- ucontrol->value.enumerated.item[0] = ice->vt1720 ? 13 : 15;
+ if (ice->is_spdif_master(ice)) {
+ ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
} else {
- val = xlate[inb(ICEMT1724(ice, RATE)) & 15];
- if (val == 255) {
- snd_BUG();
- val = 0;
+ rate = ice->get_rate(ice);
+ ucontrol->value.enumerated.item[0] = 0;
+ for (i = 0; i < ice->hw_rates->count; i++) {
+ if (ice->hw_rates->list[i] == rate) {
+ ucontrol->value.enumerated.item[0] = i;
+ break;
+ }
}
- ucontrol->value.enumerated.item[0] = val;
}
spin_unlock_irq(&ice->reg_lock);
return 0;
}
+/* setting clock to external - SPDIF */
+static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
+{
+ unsigned char oval;
+ unsigned char i2s_oval;
+ oval = inb(ICEMT1724(ice, RATE));
+ outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+ /* setting 256fs */
+ i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
+ outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT));
+}
+
static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- unsigned char oval;
- int rate;
- int change = 0;
- int spdif = ice->vt1720 ? 13 : 15;
+ unsigned int old_rate, new_rate;
+ unsigned int item = ucontrol->value.enumerated.item[0];
+ unsigned int spdif = ice->hw_rates->count;
+
+ if (item > spdif)
+ return -EINVAL;
spin_lock_irq(&ice->reg_lock);
- oval = inb(ICEMT1724(ice, RATE));
- if (ucontrol->value.enumerated.item[0] == spdif) {
- unsigned char i2s_oval;
- outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
- /* setting 256fs */
- i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
- outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X,
- ICEMT1724(ice, I2S_FORMAT));
+ if (ice->is_spdif_master(ice))
+ old_rate = 0;
+ else
+ old_rate = ice->get_rate(ice);
+ if (item == spdif) {
+ /* switching to external clock via SPDIF */
+ ice->set_spdif_clock(ice);
+ new_rate = 0;
} else {
- rate = rates[ucontrol->value.integer.value[0] % 15];
- if (rate <= get_max_rate(ice)) {
- PRO_RATE_DEFAULT = rate;
- spin_unlock_irq(&ice->reg_lock);
- snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1);
- spin_lock_irq(&ice->reg_lock);
- }
+ /* internal on-card clock */
+ new_rate = ice->hw_rates->list[item];
+ ice->pro_rate_default = new_rate;
+ spin_unlock_irq(&ice->reg_lock);
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
+ spin_lock_irq(&ice->reg_lock);
}
- change = inb(ICEMT1724(ice, RATE)) != oval;
spin_unlock_irq(&ice->reg_lock);
- if ((oval & VT1724_SPDIF_MASTER) !=
- (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER)) {
+ /* the first reset to the SPDIF master mode? */
+ if (old_rate != new_rate && !new_rate) {
/* notify akm chips as well */
- if (is_spdif_master(ice)) {
- unsigned int i;
- for (i = 0; i < ice->akm_codecs; i++) {
- if (ice->akm[i].ops.set_rate_val)
- ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
- }
+ unsigned int i;
+ if (ice->gpio.set_pro_rate)
+ ice->gpio.set_pro_rate(ice, 0);
+ for (i = 0; i < ice->akm_codecs; i++) {
+ if (ice->akm[i].ops.set_rate_val)
+ ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
}
}
- return change;
+ return old_rate != new_rate;
}
static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = {
@@ -2065,12 +2100,16 @@
-static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
+static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
{
outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
- udelay(200);
+ msleep(10);
outb(0, ICEREG1724(ice, CONTROL));
- udelay(200);
+ msleep(10);
+}
+
+static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
+{
outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
outb(ice->eeprom.data[ICE_EEP2_I2S], ICEREG1724(ice, I2S_FEATURES));
@@ -2169,10 +2208,8 @@
outb(0xff, ICEREG1724(ice, IRQMASK));
/* --- */
__hw_end:
- if (ice->irq >= 0) {
- synchronize_irq(ice->irq);
+ if (ice->irq >= 0)
free_irq(ice->irq, ice);
- }
pci_release_regions(ice->pci);
snd_ice1712_akm4xxx_free(ice);
pci_disable_device(ice->pci);
@@ -2243,6 +2280,7 @@
ice->irq = pci->irq;
+ snd_vt1724_chip_reset(ice);
if (snd_vt1724_read_eeprom(ice, modelname) < 0) {
snd_vt1724_free(ice);
return -EIO;
@@ -2253,10 +2291,7 @@
}
/* unmask used interrupts */
- if (! (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401))
- mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
- else
- mask = 0;
+ mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
outb(mask, ICEREG1724(ice, IRQMASK));
/* don't handle FIFO overrun/underruns (just yet),
* since they cause machine lockups
@@ -2335,6 +2370,19 @@
* was called so in ice1712 driver, and vt1724 driver is derived from
* ice1712 driver.
*/
+ ice->pro_rate_default = PRO_RATE_DEFAULT;
+ if (!ice->is_spdif_master)
+ ice->is_spdif_master = stdclock_is_spdif_master;
+ if (!ice->get_rate)
+ ice->get_rate = stdclock_get_rate;
+ if (!ice->set_rate)
+ ice->set_rate = stdclock_set_rate;
+ if (!ice->set_mclk)
+ ice->set_mclk = stdclock_set_mclk;
+ if (!ice->set_spdif_clock)
+ ice->set_spdif_clock = stdclock_set_spdif_clock;
+ if (!ice->hw_rates)
+ set_std_hw_rates(ice);
if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) {
snd_card_free(card);
@@ -2377,14 +2425,29 @@
if (! c->no_mpu401) {
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
+ struct snd_mpu401 *mpu;
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
ICEREG1724(ice, MPU_CTRL),
- MPU401_INFO_INTEGRATED,
+ (MPU401_INFO_INTEGRATED |
+ MPU401_INFO_TX_IRQ),
ice->irq, 0,
&ice->rmidi[0])) < 0) {
snd_card_free(card);
return err;
}
+ mpu = ice->rmidi[0]->private_data;
+ mpu->read = snd_vt1724_mpu401_read;
+ mpu->write = snd_vt1724_mpu401_write;
+ /* unmask MPU RX/TX irqs */
+ outb(inb(ICEREG1724(ice, IRQMASK)) &
+ ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
+ ICEREG1724(ice, IRQMASK));
+#if 0 /* for testing */
+ /* set watermarks */
+ outb(VT1724_MPU_RX_FIFO | 0x1,
+ ICEREG1724(ice, MPU_FIFO_WM));
+ outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
+#endif
}
}
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index e8038c0..b4e0c16 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -4,6 +4,8 @@
* Lowlevel functions for ESI Juli@ cards
*
* Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
+ * 2008 Pavel Hofman <dustin@seznam.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
@@ -27,11 +29,11 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/tlv.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "juli.h"
-
struct juli_spec {
struct ak4114 *ak4114;
unsigned int analog: 1;
@@ -44,6 +46,32 @@
#define AK4358_ADDR 0x22 /* DAC */
/*
+ * Juli does not use the standard ICE1724 clock scheme. Juli's ice1724 chip is
+ * supplied by external clock provided by Xilinx array and MK73-1 PLL frequency
+ * multiplier. Actual frequency is set by ice1724 GPIOs hooked to the Xilinx.
+ *
+ * The clock circuitry is supplied by the two ice1724 crystals. This
+ * arrangement allows to generate independent clock signal for AK4114's input
+ * rate detection circuit. As a result, Juli, unlike most other
+ * ice1724+ak4114-based cards, detects spdif input rate correctly.
+ * This fact is applied in the driver, allowing to modify PCM stream rate
+ * parameter according to the actual input rate.
+ *
+ * Juli uses the remaining three stereo-channels of its DAC to optionally
+ * monitor analog input, digital input, and digital output. The corresponding
+ * I2S signals are routed by Xilinx, controlled by GPIOs.
+ *
+ * The master mute is implemented using output muting transistors (GPIO) in
+ * combination with smuting the DAC.
+ *
+ * The card itself has no HW master volume control, implemented using the
+ * vmaster control.
+ *
+ * TODO:
+ * researching and fixing the input monitors
+ */
+
+/*
* GPIO pins
*/
#define GPIO_FREQ_MASK (3<<0)
@@ -55,17 +83,82 @@
#define GPIO_MULTI_2X (1<<2)
#define GPIO_MULTI_1X (2<<2) /* also external */
#define GPIO_MULTI_HALF (3<<2)
-#define GPIO_INTERNAL_CLOCK (1<<4)
+#define GPIO_INTERNAL_CLOCK (1<<4) /* 0 = external, 1 = internal */
+#define GPIO_CLOCK_MASK (1<<4)
#define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */
#define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */
#define GPIO_AK5385A_CKS0 (1<<8)
-#define GPIO_AK5385A_DFS0 (1<<9) /* swapped with DFS1 according doc? */
-#define GPIO_AK5385A_DFS1 (1<<10)
+#define GPIO_AK5385A_DFS1 (1<<9)
+#define GPIO_AK5385A_DFS0 (1<<10)
#define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */
#define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */
#define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */
-#define GPIO_AK5385A_MCLK (1<<14) /* must be 0 */
-#define GPIO_MUTE_CONTROL (1<<15) /* 0 = off, 1 = on */
+#define GPIO_AK5385A_CKS1 (1<<14) /* must be 0 */
+#define GPIO_MUTE_CONTROL (1<<15) /* output mute, 1 = muted */
+
+#define GPIO_RATE_MASK (GPIO_FREQ_MASK | GPIO_MULTI_MASK | \
+ GPIO_CLOCK_MASK)
+#define GPIO_AK5385A_MASK (GPIO_AK5385A_CKS0 | GPIO_AK5385A_DFS0 | \
+ GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS1)
+
+#define JULI_PCM_RATE (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define GPIO_RATE_16000 (GPIO_FREQ_32KHZ | GPIO_MULTI_HALF | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_22050 (GPIO_FREQ_44KHZ | GPIO_MULTI_HALF | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_24000 (GPIO_FREQ_48KHZ | GPIO_MULTI_HALF | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_32000 (GPIO_FREQ_32KHZ | GPIO_MULTI_1X | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_44100 (GPIO_FREQ_44KHZ | GPIO_MULTI_1X | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_48000 (GPIO_FREQ_48KHZ | GPIO_MULTI_1X | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_64000 (GPIO_FREQ_32KHZ | GPIO_MULTI_2X | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_88200 (GPIO_FREQ_44KHZ | GPIO_MULTI_2X | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_96000 (GPIO_FREQ_48KHZ | GPIO_MULTI_2X | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_176400 (GPIO_FREQ_44KHZ | GPIO_MULTI_4X | \
+ GPIO_INTERNAL_CLOCK)
+#define GPIO_RATE_192000 (GPIO_FREQ_48KHZ | GPIO_MULTI_4X | \
+ GPIO_INTERNAL_CLOCK)
+
+/*
+ * Initial setup of the conversion array GPIO <-> rate
+ */
+static unsigned int juli_rates[] = {
+ 16000, 22050, 24000, 32000,
+ 44100, 48000, 64000, 88200,
+ 96000, 176400, 192000,
+};
+
+static unsigned int gpio_vals[] = {
+ GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000,
+ GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200,
+ GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000,
+};
+
+static struct snd_pcm_hw_constraint_list juli_rates_info = {
+ .count = ARRAY_SIZE(juli_rates),
+ .list = juli_rates,
+ .mask = 0,
+};
+
+static int get_gpio_val(int rate)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(juli_rates); i++)
+ if (juli_rates[i] == rate)
+ return gpio_vals[i];
+ return 0;
+}
static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val)
{
@@ -78,6 +171,27 @@
}
/*
+ * If SPDIF capture and slaved to SPDIF-IN, setting runtime rate
+ * to the external rate
+ */
+static void juli_spdif_in_open(struct snd_ice1712 *ice,
+ struct snd_pcm_substream *substream)
+{
+ struct juli_spec *spec = ice->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int rate;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ !ice->is_spdif_master(ice))
+ return;
+ rate = snd_ak4114_external_rate(spec->ak4114);
+ if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) {
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+}
+
+/*
* AK4358 section
*/
@@ -99,57 +213,285 @@
}
/*
- * change the rate of envy24HT, AK4358
+ * change the rate of envy24HT, AK4358, AK5385
*/
static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
- unsigned char old, tmp, dfs;
+ unsigned char old, tmp, ak4358_dfs;
+ unsigned int ak5385_pins, old_gpio, new_gpio;
+ struct snd_ice1712 *ice = ak->private_data[0];
+ struct juli_spec *spec = ice->spec;
- if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ if (rate == 0) /* no hint - S/PDIF input is master or the new spdif
+ input rate undetected, simply return */
return;
-
+
/* adjust DFS on codecs */
- if (rate > 96000)
- dfs = 2;
- else if (rate > 48000)
- dfs = 1;
- else
- dfs = 0;
-
+ if (rate > 96000) {
+ ak4358_dfs = 2;
+ ak5385_pins = GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS0;
+ } else if (rate > 48000) {
+ ak4358_dfs = 1;
+ ak5385_pins = GPIO_AK5385A_DFS0;
+ } else {
+ ak4358_dfs = 0;
+ ak5385_pins = 0;
+ }
+ /* AK5385 first, since it requires cold reset affecting both codecs */
+ old_gpio = ice->gpio.get_data(ice);
+ new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins;
+ /* printk(KERN_DEBUG "JULI - ak5385 set_rate_val: new gpio 0x%x\n",
+ new_gpio); */
+ ice->gpio.set_data(ice, new_gpio);
+
+ /* cold reset */
+ old = inb(ICEMT1724(ice, AC97_CMD));
+ outb(old | VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD));
+ udelay(1);
+ outb(old & ~VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD));
+
+ /* AK4358 */
+ /* set new value, reset DFS */
tmp = snd_akm4xxx_get(ak, 0, 2);
- old = (tmp >> 4) & 0x03;
- if (old == dfs)
- return;
- /* reset DFS */
snd_akm4xxx_reset(ak, 1);
tmp = snd_akm4xxx_get(ak, 0, 2);
tmp &= ~(0x03 << 4);
- tmp |= dfs << 4;
+ tmp |= ak4358_dfs << 4;
snd_akm4xxx_set(ak, 0, 2, tmp);
snd_akm4xxx_reset(ak, 0);
+
+ /* reinit ak4114 */
+ snd_ak4114_reinit(spec->ak4114);
}
+#define AK_DAC(xname, xch) { .name = xname, .num_channels = xch }
+#define PCM_VOLUME "PCM Playback Volume"
+#define MONITOR_AN_IN_VOLUME "Monitor Analog In Volume"
+#define MONITOR_DIG_IN_VOLUME "Monitor Digital In Volume"
+#define MONITOR_DIG_OUT_VOLUME "Monitor Digital Out Volume"
+
+static const struct snd_akm4xxx_dac_channel juli_dac[] = {
+ AK_DAC(PCM_VOLUME, 2),
+ AK_DAC(MONITOR_AN_IN_VOLUME, 2),
+ AK_DAC(MONITOR_DIG_OUT_VOLUME, 2),
+ AK_DAC(MONITOR_DIG_IN_VOLUME, 2),
+};
+
+
static struct snd_akm4xxx akm_juli_dac __devinitdata = {
.type = SND_AK4358,
- .num_dacs = 2,
+ .num_dacs = 8, /* DAC1 - analog out
+ DAC2 - analog in monitor
+ DAC3 - digital out monitor
+ DAC4 - digital in monitor
+ */
.ops = {
.lock = juli_akm_lock,
.unlock = juli_akm_unlock,
.write = juli_akm_write,
.set_rate_val = juli_akm_set_rate_val
- }
+ },
+ .dac_info = juli_dac,
};
+#define juli_mute_info snd_ctl_boolean_mono_info
+
+static int juli_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ val = ice->gpio.get_data(ice) & (unsigned int) kcontrol->private_value;
+ if (kcontrol->private_value == GPIO_MUTE_CONTROL)
+ /* val 0 = signal on */
+ ucontrol->value.integer.value[0] = (val) ? 0 : 1;
+ else
+ /* val 1 = signal on */
+ ucontrol->value.integer.value[0] = (val) ? 1 : 0;
+ return 0;
+}
+
+static int juli_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned int old_gpio, new_gpio;
+ old_gpio = ice->gpio.get_data(ice);
+ if (ucontrol->value.integer.value[0]) {
+ /* unmute */
+ if (kcontrol->private_value == GPIO_MUTE_CONTROL) {
+ /* 0 = signal on */
+ new_gpio = old_gpio & ~GPIO_MUTE_CONTROL;
+ /* un-smuting DAC */
+ snd_akm4xxx_write(ice->akm, 0, 0x01, 0x01);
+ } else
+ /* 1 = signal on */
+ new_gpio = old_gpio |
+ (unsigned int) kcontrol->private_value;
+ } else {
+ /* mute */
+ if (kcontrol->private_value == GPIO_MUTE_CONTROL) {
+ /* 1 = signal off */
+ new_gpio = old_gpio | GPIO_MUTE_CONTROL;
+ /* smuting DAC */
+ snd_akm4xxx_write(ice->akm, 0, 0x01, 0x03);
+ } else
+ /* 0 = signal off */
+ new_gpio = old_gpio &
+ ~((unsigned int) kcontrol->private_value);
+ }
+ /* printk("JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, \
+ new_gpio 0x%x\n",
+ (unsigned int)ucontrol->value.integer.value[0], old_gpio,
+ new_gpio); */
+ if (old_gpio != new_gpio) {
+ ice->gpio.set_data(ice, new_gpio);
+ return 1;
+ }
+ /* no change */
+ return 0;
+}
+
+static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = juli_mute_info,
+ .get = juli_mute_get,
+ .put = juli_mute_put,
+ .private_value = GPIO_MUTE_CONTROL,
+ },
+ /* Although the following functionality respects the succint NDA'd
+ * documentation from the card manufacturer, and the same way of
+ * operation is coded in OSS Juli driver, only Digital Out monitor
+ * seems to work. Surprisingly, Analog input monitor outputs Digital
+ * output data. The two are independent, as enabling both doubles
+ * volume of the monitor sound.
+ *
+ * Checking traces on the board suggests the functionality described
+ * by the manufacturer is correct - I2S from ADC and AK4114
+ * go to ICE as well as to Xilinx, I2S inputs of DAC2,3,4 (the monitor
+ * inputs) are fed from Xilinx.
+ *
+ * I even checked traces on board and coded a support in driver for
+ * an alternative possiblity - the unused I2S ICE output channels
+ * switched to HW-IN/SPDIF-IN and providing the monitoring signal to
+ * the DAC - to no avail. The I2S outputs seem to be unconnected.
+ *
+ * The windows driver supports the monitoring correctly.
+ */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitor Analog In Switch",
+ .info = juli_mute_info,
+ .get = juli_mute_get,
+ .put = juli_mute_put,
+ .private_value = GPIO_ANAIN_MONITOR,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitor Digital Out Switch",
+ .info = juli_mute_info,
+ .get = juli_mute_get,
+ .put = juli_mute_put,
+ .private_value = GPIO_DIGOUT_MONITOR,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitor Digital In Switch",
+ .info = juli_mute_info,
+ .get = juli_mute_get,
+ .put = juli_mute_put,
+ .private_value = GPIO_DIGIN_MONITOR,
+ },
+};
+
+
+static void ak4358_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ int reg, val;
+ for (reg = 0; reg <= 0xf; reg++) {
+ val = snd_akm4xxx_get(ice->akm, 0, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+static void ak4358_proc_init(struct snd_ice1712 *ice)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry))
+ snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read);
+}
+
+static char *slave_vols[] __devinitdata = {
+ PCM_VOLUME,
+ MONITOR_AN_IN_VOLUME,
+ MONITOR_DIG_IN_VOLUME,
+ MONITOR_DIG_OUT_VOLUME,
+ NULL
+};
+
+static __devinitdata
+DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
+
+static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
+ const char *name)
+{
+ struct snd_ctl_elem_id sid;
+ memset(&sid, 0, sizeof(sid));
+ /* FIXME: strcpy is bad. */
+ strcpy(sid.name, name);
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_find_id(card, &sid);
+}
+
+static void __devinit add_slaves(struct snd_card *card,
+ struct snd_kcontrol *master, char **list)
+{
+ for (; *list; list++) {
+ struct snd_kcontrol *slave = ctl_find(card, *list);
+ /* printk(KERN_DEBUG "add_slaves - %s\n", *list); */
+ if (slave) {
+ /* printk(KERN_DEBUG "slave %s found\n", *list); */
+ snd_ctl_add_slave(master, slave);
+ }
+ }
+}
+
static int __devinit juli_add_controls(struct snd_ice1712 *ice)
{
struct juli_spec *spec = ice->spec;
int err;
+ unsigned int i;
+ struct snd_kcontrol *vmaster;
+
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
+
+ for (i = 0; i < ARRAY_SIZE(juli_mute_controls); i++) {
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&juli_mute_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+ /* Create virtual master control */
+ vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
+ juli_master_db_scale);
+ if (!vmaster)
+ return -ENOMEM;
+ add_slaves(ice->card, vmaster, slave_vols);
+ err = snd_ctl_add(ice->card, vmaster);
+ if (err < 0)
+ return err;
+
/* only capture SPDIF over AK4114 */
err = snd_ak4114_build(spec->ak4114, NULL,
- ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+ ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+
+ ak4358_proc_init(ice);
if (err < 0)
return err;
return 0;
@@ -158,6 +500,74 @@
/*
* initialize the chip
*/
+
+static inline int juli_is_spdif_master(struct snd_ice1712 *ice)
+{
+ return (ice->gpio.get_data(ice) & GPIO_INTERNAL_CLOCK) ? 0 : 1;
+}
+
+static unsigned int juli_get_rate(struct snd_ice1712 *ice)
+{
+ int i;
+ unsigned char result;
+
+ result = ice->gpio.get_data(ice) & GPIO_RATE_MASK;
+ for (i = 0; i < ARRAY_SIZE(gpio_vals); i++)
+ if (gpio_vals[i] == result)
+ return juli_rates[i];
+ return 0;
+}
+
+/* setting new rate */
+static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+ unsigned int old, new;
+ unsigned char val;
+
+ old = ice->gpio.get_data(ice);
+ new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate);
+ /* printk(KERN_DEBUG "JULI - set_rate: old %x, new %x\n",
+ old & GPIO_RATE_MASK,
+ new & GPIO_RATE_MASK); */
+
+ ice->gpio.set_data(ice, new);
+ /* switching to external clock - supplied by external circuits */
+ val = inb(ICEMT1724(ice, RATE));
+ outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+}
+
+static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
+ unsigned int rate)
+{
+ /* no change in master clock */
+ return 0;
+}
+
+/* setting clock to external - SPDIF */
+static void juli_set_spdif_clock(struct snd_ice1712 *ice)
+{
+ unsigned int old;
+ old = ice->gpio.get_data(ice);
+ /* external clock (= 0), multiply 1x, 48kHz */
+ ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
+ GPIO_FREQ_48KHZ);
+}
+
+/* Called when ak4114 detects change in the input SPDIF stream */
+static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0,
+ unsigned char c1)
+{
+ struct snd_ice1712 *ice = ak4114->change_callback_private;
+ int rate;
+ if (ice->is_spdif_master(ice) && c1) {
+ /* only for SPDIF master mode, rate was changed */
+ rate = snd_ak4114_external_rate(ak4114);
+ /* printk(KERN_DEBUG "ak4114 - input rate changed to %d\n",
+ rate); */
+ juli_akm_set_rate_val(ice->akm, rate);
+ }
+}
+
static int __devinit juli_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
@@ -187,6 +597,11 @@
ice, &spec->ak4114);
if (err < 0)
return err;
+ /* callback for codecs rate setting */
+ spec->ak4114->change_callback = juli_ak4114_change;
+ spec->ak4114->change_callback_private = ice;
+ /* AK4114 in Juli can detect external rate correctly */
+ spec->ak4114->check_flags = 0;
#if 0
/* it seems that the analog doughter board detection does not work
@@ -210,6 +625,15 @@
return err;
}
+ /* juli is clocked by Xilinx array */
+ ice->hw_rates = &juli_rates_info;
+ ice->is_spdif_master = juli_is_spdif_master;
+ ice->get_rate = juli_get_rate;
+ ice->set_rate = juli_set_rate;
+ ice->set_mclk = juli_set_mclk;
+ ice->set_spdif_clock = juli_set_spdif_clock;
+
+ ice->spdif.ops.open = juli_spdif_in_open;
return 0;
}
@@ -220,18 +644,20 @@
*/
static unsigned char juli_eeprom[] __devinitdata = {
- [ICE_EEP2_SYSCONF] = 0x20, /* clock 512, mpu401, 1xADC, 1xDACs */
+ [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs,
+ SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
- [ICE_EEP2_GPIO_DIR] = 0x9f,
+ [ICE_EEP2_GPIO_DIR] = 0x9f, /* 5, 6:inputs; 7, 4-0 outputs*/
[ICE_EEP2_GPIO_DIR1] = 0xff,
[ICE_EEP2_GPIO_DIR2] = 0x7f,
- [ICE_EEP2_GPIO_MASK] = 0x9f,
- [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK] = 0x60, /* 5, 6: locked; 7, 4-0 writable */
+ [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0-7 writable */
[ICE_EEP2_GPIO_MASK2] = 0x7f,
- [ICE_EEP2_GPIO_STATE] = 0x16, /* internal clock, multiple 1x, 48kHz */
- [ICE_EEP2_GPIO_STATE1] = 0x80, /* mute */
+ [ICE_EEP2_GPIO_STATE] = GPIO_FREQ_48KHZ | GPIO_MULTI_1X |
+ GPIO_INTERNAL_CLOCK, /* internal clock, multiple 1x, 48kHz*/
+ [ICE_EEP2_GPIO_STATE1] = 0x00, /* unmuted */
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 4945c81..203cdc1bf 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -246,7 +246,7 @@
wm_put(ice, WM_ADC_MUX, nval);
}
mutex_unlock(&ice->gpio_mutex);
- return 0;
+ return change;
}
/*
@@ -450,7 +450,7 @@
change = 1;
}
mutex_unlock(&ice->gpio_mutex);
- return 0;
+ return change;
}
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 48cf40a..48d3679 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -319,12 +319,11 @@
/*
* Handler for setting correct codec rate - called when rate change is detected
*/
-static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
+static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
{
unsigned char old, new;
int idx;
unsigned char changed[7];
- struct snd_ice1712 *ice = ak->private_data[0];
struct prodigy192_spec *spec = ice->spec;
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
@@ -357,16 +356,6 @@
mutex_unlock(&spec->mute_mutex);
}
-/* using akm infrastructure for setting rate of the codec */
-static struct snd_akm4xxx akmlike_stac9460 __devinitdata = {
- .type = NON_AKM, /* special value */
- .num_adcs = 6, /* not used in any way, just for completeness */
- .num_dacs = 2,
- .ops = {
- .set_rate_val = stac9460_set_rate_val
- }
-};
-
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
@@ -642,12 +631,19 @@
0x41, 0x02, 0x2c, 0x00, 0x00
};
struct prodigy192_spec *spec = ice->spec;
+ int err;
- return snd_ak4114_create(ice->card,
+ err = snd_ak4114_create(ice->card,
prodigy192_ak4114_read,
prodigy192_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
ice, &spec->ak4114);
+ if (err < 0)
+ return err;
+ /* AK4114 in Prodigy192 cannot detect external rate correctly.
+ * No reason to stop capture stream due to incorrect checks */
+ spec->ak4114->check_flags = AK4114_CHECK_NO_RATE;
+ return 0;
}
static void stac9460_proc_regs_read(struct snd_info_entry *entry,
@@ -743,7 +739,6 @@
};
const unsigned short *p;
int err = 0;
- struct snd_akm4xxx *ak;
struct prodigy192_spec *spec;
/* prodigy 192 */
@@ -761,15 +756,7 @@
p = stac_inits_prodigy;
for (; *p != (unsigned short)-1; p += 2)
stac9460_put(ice, p[0], p[1]);
- /* reusing the akm codecs infrastructure,
- * for setting rate on stac9460 */
- ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
- if (!ak)
- return -ENOMEM;
- ice->akm_codecs = 1;
- err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice);
- if (err < 0)
- return err;
+ ice->gpio.set_pro_rate = stac9460_set_rate_val;
/* MI/ODI/O add on card with AK4114 */
if (prodigy192_miodio_exists(ice)) {
@@ -825,10 +812,6 @@
.build_controls = prodigy192_add_controls,
.eeprom_size = sizeof(prodigy71_eeprom),
.eeprom_data = prodigy71_eeprom,
- /* the current MPU401 code loops infinitely
- * when opening midi device
- */
- .no_mpu401 = 1,
},
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index 301bf92..4d26314 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -322,17 +322,23 @@
static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
struct snd_ice1712 *ice = ak->private_data[0];
+ int dfs;
revo_set_rate_val(ak, rate);
-#if 1 /* FIXME: do we need this procedure? */
- /* reset DFS pin of AK5385A for ADC, too */
- /* DFS0 (pin 18) -- GPIO10 pin 77 */
- snd_ice1712_save_gpio_status(ice);
- snd_ice1712_gpio_write_bits(ice, 1 << 10,
- rate > 48000 ? (1 << 10) : 0);
- snd_ice1712_restore_gpio_status(ice);
-#endif
+ /* reset CKS */
+ snd_ice1712_gpio_write_bits(ice, 1 << 8, rate > 96000 ? 1 << 8 : 0);
+ /* reset DFS pins of AK5385A for ADC, too */
+ if (rate > 96000)
+ dfs = 2;
+ else if (rate > 48000)
+ dfs = 1;
+ else
+ dfs = 0;
+ snd_ice1712_gpio_write_bits(ice, 3 << 9, dfs << 9);
+ /* reset ADC */
+ snd_ice1712_gpio_write_bits(ice, 1 << 11, 0);
+ snd_ice1712_gpio_write_bits(ice, 1 << 11, 1 << 11);
}
static const struct snd_akm4xxx_dac_channel ap192_dac[] = {
@@ -353,28 +359,20 @@
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
- .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3,
- .cs_addr = VT1724_REVO_CS3,
- .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
+ .cs_addr = VT1724_REVO_CS1,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
-#if 0
-/* FIXME: ak4114 makes the sound much lower due to some confliction,
- * so let's disable it right now...
- */
-#define BUILD_AK4114_AP192
-#endif
-
-#ifdef BUILD_AK4114_AP192
/* AK4114 support on Audiophile 192 */
/* CDTO (pin 32) -- GPIO2 pin 52
* CDTI (pin 33) -- GPIO3 pin 53 (shared with AK4358)
* CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358)
* CSN (pin 35) -- GPIO7 pin 59
*/
-#define AK4114_ADDR 0x00
+#define AK4114_ADDR 0x02
static void write_data(struct snd_ice1712 *ice, unsigned int gpio,
unsigned int data, int idx)
@@ -428,7 +426,7 @@
tmp = snd_ice1712_gpio_read(ice);
tmp |= VT1724_REVO_CCLK; /* high at init */
tmp |= VT1724_REVO_CS0;
- tmp &= ~VT1724_REVO_CS3;
+ tmp &= ~VT1724_REVO_CS1;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
return tmp;
@@ -436,7 +434,7 @@
static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp)
{
- tmp |= VT1724_REVO_CS3;
+ tmp |= VT1724_REVO_CS1;
tmp |= VT1724_REVO_CS0;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
@@ -485,13 +483,17 @@
struct ak4114 *ak;
int err;
- return snd_ak4114_create(ice->card,
+ err = snd_ak4114_create(ice->card,
ap192_ak4114_read,
ap192_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
ice, &ak);
+ /* AK4114 in Revo cannot detect external rate correctly.
+ * No reason to stop capture stream due to incorrect checks */
+ ak->check_flags = AK4114_CHECK_NO_RATE;
+
+ return 0; /* error ignored; it's no fatal error */
}
-#endif /* BUILD_AK4114_AP192 */
static int __devinit revo_init(struct snd_ice1712 *ice)
{
@@ -557,6 +559,9 @@
if (err < 0)
return err;
+ /* unmute all codecs */
+ snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
+ VT1724_REVO_MUTE);
break;
}
@@ -588,11 +593,9 @@
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
-#ifdef BUILD_AK4114_AP192
err = ap192_ak4114_init(ice);
if (err < 0)
return err;
-#endif
break;
}
return 0;
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index c52abd0..048d99e 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -155,7 +155,8 @@
#define ICH_PCM_SPDIF_69 0x80000000 /* s/pdif pcm on slots 6&9 */
#define ICH_PCM_SPDIF_1011 0xc0000000 /* s/pdif pcm on slots 10&11 */
#define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */
-#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */
+#define ICH_PCM_246_MASK 0x00300000 /* chan mask (not all chips) */
+#define ICH_PCM_8 0x00300000 /* 8 channels (not all chips) */
#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */
#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */
#define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */
@@ -382,6 +383,7 @@
unsigned multi4: 1,
multi6: 1,
+ multi8 :1,
dra: 1,
smp20bit: 1;
unsigned in_ac97_init: 1,
@@ -997,6 +999,8 @@
cnt |= ICH_PCM_4;
else if (runtime->channels == 6)
cnt |= ICH_PCM_6;
+ else if (runtime->channels == 8)
+ cnt |= ICH_PCM_8;
if (chip->device_type == DEVICE_NFORCE) {
/* reset to 2ch once to keep the 6 channel data in alignment,
* to start from Front Left always
@@ -1106,6 +1110,16 @@
.mask = 0,
};
+static unsigned int channels8[] = {
+ 2, 4, 6, 8,
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
+ .count = ARRAY_SIZE(channels8),
+ .list = channels8,
+ .mask = 0,
+};
+
static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev)
{
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
@@ -1136,7 +1150,12 @@
if (err < 0)
return err;
- if (chip->multi6) {
+ if (chip->multi8) {
+ runtime->hw.channels_max = 8;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &hw_constraints_channels8);
+ } else if (chip->multi6) {
runtime->hw.channels_max = 6;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&hw_constraints_channels6);
@@ -2203,8 +2222,11 @@
}
if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
chip->multi4 = 1;
- if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE))
+ if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) {
chip->multi6 = 1;
+ if (chip->ac97[0]->flags & AC97_HAS_8CH)
+ chip->multi8 = 1;
+ }
}
if (pbus->pcms[0].r[1].rslots[0]) {
chip->dra = 1;
@@ -2446,7 +2468,7 @@
pci_write_config_dword(chip->pci, 0x4c, val);
}
/* --- */
- synchronize_irq(chip->irq);
+
__hw_end:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
@@ -2495,7 +2517,6 @@
chip->sdm_saved = igetbyte(chip, ICHREG(SDM));
if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
free_irq(chip->irq, chip);
chip->irq = -1;
}
@@ -2648,7 +2669,7 @@
t = stop_time.tv_sec - start_time.tv_sec;
t *= 1000000;
t += stop_time.tv_usec - start_time.tv_usec;
- printk(KERN_INFO "%s: measured %lu usecs\n", __FUNCTION__, t);
+ printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t);
if (t == 0) {
snd_printk(KERN_ERR "?? calculation error..\n");
return;
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index cadda8d..faf674e 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -985,17 +985,15 @@
/* reset channels */
for (i = 0; i < chip->bdbars_count; i++)
iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
- /* --- */
- synchronize_irq(chip->irq);
- __hw_end:
+ __hw_end:
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
if (chip->bdbars.area)
snd_dma_free_pages(&chip->bdbars);
if (chip->addr)
pci_iounmap(chip->pci, chip->addr);
if (chip->bmaddr)
pci_iounmap(chip->pci, chip->bmaddr);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
@@ -1017,7 +1015,6 @@
snd_pcm_suspend_all(chip->pcm[i]);
snd_ac97_suspend(chip->ac97);
if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
free_irq(chip->irq, chip);
chip->irq = -1;
}
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 10c713d..f4c85b5 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -2102,7 +2102,6 @@
snd_korg1212_TurnOffIdleMonitor(korg1212);
if (korg1212->irq >= 0) {
- synchronize_irq(korg1212->irq);
snd_korg1212_DisableCardInterrupts(korg1212);
free_irq(korg1212->irq, korg1212);
korg1212->irq = -1;
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 04fa0a6..a536c59 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2068,7 +2068,7 @@
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
- struct snd_ctl_elem_id id;
+ struct snd_ctl_elem_id elem_id;
int err;
static struct snd_ac97_bus_ops ops = {
.write = snd_m3_ac97_write,
@@ -2088,14 +2088,14 @@
schedule_timeout_uninterruptible(msecs_to_jiffies(100));
snd_ac97_write(chip->ac97, AC97_PCM, 0);
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Master Playback Switch");
- chip->master_switch = snd_ctl_find_id(chip->card, &id);
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Master Playback Volume");
- chip->master_volume = snd_ctl_find_id(chip->card, &id);
+ memset(&elem_id, 0, sizeof(elem_id));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(elem_id.name, "Master Playback Switch");
+ chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
+ memset(&elem_id, 0, sizeof(elem_id));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(elem_id.name, "Master Playback Volume");
+ chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
return 0;
}
@@ -2542,10 +2542,8 @@
vfree(chip->suspend_mem);
#endif
- if (chip->irq >= 0) {
- synchronize_irq(chip->irq);
+ if (chip->irq >= 0)
free_irq(chip->irq, chip);
- }
if (chip->iobase)
pci_release_regions(chip->pci);
@@ -2569,7 +2567,7 @@
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_m3 *chip = card->private_data;
- int i, index;
+ int i, dsp_index;
if (chip->suspend_mem == NULL)
return 0;
@@ -2583,12 +2581,12 @@
snd_m3_assp_halt(chip);
/* save dsp image */
- index = 0;
+ dsp_index = 0;
for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
- chip->suspend_mem[index++] =
+ chip->suspend_mem[dsp_index++] =
snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i);
for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
- chip->suspend_mem[index++] =
+ chip->suspend_mem[dsp_index++] =
snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i);
pci_disable_device(pci);
@@ -2601,7 +2599,7 @@
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_m3 *chip = card->private_data;
- int i, index;
+ int i, dsp_index;
if (chip->suspend_mem == NULL)
return 0;
@@ -2625,13 +2623,13 @@
snd_m3_ac97_reset(chip);
/* restore dsp image */
- index = 0;
+ dsp_index = 0;
for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i,
- chip->suspend_mem[index++]);
+ chip->suspend_mem[dsp_index++]);
for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i,
- chip->suspend_mem[index++]);
+ chip->suspend_mem[dsp_index++]);
/* tell the dma engine to restart itself */
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 7ac654e..7efb838 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1439,7 +1439,7 @@
snd_nm256_capture_stop(chip);
if (chip->irq >= 0)
- synchronize_irq(chip->irq);
+ free_irq(chip->irq, chip);
if (chip->cport)
iounmap(chip->cport);
@@ -1447,8 +1447,6 @@
iounmap(chip->buffer);
release_and_free_resource(chip->res_cport);
release_and_free_resource(chip->res_buffer);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
pci_disable_device(chip->pci);
kfree(chip->ac97_regs);
diff --git a/sound/pci/oxygen/cs4362a.h b/sound/pci/oxygen/cs4362a.h
new file mode 100644
index 0000000..6a4fedf
--- /dev/null
+++ b/sound/pci/oxygen/cs4362a.h
@@ -0,0 +1,69 @@
+/* register 01h */
+#define CS4362A_PDN 0x01
+#define CS4362A_DAC1_DIS 0x02
+#define CS4362A_DAC2_DIS 0x04
+#define CS4362A_DAC3_DIS 0x08
+#define CS4362A_MCLKDIV 0x20
+#define CS4362A_FREEZE 0x40
+#define CS4362A_CPEN 0x80
+/* register 02h */
+#define CS4362A_DIF_MASK 0x70
+#define CS4362A_DIF_LJUST 0x00
+#define CS4362A_DIF_I2S 0x10
+#define CS4362A_DIF_RJUST_16 0x20
+#define CS4362A_DIF_RJUST_24 0x30
+#define CS4362A_DIF_RJUST_20 0x40
+#define CS4362A_DIF_RJUST_18 0x50
+/* register 03h */
+#define CS4362A_MUTEC_MASK 0x03
+#define CS4362A_MUTEC_6 0x00
+#define CS4362A_MUTEC_1 0x01
+#define CS4362A_MUTEC_3 0x03
+#define CS4362A_AMUTE 0x04
+#define CS4362A_MUTEC_POL 0x08
+#define CS4362A_RMP_UP 0x10
+#define CS4362A_SNGLVOL 0x20
+#define CS4362A_ZERO_CROSS 0x40
+#define CS4362A_SOFT_RAMP 0x80
+/* register 04h */
+#define CS4362A_RMP_DN 0x01
+#define CS4362A_DEM_MASK 0x06
+#define CS4362A_DEM_NONE 0x00
+#define CS4362A_DEM_44100 0x02
+#define CS4362A_DEM_48000 0x04
+#define CS4362A_DEM_32000 0x06
+#define CS4362A_FILT_SEL 0x10
+/* register 05h */
+#define CS4362A_INV_A1 0x01
+#define CS4362A_INV_B1 0x02
+#define CS4362A_INV_A2 0x04
+#define CS4362A_INV_B2 0x08
+#define CS4362A_INV_A3 0x10
+#define CS4362A_INV_B3 0x20
+/* register 06h */
+#define CS4362A_FM_MASK 0x03
+#define CS4362A_FM_SINGLE 0x00
+#define CS4362A_FM_DOUBLE 0x01
+#define CS4362A_FM_QUAD 0x02
+#define CS4362A_FM_DSD 0x03
+#define CS4362A_ATAPI_MASK 0x7c
+#define CS4362A_ATAPI_B_MUTE 0x00
+#define CS4362A_ATAPI_B_R 0x04
+#define CS4362A_ATAPI_B_L 0x08
+#define CS4362A_ATAPI_B_LR 0x0c
+#define CS4362A_ATAPI_A_MUTE 0x00
+#define CS4362A_ATAPI_A_R 0x10
+#define CS4362A_ATAPI_A_L 0x20
+#define CS4362A_ATAPI_A_LR 0x30
+#define CS4362A_ATAPI_MIX_LR_VOL 0x40
+#define CS4362A_A_EQ_B 0x80
+/* register 07h */
+#define CS4362A_VOL_MASK 0x7f
+#define CS4362A_MUTE 0x80
+/* register 08h: like 07h */
+/* registers 09h..0Bh: like 06h..08h */
+/* registers 0Ch..0Eh: like 06h..08h */
+/* register 12h */
+#define CS4362A_REV_MASK 0x07
+#define CS4362A_PART_MASK 0xf8
+#define CS4362A_PART_CS4362A 0x50
diff --git a/sound/pci/oxygen/cs4398.h b/sound/pci/oxygen/cs4398.h
new file mode 100644
index 0000000..5faf5ef
--- /dev/null
+++ b/sound/pci/oxygen/cs4398.h
@@ -0,0 +1,69 @@
+/* register 1 */
+#define CS4398_REV_MASK 0x07
+#define CS4398_PART_MASK 0xf8
+#define CS4398_PART_CS4398 0x70
+/* register 2 */
+#define CS4398_FM_MASK 0x03
+#define CS4398_FM_SINGLE 0x00
+#define CS4398_FM_DOUBLE 0x01
+#define CS4398_FM_QUAD 0x02
+#define CS4398_FM_DSD 0x03
+#define CS4398_DEM_MASK 0x0c
+#define CS4398_DEM_NONE 0x00
+#define CS4398_DEM_44100 0x04
+#define CS4398_DEM_48000 0x08
+#define CS4398_DEM_32000 0x0c
+#define CS4398_DIF_MASK 0x70
+#define CS4398_DIF_LJUST 0x00
+#define CS4398_DIF_I2S 0x10
+#define CS4398_DIF_RJUST_16 0x20
+#define CS4398_DIF_RJUST_24 0x30
+#define CS4398_DIF_RJUST_20 0x40
+#define CS4398_DIF_RJUST_18 0x50
+#define CS4398_DSD_SRC 0x80
+/* register 3 */
+#define CS4398_ATAPI_MASK 0x1f
+#define CS4398_ATAPI_B_MUTE 0x00
+#define CS4398_ATAPI_B_R 0x01
+#define CS4398_ATAPI_B_L 0x02
+#define CS4398_ATAPI_B_LR 0x03
+#define CS4398_ATAPI_A_MUTE 0x00
+#define CS4398_ATAPI_A_R 0x04
+#define CS4398_ATAPI_A_L 0x08
+#define CS4398_ATAPI_A_LR 0x0c
+#define CS4398_ATAPI_MIX_LR_VOL 0x10
+#define CS4398_INVERT_B 0x20
+#define CS4398_INVERT_A 0x40
+#define CS4398_VOL_B_EQ_A 0x80
+/* register 4 */
+#define CS4398_MUTEP_MASK 0x03
+#define CS4398_MUTEP_AUTO 0x00
+#define CS4398_MUTEP_LOW 0x02
+#define CS4398_MUTEP_HIGH 0x03
+#define CS4398_MUTE_B 0x08
+#define CS4398_MUTE_A 0x10
+#define CS4398_MUTEC_A_EQ_B 0x20
+#define CS4398_DAMUTE 0x40
+#define CS4398_PAMUTE 0x80
+/* register 5 */
+#define CS4398_VOL_A_MASK 0xff
+/* register 6 */
+#define CS4398_VOL_B_MASK 0xff
+/* register 7 */
+#define CS4398_DIR_DSD 0x01
+#define CS4398_FILT_SEL 0x04
+#define CS4398_RMP_DN 0x10
+#define CS4398_RMP_UP 0x20
+#define CS4398_ZERO_CROSS 0x40
+#define CS4398_SOFT_RAMP 0x80
+/* register 8 */
+#define CS4398_MCLKDIV3 0x08
+#define CS4398_MCLKDIV2 0x10
+#define CS4398_FREEZE 0x20
+#define CS4398_CPEN 0x40
+#define CS4398_PDN 0x80
+/* register 9 */
+#define CS4398_DSD_PM_EN 0x01
+#define CS4398_DSD_PM_MODE 0x02
+#define CS4398_INVALID_DSD 0x04
+#define CS4398_STATIC_DSD 0x08
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 666f69a..090dd43 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -66,12 +66,12 @@
{
struct hifier_data *data = chip->model_data;
- data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
- ak4396_write(chip, AK4396_LCH_ATT, 0xff);
- ak4396_write(chip, AK4396_RCH_ATT, 0xff);
+ ak4396_write(chip, AK4396_LCH_ATT, 0);
+ ak4396_write(chip, AK4396_RCH_ATT, 0);
snd_component_add(chip->card, "AK4396");
snd_component_add(chip->card, "CS5340");
@@ -127,22 +127,8 @@
static int hifier_control_filter(struct snd_kcontrol_new *template)
{
- if (!strcmp(template->name, "Master Playback Volume")) {
- template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- template->tlv.p = ak4396_db_scale;
- } else if (!strcmp(template->name, "Stereo Upmixing")) {
+ if (!strcmp(template->name, "Stereo Upmixing"))
return 1; /* stereo only - we don't need upmixing */
- } else if (!strcmp(template->name,
- SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) ||
- !strcmp(template->name,
- SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) {
- return 1; /* no digital input */
- }
- return 0;
-}
-
-static int hifier_mixer_init(struct oxygen *chip)
-{
return 0;
}
@@ -153,18 +139,20 @@
.owner = THIS_MODULE,
.init = hifier_init,
.control_filter = hifier_control_filter,
- .mixer_init = hifier_mixer_init,
.cleanup = hifier_cleanup,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_cs5340_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
+ .dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct hifier_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_1,
.dac_channels = 2,
- .used_channels = OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH,
- .function_flags = 0,
+ .dac_volume_min = 0,
+ .dac_volume_max = 255,
+ .function_flags = OXYGEN_FUNCTION_SPI,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -181,7 +169,7 @@
++dev;
return -ENOENT;
}
- err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier);
+ err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier);
if (err >= 0)
++dev;
return err;
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 9a9941b..63f185c 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -39,7 +39,7 @@
#include <sound/tlv.h>
#include "oxygen.h"
#include "ak4396.h"
-#include "cm9780.h"
+#include "wm8785.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
@@ -78,49 +78,6 @@
#define GPIO_AK5385_DFS_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002
-#define GPIO_LINE_MUTE CM9780_GPO0
-
-#define WM8785_R0 0
-#define WM8785_R1 1
-#define WM8785_R2 2
-#define WM8785_R7 7
-
-/* R0 */
-#define WM8785_MCR_MASK 0x007
-#define WM8785_MCR_SLAVE 0x000
-#define WM8785_MCR_MASTER_128 0x001
-#define WM8785_MCR_MASTER_192 0x002
-#define WM8785_MCR_MASTER_256 0x003
-#define WM8785_MCR_MASTER_384 0x004
-#define WM8785_MCR_MASTER_512 0x005
-#define WM8785_MCR_MASTER_768 0x006
-#define WM8785_OSR_MASK 0x018
-#define WM8785_OSR_SINGLE 0x000
-#define WM8785_OSR_DOUBLE 0x008
-#define WM8785_OSR_QUAD 0x010
-#define WM8785_FORMAT_MASK 0x060
-#define WM8785_FORMAT_RJUST 0x000
-#define WM8785_FORMAT_LJUST 0x020
-#define WM8785_FORMAT_I2S 0x040
-#define WM8785_FORMAT_DSP 0x060
-/* R1 */
-#define WM8785_WL_MASK 0x003
-#define WM8785_WL_16 0x000
-#define WM8785_WL_20 0x001
-#define WM8785_WL_24 0x002
-#define WM8785_WL_32 0x003
-#define WM8785_LRP 0x004
-#define WM8785_BCLKINV 0x008
-#define WM8785_LRSWAP 0x010
-#define WM8785_DEVNO_MASK 0x0e0
-/* R2 */
-#define WM8785_HPFR 0x001
-#define WM8785_HPFL 0x002
-#define WM8785_SDODIS 0x004
-#define WM8785_PWRDNR 0x008
-#define WM8785_PWRDNL 0x010
-#define WM8785_TDM_MASK 0x1c0
-
struct generic_data {
u8 ak4396_ctl2;
};
@@ -155,7 +112,7 @@
struct generic_data *data = chip->model_data;
unsigned int i;
- data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
@@ -163,8 +120,8 @@
AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, i,
AK4396_CONTROL_3, AK4396_PCM);
- ak4396_write(chip, i, AK4396_LCH_ATT, 0xff);
- ak4396_write(chip, i, AK4396_RCH_ATT, 0xff);
+ ak4396_write(chip, i, AK4396_LCH_ATT, 0);
+ ak4396_write(chip, i, AK4396_RCH_ATT, 0);
}
snd_component_add(chip->card, "AK4396");
}
@@ -185,23 +142,16 @@
snd_component_add(chip->card, "WM8785");
}
-static void cmi9780_init(struct oxygen *chip)
-{
- oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
-}
-
static void generic_init(struct oxygen *chip)
{
ak4396_init(chip);
wm8785_init(chip);
- cmi9780_init(chip);
}
static void meridian_init(struct oxygen *chip)
{
ak4396_init(chip);
ak5385_init(chip);
- cmi9780_init(chip);
}
static void generic_cleanup(struct oxygen *chip)
@@ -297,59 +247,32 @@
value, GPIO_AK5385_DFS_MASK);
}
-static void cmi9780_switch_hook(struct oxygen *chip, unsigned int codec,
- unsigned int reg, int mute)
-{
- if (codec != 0)
- return;
- switch (reg) {
- case AC97_LINE:
- oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
- mute ? GPIO_LINE_MUTE : 0,
- GPIO_LINE_MUTE);
- break;
- case AC97_MIC:
- case AC97_CD:
- case AC97_AUX:
- if (!mute)
- oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
- GPIO_LINE_MUTE);
- break;
- }
-}
-
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-static int ak4396_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strcmp(template->name, "Master Playback Volume")) {
- template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- template->tlv.p = ak4396_db_scale;
- }
- return 0;
-}
-
static const struct oxygen_model model_generic = {
.shortname = "C-Media CMI8788",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.owner = THIS_MODULE,
.init = generic_init,
- .control_filter = ak4396_control_filter,
.cleanup = generic_cleanup,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
- .ac97_switch_hook = cmi9780_switch_hook,
+ .dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ PLAYBACK_2_TO_AC97_1 |
+ CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_1_FROM_SPDIF |
+ CAPTURE_2_FROM_AC97_1,
.dac_channels = 8,
- .used_channels = OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_C |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH |
- OXYGEN_CHANNEL_AC97,
- .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_volume_min = 0,
+ .dac_volume_max = 255,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -359,21 +282,25 @@
.chip = "CMI8788",
.owner = THIS_MODULE,
.init = meridian_init,
- .control_filter = ak4396_control_filter,
.cleanup = generic_cleanup,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_ak5385_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
- .ac97_switch_hook = cmi9780_switch_hook,
+ .dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ PLAYBACK_2_TO_AC97_1 |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF |
+ CAPTURE_2_FROM_AC97_1,
.dac_channels = 8,
- .used_channels = OXYGEN_CHANNEL_B |
- OXYGEN_CHANNEL_C |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH |
- OXYGEN_CHANNEL_AC97,
- .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_volume_min = 0,
+ .dac_volume_max = 255,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -392,7 +319,7 @@
return -ENOENT;
}
is_meridian = pci_id->driver_data;
- err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian,
+ err = oxygen_pci_probe(pci, index[dev], id[dev],
is_meridian ? &model_meridian : &model_generic);
if (err >= 0)
++dev;
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index ad50fb8..a71c6e0 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -16,6 +16,16 @@
#define PCM_AC97 5
#define PCM_COUNT 6
+/* model-specific configuration of outputs/inputs */
+#define PLAYBACK_0_TO_I2S 0x001
+#define PLAYBACK_1_TO_SPDIF 0x004
+#define PLAYBACK_2_TO_AC97_1 0x008
+#define CAPTURE_0_FROM_I2S_1 0x010
+#define CAPTURE_0_FROM_I2S_2 0x020
+#define CAPTURE_1_FROM_SPDIF 0x080
+#define CAPTURE_2_FROM_I2S_2 0x100
+#define CAPTURE_2_FROM_AC97_1 0x200
+
enum {
CONTROL_SPDIF_PCM,
CONTROL_SPDIF_INPUT_BITS,
@@ -87,12 +97,16 @@
struct snd_pcm_hw_params *params);
void (*update_dac_volume)(struct oxygen *chip);
void (*update_dac_mute)(struct oxygen *chip);
- void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec,
- unsigned int reg, int mute);
void (*gpio_changed)(struct oxygen *chip);
+ void (*ac97_switch)(struct oxygen *chip,
+ unsigned int reg, unsigned int mute);
+ const unsigned int *dac_tlv;
size_t model_data_size;
+ unsigned int pcm_dev_cfg;
u8 dac_channels;
- u8 used_channels;
+ u8 dac_volume_min;
+ u8 dac_volume_max;
+ u8 misc_flags;
u8 function_flags;
u16 dac_i2s_format;
u16 adc_i2s_format;
@@ -100,7 +114,7 @@
/* oxygen_lib.c */
-int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi,
+int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
const struct oxygen_model *model);
void oxygen_pci_remove(struct pci_dev *pci);
@@ -137,6 +151,7 @@
unsigned int index, u16 data, u16 mask);
void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
+void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
static inline void oxygen_set_bits8(struct oxygen *chip,
unsigned int reg, u8 value)
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 74e23ef..5569606 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -190,12 +190,31 @@
--count;
}
- spin_lock_irq(&chip->reg_lock);
oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
if (control & OXYGEN_SPI_DATA_LENGTH_3)
oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
- spin_unlock_irq(&chip->reg_lock);
}
EXPORT_SYMBOL(oxygen_write_spi);
+
+void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
+{
+ unsigned long timeout;
+
+ /* should not need more than about 300 us */
+ timeout = jiffies + msecs_to_jiffies(1);
+ do {
+ if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS)
+ & OXYGEN_2WIRE_BUSY))
+ break;
+ udelay(1);
+ cond_resched();
+ } while (time_after_eq(timeout, jiffies));
+
+ oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
+ oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);
+ oxygen_write8(chip, OXYGEN_2WIRE_CONTROL,
+ device | OXYGEN_2WIRE_DIR_WRITE);
+}
+EXPORT_SYMBOL(oxygen_write_i2c);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 78c2115..897697d 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -221,7 +221,8 @@
chip->dac_routing = 1;
for (i = 0; i < 8; ++i)
- chip->dac_volume[i] = 0xff;
+ chip->dac_volume[i] = chip->model->dac_volume_min;
+ chip->dac_mute = 1;
chip->spdif_playback_enable = 1;
chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
@@ -240,12 +241,12 @@
chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0;
chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0;
- oxygen_set_bits8(chip, OXYGEN_FUNCTION,
- OXYGEN_FUNCTION_RESET_CODEC |
- chip->model->function_flags);
oxygen_write8_masked(chip, OXYGEN_FUNCTION,
- OXYGEN_FUNCTION_SPI,
- OXYGEN_FUNCTION_2WIRE_SPI_MASK);
+ OXYGEN_FUNCTION_RESET_CODEC |
+ chip->model->function_flags,
+ OXYGEN_FUNCTION_RESET_CODEC |
+ OXYGEN_FUNCTION_2WIRE_SPI_MASK |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5);
oxygen_write8(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0);
oxygen_write8(chip, OXYGEN_PLAY_CHANNELS,
@@ -253,11 +254,13 @@
OXYGEN_DMA_A_BURST_8 |
OXYGEN_DMA_MULTICH_BURST_8);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
- oxygen_write8_masked(chip, OXYGEN_MISC, 0,
+ oxygen_write8_masked(chip, OXYGEN_MISC,
+ chip->model->misc_flags,
OXYGEN_MISC_WRITE_PCI_SUBID |
OXYGEN_MISC_REC_C_FROM_SPDIF |
OXYGEN_MISC_REC_B_FROM_AC97 |
- OXYGEN_MISC_REC_A_FROM_MULTICH);
+ OXYGEN_MISC_REC_A_FROM_MULTICH |
+ OXYGEN_MISC_MIDI);
oxygen_write8(chip, OXYGEN_REC_FORMAT,
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) |
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) |
@@ -267,35 +270,49 @@
(OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_RATE_48000 | chip->model->dac_i2s_format |
+ OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
- oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
- oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+ oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+ OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+ OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ else
+ oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+ if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_2_FROM_I2S_2))
+ oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
+ OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+ OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ else
+ oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
- oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
- OXYGEN_SPDIF_SENSE_MASK |
- OXYGEN_SPDIF_LOCK_MASK |
- OXYGEN_SPDIF_RATE_MASK |
- OXYGEN_SPDIF_LOCK_PAR |
- OXYGEN_SPDIF_IN_CLOCK_96,
- OXYGEN_SPDIF_OUT_ENABLE |
- OXYGEN_SPDIF_LOOPBACK |
- OXYGEN_SPDIF_SENSE_MASK |
- OXYGEN_SPDIF_LOCK_MASK |
- OXYGEN_SPDIF_RATE_MASK |
- OXYGEN_SPDIF_SENSE_PAR |
- OXYGEN_SPDIF_LOCK_PAR |
- OXYGEN_SPDIF_IN_CLOCK_MASK);
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+ oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_OUT_ENABLE |
+ OXYGEN_SPDIF_LOOPBACK);
+ if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+ oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK |
+ OXYGEN_SPDIF_LOCK_PAR |
+ OXYGEN_SPDIF_IN_CLOCK_96,
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK |
+ OXYGEN_SPDIF_SENSE_PAR |
+ OXYGEN_SPDIF_LOCK_PAR |
+ OXYGEN_SPDIF_IN_CLOCK_MASK);
+ else
+ oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK);
oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
@@ -318,9 +335,12 @@
(2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) |
(3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT));
- oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
- OXYGEN_AC97_INT_READ_DONE |
- OXYGEN_AC97_INT_WRITE_DONE);
+ if (chip->has_ac97_0 | chip->has_ac97_1)
+ oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
+ OXYGEN_AC97_INT_READ_DONE |
+ OXYGEN_AC97_INT_WRITE_DONE);
+ else
+ oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, 0);
oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0);
oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0);
if (!(chip->has_ac97_0 | chip->has_ac97_1))
@@ -351,6 +371,8 @@
oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000);
oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080);
oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080);
+ oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS,
+ CM9780_GPO0);
/* power down unused ADCs and DACs */
oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN,
AC97_PD_PR0 | AC97_PD_PR1);
@@ -388,10 +410,8 @@
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
spin_unlock_irq(&chip->reg_lock);
- if (chip->irq >= 0) {
+ if (chip->irq >= 0)
free_irq(chip->irq, chip);
- synchronize_irq(chip->irq);
- }
flush_scheduled_work();
chip->model->cleanup(chip);
mutex_destroy(&chip->mutex);
@@ -400,7 +420,7 @@
}
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
- int midi, const struct oxygen_model *model)
+ const struct oxygen_model *model)
{
struct snd_card *card;
struct oxygen *chip;
@@ -472,9 +492,7 @@
if (err < 0)
goto err_card;
- oxygen_write8_masked(chip, OXYGEN_MISC,
- midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI);
- if (midi) {
+ if (model->misc_flags & OXYGEN_MISC_MIDI) {
err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
chip->addr + OXYGEN_MPU401,
MPU401_INFO_INTEGRATED, 0, 0,
@@ -486,7 +504,10 @@
oxygen_proc_init(chip);
spin_lock_irq(&chip->reg_lock);
- chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97;
+ if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+ chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
+ if (chip->has_ac97_0 | chip->has_ac97_1)
+ chip->interrupt_mask |= OXYGEN_INT_AC97;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock);
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index a8e4623..cc0cdda 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -32,8 +32,8 @@
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
info->count = chip->model->dac_channels;
- info->value.integer.min = 0;
- info->value.integer.max = 0xff;
+ info->value.integer.min = chip->model->dac_volume_min;
+ info->value.integer.max = chip->model->dac_volume_max;
return 0;
}
@@ -446,6 +446,50 @@
return changed;
}
+static int monitor_volume_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = 0;
+ info->value.integer.max = 1;
+ return 0;
+}
+
+static int monitor_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u8 bit = ctl->private_value;
+ int invert = ctl->private_value & (1 << 8);
+
+ value->value.integer.value[0] =
+ !!invert ^ !!(oxygen_read8(chip, OXYGEN_ADC_MONITOR) & bit);
+ return 0;
+}
+
+static int monitor_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u8 bit = ctl->private_value;
+ int invert = ctl->private_value & (1 << 8);
+ u8 oldreg, newreg;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ oldreg = oxygen_read8(chip, OXYGEN_ADC_MONITOR);
+ if ((!!value->value.integer.value[0] ^ !!invert) != 0)
+ newreg = oldreg | bit;
+ else
+ newreg = oldreg & ~bit;
+ changed = newreg != oldreg;
+ if (changed)
+ oxygen_write8(chip, OXYGEN_ADC_MONITOR, newreg);
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
+
static int ac97_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
@@ -466,6 +510,21 @@
return 0;
}
+static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
+{
+ unsigned int priv_idx = chip->controls[control]->private_value & 0xff;
+ u16 value;
+
+ value = oxygen_read_ac97(chip, 0, priv_idx);
+ if (!(value & 0x8000)) {
+ oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000);
+ if (chip->model->ac97_switch)
+ chip->model->ac97_switch(chip, priv_idx, 0x8000);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->controls[control]->id);
+ }
+}
+
static int ac97_switch_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
@@ -487,9 +546,24 @@
change = newreg != oldreg;
if (change) {
oxygen_write_ac97(chip, codec, index, newreg);
- if (bitnr == 15 && chip->model->ac97_switch_hook)
- chip->model->ac97_switch_hook(chip, codec, index,
- newreg & 0x8000);
+ if (codec == 0 && chip->model->ac97_switch)
+ chip->model->ac97_switch(chip, index, newreg & 0x8000);
+ if (index == AC97_LINE) {
+ oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
+ newreg & 0x8000 ?
+ CM9780_GPO0 : 0, CM9780_GPO0);
+ if (!(newreg & 0x8000)) {
+ mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH);
+ mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH);
+ mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH);
+ }
+ } else if ((index == AC97_MIC || index == AC97_CD ||
+ index == AC97_VIDEO || index == AC97_AUX) &&
+ bitnr == 15 && !(newreg & 0x8000)) {
+ mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH);
+ oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
+ CM9780_GPO0, CM9780_GPO0);
+ }
}
mutex_unlock(&chip->mutex);
return change;
@@ -608,6 +682,7 @@
.private_value = ((codec) << 24) | (index), \
}
+static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0);
static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
@@ -667,6 +742,9 @@
.get = spdif_pcm_get,
.put = spdif_pcm_put,
},
+};
+
+static const struct snd_kcontrol_new spdif_input_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.device = 1,
@@ -692,11 +770,118 @@
},
};
+static const struct {
+ unsigned int pcm_dev;
+ struct snd_kcontrol_new controls[2];
+} monitor_controls[] = {
+ {
+ .pcm_dev = CAPTURE_0_FROM_I2S_1,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_A,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_A_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+ {
+ .pcm_dev = CAPTURE_0_FROM_I2S_2,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+ {
+ .pcm_dev = CAPTURE_2_FROM_I2S_2,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Switch",
+ .index = 1,
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Volume",
+ .index = 1,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+ {
+ .pcm_dev = CAPTURE_1_FROM_SPDIF,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Input Monitor Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_C,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Input Monitor Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_C_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+};
+
static const struct snd_kcontrol_new ac97_controls[] = {
AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC),
AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
- AC97_VOLUME("Line Capture Volume", 0, AC97_LINE),
AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
AC97_VOLUME("CD Capture Volume", 0, AC97_CD),
AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
@@ -756,6 +941,11 @@
return err;
if (err == 1)
continue;
+ if (!strcmp(template.name, "Master Playback Volume") &&
+ chip->model->dac_tlv) {
+ template.tlv.p = chip->model->dac_tlv;
+ template.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ }
ctl = snd_ctl_new1(&template, chip);
if (!ctl)
return -ENOMEM;
@@ -773,11 +963,26 @@
int oxygen_mixer_init(struct oxygen *chip)
{
+ unsigned int i;
int err;
err = add_controls(chip, controls, ARRAY_SIZE(controls));
if (err < 0)
return err;
+ if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) {
+ err = add_controls(chip, spdif_input_controls,
+ ARRAY_SIZE(spdif_input_controls));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) {
+ if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev))
+ continue;
+ err = add_controls(chip, monitor_controls[i].controls,
+ ARRAY_SIZE(monitor_controls[i].controls));
+ if (err < 0)
+ return err;
+ }
if (chip->has_ac97_0) {
err = add_controls(chip, ac97_controls,
ARRAY_SIZE(ac97_controls));
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index b70046a..b17c405 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -119,7 +119,7 @@
runtime->private_data = (void *)(uintptr_t)channel;
if (channel == PCM_B && chip->has_ac97_1 &&
- (chip->model->used_channels & OXYGEN_CHANNEL_AC97))
+ (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1))
runtime->hw = oxygen_ac97_hardware;
else
runtime->hw = *oxygen_hardware[channel];
@@ -365,7 +365,7 @@
return err;
is_ac97 = chip->has_ac97_1 &&
- (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
+ (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
@@ -640,34 +640,39 @@
int outs, ins;
int err;
- outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */
- ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_B));
- err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
- if (err < 0)
- return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops);
- if (chip->model->used_channels & OXYGEN_CHANNEL_A)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &oxygen_rec_a_ops);
- else if (chip->model->used_channels & OXYGEN_CHANNEL_B)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &oxygen_rec_b_ops);
- pcm->private_data = chip;
- pcm->private_free = oxygen_pcm_free;
- strcpy(pcm->name, "Analog");
- snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 512 * 1024, 2048 * 1024);
- if (ins)
- snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 128 * 1024, 256 * 1024);
+ outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S);
+ ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_0_FROM_I2S_2));
+ if (outs | ins) {
+ err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
+ if (err < 0)
+ return err;
+ if (outs)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &oxygen_multich_ops);
+ if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_a_ops);
+ else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_b_ops);
+ pcm->private_data = chip;
+ pcm->private_free = oxygen_pcm_free;
+ strcpy(pcm->name, "Analog");
+ if (outs)
+ snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 512 * 1024, 2048 * 1024);
+ if (ins)
+ snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 128 * 1024, 256 * 1024);
+ }
- outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF);
- ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C);
+ outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF);
+ ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF);
if (outs | ins) {
err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
if (err < 0)
@@ -686,12 +691,13 @@
128 * 1024, 256 * 1024);
}
- outs = chip->has_ac97_1 &&
- (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
- ins = outs ||
- (chip->model->used_channels & (OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_B))
- == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B);
+ if (chip->has_ac97_1) {
+ outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1);
+ ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+ } else {
+ outs = 0;
+ ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2);
+ }
if (outs | ins) {
err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
2, outs, ins, &pcm);
diff --git a/sound/pci/oxygen/pcm1796.h b/sound/pci/oxygen/pcm1796.h
new file mode 100644
index 0000000..698bf46
--- /dev/null
+++ b/sound/pci/oxygen/pcm1796.h
@@ -0,0 +1,58 @@
+#ifndef PCM1796_H_INCLUDED
+#define PCM1796_H_INCLUDED
+
+/* register 16 */
+#define PCM1796_ATL_MASK 0xff
+/* register 17 */
+#define PCM1796_ATR_MASK 0xff
+/* register 18 */
+#define PCM1796_MUTE 0x01
+#define PCM1796_DME 0x02
+#define PCM1796_DMF_MASK 0x0c
+#define PCM1796_DMF_DISABLED 0x00
+#define PCM1796_DMF_48 0x04
+#define PCM1796_DMF_441 0x08
+#define PCM1796_DMF_32 0x0c
+#define PCM1796_FMT_MASK 0x70
+#define PCM1796_FMT_16_RJUST 0x00
+#define PCM1796_FMT_20_RJUST 0x10
+#define PCM1796_FMT_24_RJUST 0x20
+#define PCM1796_FMT_24_LJUST 0x30
+#define PCM1796_FMT_16_I2S 0x40
+#define PCM1796_FMT_24_I2S 0x50
+#define PCM1796_ATLD 0x80
+/* register 19 */
+#define PCM1796_INZD 0x01
+#define PCM1796_FLT_MASK 0x02
+#define PCM1796_FLT_SHARP 0x00
+#define PCM1796_FLT_SLOW 0x02
+#define PCM1796_DFMS 0x04
+#define PCM1796_OPE 0x10
+#define PCM1796_ATS_MASK 0x60
+#define PCM1796_ATS_1 0x00
+#define PCM1796_ATS_2 0x20
+#define PCM1796_ATS_4 0x40
+#define PCM1796_ATS_8 0x60
+#define PCM1796_REV 0x80
+/* register 20 */
+#define PCM1796_OS_MASK 0x03
+#define PCM1796_OS_64 0x00
+#define PCM1796_OS_32 0x01
+#define PCM1796_OS_128 0x02
+#define PCM1796_CHSL_MASK 0x04
+#define PCM1796_CHSL_LEFT 0x00
+#define PCM1796_CHSL_RIGHT 0x04
+#define PCM1796_MONO 0x08
+#define PCM1796_DFTH 0x10
+#define PCM1796_DSD 0x20
+#define PCM1796_SRST 0x40
+/* register 21 */
+#define PCM1796_PCMZ 0x01
+#define PCM1796_DZ_MASK 0x06
+/* register 22 */
+#define PCM1796_ZFGL 0x01
+#define PCM1796_ZFGR 0x02
+/* register 23 */
+#define PCM1796_ID_MASK 0x1f
+
+#endif
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index d163397..7f84fa5 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -18,6 +18,9 @@
*/
/*
+ * Xonar D2/D2X
+ * ------------
+ *
* CMI8788:
*
* SPI 0 -> 1st PCM1796 (front)
@@ -30,10 +33,33 @@
* GPIO 5 <- external power present (D2X only)
* GPIO 7 -> ALT
* GPIO 8 -> enable output to speakers
+ */
+
+/*
+ * Xonar DX
+ * --------
*
- * CM9780:
+ * CMI8788:
*
- * GPIO 0 -> enable AC'97 bypass (line in -> ADC)
+ * I²C <-> CS4398 (front)
+ * <-> CS4362A (surround, center/LFE, back)
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> enable front panel I/O
+ * GPIO 2 -> M0 of CS5361
+ * GPIO 3 -> M1 of CS5361
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * CS4398:
+ *
+ * AD0 <- 1
+ * AD1 <- 1
+ *
+ * CS4362A:
+ *
+ * AD0 <- 0
*/
#include <linux/pci.h>
@@ -47,11 +73,14 @@
#include <sound/tlv.h>
#include "oxygen.h"
#include "cm9780.h"
+#include "pcm1796.h"
+#include "cs4398.h"
+#include "cs4362a.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_DESCRIPTION("Asus AV200 driver");
+MODULE_DESCRIPTION("Asus AVx00 driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}");
+MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -64,80 +93,44 @@
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
+enum {
+ MODEL_D2,
+ MODEL_D2X,
+ MODEL_DX,
+};
+
static struct pci_device_id xonar_ids[] __devinitdata = {
- { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */
- { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */
+ { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
+ { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
{ }
};
MODULE_DEVICE_TABLE(pci, xonar_ids);
-#define GPIO_CS5381_M_MASK 0x000c
-#define GPIO_CS5381_M_SINGLE 0x0000
-#define GPIO_CS5381_M_DOUBLE 0x0004
-#define GPIO_CS5381_M_QUAD 0x0008
-#define GPIO_EXT_POWER 0x0020
-#define GPIO_ALT 0x0080
-#define GPIO_OUTPUT_ENABLE 0x0100
+#define GPIO_CS53x1_M_MASK 0x000c
+#define GPIO_CS53x1_M_SINGLE 0x0000
+#define GPIO_CS53x1_M_DOUBLE 0x0004
+#define GPIO_CS53x1_M_QUAD 0x0008
-#define GPIO_LINE_MUTE CM9780_GPO0
+#define GPIO_D2X_EXT_POWER 0x0020
+#define GPIO_D2_ALT 0x0080
+#define GPIO_D2_OUTPUT_ENABLE 0x0100
-/* register 16 */
-#define PCM1796_ATL_MASK 0xff
-/* register 17 */
-#define PCM1796_ATR_MASK 0xff
-/* register 18 */
-#define PCM1796_MUTE 0x01
-#define PCM1796_DME 0x02
-#define PCM1796_DMF_MASK 0x0c
-#define PCM1796_DMF_DISABLED 0x00
-#define PCM1796_DMF_48 0x04
-#define PCM1796_DMF_441 0x08
-#define PCM1796_DMF_32 0x0c
-#define PCM1796_FMT_MASK 0x70
-#define PCM1796_FMT_16_RJUST 0x00
-#define PCM1796_FMT_20_RJUST 0x10
-#define PCM1796_FMT_24_RJUST 0x20
-#define PCM1796_FMT_24_LJUST 0x30
-#define PCM1796_FMT_16_I2S 0x40
-#define PCM1796_FMT_24_I2S 0x50
-#define PCM1796_ATLD 0x80
-/* register 19 */
-#define PCM1796_INZD 0x01
-#define PCM1796_FLT_MASK 0x02
-#define PCM1796_FLT_SHARP 0x00
-#define PCM1796_FLT_SLOW 0x02
-#define PCM1796_DFMS 0x04
-#define PCM1796_OPE 0x10
-#define PCM1796_ATS_MASK 0x60
-#define PCM1796_ATS_1 0x00
-#define PCM1796_ATS_2 0x20
-#define PCM1796_ATS_4 0x40
-#define PCM1796_ATS_8 0x60
-#define PCM1796_REV 0x80
-/* register 20 */
-#define PCM1796_OS_MASK 0x03
-#define PCM1796_OS_64 0x00
-#define PCM1796_OS_32 0x01
-#define PCM1796_OS_128 0x02
-#define PCM1796_CHSL_MASK 0x04
-#define PCM1796_CHSL_LEFT 0x00
-#define PCM1796_CHSL_RIGHT 0x04
-#define PCM1796_MONO 0x08
-#define PCM1796_DFTH 0x10
-#define PCM1796_DSD 0x20
-#define PCM1796_SRST 0x40
-/* register 21 */
-#define PCM1796_PCMZ 0x01
-#define PCM1796_DZ_MASK 0x06
-/* register 22 */
-#define PCM1796_ZFGL 0x01
-#define PCM1796_ZFGR 0x02
-/* register 23 */
-#define PCM1796_ID_MASK 0x1f
+#define GPI_DX_EXT_POWER 0x01
+#define GPIO_DX_OUTPUT_ENABLE 0x0001
+#define GPIO_DX_FRONT_PANEL 0x0002
+#define GPIO_DX_INPUT_ROUTE 0x0100
+
+#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
+#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
struct xonar_data {
- u8 is_d2x;
+ unsigned int anti_pop_delay;
+ u16 output_enable_bit;
+ u8 ext_power_reg;
+ u8 ext_power_int_reg;
+ u8 ext_power_bit;
u8 has_power;
};
@@ -156,62 +149,157 @@
(reg << 8) | value);
}
-static void xonar_init(struct oxygen *chip)
+static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
+}
+
+static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
+}
+
+static void xonar_common_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ if (data->ext_power_reg) {
+ oxygen_set_bits8(chip, data->ext_power_int_reg,
+ data->ext_power_bit);
+ chip->interrupt_mask |= OXYGEN_INT_GPIO;
+ data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+ & data->ext_power_bit);
+ }
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
+ oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
+ msleep(data->anti_pop_delay);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+static void xonar_d2_init(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
unsigned int i;
- data->is_d2x = chip->pci->subsystem_device == 0x82b7;
+ data->anti_pop_delay = 300;
+ data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
for (i = 0; i < 4; ++i) {
- pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD);
+ pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED |
+ PCM1796_FMT_24_LJUST | PCM1796_ATLD);
pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
pcm1796_write(chip, i, 20, PCM1796_OS_64);
pcm1796_write(chip, i, 21, 0);
- pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */
- pcm1796_write(chip, i, 17, 0xff);
+ pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */
+ pcm1796_write(chip, i, 17, 0x0f);
}
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_CS5381_M_MASK | GPIO_ALT);
- oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
- GPIO_CS5381_M_SINGLE,
- GPIO_CS5381_M_MASK | GPIO_ALT);
- if (data->is_d2x) {
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_EXT_POWER);
- oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK,
- GPIO_EXT_POWER);
- chip->interrupt_mask |= OXYGEN_INT_GPIO;
- data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
- & GPIO_EXT_POWER);
- }
- oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
- oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
- msleep(300);
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE);
- oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
+
+ xonar_common_init(chip);
snd_component_add(chip->card, "PCM1796");
snd_component_add(chip->card, "CS5381");
}
+static void xonar_d2x_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ data->ext_power_reg = OXYGEN_GPIO_DATA;
+ data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+ data->ext_power_bit = GPIO_D2X_EXT_POWER;
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
+ xonar_d2_init(chip);
+}
+
+static void xonar_dx_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ data->anti_pop_delay = 800;
+ data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
+ data->ext_power_reg = OXYGEN_GPI_DATA;
+ data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->ext_power_bit = GPI_DX_EXT_POWER;
+
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+
+ /* set CPEN (control port mode) and power down */
+ cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
+ cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+ /* configure */
+ cs4398_write(chip, 2, CS4398_FM_SINGLE |
+ CS4398_DEM_NONE | CS4398_DIF_LJUST);
+ cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
+ cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE);
+ cs4398_write(chip, 5, 0xfe);
+ cs4398_write(chip, 6, 0xfe);
+ cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
+ CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
+ cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
+ cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
+ CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
+ cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
+ cs4362a_write(chip, 0x05, 0);
+ cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
+ cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
+ cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
+ cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE);
+ /* clear power down */
+ cs4398_write(chip, 8, CS4398_CPEN);
+ cs4362a_write(chip, 0x01, CS4362A_CPEN);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+ GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
+
+ xonar_common_init(chip);
+
+ snd_component_add(chip->card, "CS4398");
+ snd_component_add(chip->card, "CS4362A");
+ snd_component_add(chip->card, "CS5361");
+}
+
static void xonar_cleanup(struct oxygen *chip)
{
- oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+ struct xonar_data *data = chip->model_data;
+
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+static void xonar_dx_cleanup(struct oxygen *chip)
+{
+ xonar_cleanup(chip);
+ cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+ oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
}
static void set_pcm1796_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
-#if 0
unsigned int i;
u8 value;
value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
for (i = 0; i < 4; ++i)
pcm1796_write(chip, i, 20, value);
-#endif
}
static void update_pcm1796_volume(struct oxygen *chip)
@@ -236,19 +324,73 @@
pcm1796_write(chip, i, 18, value);
}
-static void set_cs5381_params(struct oxygen *chip,
+static void set_cs53x1_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
unsigned int value;
if (params_rate(params) <= 54000)
- value = GPIO_CS5381_M_SINGLE;
+ value = GPIO_CS53x1_M_SINGLE;
else if (params_rate(params) <= 108000)
- value = GPIO_CS5381_M_DOUBLE;
+ value = GPIO_CS53x1_M_DOUBLE;
else
- value = GPIO_CS5381_M_QUAD;
+ value = GPIO_CS53x1_M_QUAD;
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
- value, GPIO_CS5381_M_MASK);
+ value, GPIO_CS53x1_M_MASK);
+}
+
+static void set_cs43xx_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ u8 fm_cs4398, fm_cs4362a;
+
+ fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST;
+ fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+ if (params_rate(params) <= 50000) {
+ fm_cs4398 |= CS4398_FM_SINGLE;
+ fm_cs4362a |= CS4362A_FM_SINGLE;
+ } else if (params_rate(params) <= 100000) {
+ fm_cs4398 |= CS4398_FM_DOUBLE;
+ fm_cs4362a |= CS4362A_FM_DOUBLE;
+ } else {
+ fm_cs4398 |= CS4398_FM_QUAD;
+ fm_cs4362a |= CS4362A_FM_QUAD;
+ }
+ cs4398_write(chip, 2, fm_cs4398);
+ cs4362a_write(chip, 0x06, fm_cs4362a);
+ cs4362a_write(chip, 0x09, fm_cs4362a);
+ cs4362a_write(chip, 0x0c, fm_cs4362a);
+}
+
+static void update_cs4362a_volumes(struct oxygen *chip)
+{
+ u8 mute;
+
+ mute = chip->dac_mute ? CS4362A_MUTE : 0;
+ cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
+ cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
+ cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
+ cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
+ cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
+ cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
+}
+
+static void update_cs43xx_volume(struct oxygen *chip)
+{
+ cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
+ cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
+ update_cs4362a_volumes(chip);
+}
+
+static void update_cs43xx_mute(struct oxygen *chip)
+{
+ u8 reg;
+
+ reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
+ if (chip->dac_mute)
+ reg |= CS4398_MUTE_B | CS4398_MUTE_A;
+ cs4398_write(chip, 4, reg);
+ update_cs4362a_volumes(chip);
}
static void xonar_gpio_changed(struct oxygen *chip)
@@ -256,10 +398,8 @@
struct xonar_data *data = chip->model_data;
u8 has_power;
- if (!data->is_d2x)
- return;
- has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
- & GPIO_EXT_POWER);
+ has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+ & data->ext_power_bit);
if (has_power != data->has_power) {
data->has_power = has_power;
if (has_power) {
@@ -272,66 +412,13 @@
}
}
-static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
-{
- unsigned int index = chip->controls[control]->private_value & 0xff;
- u16 value;
-
- value = oxygen_read_ac97(chip, 0, index);
- if (!(value & 0x8000)) {
- oxygen_write_ac97(chip, 0, index, value | 0x8000);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->controls[control]->id);
- }
-}
-
-static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec,
- unsigned int reg, int mute)
-{
- if (codec != 0)
- return;
- /* line-in is exclusive */
- switch (reg) {
- case AC97_LINE:
- oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
- mute ? GPIO_LINE_MUTE : 0,
- GPIO_LINE_MUTE);
- if (!mute) {
- mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH);
- mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH);
- mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH);
- }
- break;
- case AC97_MIC:
- case AC97_CD:
- case AC97_VIDEO:
- case AC97_AUX:
- if (!mute) {
- oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
- GPIO_LINE_MUTE);
- mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH);
- }
- break;
- }
-}
-
-static int pcm1796_volume_info(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_info *info)
-{
- info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- info->count = 8;
- info->value.integer.min = 0x0f;
- info->value.integer.max = 0xff;
- return 0;
-}
-
static int alt_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
value->value.integer.value[0] =
- !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT);
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT);
return 0;
}
@@ -345,9 +432,9 @@
spin_lock_irq(&chip->reg_lock);
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
if (value->value.integer.value[0])
- new_bits = old_bits | GPIO_ALT;
+ new_bits = old_bits | GPIO_D2_ALT;
else
- new_bits = old_bits & ~GPIO_ALT;
+ new_bits = old_bits & ~GPIO_D2_ALT;
changed = new_bits != old_bits;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
@@ -363,20 +450,68 @@
.put = alt_switch_put,
};
-static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0);
-
-static int xonar_control_filter(struct snd_kcontrol_new *template)
+static int front_panel_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
{
- if (!strcmp(template->name, "Master Playback Volume")) {
- template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- template->info = pcm1796_volume_info,
- template->tlv.p = pcm1796_db_scale;
- } else if (!strncmp(template->name, "CD Capture ", 11)) {
+ struct oxygen *chip = ctl->private_data;
+
+ value->value.integer.value[0] =
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DX_FRONT_PANEL);
+ return 0;
+}
+
+static int front_panel_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 old_reg, new_reg;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ if (value->value.integer.value[0])
+ new_reg = old_reg | GPIO_DX_FRONT_PANEL;
+ else
+ new_reg = old_reg & ~GPIO_DX_FRONT_PANEL;
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
+ spin_unlock_irq(&chip->reg_lock);
+ return old_reg != new_reg;
+}
+
+static const struct snd_kcontrol_new front_panel_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Panel Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = front_panel_get,
+ .put = front_panel_put,
+};
+
+static void xonar_dx_ac97_switch(struct oxygen *chip,
+ unsigned int reg, unsigned int mute)
+{
+ if (reg == AC97_LINE) {
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ mute ? GPIO_DX_INPUT_ROUTE : 0,
+ GPIO_DX_INPUT_ROUTE);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0);
+static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0);
+
+static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "CD Capture ", 11))
/* CD in is actually connected to the video in pin */
template->private_value ^= AC97_CD ^ AC97_VIDEO;
- } else if (!strcmp(template->name, "Line Capture Volume")) {
- return 1; /* line-in bypasses the AC'97 mixer */
- }
+ return 0;
+}
+
+static int xonar_dx_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "CD Capture ", 11))
+ return 1; /* no CD input */
return 0;
}
@@ -385,30 +520,96 @@
return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
}
-static const struct oxygen_model model_xonar = {
- .shortname = "Asus AV200",
- .longname = "Asus Virtuoso 200",
- .chip = "AV200",
- .owner = THIS_MODULE,
- .init = xonar_init,
- .control_filter = xonar_control_filter,
- .mixer_init = xonar_mixer_init,
- .cleanup = xonar_cleanup,
- .set_dac_params = set_pcm1796_params,
- .set_adc_params = set_cs5381_params,
- .update_dac_volume = update_pcm1796_volume,
- .update_dac_mute = update_pcm1796_mute,
- .ac97_switch_hook = xonar_ac97_switch_hook,
- .gpio_changed = xonar_gpio_changed,
- .model_data_size = sizeof(struct xonar_data),
- .dac_channels = 8,
- .used_channels = OXYGEN_CHANNEL_B |
- OXYGEN_CHANNEL_C |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH,
- .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+static int xonar_dx_mixer_init(struct oxygen *chip)
+{
+ return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
+}
+
+static const struct oxygen_model xonar_models[] = {
+ [MODEL_D2] = {
+ .shortname = "Xonar D2",
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .init = xonar_d2_init,
+ .control_filter = xonar_d2_control_filter,
+ .mixer_init = xonar_mixer_init,
+ .cleanup = xonar_cleanup,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels = 8,
+ .dac_volume_min = 0x0f,
+ .dac_volume_max = 0xff,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ },
+ [MODEL_D2X] = {
+ .shortname = "Xonar D2X",
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .init = xonar_d2x_init,
+ .control_filter = xonar_d2_control_filter,
+ .mixer_init = xonar_mixer_init,
+ .cleanup = xonar_cleanup,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .gpio_changed = xonar_gpio_changed,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels = 8,
+ .dac_volume_min = 0x0f,
+ .dac_volume_max = 0xff,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ },
+ [MODEL_DX] = {
+ .shortname = "Xonar DX",
+ .longname = "Asus Virtuoso 100",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .init = xonar_dx_init,
+ .control_filter = xonar_dx_control_filter,
+ .mixer_init = xonar_dx_mixer_init,
+ .cleanup = xonar_dx_cleanup,
+ .set_dac_params = set_cs43xx_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_cs43xx_volume,
+ .update_dac_mute = update_cs43xx_mute,
+ .gpio_changed = xonar_gpio_changed,
+ .ac97_switch = xonar_dx_ac97_switch,
+ .dac_tlv = cs4362a_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2,
+ .dac_channels = 8,
+ .dac_volume_min = 0,
+ .dac_volume_max = 127,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ },
};
static int __devinit xonar_probe(struct pci_dev *pci,
@@ -423,7 +624,8 @@
++dev;
return -ENOENT;
}
- err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar);
+ err = oxygen_pci_probe(pci, index[dev], id[dev],
+ &xonar_models[pci_id->driver_data]);
if (err >= 0)
++dev;
return err;
diff --git a/sound/pci/oxygen/wm8785.h b/sound/pci/oxygen/wm8785.h
new file mode 100644
index 0000000..8c23e31
--- /dev/null
+++ b/sound/pci/oxygen/wm8785.h
@@ -0,0 +1,45 @@
+#ifndef WM8785_H_INCLUDED
+#define WM8785_H_INCLUDED
+
+#define WM8785_R0 0
+#define WM8785_R1 1
+#define WM8785_R2 2
+#define WM8785_R7 7
+
+/* R0 */
+#define WM8785_MCR_MASK 0x007
+#define WM8785_MCR_SLAVE 0x000
+#define WM8785_MCR_MASTER_128 0x001
+#define WM8785_MCR_MASTER_192 0x002
+#define WM8785_MCR_MASTER_256 0x003
+#define WM8785_MCR_MASTER_384 0x004
+#define WM8785_MCR_MASTER_512 0x005
+#define WM8785_MCR_MASTER_768 0x006
+#define WM8785_OSR_MASK 0x018
+#define WM8785_OSR_SINGLE 0x000
+#define WM8785_OSR_DOUBLE 0x008
+#define WM8785_OSR_QUAD 0x010
+#define WM8785_FORMAT_MASK 0x060
+#define WM8785_FORMAT_RJUST 0x000
+#define WM8785_FORMAT_LJUST 0x020
+#define WM8785_FORMAT_I2S 0x040
+#define WM8785_FORMAT_DSP 0x060
+/* R1 */
+#define WM8785_WL_MASK 0x003
+#define WM8785_WL_16 0x000
+#define WM8785_WL_20 0x001
+#define WM8785_WL_24 0x002
+#define WM8785_WL_32 0x003
+#define WM8785_LRP 0x004
+#define WM8785_BCLKINV 0x008
+#define WM8785_LRSWAP 0x010
+#define WM8785_DEVNO_MASK 0x0e0
+/* R2 */
+#define WM8785_HPFR 0x001
+#define WM8785_HPFL 0x002
+#define WM8785_SDODIS 0x004
+#define WM8785_PWRDNR 0x008
+#define WM8785_PWRDNL 0x010
+#define WM8785_TDM_MASK 0x1c0
+
+#endif
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 9d5bb76..7fdcdc8 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -458,7 +458,7 @@
snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n",
is_capture ? 'c' : 'p',
- chip->chip_idx, (void*)subs->runtime->dma_addr,
+ chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
subs->runtime->dma_bytes, subs->number);
pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
@@ -626,7 +626,7 @@
#ifdef CONFIG_SND_DEBUG_DETECT
do_gettimeofday(&my_tv2);
snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n",
- my_tv2.tv_usec - my_tv1.tv_usec, err);
+ (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
#endif
}
@@ -846,7 +846,6 @@
struct pcxhr_mgr *mgr = chip->mgr;
struct snd_pcm_runtime *runtime = subs->runtime;
struct pcxhr_stream *stream;
- int is_capture;
mutex_lock(&mgr->setup_mutex);
@@ -856,12 +855,10 @@
if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
snd_printdd("pcxhr_open playback chip%d subs%d\n",
chip->chip_idx, subs->number);
- is_capture = 0;
stream = &chip->playback_stream[subs->number];
} else {
snd_printdd("pcxhr_open capture chip%d subs%d\n",
chip->chip_idx, subs->number);
- is_capture = 1;
if (mgr->mono_capture)
runtime->hw.channels_max = 1;
else
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index c4e415d..78aa81f 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -897,7 +897,7 @@
#ifdef CONFIG_SND_DEBUG_DETECT
do_gettimeofday(&my_tv2);
snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n",
- my_tv2.tv_usec - my_tv1.tv_usec, err);
+ (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
#endif
return 0;
}
@@ -1005,30 +1005,37 @@
int nb_stream = (prmh->stat[i] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD;
int pipe = prmh->stat[i] & MASK_FIRST_FIELD;
int is_capture = prmh->stat[i] & 0x400000;
- u32 err;
+ u32 err2;
if (prmh->stat[i] & 0x800000) { /* if BIT_END */
snd_printdd("TASKLET : End%sPipe %d\n",
is_capture ? "Record" : "Play", pipe);
}
i++;
- err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
- if (err)
- pcxhr_handle_async_err(mgr, err, PCXHR_ERR_PIPE,
+ err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
+ if (err2)
+ pcxhr_handle_async_err(mgr, err2,
+ PCXHR_ERR_PIPE,
pipe, is_capture);
i += 2;
for (j = 0; j < nb_stream; j++) {
- err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
- if (err)
- pcxhr_handle_async_err(mgr, err, PCXHR_ERR_STREAM,
- pipe, is_capture);
+ err2 = prmh->stat[i] ?
+ prmh->stat[i] : prmh->stat[i+1];
+ if (err2)
+ pcxhr_handle_async_err(mgr, err2,
+ PCXHR_ERR_STREAM,
+ pipe,
+ is_capture);
i += 2;
}
for (j = 0; j < nb_audio; j++) {
- err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
- if (err)
- pcxhr_handle_async_err(mgr, err, PCXHR_ERR_AUDIO,
- pipe, is_capture);
+ err2 = prmh->stat[i] ?
+ prmh->stat[i] : prmh->stat[i+1];
+ if (err2)
+ pcxhr_handle_async_err(mgr, err2,
+ PCXHR_ERR_AUDIO,
+ pipe,
+ is_capture);
i += 2;
}
}
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 9408b1e..979f7da 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -1630,14 +1630,14 @@
struct snd_riptide *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct pcmhw *data;
- int index = substream->number;
+ int sub_num = substream->number;
- chip->playback_substream[index] = substream;
+ chip->playback_substream[sub_num] = substream;
runtime->hw = snd_riptide_playback;
data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL);
- data->paths = lbus_play_paths[index];
- data->id = play_ids[index];
- data->source = play_sources[index];
+ data->paths = lbus_play_paths[sub_num];
+ data->id = play_ids[sub_num];
+ data->source = play_sources[sub_num];
data->intdec[0] = 0xff;
data->intdec[1] = 0xff;
data->state = ST_STOP;
@@ -1670,10 +1670,10 @@
{
struct snd_riptide *chip = snd_pcm_substream_chip(substream);
struct pcmhw *data = get_pcmhwdev(substream);
- int index = substream->number;
+ int sub_num = substream->number;
substream->runtime->private_data = NULL;
- chip->playback_substream[index] = NULL;
+ chip->playback_substream[sub_num] = NULL;
kfree(data);
return 0;
}
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index df184aa..e7ef3a1 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1350,7 +1350,8 @@
return err;
rme32->port = pci_resource_start(rme32->pci, 0);
- if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) {
+ rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE);
+ if (!rme32->iobase) {
snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n",
rme32->port, rme32->port + RME32_IO_SIZE - 1);
return -ENOMEM;
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index fb0a4ee8..3fdd488 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1559,7 +1559,8 @@
return err;
rme96->port = pci_resource_start(rme96->pci, 0);
- if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) {
+ rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE);
+ if (!rme96->iobase) {
snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
return -ENOMEM;
}
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 1be84f2..4d6fbb3 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -318,6 +318,10 @@
#define HDSP_midi1IRQPending (1<<31)
#define HDSP_spdifFrequencyMask (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2)
+#define HDSP_spdifFrequencyMask_9632 (HDSP_spdifFrequency0|\
+ HDSP_spdifFrequency1|\
+ HDSP_spdifFrequency2|\
+ HDSP_spdifFrequency3)
#define HDSP_spdifFrequency32KHz (HDSP_spdifFrequency0)
#define HDSP_spdifFrequency44_1KHz (HDSP_spdifFrequency1)
@@ -328,7 +332,9 @@
#define HDSP_spdifFrequency96KHz (HDSP_spdifFrequency2|HDSP_spdifFrequency1)
/* This is for H9632 cards */
-#define HDSP_spdifFrequency128KHz HDSP_spdifFrequencyMask
+#define HDSP_spdifFrequency128KHz (HDSP_spdifFrequency0|\
+ HDSP_spdifFrequency1|\
+ HDSP_spdifFrequency2)
#define HDSP_spdifFrequency176_4KHz HDSP_spdifFrequency3
#define HDSP_spdifFrequency192KHz (HDSP_spdifFrequency3|HDSP_spdifFrequency0)
@@ -885,28 +891,15 @@
return ret;
}
-static int hdsp_external_sample_rate (struct hdsp *hdsp)
-{
- unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register);
- unsigned int rate_bits = status2 & HDSP_systemFrequencyMask;
-
- switch (rate_bits) {
- case HDSP_systemFrequency32: return 32000;
- case HDSP_systemFrequency44_1: return 44100;
- case HDSP_systemFrequency48: return 48000;
- case HDSP_systemFrequency64: return 64000;
- case HDSP_systemFrequency88_2: return 88200;
- case HDSP_systemFrequency96: return 96000;
- default:
- return 0;
- }
-}
-
static int hdsp_spdif_sample_rate(struct hdsp *hdsp)
{
unsigned int status = hdsp_read(hdsp, HDSP_statusRegister);
unsigned int rate_bits = (status & HDSP_spdifFrequencyMask);
+ /* For the 9632, the mask is different */
+ if (hdsp->io_type == H9632)
+ rate_bits = (status & HDSP_spdifFrequencyMask_9632);
+
if (status & HDSP_SPDIFErrorFlag)
return 0;
@@ -933,6 +926,31 @@
return 0;
}
+static int hdsp_external_sample_rate(struct hdsp *hdsp)
+{
+ unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register);
+ unsigned int rate_bits = status2 & HDSP_systemFrequencyMask;
+
+ /* For the 9632 card, there seems to be no bit for indicating external
+ * sample rate greater than 96kHz. The card reports the corresponding
+ * single speed. So the best means seems to get spdif rate when
+ * autosync reference is spdif */
+ if (hdsp->io_type == H9632 &&
+ hdsp_autosync_ref(hdsp) == HDSP_AUTOSYNC_FROM_SPDIF)
+ return hdsp_spdif_sample_rate(hdsp);
+
+ switch (rate_bits) {
+ case HDSP_systemFrequency32: return 32000;
+ case HDSP_systemFrequency44_1: return 44100;
+ case HDSP_systemFrequency48: return 48000;
+ case HDSP_systemFrequency64: return 64000;
+ case HDSP_systemFrequency88_2: return 88200;
+ case HDSP_systemFrequency96: return 96000;
+ default:
+ return 0;
+ }
+}
+
static void hdsp_compute_period_size(struct hdsp *hdsp)
{
hdsp->period_bytes = 1 << ((hdsp_decode_latency(hdsp->control_register) + 8));
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 9a19ae6..ab423bc 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -540,7 +540,8 @@
static inline int HDSPM_bit2freq(int n)
{
- static int bit2freq_tab[] = { 0, 32000, 44100, 48000, 64000, 88200,
+ static const int bit2freq_tab[] = {
+ 0, 32000, 44100, 48000, 64000, 88200,
96000, 128000, 176400, 192000 };
if (n < 1 || n > 9)
return 0;
@@ -582,7 +583,7 @@
return hdspm->mixer->ch[chan].pb[pb];
}
-static inline int hdspm_write_in_gain(struct hdspm * hdspm, unsigned int chan,
+static int hdspm_write_in_gain(struct hdspm *hdspm, unsigned int chan,
unsigned int in, unsigned short data)
{
if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
@@ -595,7 +596,7 @@
return 0;
}
-static inline int hdspm_write_pb_gain(struct hdspm * hdspm, unsigned int chan,
+static int hdspm_write_pb_gain(struct hdspm *hdspm, unsigned int chan,
unsigned int pb, unsigned short data)
{
if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
@@ -621,7 +622,7 @@
}
/* check if same process is writing and reading */
-static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm)
+static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
{
unsigned long flags;
int ret = 1;
@@ -636,7 +637,7 @@
}
/* check for external sample rate */
-static inline int hdspm_external_sample_rate(struct hdspm * hdspm)
+static int hdspm_external_sample_rate(struct hdspm *hdspm)
{
if (hdspm->is_aes32) {
unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
@@ -787,7 +788,7 @@
}
/* should I silence all or only opened ones ? doit all for first even is 4MB*/
-static inline void hdspm_silence_playback(struct hdspm * hdspm)
+static void hdspm_silence_playback(struct hdspm *hdspm)
{
int i;
int n = hdspm->period_bytes;
@@ -1028,9 +1029,9 @@
{
/* the hardware already does the relevant bit-mask with 0xff */
if (id)
- return hdspm_write(hdspm, HDSPM_midiDataOut1, val);
+ hdspm_write(hdspm, HDSPM_midiDataOut1, val);
else
- return hdspm_write(hdspm, HDSPM_midiDataOut0, val);
+ hdspm_write(hdspm, HDSPM_midiDataOut0, val);
}
static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id)
@@ -1057,7 +1058,7 @@
return 0;
}
-static inline void snd_hdspm_flush_midi_input (struct hdspm *hdspm, int id)
+static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id)
{
while (snd_hdspm_midi_input_available (hdspm, id))
snd_hdspm_midi_read_byte (hdspm, id);
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 742f118..df2007e 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1194,7 +1194,6 @@
/* snd_pcm_suspend_all() stopped all channels, so we're quiescent.
*/
if (sis->irq >= 0) {
- synchronize_irq(sis->irq);
free_irq(sis->irq, sis);
sis->irq = -1;
}
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index 71138ff..bbcee2c 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -3676,6 +3676,8 @@
else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
}
+ if (trident->irq >= 0)
+ free_irq(trident->irq, trident);
if (trident->tlb.buffer.area) {
outl(0, TRID_REG(trident, NX_TLBC));
if (trident->tlb.memhdr)
@@ -3685,8 +3687,6 @@
vfree(trident->tlb.shadow_entries);
snd_dma_free_pages(&trident->tlb.buffer);
}
- if (trident->irq >= 0)
- free_irq(trident->irq, trident);
pci_release_regions(trident->pci);
pci_disable_device(trident->pci);
kfree(trident);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index a756be6..b585cc3 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -2236,7 +2236,7 @@
/* disable interrupts */
for (i = 0; i < chip->num_devs; i++)
snd_via82xx_channel_reset(chip, &chip->devs[i]);
- synchronize_irq(chip->irq);
+
if (chip->irq >= 0)
free_irq(chip->irq, chip);
__end_hw:
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index f5df1c7..31f64ee 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -1075,7 +1075,7 @@
/* disable interrupts */
for (i = 0; i < chip->num_devs; i++)
snd_via82xx_channel_reset(chip, &chip->devs[i]);
- synchronize_irq(chip->irq);
+
__end_hw:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 42c1eb7d..29b3056 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -2249,6 +2249,8 @@
#ifdef CONFIG_PM
vfree(chip->saved_regs);
#endif
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
release_and_free_resource(chip->mpu_res);
release_and_free_resource(chip->fm_res);
snd_ymfpci_free_gameport(chip);
@@ -2257,8 +2259,6 @@
if (chip->work_ptr.area)
snd_dma_free_pages(&chip->work_ptr);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
release_and_free_resource(chip->res_reg_area);
pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 8441e78..566a6d0 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -141,7 +141,7 @@
uinfo->value.integer.max = 15;
return 0;
}
-
+
static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -267,7 +267,8 @@
static void awacs_set_cuda(int reg, int val)
{
struct adb_request req;
- cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val);
+ cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a,
+ reg, val);
while (! req.complete)
cuda_poll();
}
@@ -289,11 +290,11 @@
/*
* vol = 0 - 31 (attenuation), 32 = mute bit, stereo
*/
-static int awacs_amp_set_vol(struct awacs_amp *amp, int index, int lvol, int rvol,
- int do_check)
+static int awacs_amp_set_vol(struct awacs_amp *amp, int index,
+ int lvol, int rvol, int do_check)
{
if (do_check && amp->amp_vol[index][0] == lvol &&
- amp->amp_vol[index][1] == rvol)
+ amp->amp_vol[index][1] == rvol)
return 0;
awacs_set_cuda(3 + index, lvol);
awacs_set_cuda(5 + index, rvol);
@@ -337,7 +338,7 @@
uinfo->value.integer.max = 31;
return 0;
}
-
+
static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -361,8 +362,10 @@
snd_assert(amp, return -EINVAL);
snd_assert(index >= 0 && index <= 1, return -EINVAL);
- vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32);
- vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32);
+ vol[0] = (31 - (ucontrol->value.integer.value[0] & 31))
+ | (amp->amp_vol[index][0] & 32);
+ vol[1] = (31 - (ucontrol->value.integer.value[1] & 31))
+ | (amp->amp_vol[index][1] & 32);
return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
}
@@ -374,8 +377,10 @@
struct awacs_amp *amp = chip->mixer_data;
snd_assert(amp, return -EINVAL);
snd_assert(index >= 0 && index <= 1, return -EINVAL);
- ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1;
- ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1;
+ ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32)
+ ? 0 : 1;
+ ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32)
+ ? 0 : 1;
return 0;
}
@@ -389,8 +394,10 @@
snd_assert(amp, return -EINVAL);
snd_assert(index >= 0 && index <= 1, return -EINVAL);
- vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31);
- vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31);
+ vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32)
+ | (amp->amp_vol[index][0] & 31);
+ vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32)
+ | (amp->amp_vol[index][1] & 31);
return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
}
@@ -403,7 +410,7 @@
uinfo->value.integer.max = 14;
return 0;
}
-
+
static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -445,7 +452,7 @@
uinfo->value.integer.max = 99;
return 0;
}
-
+
static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -544,7 +551,7 @@
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 2;
+ uinfo->value.integer.max = 3;
return 0;
}
@@ -552,16 +559,14 @@
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
- int val;
+ int val = 0;
unsigned long flags;
spin_lock_irqsave(&chip->reg_lock, flags);
if (chip->awacs_reg[6] & MASK_MIC_BOOST)
- val = 2;
- else if (chip->awacs_reg[0] & MASK_GAINLINE)
- val = 1;
- else
- val = 0;
+ val |= 2;
+ if (chip->awacs_reg[0] & MASK_GAINLINE)
+ val |= 1;
spin_unlock_irqrestore(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = val;
return 0;
@@ -578,11 +583,10 @@
spin_lock_irqsave(&chip->reg_lock, flags);
val0 = chip->awacs_reg[0] & ~MASK_GAINLINE;
val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST;
- if (ucontrol->value.integer.value[0] > 0) {
+ if (ucontrol->value.integer.value[0] & 1)
val0 |= MASK_GAINLINE;
- if (ucontrol->value.integer.value[0] > 1)
- val6 |= MASK_MIC_BOOST;
- }
+ if (ucontrol->value.integer.value[0] & 2)
+ val6 |= MASK_MIC_BOOST;
if (val0 != chip->awacs_reg[0]) {
snd_pmac_awacs_write_reg(chip, 0, val0);
changed = 1;
@@ -599,9 +603,32 @@
* lists of mixer elements
*/
static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __initdata = {
- AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
- AWACS_VOLUME("Capture Volume", 0, 4, 0),
+ AWACS_VOLUME("Master Capture Volume", 0, 4, 0),
+/* AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */
+};
+
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] __initdata = {
+ AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
+ AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
+ AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
+ AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
+};
+
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = {
+ AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
+ AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
+ AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
+};
+
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __initdata = {
+ AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
+ AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
+ AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
+};
+
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __initdata = {
+ AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
};
@@ -621,35 +648,61 @@
static struct snd_kcontrol_new snd_pmac_awacs_master_sw __initdata =
AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __initdata =
+AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
+
static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __initdata = {
- AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0),
+ AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
};
static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __initdata = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mic Boost",
+ .name = "Mic Boost Capture Volume",
.info = snd_pmac_screamer_mic_boost_info,
.get = snd_pmac_screamer_mic_boost_get,
.put = snd_pmac_screamer_mic_boost_put,
},
};
+static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] __initdata =
+{
+ AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
+};
+
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] __initdata =
+{
+ AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
+ AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
+};
+
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __initdata =
+{
+ AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
+ AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
+};
+
static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = {
AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1),
};
+
static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata =
AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac __initdata =
+AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
+
/*
* add new mixer elements to the card
*/
-static int build_mixers(struct snd_pmac *chip, int nums, struct snd_kcontrol_new *mixers)
+static int build_mixers(struct snd_pmac *chip, int nums,
+ struct snd_kcontrol_new *mixers)
{
int i, err;
for (i = 0; i < nums; i++) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip));
+ if (err < 0)
return err;
}
return 0;
@@ -699,8 +752,10 @@
#ifdef PMAC_AMP_AVAIL
if (chip->mixer_data) {
struct awacs_amp *amp = chip->mixer_data;
- awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0);
- awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0);
+ awacs_amp_set_vol(amp, 0,
+ amp->amp_vol[0][0], amp->amp_vol[0][1], 0);
+ awacs_amp_set_vol(amp, 1,
+ amp->amp_vol[1][0], amp->amp_vol[1][1], 0);
awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
awacs_amp_set_master(amp, amp->amp_master);
}
@@ -708,6 +763,14 @@
}
#endif /* CONFIG_PM */
+#define IS_PM7500 (machine_is_compatible("AAPL,7500"))
+#define IS_BEIGE (machine_is_compatible("AAPL,Gossamer"))
+#define IS_IMAC (machine_is_compatible("PowerMac2,1") \
+ || machine_is_compatible("PowerMac2,2") \
+ || machine_is_compatible("PowerMac4,1"))
+
+static int imac;
+
#ifdef PMAC_SUPPORT_AUTOMUTE
/*
* auto-mute stuffs
@@ -750,9 +813,16 @@
} else
#endif
{
- int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE);
+ int reg = chip->awacs_reg[1]
+ | (MASK_HDMUTE | MASK_SPKMUTE);
+ if (imac) {
+ reg &= ~MASK_SPKMUTE;
+ reg &= ~MASK_PAROUT1;
+ }
if (snd_pmac_awacs_detect_headphone(chip))
reg &= ~MASK_HDMUTE;
+ else if (imac)
+ reg |= MASK_PAROUT1;
else
reg &= ~MASK_SPKMUTE;
if (do_notify && reg == chip->awacs_reg[1])
@@ -778,8 +848,11 @@
int __init
snd_pmac_awacs_init(struct snd_pmac *chip)
{
+ int pm7500 = IS_PM7500;
+ int beige = IS_BEIGE;
int err, vol;
+ imac = IS_IMAC;
/* looks like MASK_GAINLINE triggers something, so we set here
* as start-up
*/
@@ -787,7 +860,7 @@
chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE;
/* FIXME: Only machines with external SRS module need MASK_PAROUT */
if (chip->has_iic || chip->device_id == 0x5 ||
- /*chip->_device_id == 0x8 || */
+ /* chip->_device_id == 0x8 || */
chip->device_id == 0xb)
chip->awacs_reg[1] |= MASK_PAROUT;
/* get default volume from nvram */
@@ -798,8 +871,10 @@
chip->awacs_reg[2] = vol;
chip->awacs_reg[4] = vol;
if (chip->model == PMAC_SCREAMER) {
- chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */
- chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */
+ /* FIXME: screamer has loopthru vol control */
+ chip->awacs_reg[5] = vol;
+ /* FIXME: maybe should be vol << 3 for PCMCIA speaker */
+ chip->awacs_reg[6] = MASK_MIC_BOOST;
chip->awacs_reg[7] = 0;
}
@@ -815,7 +890,8 @@
return -ENOMEM;
chip->mixer_data = amp;
chip->mixer_free = awacs_amp_free;
- awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */
+ /* mute and zero vol */
+ awacs_amp_set_vol(amp, 0, 63, 63, 0);
awacs_amp_set_vol(amp, 1, 63, 63, 0);
awacs_amp_set_tone(amp, 7, 7); /* 0 dB */
awacs_amp_set_master(amp, 79); /* 0 dB */
@@ -826,20 +902,25 @@
/* set headphone-jack detection bit */
switch (chip->model) {
case PMAC_AWACS:
- chip->hp_stat_mask = 0x04;
+ chip->hp_stat_mask = pm7500 ? MASK_HDPCONN
+ : MASK_LOCONN;
break;
case PMAC_SCREAMER:
switch (chip->device_id) {
case 0x08:
- /* 1 = side jack, 2 = front jack */
- chip->hp_stat_mask = 0x03;
+ case 0x0B:
+ chip->hp_stat_mask = imac
+ ? MASK_LOCONN_IMAC |
+ MASK_HDPLCONN_IMAC |
+ MASK_HDPRCONN_IMAC
+ : MASK_HDPCONN;
break;
case 0x00:
case 0x05:
- chip->hp_stat_mask = 0x04;
+ chip->hp_stat_mask = MASK_LOCONN;
break;
default:
- chip->hp_stat_mask = 0x08;
+ chip->hp_stat_mask = MASK_HDPCONN;
break;
}
break;
@@ -854,19 +935,43 @@
*/
strcpy(chip->card->mixername, "PowerMac AWACS");
- if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers),
- snd_pmac_awacs_mixers)) < 0)
+ err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers),
+ snd_pmac_awacs_mixers);
+ if (err < 0)
return err;
- if (chip->model == PMAC_SCREAMER)
+ if (beige)
+ ;
+ else if (chip->model == PMAC_SCREAMER)
err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2),
snd_pmac_screamer_mixers2);
- else
+ else if (!pm7500)
err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2),
snd_pmac_awacs_mixers2);
if (err < 0)
return err;
- chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
+ if (pm7500)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
+ snd_pmac_awacs_mixers_pmac7500);
+ else if (beige)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_screamer_mixers_beige),
+ snd_pmac_screamer_mixers_beige);
+ else if (imac)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_screamer_mixers_imac),
+ snd_pmac_screamer_mixers_imac);
+ else
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_awacs_mixers_pmac),
+ snd_pmac_awacs_mixers_pmac);
+ if (err < 0)
+ return err;
+ chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac)
+ ? &snd_pmac_awacs_master_sw_imac
+ : &snd_pmac_awacs_master_sw, chip);
+ err = snd_ctl_add(chip->card, chip->master_sw_ctl);
+ if (err < 0)
return err;
#ifdef PMAC_AMP_AVAIL
if (chip->mixer_data) {
@@ -876,37 +981,58 @@
* screamer registers.
* in this case, it seems the route C is not used.
*/
- if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol),
- snd_pmac_awacs_amp_vol)) < 0)
+ err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol),
+ snd_pmac_awacs_amp_vol);
+ if (err < 0)
return err;
/* overwrite */
- chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
+ chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw,
+ chip);
+ err = snd_ctl_add(chip->card, chip->master_sw_ctl);
+ if (err < 0)
return err;
- chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
+ chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw,
+ chip);
+ err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
+ if (err < 0)
return err;
} else
#endif /* PMAC_AMP_AVAIL */
{
/* route A = headphone, route C = speaker */
- if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol),
- snd_pmac_awacs_speaker_vol)) < 0)
+ err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol),
+ snd_pmac_awacs_speaker_vol);
+ if (err < 0)
return err;
- chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
+ chip->speaker_sw_ctl = snd_ctl_new1(imac
+ ? &snd_pmac_awacs_speaker_sw_imac
+ : &snd_pmac_awacs_speaker_sw, chip);
+ err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
+ if (err < 0)
return err;
}
- if (chip->model == PMAC_SCREAMER) {
- if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost),
- snd_pmac_screamer_mic_boost)) < 0)
- return err;
- } else {
- if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost),
- snd_pmac_awacs_mic_boost)) < 0)
- return err;
- }
+ if (beige)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige),
+ snd_pmac_screamer_mic_boost_beige);
+ else if (imac)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_screamer_mic_boost_imac),
+ snd_pmac_screamer_mic_boost_imac);
+ else if (chip->model == PMAC_SCREAMER)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_screamer_mic_boost),
+ snd_pmac_screamer_mic_boost);
+ else if (pm7500)
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_awacs_mic_boost_pmac7500),
+ snd_pmac_awacs_mic_boost_pmac7500);
+ else
+ err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost),
+ snd_pmac_awacs_mic_boost);
+ if (err < 0)
+ return err;
/*
* set lowlevel callbacks
@@ -917,7 +1043,8 @@
chip->resume = snd_pmac_awacs_resume;
#endif
#ifdef PMAC_SUPPORT_AUTOMUTE
- if ((err = snd_pmac_add_automute(chip)) < 0)
+ err = snd_pmac_add_automute(chip);
+ if (err < 0)
return err;
chip->detect_headphone = snd_pmac_awacs_detect_headphone;
chip->update_automute = snd_pmac_awacs_update_automute;
diff --git a/sound/ppc/awacs.h b/sound/ppc/awacs.h
index 1b2cc44..c33e6a5 100644
--- a/sound/ppc/awacs.h
+++ b/sound/ppc/awacs.h
@@ -116,6 +116,11 @@
#define MASK_HDMUTE MASK_AMUTE
#define SHIFT_HDMUTE 9
#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */
+#define MASK_PAROUT0 (0x1 << 10) /* Parallel Out (???) */
+#define MASK_PAROUT1 (0x1 << 11) /* Parallel Out (enable speaker) */
+#define SHIFT_PAROUT 10
+#define SHIFT_PAROUT0 10
+#define SHIFT_PAROUT1 11
#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */
#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */
@@ -139,7 +144,7 @@
#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT)
/* address 6 */
-#define MASK_MIC_BOOST (0x4) /* screamer mic boost */
+#define MASK_MIC_BOOST (0x4) /* screamer mic boost */
#define SHIFT_MIC_BOOST 2
/* Audio Codec Status Reg Bit Masks */
@@ -152,8 +157,15 @@
#define MASK_REVISION (0xf << 12) /* Revision Number */
#define MASK_MFGID (0xf << 8) /* Mfg. ID */
#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */
-#define MASK_INPPORT (0xf) /* Input Port */
-#define MASK_HDPCONN 8 /* headphone plugged in */
+#define MASK_INSENSE (0xf) /* port sense bits: */
+#define MASK_HDPCONN 8 /* headphone plugged in */
+#define MASK_LOCONN 4 /* line-out plugged in */
+#define MASK_LICONN 2 /* line-in plugged in */
+#define MASK_MICCONN 1 /* microphone plugged in */
+#define MASK_LICONN_IMAC 8 /* line-in plugged in */
+#define MASK_HDPRCONN_IMAC 4 /* headphone right plugged in */
+#define MASK_HDPLCONN_IMAC 2 /* headphone left plugged in */
+#define MASK_LOCONN_IMAC 1 /* line-out plugged in */
/* Clipping Count Reg Bit Masks */
/* -------- ----- --- --- ----- */
@@ -163,7 +175,8 @@
/* DBDMA ChannelStatus Bit Masks */
/* ----- ------------- --- ----- */
#define MASK_CSERR (0x1 << 7) /* Error */
-#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */
+#define MASK_EOI (0x1 << 6) /* End of Input --
+ only for Input Channel */
#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */
#define MASK_WAIT (0x1) /* Wait */
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index 1a545ac..f860d39 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -102,7 +102,8 @@
}
static void
-snd_pmac_burgundy_wcb(struct snd_pmac *chip, unsigned int addr, unsigned int val)
+snd_pmac_burgundy_wcb(struct snd_pmac *chip, unsigned int addr,
+ unsigned int val)
{
out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
snd_pmac_burgundy_busy_wait(chip);
@@ -126,8 +127,11 @@
return val;
}
+#define BASE2ADDR(base) ((base) << 12)
+#define ADDR2BASE(addr) ((addr) >> 12)
+
/*
- * Burgundy volume: 0 - 100, stereo
+ * Burgundy volume: 0 - 100, stereo, word reg
*/
static void
snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address,
@@ -168,13 +172,6 @@
volume[1] = 0;
}
-
-/*
- */
-
-#define BASE2ADDR(base) ((base) << 12)
-#define ADDR2BASE(addr) ((addr) >> 12)
-
static int snd_pmac_burgundy_info_volume(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -191,8 +188,8 @@
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
int shift = (kcontrol->private_value >> 8) & 0xff;
- snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value,
- shift);
+ snd_pmac_burgundy_read_volume(chip, addr,
+ ucontrol->value.integer.value, shift);
return 0;
}
@@ -204,75 +201,101 @@
int shift = (kcontrol->private_value >> 8) & 0xff;
long nvoices[2];
- snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value,
- shift);
+ snd_pmac_burgundy_write_volume(chip, addr,
+ ucontrol->value.integer.value, shift);
snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift);
return (nvoices[0] != ucontrol->value.integer.value[0] ||
nvoices[1] != ucontrol->value.integer.value[1]);
}
-#define BURGUNDY_VOLUME(xname, xindex, addr, shift) \
+#define BURGUNDY_VOLUME_W(xname, xindex, addr, shift) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
.info = snd_pmac_burgundy_info_volume,\
.get = snd_pmac_burgundy_get_volume,\
.put = snd_pmac_burgundy_put_volume,\
.private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) }
-/* lineout/speaker */
-
-static int snd_pmac_burgundy_info_switch_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+/*
+ * Burgundy volume: 0 - 100, stereo, 2-byte reg
+ */
+static void
+snd_pmac_burgundy_write_volume_2b(struct snd_pmac *chip, unsigned int address,
+ long *volume, int off)
{
- int stereo = (kcontrol->private_value >> 24) & 1;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = stereo + 1;
+ int lvolume, rvolume;
+
+ off |= off << 2;
+ lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0;
+ rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0;
+
+ snd_pmac_burgundy_wcb(chip, address + off, lvolume);
+ snd_pmac_burgundy_wcb(chip, address + off + 0x500, rvolume);
+}
+
+static void
+snd_pmac_burgundy_read_volume_2b(struct snd_pmac *chip, unsigned int address,
+ long *volume, int off)
+{
+ volume[0] = snd_pmac_burgundy_rcb(chip, address + off);
+ if (volume[0] >= BURGUNDY_VOLUME_OFFSET)
+ volume[0] -= BURGUNDY_VOLUME_OFFSET;
+ else
+ volume[0] = 0;
+ volume[1] = snd_pmac_burgundy_rcb(chip, address + off + 0x100);
+ if (volume[1] >= BURGUNDY_VOLUME_OFFSET)
+ volume[1] -= BURGUNDY_VOLUME_OFFSET;
+ else
+ volume[1] = 0;
+}
+
+static int snd_pmac_burgundy_info_volume_2b(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
+ uinfo->value.integer.max = 100;
return 0;
}
-static int snd_pmac_burgundy_get_switch_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_pmac_burgundy_get_volume_2b(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
- int lmask = kcontrol->private_value & 0xff;
- int rmask = (kcontrol->private_value >> 8) & 0xff;
- int stereo = (kcontrol->private_value >> 24) & 1;
- int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES);
- ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0;
- if (stereo)
- ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0;
+ unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
+ int off = kcontrol->private_value & 0x300;
+ snd_pmac_burgundy_read_volume_2b(chip, addr,
+ ucontrol->value.integer.value, off);
return 0;
}
-static int snd_pmac_burgundy_put_switch_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_pmac_burgundy_put_volume_2b(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
- int lmask = kcontrol->private_value & 0xff;
- int rmask = (kcontrol->private_value >> 8) & 0xff;
- int stereo = (kcontrol->private_value >> 24) & 1;
- int val, oval;
- oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES);
- val = oval & ~(lmask | rmask);
- if (ucontrol->value.integer.value[0])
- val |= lmask;
- if (stereo && ucontrol->value.integer.value[1])
- val |= rmask;
- snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val);
- return val != oval;
+ unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
+ int off = kcontrol->private_value & 0x300;
+ long nvoices[2];
+
+ snd_pmac_burgundy_write_volume_2b(chip, addr,
+ ucontrol->value.integer.value, off);
+ snd_pmac_burgundy_read_volume_2b(chip, addr, nvoices, off);
+ return (nvoices[0] != ucontrol->value.integer.value[0] ||
+ nvoices[1] != ucontrol->value.integer.value[1]);
}
-#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \
+#define BURGUNDY_VOLUME_2B(xname, xindex, addr, off) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
- .info = snd_pmac_burgundy_info_switch_out,\
- .get = snd_pmac_burgundy_get_switch_out,\
- .put = snd_pmac_burgundy_put_switch_out,\
- .private_value = ((lmask) | ((rmask) << 8) | ((stereo) << 24)) }
+ .info = snd_pmac_burgundy_info_volume_2b,\
+ .get = snd_pmac_burgundy_get_volume_2b,\
+ .put = snd_pmac_burgundy_put_volume_2b,\
+ .private_value = ((ADDR2BASE(addr) & 0xff) | ((off) << 8)) }
-/* line/speaker output volume */
-static int snd_pmac_burgundy_info_volume_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+/*
+ * Burgundy gain/attenuation: 0 - 15, mono/stereo, byte reg
+ */
+static int snd_pmac_burgundy_info_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
int stereo = (kcontrol->private_value >> 24) & 1;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -282,60 +305,269 @@
return 0;
}
-static int snd_pmac_burgundy_get_volume_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_pmac_burgundy_get_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
int stereo = (kcontrol->private_value >> 24) & 1;
+ int atten = (kcontrol->private_value >> 25) & 1;
int oval;
- oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff;
+ oval = snd_pmac_burgundy_rcb(chip, addr);
+ if (atten)
+ oval = ~oval & 0xff;
ucontrol->value.integer.value[0] = oval & 0xf;
if (stereo)
ucontrol->value.integer.value[1] = (oval >> 4) & 0xf;
return 0;
}
-static int snd_pmac_burgundy_put_volume_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_pmac_burgundy_put_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
int stereo = (kcontrol->private_value >> 24) & 1;
- unsigned int oval, val;
+ int atten = (kcontrol->private_value >> 25) & 1;
+ int oval, val;
- oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff;
- val = ucontrol->value.integer.value[0] & 15;
+ oval = snd_pmac_burgundy_rcb(chip, addr);
+ if (atten)
+ oval = ~oval & 0xff;
+ val = ucontrol->value.integer.value[0];
if (stereo)
- val |= (ucontrol->value.integer.value[1] & 15) << 4;
+ val |= ucontrol->value.integer.value[1] << 4;
else
- val |= val << 4;
- val = ~val & 0xff;
+ val |= ucontrol->value.integer.value[0] << 4;
+ if (atten)
+ val = ~val & 0xff;
snd_pmac_burgundy_wcb(chip, addr, val);
return val != oval;
}
-#define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \
+#define BURGUNDY_VOLUME_B(xname, xindex, addr, stereo, atten) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
- .info = snd_pmac_burgundy_info_volume_out,\
- .get = snd_pmac_burgundy_get_volume_out,\
- .put = snd_pmac_burgundy_put_volume_out,\
- .private_value = (ADDR2BASE(addr) | ((stereo) << 24)) }
+ .info = snd_pmac_burgundy_info_gain,\
+ .get = snd_pmac_burgundy_get_gain,\
+ .put = snd_pmac_burgundy_put_gain,\
+ .private_value = (ADDR2BASE(addr) | ((stereo) << 24) | ((atten) << 25)) }
+/*
+ * Burgundy switch: 0/1, mono/stereo, word reg
+ */
+static int snd_pmac_burgundy_info_switch_w(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int stereo = (kcontrol->private_value >> 24) & 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = stereo + 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_pmac_burgundy_get_switch_w(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
+ int lmask = 1 << (kcontrol->private_value & 0xff);
+ int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff);
+ int stereo = (kcontrol->private_value >> 24) & 1;
+ int val = snd_pmac_burgundy_rcw(chip, addr);
+ ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0;
+ if (stereo)
+ ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0;
+ return 0;
+}
+
+static int snd_pmac_burgundy_put_switch_w(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
+ int lmask = 1 << (kcontrol->private_value & 0xff);
+ int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff);
+ int stereo = (kcontrol->private_value >> 24) & 1;
+ int val, oval;
+ oval = snd_pmac_burgundy_rcw(chip, addr);
+ val = oval & ~(lmask | (stereo ? rmask : 0));
+ if (ucontrol->value.integer.value[0])
+ val |= lmask;
+ if (stereo && ucontrol->value.integer.value[1])
+ val |= rmask;
+ snd_pmac_burgundy_wcw(chip, addr, val);
+ return val != oval;
+}
+
+#define BURGUNDY_SWITCH_W(xname, xindex, addr, lbit, rbit, stereo) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
+ .info = snd_pmac_burgundy_info_switch_w,\
+ .get = snd_pmac_burgundy_get_switch_w,\
+ .put = snd_pmac_burgundy_put_switch_w,\
+ .private_value = ((lbit) | ((rbit) << 8)\
+ | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) }
+
+/*
+ * Burgundy switch: 0/1, mono/stereo, byte reg, bit mask
+ */
+static int snd_pmac_burgundy_info_switch_b(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int stereo = (kcontrol->private_value >> 24) & 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = stereo + 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_pmac_burgundy_get_switch_b(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
+ int lmask = kcontrol->private_value & 0xff;
+ int rmask = (kcontrol->private_value >> 8) & 0xff;
+ int stereo = (kcontrol->private_value >> 24) & 1;
+ int val = snd_pmac_burgundy_rcb(chip, addr);
+ ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0;
+ if (stereo)
+ ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0;
+ return 0;
+}
+
+static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
+ int lmask = kcontrol->private_value & 0xff;
+ int rmask = (kcontrol->private_value >> 8) & 0xff;
+ int stereo = (kcontrol->private_value >> 24) & 1;
+ int val, oval;
+ oval = snd_pmac_burgundy_rcb(chip, addr);
+ val = oval & ~(lmask | rmask);
+ if (ucontrol->value.integer.value[0])
+ val |= lmask;
+ if (stereo && ucontrol->value.integer.value[1])
+ val |= rmask;
+ snd_pmac_burgundy_wcb(chip, addr, val);
+ return val != oval;
+}
+
+#define BURGUNDY_SWITCH_B(xname, xindex, addr, lmask, rmask, stereo) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
+ .info = snd_pmac_burgundy_info_switch_b,\
+ .get = snd_pmac_burgundy_get_switch_b,\
+ .put = snd_pmac_burgundy_put_switch_b,\
+ .private_value = ((lmask) | ((rmask) << 8)\
+ | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) }
+
+/*
+ * Burgundy mixers
+ */
static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __initdata = {
- BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
- BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16),
- BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16),
- BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16),
- BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0),
- /*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/
- BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1),
-};
-static struct snd_kcontrol_new snd_pmac_burgundy_master_sw __initdata =
-BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw __initdata =
-BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0);
+ BURGUNDY_VOLUME_W("Master Playback Volume", 0,
+ MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
+ BURGUNDY_VOLUME_W("CD Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_VOLCD, 16),
+ BURGUNDY_VOLUME_2B("Input Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_VOLMIX01, 2),
+ BURGUNDY_VOLUME_2B("Mixer Playback Volume", 0,
+ MASK_ADDR_BURGUNDY_VOLMIX23, 0),
+ BURGUNDY_VOLUME_B("CD Gain Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_GAINCD, 1, 0),
+ BURGUNDY_SWITCH_W("Master Capture Switch", 0,
+ MASK_ADDR_BURGUNDY_OUTPUTENABLES, 24, 0, 0),
+ BURGUNDY_SWITCH_W("CD Capture Switch", 0,
+ MASK_ADDR_BURGUNDY_CAPTURESELECTS, 0, 16, 1),
+ BURGUNDY_SWITCH_W("CD Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 0, 16, 1),
+/* BURGUNDY_SWITCH_W("Loop Capture Switch", 0,
+ * MASK_ADDR_BURGUNDY_CAPTURESELECTS, 8, 24, 1),
+ * BURGUNDY_SWITCH_B("Mixer out Capture Switch", 0,
+ * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x02, 0, 0),
+ * BURGUNDY_SWITCH_B("Mixer Capture Switch", 0,
+ * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x01, 0, 0),
+ * BURGUNDY_SWITCH_B("PCM out Capture Switch", 0,
+ * MASK_ADDR_BURGUNDY_HOSTIFEH, 0x02, 0, 0),
+ */ BURGUNDY_SWITCH_B("PCM Capture Switch", 0,
+ MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0)
+};
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __initdata = {
+ BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_VOLLINE, 16),
+ BURGUNDY_VOLUME_W("Mic Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_VOLMIC, 16),
+ BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_GAINLINE, 1, 0),
+ BURGUNDY_VOLUME_B("Mic Gain Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_GAINMIC, 1, 0),
+ BURGUNDY_VOLUME_B("PC Speaker Playback Volume", 0,
+ MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1),
+ BURGUNDY_VOLUME_B("Line out Playback Volume", 0,
+ MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1, 1),
+ BURGUNDY_VOLUME_B("Headphone Playback Volume", 0,
+ MASK_ADDR_BURGUNDY_ATTENHP, 1, 1),
+ BURGUNDY_SWITCH_W("Line in Capture Switch", 0,
+ MASK_ADDR_BURGUNDY_CAPTURESELECTS, 1, 17, 1),
+ BURGUNDY_SWITCH_W("Mic Capture Switch", 0,
+ MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1),
+ BURGUNDY_SWITCH_W("Line in Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 1, 17, 1),
+ BURGUNDY_SWITCH_W("Mic Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1),
+ BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0,
+ MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1)
+};
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __initdata = {
+ BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_VOLMIC, 16),
+ BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
+ MASK_ADDR_BURGUNDY_GAINMIC, 1, 0),
+ BURGUNDY_VOLUME_B("PC Speaker Playback Volume", 0,
+ MASK_ADDR_BURGUNDY_ATTENMONO, 0, 1),
+ BURGUNDY_VOLUME_B("Line out Playback Volume", 0,
+ MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1),
+ BURGUNDY_SWITCH_W("Line in Capture Switch", 0,
+ MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1),
+ BURGUNDY_SWITCH_W("Line in Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1),
+/* BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0,
+ * MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */
+};
+static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __initdata =
+BURGUNDY_SWITCH_B("Master Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+ BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT,
+ BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1);
+static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __initdata =
+BURGUNDY_SWITCH_B("Master Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+ BURGUNDY_OUTPUT_INTERN
+ | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
+static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __initdata =
+BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+ BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
+static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __initdata =
+BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+ BURGUNDY_OUTPUT_INTERN, 0, 0);
+static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __initdata =
+BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+ BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1);
+static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __initdata =
+BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+ BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
+static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __initdata =
+BURGUNDY_SWITCH_B("Headphone Playback Switch", 0,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+ BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1);
#ifdef PMAC_SUPPORT_AUTOMUTE
@@ -350,16 +582,26 @@
static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_notify)
{
if (chip->auto_mute) {
+ int imac = machine_is_compatible("iMac");
int reg, oreg;
- reg = oreg = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES);
- reg &= ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT | BURGUNDY_OUTPUT_INTERN);
+ reg = oreg = snd_pmac_burgundy_rcb(chip,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES);
+ reg &= imac ? ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT
+ | BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT)
+ : ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT
+ | BURGUNDY_OUTPUT_INTERN);
if (snd_pmac_burgundy_detect_headphone(chip))
- reg |= BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT;
+ reg |= imac ? (BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT)
+ : (BURGUNDY_OUTPUT_LEFT
+ | BURGUNDY_OUTPUT_RIGHT);
else
- reg |= BURGUNDY_OUTPUT_INTERN;
+ reg |= imac ? (BURGUNDY_OUTPUT_LEFT
+ | BURGUNDY_OUTPUT_RIGHT)
+ : (BURGUNDY_OUTPUT_INTERN);
if (do_notify && reg == oreg)
return;
- snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg);
+ snd_pmac_burgundy_wcb(chip,
+ MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg);
if (do_notify) {
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->master_sw_ctl->id);
@@ -378,6 +620,7 @@
*/
int __init snd_pmac_burgundy_init(struct snd_pmac *chip)
{
+ int imac = machine_is_compatible("iMac");
int i, err;
/* Checks to see the chip is alive and kicking */
@@ -386,7 +629,7 @@
return 1;
}
- snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES,
+ snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES,
DEF_BURGUNDY_OUTPUTENABLES);
snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
DEF_BURGUNDY_MORE_OUTPUTENABLES);
@@ -396,7 +639,8 @@
snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21,
DEF_BURGUNDY_INPSEL21);
snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3,
- DEF_BURGUNDY_INPSEL3);
+ imac ? DEF_BURGUNDY_INPSEL3_IMAC
+ : DEF_BURGUNDY_INPSEL3_PMAC);
snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD,
DEF_BURGUNDY_GAINCD);
snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE,
@@ -422,27 +666,62 @@
snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC,
DEF_BURGUNDY_VOLMIC);
- if (chip->hp_stat_mask == 0)
+ if (chip->hp_stat_mask == 0) {
/* set headphone-jack detection bit */
- chip->hp_stat_mask = 0x04;
-
+ if (imac)
+ chip->hp_stat_mask = BURGUNDY_HPDETECT_IMAC_UPPER
+ | BURGUNDY_HPDETECT_IMAC_LOWER
+ | BURGUNDY_HPDETECT_IMAC_SIDE;
+ else
+ chip->hp_stat_mask = BURGUNDY_HPDETECT_PMAC_BACK;
+ }
/*
* build burgundy mixers
*/
strcpy(chip->card->mixername, "PowerMac Burgundy");
for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip));
+ if (err < 0)
return err;
}
- chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_master_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
+ for (i = 0; i < (imac ? ARRAY_SIZE(snd_pmac_burgundy_mixers_imac)
+ : ARRAY_SIZE(snd_pmac_burgundy_mixers_pmac)); i++) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(imac ? &snd_pmac_burgundy_mixers_imac[i]
+ : &snd_pmac_burgundy_mixers_pmac[i], chip));
+ if (err < 0)
+ return err;
+ }
+ chip->master_sw_ctl = snd_ctl_new1(imac
+ ? &snd_pmac_burgundy_master_sw_imac
+ : &snd_pmac_burgundy_master_sw_pmac, chip);
+ err = snd_ctl_add(chip->card, chip->master_sw_ctl);
+ if (err < 0)
return err;
- chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_speaker_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
+ chip->master_sw_ctl = snd_ctl_new1(imac
+ ? &snd_pmac_burgundy_line_sw_imac
+ : &snd_pmac_burgundy_line_sw_pmac, chip);
+ err = snd_ctl_add(chip->card, chip->master_sw_ctl);
+ if (err < 0)
+ return err;
+ if (imac) {
+ chip->master_sw_ctl = snd_ctl_new1(
+ &snd_pmac_burgundy_hp_sw_imac, chip);
+ err = snd_ctl_add(chip->card, chip->master_sw_ctl);
+ if (err < 0)
+ return err;
+ }
+ chip->speaker_sw_ctl = snd_ctl_new1(imac
+ ? &snd_pmac_burgundy_speaker_sw_imac
+ : &snd_pmac_burgundy_speaker_sw_pmac, chip);
+ err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
+ if (err < 0)
return err;
#ifdef PMAC_SUPPORT_AUTOMUTE
- if ((err = snd_pmac_add_automute(chip)) < 0)
+ err = snd_pmac_add_automute(chip);
+ if (err < 0)
return err;
chip->detect_headphone = snd_pmac_burgundy_detect_headphone;
diff --git a/sound/ppc/burgundy.h b/sound/ppc/burgundy.h
index ebb457a..7a7f9cf 100644
--- a/sound/ppc/burgundy.h
+++ b/sound/ppc/burgundy.h
@@ -22,6 +22,7 @@
#ifndef __BURGUNDY_H
#define __BURGUNDY_H
+#define MASK_ADDR_BURGUNDY_INPBOOST (0x10 << 12)
#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
@@ -35,7 +36,10 @@
#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
+#define MASK_ADDR_BURGUNDY_CAPTURESELECTS (0x2A << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
+#define MASK_ADDR_BURGUNDY_VOLMIX01 (0x2D << 12)
+#define MASK_ADDR_BURGUNDY_VOLMIX23 (0x2E << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
@@ -45,6 +49,10 @@
#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENMONO (0x65 << 12)
+
+#define MASK_ADDR_BURGUNDY_HOSTIFAD (0x78 << 12)
+#define MASK_ADDR_BURGUNDY_HOSTIFEH (0x79 << 12)
#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
@@ -59,21 +67,22 @@
/* These are all default values for the burgundy */
#define DEF_BURGUNDY_INPSEL21 (0xAA)
-#define DEF_BURGUNDY_INPSEL3 (0x0A)
+#define DEF_BURGUNDY_INPSEL3_IMAC (0x0A)
+#define DEF_BURGUNDY_INPSEL3_PMAC (0x05)
#define DEF_BURGUNDY_GAINCD (0x33)
#define DEF_BURGUNDY_GAINLINE (0x44)
#define DEF_BURGUNDY_GAINMIC (0x44)
#define DEF_BURGUNDY_GAINMODEM (0x06)
-/* Remember: lowest volume here is 0x9b */
+/* Remember: lowest volume here is 0x9B (155) */
#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
#define DEF_BURGUNDY_VOLLINE (0x00000000)
#define DEF_BURGUNDY_VOLMIC (0x00000000)
#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
-#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
-#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
+#define DEF_BURGUNDY_OUTPUTSELECTS (0x010F010F)
+#define DEF_BURGUNDY_OUTPUTENABLES (0x0100000A)
/* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */
#define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD)
@@ -84,12 +93,22 @@
#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
#define DEF_BURGUNDY_ATTENHP (0xCC)
-/* OUTPUTENABLES bits */
+/* MORE_OUTPUTENABLES bits */
#define BURGUNDY_OUTPUT_LEFT 0x02
#define BURGUNDY_OUTPUT_RIGHT 0x04
+#define BURGUNDY_LINEOUT_LEFT 0x08
+#define BURGUNDY_LINEOUT_RIGHT 0x10
+#define BURGUNDY_HP_LEFT 0x20
+#define BURGUNDY_HP_RIGHT 0x40
#define BURGUNDY_OUTPUT_INTERN 0x80
-/* volume offset */
+/* Headphone detection bits */
+#define BURGUNDY_HPDETECT_PMAC_BACK 0x04
+#define BURGUNDY_HPDETECT_IMAC_SIDE 0x04
+#define BURGUNDY_HPDETECT_IMAC_UPPER 0x08
+#define BURGUNDY_HPDETECT_IMAC_LOWER 0x01
+
+/* Volume offset */
#define BURGUNDY_VOLUME_OFFSET 155
#endif /* __BURGUNDY_H */
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index 613a565..a38c0c7 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -214,7 +214,7 @@
int rate_index;
long offset;
struct pmac_stream *astr;
-
+
rec->dma_size = snd_pcm_lib_buffer_bytes(subs);
rec->period_size = snd_pcm_lib_period_bytes(subs);
rec->nperiods = rec->dma_size / rec->period_size;
@@ -643,7 +643,7 @@
/* reset constraints */
astr->cur_freqs = chip->freqs_ok;
astr->cur_formats = chip->formats_ok;
-
+
return 0;
}
@@ -1300,9 +1300,9 @@
snd_pmac_sound_feature(chip, 1);
- /* reset */
- if (chip->model == PMAC_AWACS)
- out_le32(&chip->awacs->control, 0x11);
+ /* reset & enable interrupts */
+ if (chip->model <= PMAC_BURGUNDY)
+ out_le32(&chip->awacs->control, chip->control_mask);
/* Powerbooks have odd ways of enabling inputs such as
an expansion-bay CD or sound from an internal modem
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 2765852..a3b51df 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -29,6 +29,7 @@
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/fsl/Kconfig"
+source "sound/soc/davinci/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 4869c9a..e489dbd 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
-obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/
+obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 898a7d3..3903ab7 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -18,6 +18,10 @@
tristate
depends on SND_SOC
+config SND_SOC_WM9713
+ tristate
+ depends on SND_SOC
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index c6e5338..4e1314c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -3,6 +3,7 @@
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm9712-objs := wm9712.o
+snd-soc-wm9713-objs := wm9713.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
@@ -11,5 +12,6 @@
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
+obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 242130c..2a1ffe3 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -40,7 +40,8 @@
}
#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
struct snd_soc_codec_dai ac97_dai = {
.name = "AC97 HiFi",
@@ -86,7 +87,7 @@
printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (socdev->codec == NULL)
+ if (!socdev->codec)
return -ENOMEM;
codec = socdev->codec;
mutex_init(&codec->mutex);
@@ -102,17 +103,17 @@
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if(ret < 0)
+ if (ret < 0)
goto err;
/* add codec as bus device for standard ac97 */
ret = snd_ac97_bus(codec->card, 0, &soc_ac97_ops, NULL, &ac97_bus);
- if(ret < 0)
+ if (ret < 0)
goto bus_err;
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
- if(ret < 0)
+ if (ret < 0)
goto bus_err;
ret = snd_soc_register_card(socdev);
@@ -135,7 +136,7 @@
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
- if(codec == NULL)
+ if (!codec)
return 0;
snd_soc_free_pcms(socdev);
@@ -145,11 +146,10 @@
return 0;
}
-struct snd_soc_codec_device soc_codec_dev_ac97= {
+struct snd_soc_codec_device soc_codec_dev_ac97 = {
.probe = ac97_soc_probe,
.remove = ac97_soc_remove,
};
-
EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
MODULE_DESCRIPTION("Soc Generic AC97 driver");
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index bf2ab72..e73fcfd 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -372,7 +372,7 @@
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct cs4270_private *cs4270 = codec->private_data;
- unsigned int ret = 0;
+ int ret;
unsigned int i;
unsigned int rate;
unsigned int ratio;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 889a897..630684f 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -660,33 +660,53 @@
/* AIC3X codec mclk clock divider coefficients */
static const struct aic3x_rate_divs aic3x_divs[] = {
/* 8k */
+ {12000000, 8000, 48000, 0xa, 16, 3840},
+ {19200000, 8000, 48000, 0xa, 10, 2400},
{22579200, 8000, 48000, 0xa, 8, 7075},
{33868800, 8000, 48000, 0xa, 5, 8049},
/* 11.025k */
+ {12000000, 11025, 44100, 0x6, 15, 528},
+ {19200000, 11025, 44100, 0x6, 9, 4080},
{22579200, 11025, 44100, 0x6, 8, 0},
{33868800, 11025, 44100, 0x6, 5, 3333},
/* 16k */
+ {12000000, 16000, 48000, 0x4, 16, 3840},
+ {19200000, 16000, 48000, 0x4, 10, 2400},
{22579200, 16000, 48000, 0x4, 8, 7075},
{33868800, 16000, 48000, 0x4, 5, 8049},
/* 22.05k */
+ {12000000, 22050, 44100, 0x2, 15, 528},
+ {19200000, 22050, 44100, 0x2, 9, 4080},
{22579200, 22050, 44100, 0x2, 8, 0},
{33868800, 22050, 44100, 0x2, 5, 3333},
/* 32k */
+ {12000000, 32000, 48000, 0x1, 16, 3840},
+ {19200000, 32000, 48000, 0x1, 10, 2400},
{22579200, 32000, 48000, 0x1, 8, 7075},
{33868800, 32000, 48000, 0x1, 5, 8049},
/* 44.1k */
+ {12000000, 44100, 44100, 0x0, 15, 528},
+ {19200000, 44100, 44100, 0x0, 9, 4080},
{22579200, 44100, 44100, 0x0, 8, 0},
{33868800, 44100, 44100, 0x0, 5, 3333},
/* 48k */
+ {12000000, 48000, 48000, 0x0, 16, 3840},
+ {19200000, 48000, 48000, 0x0, 10, 2400},
{22579200, 48000, 48000, 0x0, 8, 7075},
{33868800, 48000, 48000, 0x0, 5, 8049},
/* 64k */
+ {12000000, 64000, 96000, 0x1, 16, 3840},
+ {19200000, 64000, 96000, 0x1, 10, 2400},
{22579200, 64000, 96000, 0x1, 8, 7075},
{33868800, 64000, 96000, 0x1, 5, 8049},
/* 88.2k */
+ {12000000, 88200, 88200, 0x0, 15, 528},
+ {19200000, 88200, 88200, 0x0, 9, 4080},
{22579200, 88200, 88200, 0x0, 8, 0},
{33868800, 88200, 88200, 0x0, 5, 3333},
/* 96k */
+ {12000000, 96000, 96000, 0x0, 16, 3840},
+ {19200000, 96000, 96000, 0x0, 10, 2400},
{22579200, 96000, 96000, 0x0, 8, 7075},
{33868800, 96000, 96000, 0x0, 5, 8049},
};
@@ -807,6 +827,8 @@
struct aic3x_priv *aic3x = codec->private_data;
switch (freq) {
+ case 12000000:
+ case 19200000:
case 22579200:
case 33868800:
aic3x->sysclk = freq;
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 9c33fe8..0cf9265 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -110,7 +110,7 @@
data[0] = (reg << 1) | ((value >> 8) & 0x0001);
data[1] = value & 0x00ff;
- wm8731_write_reg_cache (codec, reg, value);
+ wm8731_write_reg_cache(codec, reg, value);
if (codec->hw_write(codec->control_data, data, 2) == 2)
return 0;
else
@@ -154,8 +154,10 @@
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
- if ((err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0)
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8731_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
return err;
}
@@ -221,15 +223,13 @@
{
int i;
- for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
+ for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
- }
/* set up audio path interconnects */
- for(i = 0; intercon[i][0] != NULL; i++) {
+ for (i = 0; intercon[i][0] != NULL; i++)
snd_soc_dapm_connect_input(codec, intercon[i][0],
intercon[i][1], intercon[i][2]);
- }
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -589,7 +589,7 @@
static struct snd_soc_device *wm8731_socdev;
-#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* WM8731 2 wire address is determined by GPIO5
@@ -651,7 +651,7 @@
static int wm8731_i2c_detach(struct i2c_client *client)
{
- struct snd_soc_codec* codec = i2c_get_clientdata(client);
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
i2c_detach_client(client);
kfree(codec->reg_cache);
kfree(client);
@@ -709,7 +709,7 @@
INIT_LIST_HEAD(&codec->dapm_paths);
wm8731_socdev = socdev;
-#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
normal_i2c[0] = setup->i2c_address;
codec->hw_write = (hw_write_t)i2c_master_send;
@@ -734,7 +734,7 @@
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8731_i2c_driver);
#endif
kfree(codec->private_data);
@@ -749,7 +749,6 @@
.suspend = wm8731_suspend,
.resume = wm8731_resume,
};
-
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
MODULE_DESCRIPTION("ASoC WM8731 driver");
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 77a857b..16cd5d4 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -110,7 +110,7 @@
data[0] = (reg << 1) | ((value >> 8) & 0x0001);
data[1] = value & 0x00ff;
- wm8750_write_reg_cache (codec, reg, value);
+ wm8750_write_reg_cache(codec, reg, value);
if (codec->hw_write(codec->control_data, data, 2) == 2)
return 0;
else
@@ -257,7 +257,8 @@
for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8750_snd_controls[i],codec, NULL));
+ snd_soc_cnew(&wm8750_snd_controls[i],
+ codec, NULL));
if (err < 0)
return err;
}
@@ -478,15 +479,13 @@
{
int i;
- for(i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) {
+ for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
- }
/* set up audio path audio_mapnects */
- for(i = 0; audio_map[i][0] != NULL; i++) {
+ for (i = 0; audio_map[i][0] != NULL; i++)
snd_soc_dapm_connect_input(codec, audio_map[i][0],
audio_map[i][1], audio_map[i][2]);
- }
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -714,8 +713,8 @@
}
#define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
@@ -784,7 +783,8 @@
if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
codec->dapm_state = SNDRV_CTL_POWER_D0;
- schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
+ schedule_delayed_work(&codec->delayed_work,
+ msecs_to_jiffies(1000));
}
return 0;
@@ -864,7 +864,7 @@
around */
static struct snd_soc_device *wm8750_socdev;
-#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* WM8731 2 wire address is determined by GPIO5
@@ -979,8 +979,8 @@
INIT_LIST_HEAD(&codec->dapm_paths);
wm8750_socdev = socdev;
INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work);
-
-#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
normal_i2c[0] = setup->i2c_address;
codec->hw_write = (hw_write_t)i2c_master_send;
@@ -1025,7 +1025,7 @@
run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8750_i2c_driver);
#endif
kfree(codec->private_data);
@@ -1040,7 +1040,6 @@
.suspend = wm8750_suspend,
.resume = wm8750_resume,
};
-
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
MODULE_DESCRIPTION("ASoC WM8750 driver");
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index ddd9c71..76a5c7b 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -198,6 +198,7 @@
static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"};
static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC",
"Channel Swap"};
+static const char *wm8753_rout2_phase[] = {"Non Inverted", "Inverted"};
static const struct soc_enum wm8753_enum[] = {
SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base),
@@ -228,6 +229,7 @@
SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel),
SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode),
SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel),
+SOC_ENUM_SINGLE(WM8753_OUTCTL, 2, 2, wm8753_rout2_phase),
};
@@ -279,7 +281,7 @@
SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
-SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1),
+SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1),
SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
SOC_ENUM("Bass Boost", wm8753_enum[0]),
@@ -330,6 +332,7 @@
SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
SOC_ENUM("ADC Data Select", wm8753_enum[27]),
+SOC_ENUM("ROUT2 Phase", wm8753_enum[28]),
};
/* add non dapm controls */
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 524f745..d2d79e1 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -581,22 +581,14 @@
switch (event) {
case SNDRV_CTL_POWER_D0: /* full On */
- /* liam - maybe enable thermal shutdown */
- reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff;
- ac97_write(codec, AC97_EXTENDED_MID, reg);
- break;
case SNDRV_CTL_POWER_D1: /* partial On */
case SNDRV_CTL_POWER_D2: /* partial On */
break;
case SNDRV_CTL_POWER_D3hot: /* Off, with power */
- /* enable master bias and vmid */
- reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff;
- ac97_write(codec, AC97_EXTENDED_MID, reg);
ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
case SNDRV_CTL_POWER_D3cold: /* Off, without power */
/* disable everything including AC link */
- ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
new file mode 100644
index 0000000..1f24116
--- /dev/null
+++ b/sound/soc/codecs/wm9713.c
@@ -0,0 +1,1300 @@
+/*
+ * wm9713.c -- ALSA Soc WM9713 codec support
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * 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.
+ *
+ * Revision history
+ * 4th Feb 2006 Initial version.
+ *
+ * Features:-
+ *
+ * o Support for AC97 Codec, Voice DAC and Aux DAC
+ * o Support for DAPM
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "wm9713.h"
+
+#define WM9713_VERSION "0.15"
+
+struct wm9713_priv {
+ u32 pll_in; /* PLL input frequency */
+ u32 pll_out; /* PLL output frequency */
+};
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static int ac97_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int val);
+
+/*
+ * WM9713 register cache
+ * Reg 0x3c bit 15 is used by touch driver.
+ */
+static const u16 wm9713_reg[] = {
+ 0x6174, 0x8080, 0x8080, 0x8080,
+ 0xc880, 0xe808, 0xe808, 0x0808,
+ 0x00da, 0x8000, 0xd600, 0xaaa0,
+ 0xaaa0, 0xaaa0, 0x0000, 0x0000,
+ 0x0f0f, 0x0040, 0x0000, 0x7f00,
+ 0x0405, 0x0410, 0xbb80, 0xbb80,
+ 0x0000, 0xbb80, 0x0000, 0x4523,
+ 0x0000, 0x2000, 0x7eff, 0xffff,
+ 0x0000, 0x0000, 0x0080, 0x0000,
+ 0x0000, 0x0000, 0xfffe, 0xffff,
+ 0x0000, 0x0000, 0x0000, 0xfffe,
+ 0x4000, 0x0000, 0x0000, 0x0000,
+ 0xb032, 0x3e00, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0006,
+ 0x0001, 0x0000, 0x574d, 0x4c13,
+ 0x0000, 0x0000, 0x0000
+};
+
+/* virtual HP mixers regs */
+#define HPL_MIXER 0x80
+#define HPR_MIXER 0x82
+#define MICB_MUX 0x82
+
+static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
+static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
+static const char *wm9713_rec_src[] =
+ {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker",
+ "Mono Out", "Zh"};
+static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
+static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
+static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv",
+ "Mono Vmid", "Inv Vmid"};
+static const char *wm9713_spk_pga[] =
+ {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
+ "Speaker Vmid", "Inv Vmid"};
+static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone",
+ "Headphone Vmid"};
+static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"};
+static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"};
+static const char *wm9713_dac_inv[] =
+ {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone",
+ "Headphone Mono", "NC", "Vmid"};
+static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"};
+static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"};
+static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"};
+static const char *wm9713_micb_select[] = {"MPB", "MPA"};
+
+static const struct soc_enum wm9713_enum[] = {
+SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/
+SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
+SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
+SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
+SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
+};
+
+static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
+SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1),
+SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
+SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
+SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
+SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
+
+SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
+SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
+
+SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
+SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
+SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
+SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
+
+SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
+SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
+SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
+
+SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
+SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
+SOC_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0),
+SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
+SOC_ENUM("ALC Function", wm9713_enum[6]),
+SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
+SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
+SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
+SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
+SOC_ENUM("ALC NG Type", wm9713_enum[17]),
+SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
+
+SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0),
+SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
+
+SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
+SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1),
+
+SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
+SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
+SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1),
+
+SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1),
+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
+SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
+
+SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
+SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
+SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
+
+SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
+SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
+SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
+
+SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
+SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
+SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
+
+SOC_ENUM("Bass Control", wm9713_enum[16]),
+SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
+SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
+SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
+SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
+SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
+
+SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
+SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
+SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
+};
+
+/* add non dapm controls */
+static int wm9713_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm9713_snd_ac97_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* We have to create a fake left and right HP mixers because
+ * the codec only has a single control that is shared by both channels.
+ * This makes it impossible to determine the audio path using the current
+ * register map, thus we add a new (virtual) register to help determine the
+ * audio route within the device.
+ */
+static int mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u16 l, r, beep, tone, phone, rec, pcm, aux;
+
+ l = ac97_read(w->codec, HPL_MIXER);
+ r = ac97_read(w->codec, HPR_MIXER);
+ beep = ac97_read(w->codec, AC97_PC_BEEP);
+ tone = ac97_read(w->codec, AC97_MASTER_TONE);
+ phone = ac97_read(w->codec, AC97_PHONE);
+ rec = ac97_read(w->codec, AC97_REC_SEL);
+ pcm = ac97_read(w->codec, AC97_PCM);
+ aux = ac97_read(w->codec, AC97_AUX);
+
+ if (event & SND_SOC_DAPM_PRE_REG)
+ return 0;
+ if ((l & 0x1) || (r & 0x1))
+ ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+
+ if ((l & 0x2) || (r & 0x2))
+ ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
+
+ if ((l & 0x4) || (r & 0x4))
+ ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+
+ if ((l & 0x8) || (r & 0x8))
+ ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
+
+ if ((l & 0x10) || (r & 0x10))
+ ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+
+ if ((l & 0x20) || (r & 0x20))
+ ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
+ else
+ ac97_write(w->codec, AC97_AUX, aux | 0x8000);
+
+ return 0;
+}
+
+/* Left Headphone Mixers */
+static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
+SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
+SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
+};
+
+/* Right Headphone Mixers */
+static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
+SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
+SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
+};
+
+/* headphone capture mux */
+static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[1]);
+
+/* headphone mic mux */
+static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[0]);
+
+/* Speaker Mixer */
+static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1),
+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1),
+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1),
+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1),
+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1),
+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1),
+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1),
+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1),
+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1),
+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1),
+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1),
+SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1),
+SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1),
+};
+
+/* mono mic mux */
+static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[2]);
+
+/* mono output mux */
+static const struct snd_kcontrol_new wm9713_mono_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[7]);
+
+/* speaker left output mux */
+static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[8]);
+
+/* speaker right output mux */
+static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[9]);
+
+/* headphone left output mux */
+static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[10]);
+
+/* headphone right output mux */
+static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[11]);
+
+/* Out3 mux */
+static const struct snd_kcontrol_new wm9713_out3_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[12]);
+
+/* Out4 mux */
+static const struct snd_kcontrol_new wm9713_out4_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[13]);
+
+/* DAC inv mux 1 */
+static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[14]);
+
+/* DAC inv mux 2 */
+static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[15]);
+
+/* Capture source left */
+static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[3]);
+
+/* Capture source right */
+static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[4]);
+
+/* mic source */
+static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[18]);
+
+/* mic source B virtual control */
+static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[19]);
+
+static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_rec_mux_controls),
+SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_mic_mux_controls),
+SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_mono_mic_mux_controls),
+SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_mono_mux_controls),
+SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_spkl_mux_controls),
+SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_spkr_mux_controls),
+SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hpl_out_mux_controls),
+SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hpr_out_mux_controls),
+SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_out3_mux_controls),
+SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_out4_mux_controls),
+SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0,
+ &wm9713_dac_inv1_mux_controls),
+SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0,
+ &wm9713_dac_inv2_mux_controls),
+SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_rec_srcl_mux_controls),
+SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_rec_srcr_mux_controls),
+SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_mic_sel_mux_controls),
+SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_micb_sel_mux_controls),
+SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
+ &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
+ mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
+ &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
+ mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
+ &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
+ &wm9713_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm9713_speaker_mixer_controls)),
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1),
+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
+SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1),
+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1),
+SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1),
+SND_SOC_DAPM_OUTPUT("MONO"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("SPKL"),
+SND_SOC_DAPM_OUTPUT("SPKR"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_INPUT("LINEL"),
+SND_SOC_DAPM_INPUT("LINER"),
+SND_SOC_DAPM_INPUT("MONOIN"),
+SND_SOC_DAPM_INPUT("PCBEEP"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2A"),
+SND_SOC_DAPM_INPUT("MIC2B"),
+SND_SOC_DAPM_VMID("VMID"),
+};
+
+static const char *audio_map[][3] = {
+ /* left HP mixer */
+ {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"},
+ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
+ {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"},
+ {"Left HP Mixer", NULL, "Capture Headphone Mux"},
+
+ /* right HP mixer */
+ {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"},
+ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
+ {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"},
+ {"Right HP Mixer", NULL, "Capture Headphone Mux"},
+
+ /* virtual mixer - mixes left & right channels for spk and mono */
+ {"AC97 Mixer", NULL, "Left DAC"},
+ {"AC97 Mixer", NULL, "Right DAC"},
+ {"Line Mixer", NULL, "Right Line In"},
+ {"Line Mixer", NULL, "Left Line In"},
+ {"HP Mixer", NULL, "Left HP Mixer"},
+ {"HP Mixer", NULL, "Right HP Mixer"},
+ {"Capture Mixer", NULL, "Left Capture Source"},
+ {"Capture Mixer", NULL, "Right Capture Source"},
+
+ /* speaker mixer */
+ {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"},
+ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"},
+
+ /* mono mixer */
+ {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"},
+ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"},
+ {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"},
+ {"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"},
+ {"Mono Mixer", NULL, "Capture Mono Mux"},
+
+ /* DAC inv mux 1 */
+ {"DAC Inv Mux 1", "Mono", "Mono Mixer"},
+ {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"},
+ {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"},
+ {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"},
+ {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"},
+
+ /* DAC inv mux 2 */
+ {"DAC Inv Mux 2", "Mono", "Mono Mixer"},
+ {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"},
+ {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"},
+ {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"},
+ {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"},
+
+ /* headphone left mux */
+ {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"},
+
+ /* headphone right mux */
+ {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"},
+
+ /* speaker left mux */
+ {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"},
+ {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"},
+ {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"},
+
+ /* speaker right mux */
+ {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"},
+ {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"},
+ {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"},
+
+ /* mono mux */
+ {"Mono Out Mux", "Mono", "Mono Mixer"},
+ {"Mono Out Mux", "Inv", "DAC Inv Mux 1"},
+
+ /* out 3 mux */
+ {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"},
+
+ /* out 4 mux */
+ {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"},
+
+ /* output pga */
+ {"HPL", NULL, "Left Headphone"},
+ {"Left Headphone", NULL, "Left Headphone Out Mux"},
+ {"HPR", NULL, "Right Headphone"},
+ {"Right Headphone", NULL, "Right Headphone Out Mux"},
+ {"OUT3", NULL, "Out 3"},
+ {"Out 3", NULL, "Out 3 Mux"},
+ {"OUT4", NULL, "Out 4"},
+ {"Out 4", NULL, "Out 4 Mux"},
+ {"SPKL", NULL, "Left Speaker"},
+ {"Left Speaker", NULL, "Left Speaker Out Mux"},
+ {"SPKR", NULL, "Right Speaker"},
+ {"Right Speaker", NULL, "Right Speaker Out Mux"},
+ {"MONO", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono Out Mux"},
+
+ /* input pga */
+ {"Left Line In", NULL, "LINEL"},
+ {"Right Line In", NULL, "LINER"},
+ {"Mono In", NULL, "MONOIN"},
+ {"Mic A PGA", NULL, "Mic A Pre Amp"},
+ {"Mic B PGA", NULL, "Mic B Pre Amp"},
+
+ /* left capture select */
+ {"Left Capture Source", "Mic 1", "Mic A Pre Amp"},
+ {"Left Capture Source", "Mic 2", "Mic B Pre Amp"},
+ {"Left Capture Source", "Line", "LINEL"},
+ {"Left Capture Source", "Mono In", "MONOIN"},
+ {"Left Capture Source", "Headphone", "Left HP Mixer"},
+ {"Left Capture Source", "Speaker", "Speaker Mixer"},
+ {"Left Capture Source", "Mono Out", "Mono Mixer"},
+
+ /* right capture select */
+ {"Right Capture Source", "Mic 1", "Mic A Pre Amp"},
+ {"Right Capture Source", "Mic 2", "Mic B Pre Amp"},
+ {"Right Capture Source", "Line", "LINER"},
+ {"Right Capture Source", "Mono In", "MONOIN"},
+ {"Right Capture Source", "Headphone", "Right HP Mixer"},
+ {"Right Capture Source", "Speaker", "Speaker Mixer"},
+ {"Right Capture Source", "Mono Out", "Mono Mixer"},
+
+ /* left ADC */
+ {"Left ADC", NULL, "Left Capture Source"},
+
+ /* right ADC */
+ {"Right ADC", NULL, "Right Capture Source"},
+
+ /* mic */
+ {"Mic A Pre Amp", NULL, "Mic A Source"},
+ {"Mic A Source", "Mic 1", "MIC1"},
+ {"Mic A Source", "Mic 2 A", "MIC2A"},
+ {"Mic A Source", "Mic 2 B", "Mic B Source"},
+ {"Mic B Pre Amp", "MPB", "Mic B Source"},
+ {"Mic B Source", NULL, "MIC2B"},
+
+ /* headphone capture */
+ {"Capture Headphone Mux", "Stereo", "Capture Mixer"},
+ {"Capture Headphone Mux", "Left", "Left Capture Source"},
+ {"Capture Headphone Mux", "Right", "Right Capture Source"},
+
+ /* mono capture */
+ {"Capture Mono Mux", "Stereo", "Capture Mixer"},
+ {"Capture Mono Mux", "Left", "Left Capture Source"},
+ {"Capture Mono Mux", "Right", "Right Capture Source"},
+
+ {NULL, NULL, NULL},
+};
+
+static int wm9713_add_widgets(struct snd_soc_codec *codec)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++)
+ snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
+
+ /* set up audio path audio_mapnects */
+ for (i = 0; audio_map[i][0] != NULL; i++)
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
+ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
+ reg == AC97_CD)
+ return soc_ac97_ops.read(codec->ac97, reg);
+ else {
+ reg = reg >> 1;
+
+ if (reg > (ARRAY_SIZE(wm9713_reg)))
+ return -EIO;
+
+ return cache[reg];
+ }
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg < 0x7c)
+ soc_ac97_ops.write(codec->ac97, reg, val);
+ reg = reg >> 1;
+ if (reg <= (ARRAY_SIZE(wm9713_reg)))
+ cache[reg] = val;
+
+ return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+ u32 divsel:1;
+ u32 divctl:1;
+ u32 lf:1;
+ u32 n:4;
+ u32 k:24;
+};
+
+/* The size in bits of the PLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 22) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+
+ /* The the PLL output is always 98.304MHz. */
+ target = 98304000;
+
+ /* If the input frequency is over 14.4MHz then scale it down. */
+ if (source > 14400000) {
+ source >>= 1;
+ pll_div->divsel = 1;
+
+ if (source > 14400000) {
+ source >>= 1;
+ pll_div->divctl = 1;
+ } else
+ pll_div->divctl = 0;
+
+ } else {
+ pll_div->divsel = 0;
+ pll_div->divctl = 0;
+ }
+
+ /* Low frequency sources require an additional divide in the
+ * loop.
+ */
+ if (source < 8192000) {
+ pll_div->lf = 1;
+ target >>= 2;
+ } else
+ pll_div->lf = 0;
+
+ Ndiv = target / source;
+ if ((Ndiv < 5) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM9713 PLL N value %d out of recommended range!\n",
+ Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+/**
+ * Please note that changing the PLL input frequency may require
+ * resynchronisation with the AC97 controller.
+ */
+static int wm9713_set_pll(struct snd_soc_codec *codec,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct wm9713_priv *wm9713 = codec->private_data;
+ u16 reg, reg2;
+ struct _pll_div pll_div;
+
+ /* turn PLL off ? */
+ if (freq_in == 0 || freq_out == 0) {
+ /* disable PLL power and select ext source */
+ reg = ac97_read(codec, AC97_HANDSET_RATE);
+ ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
+ reg = ac97_read(codec, AC97_EXTENDED_MID);
+ ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
+ wm9713->pll_out = 0;
+ return 0;
+ }
+
+ pll_factors(&pll_div, freq_in);
+
+ if (pll_div.k == 0) {
+ reg = (pll_div.n << 12) | (pll_div.lf << 11) |
+ (pll_div.divsel << 9) | (pll_div.divctl << 8);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+ } else {
+ /* write the fractional k to the reg 0x46 pages */
+ reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) |
+ (pll_div.divsel << 9) | (pll_div.divctl << 8);
+
+ /* K [21:20] */
+ reg = reg2 | (0x5 << 4) | (pll_div.k >> 20);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [19:16] */
+ reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [15:12] */
+ reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [11:8] */
+ reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [7:4] */
+ reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+ }
+
+ /* turn PLL on and select as source */
+ reg = ac97_read(codec, AC97_EXTENDED_MID);
+ ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
+ reg = ac97_read(codec, AC97_HANDSET_RATE);
+ ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
+ wm9713->pll_out = freq_out;
+ wm9713->pll_in = freq_in;
+
+ /* wait 10ms AC97 link frames for the link to stabilise */
+ schedule_timeout_interruptible(msecs_to_jiffies(10));
+ return 0;
+}
+
+static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
+}
+
+/*
+ * Tristate the PCM DAI lines, tristate can be disabled by calling
+ * wm9713_set_dai_fmt()
+ */
+static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
+ int tristate)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff;
+
+ if (tristate)
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
+
+ return 0;
+}
+
+/*
+ * Configure WM9713 clock dividers.
+ * Voice DAC needs 256 FS
+ */
+static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM9713_PCMCLK_DIV:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_CLKA_MULT:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_CLKB_MULT:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_HIFI_DIV:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_PCMBCLK_DIV:
+ reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff;
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div);
+ break;
+ case WM9713_PCMCLK_PLL_DIV:
+ reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
+ ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div);
+ break;
+ case WM9713_HIFI_PLL_DIV:
+ reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
+ ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5;
+ u16 reg = 0x8000;
+
+ /* clock masters */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ reg |= 0x4000;
+ gpio |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ reg |= 0x6000;
+ gpio |= 0x0018;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg |= 0x0200;
+ gpio |= 0x001a;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ gpio |= 0x0012;
+ break;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ reg |= 0x00c0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ reg |= 0x0040;
+ break;
+ }
+
+ /* DAI format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ reg |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg |= 0x0043;
+ break;
+ }
+
+ ac97_write(codec, AC97_GPIO_CFG, gpio);
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
+ return 0;
+}
+
+static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ reg |= 0x0004;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ reg |= 0x0008;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ reg |= 0x000c;
+ break;
+ }
+
+ /* enable PCM interface in master mode */
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
+ return 0;
+}
+
+static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 status;
+
+ /* Gracefully shut down the voice interface. */
+ status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
+ ac97_write(codec, AC97_HANDSET_RATE, 0x0280);
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ ac97_write(codec, AC97_HANDSET_RATE, 0x0F80);
+ ac97_write(codec, AC97_EXTENDED_MID, status);
+}
+
+static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg;
+ u16 vra;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = AC97_PCM_FRONT_DAC_RATE;
+ else
+ reg = AC97_PCM_LR_ADC_RATE;
+
+ return ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 vra, xsle;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+ xsle = ac97_read(codec, AC97_PCI_SID);
+ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENODEV;
+
+ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+}
+
+#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+
+#define WM9713_PCM_FORMATS \
+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
+
+struct snd_soc_codec_dai wm9713_dai[] = {
+{
+ .name = "AC97 HiFi",
+ .type = SND_SOC_DAI_AC97_BUS,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9713_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9713_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .prepare = ac97_hifi_prepare,},
+ .dai_ops = {
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,},
+ },
+ {
+ .name = "AC97 Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9713_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .prepare = ac97_aux_prepare,},
+ .dai_ops = {
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,},
+ },
+ {
+ .name = "WM9713 Voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9713_RATES,
+ .formats = WM9713_PCM_FORMATS,},
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9713_RATES,
+ .formats = WM9713_PCM_FORMATS,},
+ .ops = {
+ .hw_params = wm9713_pcm_hw_params,
+ .shutdown = wm9713_voiceshutdown,},
+ .dai_ops = {
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,
+ .set_fmt = wm9713_set_dai_fmt,
+ .set_tristate = wm9713_set_dai_tristate,
+ },
+ },
+};
+EXPORT_SYMBOL_GPL(wm9713_dai);
+
+int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
+{
+ if (try_warm && soc_ac97_ops.warm_reset) {
+ soc_ac97_ops.warm_reset(codec->ac97);
+ if (!(ac97_read(codec, 0) & 0x8000))
+ return 1;
+ }
+
+ soc_ac97_ops.reset(codec->ac97);
+ if (ac97_read(codec, 0) & 0x8000)
+ return -EIO;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm9713_reset);
+
+static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
+{
+ u16 reg;
+
+ switch (event) {
+ case SNDRV_CTL_POWER_D0: /* full On */
+ /* enable thermal shutdown */
+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
+ ac97_write(codec, AC97_EXTENDED_MID, reg);
+ break;
+ case SNDRV_CTL_POWER_D1: /* partial On */
+ case SNDRV_CTL_POWER_D2: /* partial On */
+ break;
+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ /* enable master bias and vmid */
+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
+ ac97_write(codec, AC97_EXTENDED_MID, reg);
+ ac97_write(codec, AC97_POWERDOWN, 0x0000);
+ break;
+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ /* disable everything including AC link */
+ ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
+ break;
+ }
+ codec->dapm_state = event;
+ return 0;
+}
+
+static int wm9713_soc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 reg;
+
+ /* Disable everything except touchpanel - that will be handled
+ * by the touch driver and left disabled if touch is not in
+ * use. */
+ reg = ac97_read(codec, AC97_EXTENDED_MID);
+ ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff);
+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
+ ac97_write(codec, AC97_POWERDOWN, 0x6f00);
+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
+
+ return 0;
+}
+
+static int wm9713_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm9713_priv *wm9713 = codec->private_data;
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+ ret = wm9713_reset(codec, 1);
+ if (ret < 0) {
+ printk(KERN_ERR "could not reset AC97 codec\n");
+ return ret;
+ }
+
+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+ /* do we need to re-start the PLL ? */
+ if (wm9713->pll_out)
+ wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out);
+
+ /* only synchronise the codec if warm reset failed */
+ if (ret == 0) {
+ for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) {
+ if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
+ i == AC97_EXTENDED_MSTATUS || i > 0x66)
+ continue;
+ soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ }
+ }
+
+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
+
+ return ret;
+}
+
+static int wm9713_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0, reg;
+
+ printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
+
+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (socdev->codec == NULL)
+ return -ENOMEM;
+ codec = socdev->codec;
+ mutex_init(&codec->mutex);
+
+ codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto cache_err;
+ }
+ codec->reg_cache_size = sizeof(wm9713_reg);
+ codec->reg_cache_step = 2;
+
+ codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
+ if (codec->private_data == NULL) {
+ ret = -ENOMEM;
+ goto priv_err;
+ }
+
+ codec->name = "WM9713";
+ codec->owner = THIS_MODULE;
+ codec->dai = wm9713_dai;
+ codec->num_dai = ARRAY_SIZE(wm9713_dai);
+ codec->write = ac97_write;
+ codec->read = ac97_read;
+ codec->dapm_event = wm9713_dapm_event;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ if (ret < 0)
+ goto codec_err;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0)
+ goto pcm_err;
+
+ /* do a cold reset for the controller and then try
+ * a warm reset followed by an optional cold reset for codec */
+ wm9713_reset(codec, 0);
+ ret = wm9713_reset(codec, 1);
+ if (ret < 0) {
+ printk(KERN_ERR "AC97 link error\n");
+ goto reset_err;
+ }
+
+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+ /* unmute the adc - move to kcontrol */
+ reg = ac97_read(codec, AC97_CD) & 0x7fff;
+ ac97_write(codec, AC97_CD, reg);
+
+ wm9713_add_controls(codec);
+ wm9713_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0)
+ goto reset_err;
+ return 0;
+
+reset_err:
+ snd_soc_free_pcms(socdev);
+
+pcm_err:
+ snd_soc_free_ac97_codec(codec);
+
+codec_err:
+ kfree(codec->private_data);
+
+priv_err:
+ kfree(codec->reg_cache);
+
+cache_err:
+ kfree(socdev->codec);
+ socdev->codec = NULL;
+ return ret;
+}
+
+static int wm9713_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec == NULL)
+ return 0;
+
+ snd_soc_dapm_free(socdev);
+ snd_soc_free_pcms(socdev);
+ snd_soc_free_ac97_codec(codec);
+ kfree(codec->private_data);
+ kfree(codec->reg_cache);
+ kfree(codec->dai);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm9713 = {
+ .probe = wm9713_soc_probe,
+ .remove = wm9713_soc_remove,
+ .suspend = wm9713_soc_suspend,
+ .resume = wm9713_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713);
+
+MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h
new file mode 100644
index 0000000..d357b6c
--- /dev/null
+++ b/sound/soc/codecs/wm9713.h
@@ -0,0 +1,53 @@
+/*
+ * wm9713.h -- WM9713 Soc Audio driver
+ */
+
+#ifndef _WM9713_H
+#define _WM9713_H
+
+/* clock inputs */
+#define WM9713_CLKA_PIN 0
+#define WM9713_CLKB_PIN 1
+
+/* clock divider ID's */
+#define WM9713_PCMCLK_DIV 0
+#define WM9713_CLKA_MULT 1
+#define WM9713_CLKB_MULT 2
+#define WM9713_HIFI_DIV 3
+#define WM9713_PCMBCLK_DIV 4
+#define WM9713_PCMCLK_PLL_DIV 5
+#define WM9713_HIFI_PLL_DIV 6
+
+/* Calculate the appropriate bit mask for the external PCM clock divider */
+#define WM9713_PCMDIV(x) ((x - 1) << 8)
+
+/* Calculate the appropriate bit mask for the external HiFi clock divider */
+#define WM9713_HIFIDIV(x) ((x - 1) << 12)
+
+/* MCLK clock mulitipliers */
+#define WM9713_CLKA_X1 (0 << 1)
+#define WM9713_CLKA_X2 (1 << 1)
+#define WM9713_CLKB_X1 (0 << 2)
+#define WM9713_CLKB_X2 (1 << 2)
+
+/* MCLK clock MUX */
+#define WM9713_CLK_MUX_A (0 << 0)
+#define WM9713_CLK_MUX_B (1 << 0)
+
+/* Voice DAI BCLK divider */
+#define WM9713_PCMBCLK_DIV_1 (0 << 9)
+#define WM9713_PCMBCLK_DIV_2 (1 << 9)
+#define WM9713_PCMBCLK_DIV_4 (2 << 9)
+#define WM9713_PCMBCLK_DIV_8 (3 << 9)
+#define WM9713_PCMBCLK_DIV_16 (4 << 9)
+
+#define WM9713_DAI_AC97_HIFI 0
+#define WM9713_DAI_AC97_AUX 1
+#define WM9713_DAI_PCM_VOICE 2
+
+extern struct snd_soc_codec_device soc_codec_dev_wm9713;
+extern struct snd_soc_codec_dai wm9713_dai[3];
+
+int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
+
+#endif
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
new file mode 100644
index 0000000..20680c5
--- /dev/null
+++ b/sound/soc/davinci/Kconfig
@@ -0,0 +1,19 @@
+config SND_DAVINCI_SOC
+ tristate "SoC Audio for the TI DAVINCI chip"
+ depends on ARCH_DAVINCI && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the DAVINCI AC97 or I2S interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_DAVINCI_SOC_I2S
+ tristate
+
+config SND_DAVINCI_SOC_EVM
+ tristate "SoC Audio support for DaVinci EVM"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_EVM
+ select SND_DAVINCI_SOC_I2S
+ select SND_SOC_TLV320AIC3X
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DaVinci EVM platform.
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
new file mode 100644
index 0000000..ca772e5
--- /dev/null
+++ b/sound/soc/davinci/Makefile
@@ -0,0 +1,11 @@
+# DAVINCI Platform Support
+snd-soc-davinci-objs := davinci-pcm.o
+snd-soc-davinci-i2s-objs := davinci-i2s.o
+
+obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o
+obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
+
+# DAVINCI Machine Support
+snd-soc-evm-objs := davinci-evm.o
+
+obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
new file mode 100644
index 0000000..fcd1652
--- /dev/null
+++ b/sound/soc/davinci/davinci-evm.c
@@ -0,0 +1,208 @@
+/*
+ * ASoC driver for TI DAVINCI EVM platform
+ *
+ * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/dma.h>
+#include <asm/arch/hardware.h>
+
+#include "../codecs/tlv320aic3x.h"
+#include "davinci-pcm.h"
+#include "davinci-i2s.h"
+
+#define EVM_CODEC_CLOCK 22579200
+
+static int evm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret = 0;
+
+ /* set codec DAI configuration */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF);
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock */
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops evm_ops = {
+ .hw_params = evm_hw_params,
+};
+
+/* davinci-evm machine dapm widgets */
+static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+/* davinci-evm machine audio_mapnections to the codec pins */
+static const char *audio_map[][3] = {
+ /* Headphone connected to HPLOUT, HPROUT */
+ {"Headphone Jack", NULL, "HPLOUT"},
+ {"Headphone Jack", NULL, "HPROUT"},
+
+ /* Line Out connected to LLOUT, RLOUT */
+ {"Line Out", NULL, "LLOUT"},
+ {"Line Out", NULL, "RLOUT"},
+
+ /* Mic connected to (MIC3L | MIC3R) */
+ {"MIC3L", NULL, "Mic Bias 2V"},
+ {"MIC3R", NULL, "Mic Bias 2V"},
+ {"Mic Bias 2V", NULL, "Mic Jack"},
+
+ /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
+ {"LINE1L", NULL, "Line In"},
+ {"LINE2L", NULL, "Line In"},
+ {"LINE1R", NULL, "Line In"},
+ {"LINE2R", NULL, "Line In"},
+
+ {NULL, NULL, NULL},
+};
+
+/* Logic for a aic3x as connected on a davinci-evm */
+static int evm_aic3x_init(struct snd_soc_codec *codec)
+{
+ int i;
+
+ /* Add davinci-evm specific widgets */
+ for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
+ snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+
+ /* Set up davinci-evm specific audio path audio_map */
+ for (i = 0; audio_map[i][0] != NULL; i++)
+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
+ audio_map[i][1], audio_map[i][2]);
+
+ /* not connected */
+ snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
+ snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
+ snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+
+ /* always connected */
+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+ snd_soc_dapm_set_endpoint(codec, "Line Out", 1);
+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
+ snd_soc_dapm_set_endpoint(codec, "Line In", 1);
+
+ snd_soc_dapm_sync_endpoints(codec);
+
+ return 0;
+}
+
+/* davinci-evm digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai = &davinci_i2s_dai,
+ .codec_dai = &aic3x_dai,
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+};
+
+/* davinci-evm audio machine driver */
+static struct snd_soc_machine snd_soc_machine_evm = {
+ .name = "DaVinci EVM",
+ .dai_link = &evm_dai,
+ .num_links = 1,
+};
+
+/* evm audio private data */
+static struct aic3x_setup_data evm_aic3x_setup = {
+ .i2c_address = 0x1b,
+};
+
+/* evm audio subsystem */
+static struct snd_soc_device evm_snd_devdata = {
+ .machine = &snd_soc_machine_evm,
+ .platform = &davinci_soc_platform,
+ .codec_dev = &soc_codec_dev_aic3x,
+ .codec_data = &evm_aic3x_setup,
+};
+
+static struct resource evm_snd_resources[] = {
+ {
+ .start = DAVINCI_MCBSP_BASE,
+ .end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct evm_snd_platform_data evm_snd_data = {
+ .tx_dma_ch = DM644X_DMACH_MCBSP_TX,
+ .rx_dma_ch = DM644X_DMACH_MCBSP_RX,
+};
+
+static struct platform_device *evm_snd_device;
+
+static int __init evm_init(void)
+{
+ int ret;
+
+ evm_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!evm_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(evm_snd_device, &evm_snd_devdata);
+ evm_snd_devdata.dev = &evm_snd_device->dev;
+ evm_snd_device->dev.platform_data = &evm_snd_data;
+
+ ret = platform_device_add_resources(evm_snd_device, evm_snd_resources,
+ ARRAY_SIZE(evm_snd_resources));
+ if (ret) {
+ platform_device_put(evm_snd_device);
+ return ret;
+ }
+
+ ret = platform_device_add(evm_snd_device);
+ if (ret)
+ platform_device_put(evm_snd_device);
+
+ return ret;
+}
+
+static void __exit evm_exit(void)
+{
+ platform_device_unregister(evm_snd_device);
+}
+
+module_init(evm_init);
+module_exit(evm_exit);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
new file mode 100644
index 0000000..c421774
--- /dev/null
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -0,0 +1,407 @@
+/*
+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
+ *
+ * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "davinci-pcm.h"
+
+#define DAVINCI_MCBSP_DRR_REG 0x00
+#define DAVINCI_MCBSP_DXR_REG 0x04
+#define DAVINCI_MCBSP_SPCR_REG 0x08
+#define DAVINCI_MCBSP_RCR_REG 0x0c
+#define DAVINCI_MCBSP_XCR_REG 0x10
+#define DAVINCI_MCBSP_SRGR_REG 0x14
+#define DAVINCI_MCBSP_PCR_REG 0x24
+
+#define DAVINCI_MCBSP_SPCR_RRST (1 << 0)
+#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4)
+#define DAVINCI_MCBSP_SPCR_XRST (1 << 16)
+#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20)
+#define DAVINCI_MCBSP_SPCR_GRST (1 << 22)
+#define DAVINCI_MCBSP_SPCR_FRST (1 << 23)
+#define DAVINCI_MCBSP_SPCR_FREE (1 << 25)
+
+#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5)
+#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8)
+#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
+#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
+
+#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
+#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8)
+#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16)
+#define DAVINCI_MCBSP_XCR_XFIG (1 << 18)
+#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21)
+
+#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8)
+#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
+#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
+
+#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0)
+#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
+#define DAVINCI_MCBSP_PCR_FSRP (1 << 2)
+#define DAVINCI_MCBSP_PCR_FSXP (1 << 3)
+#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8)
+#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9)
+#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
+#define DAVINCI_MCBSP_PCR_FSXM (1 << 11)
+
+#define MOD_REG_BIT(val, mask, set) do { \
+ if (set) { \
+ val |= mask; \
+ } else { \
+ val &= ~mask; \
+ } \
+} while (0)
+
+enum {
+ DAVINCI_MCBSP_WORD_8 = 0,
+ DAVINCI_MCBSP_WORD_12,
+ DAVINCI_MCBSP_WORD_16,
+ DAVINCI_MCBSP_WORD_20,
+ DAVINCI_MCBSP_WORD_24,
+ DAVINCI_MCBSP_WORD_32,
+};
+
+static struct davinci_pcm_dma_params davinci_i2s_pcm_out = {
+ .name = "I2S PCM Stereo out",
+};
+
+static struct davinci_pcm_dma_params davinci_i2s_pcm_in = {
+ .name = "I2S PCM Stereo in",
+};
+
+struct davinci_mcbsp_dev {
+ void __iomem *base;
+ struct clk *clk;
+ struct davinci_pcm_dma_params *dma_params[2];
+};
+
+static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
+ int reg, u32 val)
+{
+ __raw_writel(val, dev->base + reg);
+}
+
+static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg)
+{
+ return __raw_readl(dev->base + reg);
+}
+
+static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+ u32 w;
+
+ /* Start the sample generator and enable transmitter/receiver */
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
+ else
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+ /* Start frame sync */
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+}
+
+static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+ u32 w;
+
+ /* Reset transmitter/receiver and sample rate/frame sync generators */
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST |
+ DAVINCI_MCBSP_SPCR_FRST, 0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
+ else
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+}
+
+static int davinci_i2s_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+
+ cpu_dai->dma_data = dev->dma_params[substream->stream];
+
+ return 0;
+}
+
+static int davinci_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+ u32 w;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG,
+ DAVINCI_MCBSP_PCR_FSXM |
+ DAVINCI_MCBSP_PCR_FSRM |
+ DAVINCI_MCBSP_PCR_CLKXM |
+ DAVINCI_MCBSP_PCR_CLKRM);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
+ DAVINCI_MCBSP_SRGR_FSGM);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
+ DAVINCI_MCBSP_PCR_CLKRP, 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_FSXP |
+ DAVINCI_MCBSP_PCR_FSRP, 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
+ DAVINCI_MCBSP_PCR_CLKRP |
+ DAVINCI_MCBSP_PCR_FSXP |
+ DAVINCI_MCBSP_PCR_FSRP, 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
+ struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+ struct snd_interval *i = NULL;
+ int mcbsp_word_length;
+ u32 w;
+
+ /* general line settings */
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
+ DAVINCI_MCBSP_SPCR_RINTM(3) |
+ DAVINCI_MCBSP_SPCR_XINTM(3) |
+ DAVINCI_MCBSP_SPCR_FREE);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
+ DAVINCI_MCBSP_RCR_RFRLEN1(1) |
+ DAVINCI_MCBSP_RCR_RDATDLY(1));
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
+ DAVINCI_MCBSP_XCR_XFRLEN1(1) |
+ DAVINCI_MCBSP_XCR_XDATDLY(1) |
+ DAVINCI_MCBSP_XCR_XFIG);
+
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
+
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
+
+ /* Determine xfer data type */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ dma_params->data_type = 1;
+ mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dma_params->data_type = 2;
+ mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dma_params->data_type = 4;
+ mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
+ break;
+ default:
+ printk(KERN_WARNING "davinci-i2s: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
+
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+
+ return 0;
+}
+
+static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_mcbsp_start(substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_mcbsp_stop(substream);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int davinci_i2s_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct davinci_mcbsp_dev *dev;
+ struct resource *mem, *ioarea;
+ struct evm_snd_platform_data *pdata;
+ int ret;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+
+ ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "McBSP region already claimed\n");
+ return -EBUSY;
+ }
+
+ dev = kzalloc(sizeof(struct davinci_mcbsp_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_release_region;
+ }
+
+ cpu_dai->private_data = dev;
+
+ dev->clk = clk_get(&pdev->dev, "McBSPCLK");
+ if (IS_ERR(dev->clk)) {
+ ret = -ENODEV;
+ goto err_free_mem;
+ }
+ clk_enable(dev->clk);
+
+ dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+ pdata = pdev->dev.platform_data;
+
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out;
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch;
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr =
+ (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
+
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in;
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch;
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr =
+ (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
+
+ return 0;
+
+err_free_mem:
+ kfree(dev);
+err_release_region:
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+ return ret;
+}
+
+static void davinci_i2s_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+ struct resource *mem;
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ dev->clk = NULL;
+
+ kfree(dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+}
+
+#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
+
+struct snd_soc_cpu_dai davinci_i2s_dai = {
+ .name = "davinci-i2s",
+ .id = 0,
+ .type = SND_SOC_DAI_I2S,
+ .probe = davinci_i2s_probe,
+ .remove = davinci_i2s_remove,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .startup = davinci_i2s_startup,
+ .trigger = davinci_i2s_trigger,
+ .hw_params = davinci_i2s_hw_params,},
+ .dai_ops = {
+ .set_fmt = davinci_i2s_set_dai_fmt,
+ },
+};
+EXPORT_SYMBOL_GPL(davinci_i2s_dai);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
new file mode 100644
index 0000000..9592d17
--- /dev/null
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -0,0 +1,17 @@
+/*
+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
+ *
+ * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DAVINCI_I2S_H
+#define _DAVINCI_I2S_H
+
+extern struct snd_soc_cpu_dai davinci_i2s_dai;
+
+#endif
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
new file mode 100644
index 0000000..6a76927
--- /dev/null
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -0,0 +1,389 @@
+/*
+ * ALSA PCM interface for the TI DAVINCI processor
+ *
+ * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+
+#include "davinci-pcm.h"
+
+#define DAVINCI_PCM_DEBUG 0
+#if DAVINCI_PCM_DEBUG
+#define DPRINTK(x...) printk(KERN_DEBUG x)
+#else
+#define DPRINTK(x...)
+#endif
+
+static struct snd_pcm_hardware davinci_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+struct davinci_runtime_data {
+ spinlock_t lock;
+ int period; /* current DMA period */
+ int master_lch; /* Master DMA channel */
+ int slave_lch; /* Slave DMA channel */
+ struct davinci_pcm_dma_params *params; /* DMA params */
+};
+
+static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int lch = prtd->slave_lch;
+ unsigned int period_size;
+ unsigned int dma_offset;
+ dma_addr_t dma_pos;
+ dma_addr_t src, dst;
+ unsigned short src_bidx, dst_bidx;
+ unsigned int data_type;
+ unsigned int count;
+
+ period_size = snd_pcm_lib_period_bytes(substream);
+ dma_offset = prtd->period * period_size;
+ dma_pos = runtime->dma_addr + dma_offset;
+
+ DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x "
+ "period_size=%x\n", lch, dma_pos, period_size);
+
+ data_type = prtd->params->data_type;
+ count = period_size / data_type;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ src = dma_pos;
+ dst = prtd->params->dma_addr;
+ src_bidx = data_type;
+ dst_bidx = 0;
+ } else {
+ src = prtd->params->dma_addr;
+ dst = dma_pos;
+ src_bidx = 0;
+ dst_bidx = data_type;
+ }
+
+ davinci_set_dma_src_params(lch, src, INCR, W8BIT);
+ davinci_set_dma_dest_params(lch, dst, INCR, W8BIT);
+ davinci_set_dma_src_index(lch, src_bidx, 0);
+ davinci_set_dma_dest_index(lch, dst_bidx, 0);
+ davinci_set_dma_transfer_params(lch, data_type, count, 1, 0, ASYNC);
+
+ prtd->period++;
+ if (unlikely(prtd->period >= runtime->periods))
+ prtd->period = 0;
+}
+
+static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data)
+{
+ struct snd_pcm_substream *substream = data;
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+
+ DPRINTK("lch=%d, status=0x%x\n", lch, ch_status);
+
+ if (unlikely(ch_status != DMA_COMPLETE))
+ return;
+
+ if (snd_pcm_running(substream)) {
+ snd_pcm_period_elapsed(substream);
+
+ spin_lock(&prtd->lock);
+ davinci_pcm_enqueue_dma(substream);
+ spin_unlock(&prtd->lock);
+ }
+}
+
+static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data;
+ int tcc = TCC_ANY;
+ int ret;
+
+ if (!dma_data)
+ return -ENODEV;
+
+ prtd->params = dma_data;
+
+ /* Request master DMA channel */
+ ret = davinci_request_dma(prtd->params->channel, prtd->params->name,
+ davinci_pcm_dma_irq, substream,
+ &prtd->master_lch, &tcc, EVENTQ_0);
+ if (ret)
+ return ret;
+
+ /* Request slave DMA channel */
+ ret = davinci_request_dma(PARAM_ANY, "Link",
+ NULL, NULL, &prtd->slave_lch, &tcc, EVENTQ_0);
+ if (ret) {
+ davinci_free_dma(prtd->master_lch);
+ return ret;
+ }
+
+ /* Link slave DMA channel in loopback */
+ davinci_dma_link_lch(prtd->slave_lch, prtd->slave_lch);
+
+ return 0;
+}
+
+static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ spin_lock(&prtd->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_start_dma(prtd->master_lch);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_stop_dma(prtd->master_lch);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ struct paramentry_descriptor temp;
+
+ prtd->period = 0;
+ davinci_pcm_enqueue_dma(substream);
+
+ /* Get slave channel dma params for master channel startup */
+ davinci_get_dma_params(prtd->slave_lch, &temp);
+ davinci_set_dma_params(prtd->master_lch, &temp);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+davinci_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct davinci_runtime_data *prtd = runtime->private_data;
+ unsigned int offset;
+ dma_addr_t count;
+ dma_addr_t src, dst;
+
+ spin_lock(&prtd->lock);
+
+ davinci_dma_getposition(prtd->master_lch, &src, &dst);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ count = src - runtime->dma_addr;
+ else
+ count = dst - runtime->dma_addr;;
+
+ spin_unlock(&prtd->lock);
+
+ offset = bytes_to_frames(runtime, count);
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+static int davinci_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct davinci_runtime_data *prtd;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware);
+
+ prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&prtd->lock);
+
+ runtime->private_data = prtd;
+
+ ret = davinci_pcm_dma_request(substream);
+ if (ret) {
+ printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n");
+ kfree(prtd);
+ }
+
+ return ret;
+}
+
+static int davinci_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct davinci_runtime_data *prtd = runtime->private_data;
+
+ davinci_dma_unlink_lch(prtd->slave_lch, prtd->slave_lch);
+
+ davinci_free_dma(prtd->slave_lch);
+ davinci_free_dma(prtd->master_lch);
+
+ kfree(prtd);
+
+ return 0;
+}
+
+static int davinci_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int davinci_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int davinci_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+struct snd_pcm_ops davinci_pcm_ops = {
+ .open = davinci_pcm_open,
+ .close = davinci_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = davinci_pcm_hw_params,
+ .hw_free = davinci_pcm_hw_free,
+ .prepare = davinci_pcm_prepare,
+ .trigger = davinci_pcm_trigger,
+ .pointer = davinci_pcm_pointer,
+ .mmap = davinci_pcm_mmap,
+};
+
+static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = davinci_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+
+ DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
+ (void *) buf->area, (void *) buf->addr, size);
+
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void davinci_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static u64 davinci_pcm_dmamask = 0xffffffff;
+
+static int davinci_pcm_new(struct snd_card *card,
+ struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+{
+ int ret;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &davinci_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = davinci_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ return ret;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = davinci_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+struct snd_soc_platform davinci_soc_platform = {
+ .name = "davinci-audio",
+ .pcm_ops = &davinci_pcm_ops,
+ .pcm_new = davinci_pcm_new,
+ .pcm_free = davinci_pcm_free,
+};
+EXPORT_SYMBOL_GPL(davinci_soc_platform);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
new file mode 100644
index 0000000..8d6a45e
--- /dev/null
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -0,0 +1,29 @@
+/*
+ * ALSA PCM interface for the TI DAVINCI processor
+ *
+ * Author: Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DAVINCI_PCM_H
+#define _DAVINCI_PCM_H
+
+struct davinci_pcm_dma_params {
+ char *name; /* stream identifier */
+ int channel; /* sync dma channel ID */
+ dma_addr_t dma_addr; /* device physical address for DMA */
+ unsigned int data_type; /* xfer data type */
+};
+
+struct evm_snd_platform_data {
+ int tx_dma_ch;
+ int rx_dma_ch;
+};
+
+extern struct snd_soc_platform davinci_soc_platform;
+
+#endif
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 652514f..78de716 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -20,7 +20,6 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 145ad13..b2a11b0 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -15,7 +15,6 @@
#include <linux/device.h>
#include <linux/delay.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 1a70a6a..7f32a11 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -297,21 +297,19 @@
/* Add corgi specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8731_corgi_controls[i],codec, NULL));
+ snd_soc_cnew(&wm8731_corgi_controls[i], codec, NULL));
if (err < 0)
return err;
}
/* Add corgi specific widgets */
- for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
+ for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
- }
/* Set up corgi specific audio path audio_map */
- for(i = 0; audio_map[i][0] != NULL; i++) {
+ for (i = 0; audio_map[i][0] != NULL; i++)
snd_soc_dapm_connect_input(codec, audio_map[i][0],
audio_map[i][1], audio_map[i][2]);
- }
snd_soc_dapm_sync_endpoints(codec);
return 0;
@@ -353,7 +351,8 @@
{
int ret;
- if (!(machine_is_corgi() || machine_is_shepherd() || machine_is_husky()))
+ if (!(machine_is_corgi() || machine_is_shepherd() ||
+ machine_is_husky()))
return -ENODEV;
corgi_snd_device = platform_device_alloc("soc-audio", -1);
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 4fbf8bb..7e830b2 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -257,21 +257,19 @@
/* Add poodle specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8731_poodle_controls[i],codec, NULL));
+ snd_soc_cnew(&wm8731_poodle_controls[i], codec, NULL));
if (err < 0)
return err;
}
/* Add poodle specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
+ for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
- }
/* Set up poodle specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++) {
+ for (i = 0; audio_map[i][0] != NULL; i++)
snd_soc_dapm_connect_input(codec, audio_map[i][0],
audio_map[i][1], audio_map[i][2]);
- }
snd_soc_dapm_sync_endpoints(codec);
return 0;
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index e173799..97ec2d9 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -61,7 +61,7 @@
mutex_lock(&car_mutex);
/* set up primary or secondary codec/modem space */
-#ifdef CONFIG_PXA27x
+#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
#else
if (reg == AC97_GPIO_STATUS)
@@ -87,7 +87,7 @@
wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
if (!((GSR | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n",
- __FUNCTION__, reg, GSR | gsr_bits);
+ __func__, reg, GSR | gsr_bits);
val = -1;
goto out;
}
@@ -111,7 +111,7 @@
mutex_lock(&car_mutex);
/* set up primary or secondary codec/modem space */
-#ifdef CONFIG_PXA27x
+#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
#else
if (reg == AC97_GPIO_STATUS)
@@ -127,13 +127,16 @@
wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1);
if (!((GSR | gsr_bits) & GSR_CDONE))
printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n",
- __FUNCTION__, reg, GSR | gsr_bits);
+ __func__, reg, GSR | gsr_bits);
mutex_unlock(&car_mutex);
}
static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
{
+#ifdef CONFIG_PXA3xx
+ int timeout = 100;
+#endif
gsr_bits = 0;
#ifdef CONFIG_PXA27x
@@ -144,6 +147,11 @@
GCR |= GCR_WARM_RST;
pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
udelay(500);
+#elif defined(CONFIG_PXA3xx)
+ /* Can't use interrupts */
+ GCR |= GCR_WARM_RST;
+ while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(1);
#else
GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
@@ -151,7 +159,7 @@
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
- __FUNCTION__, gsr_bits);
+ __func__, gsr_bits);
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
@@ -159,6 +167,16 @@
static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
{
+#ifdef CONFIG_PXA3xx
+ int timeout = 1000;
+
+ /* Hold CLKBPB for 100us */
+ GCR = 0;
+ GCR = GCR_CLKBPB;
+ udelay(100);
+ GCR = 0;
+#endif
+
GCR &= GCR_COLD_RST; /* clear everything but nCRST */
GCR &= ~GCR_COLD_RST; /* then assert nCRST */
@@ -170,6 +188,13 @@
clk_disable(ac97conf_clk);
GCR = GCR_COLD_RST;
udelay(50);
+#elif defined(CONFIG_PXA3xx)
+ /* Can't use interrupts on PXA3xx */
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+
+ GCR = GCR_WARM_RST | GCR_COLD_RST;
+ while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(10);
#else
GCR = GCR_COLD_RST;
GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
@@ -178,7 +203,7 @@
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
- __FUNCTION__, gsr_bits);
+ __func__, gsr_bits);
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index daeaa4c..01ad7bf 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -64,8 +64,8 @@
if (dcsr & DCSR_ENDINTR) {
snd_pcm_period_elapsed(substream);
} else {
- printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
- prtd->params->name, dma_ch, dcsr );
+ printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+ prtd->params->name, dma_ch, dcsr);
}
}
@@ -84,8 +84,8 @@
/* return if this is a bufferless transfer e.g.
* codec <--> BT codec or GSM modem -- lg FIXME */
- if (!dma)
- return 0;
+ if (!dma)
+ return 0;
/* this may get called several times by oss emulation
* with different params */
@@ -363,7 +363,6 @@
.pcm_new = pxa2xx_pcm_new,
.pcm_free = pxa2xx_pcm_free_dma_buffers,
};
-
EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
MODULE_AUTHOR("Nicolas Pitre");
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index ecca390..d8b8372 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -313,15 +313,13 @@
}
/* Add spitz specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) {
+ for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
- }
/* Set up spitz specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++) {
+ for (i = 0; audio_map[i][0] != NULL; i++)
snd_soc_dapm_connect_input(codec, audio_map[i][0],
audio_map[i][1], audio_map[i][2]);
- }
snd_soc_dapm_sync_endpoints(codec);
return 0;
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 6ee115c..962cc20 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -659,6 +659,7 @@
static void __exit neo1973_exit(void)
{
+ i2c_del_driver(&lm4857_i2c_driver);
platform_device_unregister(neo1973_snd_device);
}
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 0a3c630..4ebcd6a 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -25,6 +25,7 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/jiffies.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -32,7 +33,6 @@
#include <sound/soc.h>
#include <asm/hardware.h>
-#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-clock.h>
#include <asm/arch/audio.h>
@@ -46,7 +46,7 @@
#define S3C24XX_I2S_DEBUG 0
#if S3C24XX_I2S_DEBUG
-#define DBG(x...) printk(KERN_DEBUG x)
+#define DBG(x...) printk(KERN_DEBUG "s3c24xx-i2s: " x)
#else
#define DBG(x...)
#endif
@@ -89,7 +89,7 @@
u32 iiscon;
u32 iismod;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
@@ -134,7 +134,7 @@
u32 iiscon;
u32 iismod;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
@@ -159,10 +159,10 @@
* DMA engine will simply freeze randomly.
*/
- iisfcon &= ~S3C2410_IISFCON_RXENABLE;
- iisfcon &= ~S3C2410_IISFCON_RXDMA;
- iiscon |= S3C2410_IISCON_RXIDLE;
- iiscon &= ~S3C2410_IISCON_RXDMAEN;
+ iisfcon &= ~S3C2410_IISFCON_RXENABLE;
+ iisfcon &= ~S3C2410_IISFCON_RXDMA;
+ iiscon |= S3C2410_IISCON_RXIDLE;
+ iiscon &= ~S3C2410_IISCON_RXDMAEN;
iismod &= ~S3C2410_IISMOD_RXMODE;
writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
@@ -182,7 +182,7 @@
u32 iiscon;
unsigned long timeout = jiffies + msecs_to_jiffies(5);
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
while (1) {
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
@@ -201,7 +201,7 @@
*/
static inline int s3c24xx_snd_is_clkmaster(void)
{
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
}
@@ -214,7 +214,7 @@
{
u32 iismod;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
DBG("hw_params r: IISMOD: %lx \n", iismod);
@@ -250,7 +250,7 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
u32 iismod;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;
@@ -278,7 +278,7 @@
{
int ret = 0;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -320,7 +320,7 @@
{
u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
iismod &= ~S3C2440_IISMOD_MPLL;
@@ -346,7 +346,7 @@
{
u32 reg;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
switch (div_id) {
case S3C24XX_DIV_BCLK:
@@ -381,13 +381,13 @@
static int s3c24xx_i2s_probe(struct platform_device *pdev)
{
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
if (s3c24xx_i2s.regs == NULL)
return -ENXIO;
- s3c24xx_i2s.iis_clk=clk_get(&pdev->dev, "iis");
+ s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis");
if (s3c24xx_i2s.iis_clk == NULL) {
DBG("failed to get iis_clock\n");
iounmap(s3c24xx_i2s.regs);
@@ -411,9 +411,11 @@
}
#ifdef CONFIG_PM
-int s3c24xx_i2s_suspend(struct platform_device *pdev,
+static int s3c24xx_i2s_suspend(struct platform_device *pdev,
struct snd_soc_cpu_dai *cpu_dai)
{
+ DBG("Entered %s\n", __func__);
+
s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
@@ -424,9 +426,10 @@
return 0;
}
-int s3c24xx_i2s_resume(struct platform_device *pdev,
+static int s3c24xx_i2s_resume(struct platform_device *pdev,
struct snd_soc_cpu_dai *cpu_dai)
{
+ DBG("Entered %s\n", __func__);
clk_enable(s3c24xx_i2s.iis_clk);
writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 29a6c82f..49580fb 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -39,7 +39,7 @@
#define S3C24XX_PCM_DEBUG 0
#if S3C24XX_PCM_DEBUG
-#define DBG(x...) printk(KERN_DEBUG x)
+#define DBG(x...) printk(KERN_DEBUG "s3c24xx-pcm: " x)
#else
#define DBG(x...)
#endif
@@ -88,7 +88,7 @@
dma_addr_t pos = prtd->dma_pos;
int ret;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
while (prtd->dma_loaded < prtd->dma_limit) {
unsigned long len = prtd->dma_period;
@@ -98,7 +98,7 @@
if ((pos + len) > prtd->dma_end) {
len = prtd->dma_end - pos;
DBG(KERN_DEBUG "%s: corrected dma len %ld\n",
- __FUNCTION__, len);
+ __func__, len);
}
ret = s3c2410_dma_enqueue(prtd->params->channel,
@@ -123,7 +123,7 @@
struct snd_pcm_substream *substream = dev_id;
struct s3c24xx_runtime_data *prtd;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
return;
@@ -152,7 +152,7 @@
unsigned long totbytes = params_buffer_bytes(params);
int ret=0;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
/* return if this is a bufferless transfer e.g.
* codec <--> BT codec or GSM modem -- lg FIXME */
@@ -200,7 +200,7 @@
{
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
/* TODO - do we need to ensure DMA flushed */
snd_pcm_set_runtime_buffer(substream, NULL);
@@ -218,7 +218,7 @@
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
/* return if this is a bufferless transfer e.g.
* codec <--> BT codec or GSM modem -- lg FIXME */
@@ -263,7 +263,7 @@
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
spin_lock(&prtd->lock);
@@ -301,7 +301,7 @@
unsigned long res;
dma_addr_t src, dst;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
spin_lock(&prtd->lock);
s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
@@ -334,7 +334,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
@@ -353,7 +353,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd = runtime->private_data;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
if (prtd)
kfree(prtd);
@@ -368,7 +368,7 @@
{
struct snd_pcm_runtime *runtime = substream->runtime;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area,
@@ -394,7 +394,7 @@
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = s3c24xx_pcm_hardware.buffer_bytes_max;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
@@ -413,7 +413,7 @@
struct snd_dma_buffer *buf;
int stream;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
@@ -437,7 +437,7 @@
{
int ret = 0;
- DBG("Entered %s\n", __FUNCTION__);
+ DBG("Entered %s\n", __func__);
if (!card->dev->dma_mask)
card->dev->dma_mask = &s3c24xx_pcm_dmamask;
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index f03220d2..4c1e013 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -1,4 +1,5 @@
menu "SoC Audio support for SuperH"
+ depends on SUPERH
config SND_SOC_PCM_SH7760
tristate "SoC Audio support for Renesas SH7760"
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 9eb5479..e148db9 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -839,6 +839,7 @@
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
+ .owner = THIS_MODULE,
},
.probe = soc_probe,
.remove = soc_remove,
@@ -1601,3 +1602,4 @@
MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
MODULE_DESCRIPTION("ALSA SoC Core");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-audio");
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 620d7ea..af3326c 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -226,7 +226,7 @@
snd_soc_write(codec, widget->reg, new);
pop_wait(POP_TIME);
}
- dbg("reg old %x new %x change %d\n", old, new, change);
+ dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change);
return change;
}
@@ -1288,7 +1288,7 @@
mutex_unlock(&codec->mutex);
dapm_power_widgets(codec, event);
- dump_dapm(codec, __FUNCTION__);
+ dump_dapm(codec, __func__);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
@@ -1334,10 +1334,11 @@
list_for_each_entry(w, &codec->dapm_widgets, list) {
if (!strcmp(w->name, endpoint)) {
w->connected = status;
+ return 0;
}
}
- return 0;
+ return -ENODEV;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
index 89d6e9c..09802e8 100644
--- a/sound/spi/at73c213.c
+++ b/sound/spi/at73c213.c
@@ -118,7 +118,7 @@
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 8000, /* Replaced by chip->bitrate later. */
.rate_max = 50000, /* Replaced by chip->bitrate later. */
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 64 * 1024 - 1,
.period_bytes_min = 512,
@@ -133,7 +133,8 @@
static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip)
{
unsigned long ssc_rate = clk_get_rate(chip->ssc->clk);
- unsigned long dac_rate_new, ssc_div, status;
+ unsigned long dac_rate_new, ssc_div;
+ int status;
unsigned long ssc_div_max, ssc_div_min;
int max_tries;
@@ -209,7 +210,13 @@
{
struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+ /* ensure buffer_size is a multiple of period_size */
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
snd_at73c213_playback_hw.rate_min = chip->bitrate;
snd_at73c213_playback_hw.rate_max = chip->bitrate;
runtime->hw = snd_at73c213_playback_hw;
@@ -228,6 +235,14 @@
static int snd_at73c213_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
+ struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
+ int channels = params_channels(hw_params);
+ int val;
+
+ val = ssc_readl(chip->ssc->regs, TFMR);
+ val = SSC_BFINS(TFMR_DATNB, channels - 1, val);
+ ssc_writel(chip->ssc->regs, TFMR, val);
+
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
@@ -249,10 +264,12 @@
ssc_writel(chip->ssc->regs, PDC_TPR,
(long)runtime->dma_addr);
- ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2);
+ ssc_writel(chip->ssc->regs, PDC_TCR,
+ runtime->period_size * runtime->channels);
ssc_writel(chip->ssc->regs, PDC_TNPR,
(long)runtime->dma_addr + block_size);
- ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+ ssc_writel(chip->ssc->regs, PDC_TNCR,
+ runtime->period_size * runtime->channels);
return 0;
}
@@ -314,15 +331,6 @@
.pointer = snd_at73c213_pcm_pointer,
};
-static void snd_at73c213_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_at73c213 *chip = snd_pcm_chip(pcm);
- if (chip->pcm) {
- snd_pcm_lib_preallocate_free_for_all(chip->pcm);
- chip->pcm = NULL;
- }
-}
-
static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device)
{
struct snd_pcm *pcm;
@@ -334,7 +342,6 @@
goto out;
pcm->private_data = chip;
- pcm->private_free = snd_at73c213_pcm_free;
pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER;
strcpy(pcm->name, "at73c213");
chip->pcm = pcm;
@@ -375,7 +382,8 @@
ssc_writel(chip->ssc->regs, PDC_TNPR,
(long)runtime->dma_addr + offset);
- ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2);
+ ssc_writel(chip->ssc->regs, PDC_TNCR,
+ runtime->period_size * runtime->channels);
retval = IRQ_HANDLED;
}
@@ -737,7 +745,7 @@
/*
* Device functions
*/
-static int snd_at73c213_ssc_init(struct snd_at73c213 *chip)
+static int __devinit snd_at73c213_ssc_init(struct snd_at73c213 *chip)
{
/*
* Continuous clock output.
@@ -767,7 +775,7 @@
return 0;
}
-static int snd_at73c213_chip_init(struct snd_at73c213 *chip)
+static int __devinit snd_at73c213_chip_init(struct snd_at73c213 *chip)
{
int retval;
unsigned char dac_ctrl = 0;
@@ -933,7 +941,7 @@
return retval;
}
-static int snd_at73c213_probe(struct spi_device *spi)
+static int __devinit snd_at73c213_probe(struct spi_device *spi)
{
struct snd_card *card;
struct snd_at73c213 *chip;
diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c
index 9cc4cd8..24970a5 100644
--- a/sound/usb/caiaq/caiaq-audio.c
+++ b/sound/usb/caiaq/caiaq-audio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006,2007 Daniel Mack, Karsten Wiese
+ * Copyright (c) 2006-2008 Daniel Mack, Karsten Wiese
*
* 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
@@ -39,7 +39,8 @@
#define BYTES_PER_SAMPLE 3
#define BYTES_PER_SAMPLE_USB 4
#define MAX_BUFFER_SIZE (128*1024)
-
+#define MAX_ENDPOINT_SIZE 512
+
#define ENDPOINT_CAPTURE 2
#define ENDPOINT_PLAYBACK 6
@@ -77,10 +78,15 @@
deactivate_substream(struct snd_usb_caiaqdev *dev,
struct snd_pcm_substream *sub)
{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->spinlock, flags);
+
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
dev->sub_playback[sub->number] = NULL;
else
dev->sub_capture[sub->number] = NULL;
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
}
static int
@@ -97,13 +103,13 @@
{
int i, ret;
- debug("stream_start(%p)\n", dev);
- spin_lock_irq(&dev->spinlock);
- if (dev->streaming) {
- spin_unlock_irq(&dev->spinlock);
- return -EINVAL;
- }
+ debug("%s(%p)\n", __func__, dev);
+ if (dev->streaming)
+ return -EINVAL;
+
+ memset(dev->sub_playback, 0, sizeof(dev->sub_playback));
+ memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
dev->input_panic = 0;
dev->output_panic = 0;
dev->first_packet = 1;
@@ -112,37 +118,35 @@
for (i = 0; i < N_URBS; i++) {
ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC);
if (ret) {
- log("unable to trigger initial read #%d! (ret = %d)\n",
- i, ret);
+ log("unable to trigger read #%d! (ret %d)\n", i, ret);
dev->streaming = 0;
- spin_unlock_irq(&dev->spinlock);
return -EPIPE;
}
}
- spin_unlock_irq(&dev->spinlock);
return 0;
}
static void stream_stop(struct snd_usb_caiaqdev *dev)
{
int i;
-
- debug("stream_stop(%p)\n", dev);
+
+ debug("%s(%p)\n", __func__, dev);
if (!dev->streaming)
return;
dev->streaming = 0;
+
for (i = 0; i < N_URBS; i++) {
- usb_unlink_urb(dev->data_urbs_in[i]);
- usb_unlink_urb(dev->data_urbs_out[i]);
+ usb_kill_urb(dev->data_urbs_in[i]);
+ usb_kill_urb(dev->data_urbs_out[i]);
}
}
static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream)
{
struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
- debug("snd_usb_caiaq_substream_open(%p)\n", substream);
+ debug("%s(%p)\n", __func__, substream);
substream->runtime->hw = dev->pcm_info;
snd_pcm_limit_hw_rates(substream->runtime);
return 0;
@@ -152,7 +156,7 @@
{
struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
- debug("snd_usb_caiaq_substream_close(%p)\n", substream);
+ debug("%s(%p)\n", __func__, substream);
if (all_substreams_zero(dev->sub_playback) &&
all_substreams_zero(dev->sub_capture)) {
/* when the last client has stopped streaming,
@@ -160,24 +164,22 @@
stream_stop(dev);
dev->pcm_info.rates = dev->samplerates;
}
-
+
return 0;
}
static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub,
struct snd_pcm_hw_params *hw_params)
{
- debug("snd_usb_caiaq_pcm_hw_params(%p)\n", sub);
+ debug("%s(%p)\n", __func__, sub);
return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));
}
static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
{
struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
- debug("snd_usb_caiaq_pcm_hw_free(%p)\n", sub);
- spin_lock_irq(&dev->spinlock);
+ debug("%s(%p)\n", __func__, sub);
deactivate_substream(dev, sub);
- spin_unlock_irq(&dev->spinlock);
return snd_pcm_lib_free_pages(sub);
}
@@ -196,12 +198,12 @@
struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- debug("snd_usb_caiaq_pcm_prepare(%p)\n", substream);
+ debug("%s(%p)\n", __func__, substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;
else
- dev->audio_in_buf_pos[index] = 0;
+ dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE;
if (dev->streaming)
return 0;
@@ -220,7 +222,10 @@
bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
* bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams;
-
+
+ if (bpp > MAX_ENDPOINT_SIZE)
+ bpp = MAX_ENDPOINT_SIZE;
+
ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate,
runtime->sample_bits, bpp);
if (ret)
@@ -247,15 +252,11 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- spin_lock(&dev->spinlock);
activate_substream(dev, sub);
- spin_unlock(&dev->spinlock);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- spin_lock(&dev->spinlock);
deactivate_substream(dev, sub);
- spin_unlock(&dev->spinlock);
break;
default:
return -EINVAL;
@@ -328,8 +329,6 @@
if (all_substreams_zero(dev->sub_capture))
return;
- spin_lock(&dev->spinlock);
-
for (i = 0; i < iso->actual_length;) {
for (stream = 0; stream < dev->n_streams; stream++, i++) {
sub = dev->sub_capture[stream];
@@ -345,8 +344,6 @@
}
}
}
-
- spin_unlock(&dev->spinlock);
}
static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,
@@ -358,8 +355,6 @@
struct snd_pcm_substream *sub;
int stream, i;
- spin_lock(&dev->spinlock);
-
for (i = 0; i < iso->actual_length;) {
if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) {
for (stream = 0;
@@ -393,8 +388,6 @@
}
}
}
-
- spin_unlock(&dev->spinlock);
}
static void read_in_urb(struct snd_usb_caiaqdev *dev,
@@ -418,8 +411,6 @@
dev->input_panic ? "(input)" : "",
dev->output_panic ? "(output)" : "");
}
-
- check_for_elapsed_periods(dev, dev->sub_capture);
}
static void fill_out_urb(struct snd_usb_caiaqdev *dev,
@@ -429,8 +420,6 @@
unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
struct snd_pcm_substream *sub;
int stream, i;
-
- spin_lock(&dev->spinlock);
for (i = 0; i < iso->length;) {
for (stream = 0; stream < dev->n_streams; stream++, i++) {
@@ -456,9 +445,6 @@
for (stream = 0; stream < dev->n_streams; stream++, i++)
usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
}
-
- spin_unlock(&dev->spinlock);
- check_for_elapsed_periods(dev, dev->sub_playback);
}
static void read_completed(struct urb *urb)
@@ -472,6 +458,7 @@
return;
dev = info->dev;
+
if (!dev->streaming)
return;
@@ -489,8 +476,12 @@
out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame;
if (len > 0) {
+ spin_lock(&dev->spinlock);
fill_out_urb(dev, out, &out->iso_frame_desc[outframe]);
read_in_urb(dev, urb, &urb->iso_frame_desc[frame]);
+ spin_unlock(&dev->spinlock);
+ check_for_elapsed_periods(dev, dev->sub_playback);
+ check_for_elapsed_periods(dev, dev->sub_capture);
send_it = 1;
}
@@ -696,7 +687,7 @@
void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev)
{
- debug("snd_usb_caiaq_audio_free (%p)\n", dev);
+ debug("%s(%p)\n", __func__, dev);
stream_stop(dev);
free_urbs(dev->data_urbs_in);
free_urbs(dev->data_urbs_out);
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index 7c44a2c..e97d8b2 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -42,7 +42,7 @@
#endif
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.2");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.6");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, RigKontrol3},"
@@ -456,7 +456,7 @@
struct snd_usb_caiaqdev *dev;
struct snd_card *card = dev_get_drvdata(&intf->dev);
- debug("snd_disconnect(%p)\n", intf);
+ debug("%s(%p)\n", __func__, intf);
if (!card)
return;
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index f48838a..410be4a 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -64,9 +64,10 @@
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */
-static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+/* Vendor/product IDs for this card */
+static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
+static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
static int nrpacks = 8; /* max. number of packets per urb */
static int async_unlink = 1;
static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/
@@ -687,7 +688,7 @@
int err = 0;
if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
- ! subs->running || /* can be stopped during retire callback */
+ !subs->running || /* can be stopped during retire callback */
(err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
(err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
clear_bit(ctx->index, &subs->active_mask);
@@ -710,7 +711,7 @@
int err = 0;
if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
- ! subs->running || /* can be stopped during retire callback */
+ !subs->running || /* can be stopped during retire callback */
(err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
(err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
clear_bit(ctx->index + 16, &subs->active_mask);
@@ -740,7 +741,7 @@
vfree(runtime->dma_area);
}
runtime->dma_area = vmalloc(size);
- if (! runtime->dma_area)
+ if (!runtime->dma_area)
return -ENOMEM;
runtime->dma_bytes = size;
return 0;
@@ -772,12 +773,12 @@
async = !can_sleep && async_unlink;
- if (! async && in_interrupt())
+ if (!async && in_interrupt())
return 0;
for (i = 0; i < subs->nurbs; i++) {
if (test_bit(i, &subs->active_mask)) {
- if (! test_and_set_bit(i, &subs->unlink_mask)) {
+ if (!test_and_set_bit(i, &subs->unlink_mask)) {
struct urb *u = subs->dataurb[i].urb;
if (async)
usb_unlink_urb(u);
@@ -789,7 +790,7 @@
if (subs->syncpipe) {
for (i = 0; i < SYNC_URBS; i++) {
if (test_bit(i+16, &subs->active_mask)) {
- if (! test_and_set_bit(i+16, &subs->unlink_mask)) {
+ if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
struct urb *u = subs->syncurb[i].urb;
if (async)
usb_unlink_urb(u);
@@ -1137,12 +1138,12 @@
if (subs->fmt_type == USB_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
- if (! u->urb)
+ if (!u->urb)
goto out_of_memory;
u->urb->transfer_buffer =
usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
&u->urb->transfer_dma);
- if (! u->urb->transfer_buffer)
+ if (!u->urb->transfer_buffer)
goto out_of_memory;
u->urb->pipe = subs->datapipe;
u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
@@ -1155,7 +1156,7 @@
/* allocate and initialize sync urbs */
subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
GFP_KERNEL, &subs->sync_dma);
- if (! subs->syncbuf)
+ if (!subs->syncbuf)
goto out_of_memory;
for (i = 0; i < SYNC_URBS; i++) {
struct snd_urb_ctx *u = &subs->syncurb[i];
@@ -1163,7 +1164,7 @@
u->subs = subs;
u->packets = 1;
u->urb = usb_alloc_urb(1, GFP_KERNEL);
- if (! u->urb)
+ if (!u->urb)
goto out_of_memory;
u->urb->transfer_buffer = subs->syncbuf + i * 4;
u->urb->transfer_dma = subs->sync_dma + i * 4;
@@ -1427,8 +1428,8 @@
subs->cur_audiofmt = fmt;
#if 0
- printk("setting done: format = %d, rate = %d, channels = %d\n",
- fmt->format, fmt->rate, fmt->channels);
+ printk("setting done: format = %d, rate = %d..%d, channels = %d\n",
+ fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
printk(" datapipe = 0x%0x, syncpipe = 0x%0x\n",
subs->datapipe, subs->syncpipe);
#endif
@@ -1463,7 +1464,7 @@
rate = params_rate(hw_params);
channels = params_channels(hw_params);
fmt = find_format(subs, format, rate, channels);
- if (! fmt) {
+ if (!fmt) {
snd_printd(KERN_DEBUG "cannot set format: format = 0x%x, rate = %d, channels = %d\n",
format, rate, channels);
return -EINVAL;
@@ -1584,7 +1585,7 @@
struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* check the format */
- if (! snd_mask_test(fmts, fp->format)) {
+ if (!snd_mask_test(fmts, fp->format)) {
hwc_debug(" > check: no supported format %d\n", fp->format);
return 0;
}
@@ -1620,7 +1621,7 @@
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
- if (! hw_check_valid_format(params, fp))
+ if (!hw_check_valid_format(params, fp))
continue;
if (changed++) {
if (rmin > fp->rate_min)
@@ -1633,7 +1634,7 @@
}
}
- if (! changed) {
+ if (!changed) {
hwc_debug(" --> get empty\n");
it->empty = 1;
return -EINVAL;
@@ -1674,7 +1675,7 @@
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
- if (! hw_check_valid_format(params, fp))
+ if (!hw_check_valid_format(params, fp))
continue;
if (changed++) {
if (rmin > fp->channels)
@@ -1687,7 +1688,7 @@
}
}
- if (! changed) {
+ if (!changed) {
hwc_debug(" --> get empty\n");
it->empty = 1;
return -EINVAL;
@@ -1727,7 +1728,7 @@
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
- if (! hw_check_valid_format(params, fp))
+ if (!hw_check_valid_format(params, fp))
continue;
fbits |= (1ULL << fp->format);
}
@@ -1736,7 +1737,7 @@
oldbits[1] = fmt->bits[1];
fmt->bits[0] &= (u32)fbits;
fmt->bits[1] &= (u32)(fbits >> 32);
- if (! fmt->bits[0] && ! fmt->bits[1]) {
+ if (!fmt->bits[0] && !fmt->bits[1]) {
hwc_debug(" --> get empty\n");
return -EINVAL;
}
@@ -1762,8 +1763,10 @@
channels = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL);
rates = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL);
- if (!channels || !rates)
+ if (!channels || !rates) {
+ err = -ENOMEM;
goto __out;
+ }
list_for_each(p, &subs->fmt_list) {
struct audioformat *f;
@@ -1916,7 +1919,10 @@
1000 * MIN_PACKS_URB,
/*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX);
- if (check_hw_params_convention(subs)) {
+ err = check_hw_params_convention(subs);
+ if (err < 0)
+ return err;
+ else if (err) {
hwc_debug("setting extra hw constraints...\n");
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
hw_rule_rate, subs,
@@ -2222,7 +2228,7 @@
struct snd_card *card = stream->chip->card;
sprintf(name, "stream%d", stream->pcm_index);
- if (! snd_card_proc_new(card, name, &entry))
+ if (!snd_card_proc_new(card, name, &entry))
snd_info_set_text_ops(entry, stream, proc_pcm_format_read);
}
@@ -2278,7 +2284,7 @@
{
struct list_head *p, *n;
- if (! subs->num_formats)
+ if (!subs->num_formats)
return; /* not initialized */
list_for_each_safe(p, n, &subs->fmt_list) {
struct audioformat *fp = list_entry(p, struct audioformat, list);
@@ -2328,7 +2334,7 @@
if (as->fmt_type != fp->fmt_type)
continue;
subs = &as->substream[stream];
- if (! subs->endpoint)
+ if (!subs->endpoint)
continue;
if (subs->endpoint == fp->endpoint) {
list_add_tail(&fp->list, &subs->fmt_list);
@@ -2354,7 +2360,7 @@
/* create a new pcm */
as = kzalloc(sizeof(*as), GFP_KERNEL);
- if (! as)
+ if (!as)
return -ENOMEM;
as->pcm_index = chip->pcm_devs;
as->chip = chip;
@@ -2463,11 +2469,12 @@
}
break;
case USB_AUDIO_FORMAT_PCM8:
- /* Dallas DS4201 workaround */
+ pcm_format = SNDRV_PCM_FORMAT_U8;
+
+ /* Dallas DS4201 workaround: it advertises U8 format, but really
+ supports S8. */
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
pcm_format = SNDRV_PCM_FORMAT_S8;
- else
- pcm_format = SNDRV_PCM_FORMAT_U8;
break;
case USB_AUDIO_FORMAT_IEEE_FLOAT:
pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
@@ -2671,12 +2678,23 @@
int format;
struct audioformat *fp;
unsigned char *fmt, *csep;
+ int num;
dev = chip->dev;
/* parse the interface's altsettings */
iface = usb_ifnum_to_if(dev, iface_no);
- for (i = 0; i < iface->num_altsetting; i++) {
+
+ num = iface->num_altsetting;
+
+ /*
+ * Dallas DS4201 workaround: It presents 5 altsettings, but the last
+ * one misses syncpipe, and does not produce any sound.
+ */
+ if (chip->usb_id == USB_ID(0x04fa, 0x4201))
+ num = 4;
+
+ for (i = 0; i < num; i++) {
alts = &iface->altsetting[i];
altsd = get_iface_desc(alts);
/* skip invalid one */
@@ -3375,14 +3393,14 @@
static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_usb_audio *chip = entry->private_data;
- if (! chip->shutdown)
+ if (!chip->shutdown)
snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
}
static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_usb_audio *chip = entry->private_data;
- if (! chip->shutdown)
+ if (!chip->shutdown)
snd_iprintf(buffer, "%04x:%04x\n",
USB_ID_VENDOR(chip->usb_id),
USB_ID_PRODUCT(chip->usb_id));
@@ -3391,9 +3409,9 @@
static void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(chip->card, "usbbus", &entry))
+ if (!snd_card_proc_new(chip->card, "usbbus", &entry))
snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read);
- if (! snd_card_proc_new(chip->card, "usbid", &entry))
+ if (!snd_card_proc_new(chip->card, "usbid", &entry))
snd_info_set_text_ops(entry, chip, proc_audio_usbid_read);
}
@@ -3406,7 +3424,6 @@
static int snd_usb_audio_free(struct snd_usb_audio *chip)
{
- usb_chip[chip->index] = NULL;
kfree(chip);
return 0;
}
@@ -3600,8 +3617,8 @@
snd_card_set_dev(chip->card, &intf->dev);
break;
}
- if (! chip) {
- snd_printk(KERN_ERR "no available usb audio device\n");
+ if (!chip) {
+ printk(KERN_ERR "no available usb audio device\n");
goto __error;
}
}
@@ -3671,6 +3688,7 @@
list_for_each(p, &chip->mixer_list) {
snd_usb_mixer_disconnect(p);
}
+ usb_chip[chip->index] = NULL;
mutex_unlock(®ister_mutex);
snd_card_free_when_closed(card);
} else {
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 938dff5..82a8d14 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -39,6 +39,30 @@
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
+/* Creative/E-Mu devices */
+{
+ USB_DEVICE(0x041e, 0x3010),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Creative Labs",
+ .product_name = "Sound Blaster MP3+",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
+{
+ /* E-Mu 0202 USB */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x041e,
+ .idProduct = 0x3f02,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+},
+{
+ /* E-Mu 0404 USB */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x041e,
+ .idProduct = 0x3f04,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+},
+
/*
* Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface
* class matches do not take effect without an explicit ID match.
@@ -97,19 +121,7 @@
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
},
-/* E-Mu devices */
-{
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = 0x041e,
- .idProduct = 0x3f02,
- .bInterfaceClass = USB_CLASS_AUDIO,
-},
-{
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = 0x041e,
- .idProduct = 0x3f04,
- .bInterfaceClass = USB_CLASS_AUDIO,
-},
+
/*
* Yamaha devices
*/
@@ -1165,19 +1177,6 @@
}
}
},
-{
- USB_DEVICE(0x582, 0x00a6),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "Juno-G",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- }
-},
{ /*
* This quirk is for the "Advanced" modes of the Edirol UA-25.
* If the switch is not in an advanced setting, the UA-25 has
@@ -1336,6 +1335,19 @@
},
/* TODO: add Edirol MD-P1 support */
{
+ USB_DEVICE(0x582, 0x00a6),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Roland",
+ .product_name = "Juno-G",
+ .ifnum = 0,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ }
+},
+{
/* Roland SH-201 */
USB_DEVICE(0x0582, 0x00ad),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -1719,17 +1731,6 @@
}
},
-{
- /* Creative Sound Blaster MP3+ */
- USB_DEVICE(0x041e, 0x3010),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Creative Labs",
- .product_name = "Sound Blaster MP3+",
- .ifnum = QUIRK_NO_INTERFACE
- }
-
-},
-
/* Emagic devices */
{
USB_DEVICE(0x086a, 0x0001),