diff options
276 files changed, 12154 insertions, 2951 deletions
@@ -403,6 +403,8 @@ D: Linux CD and Support Giveaway List 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: Post Office Box 64132 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 e985cf5e0410..fd4c32a031c9 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -284,6 +284,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 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 c0cc52a9afe5..f50e927a1189 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2558,12 +2558,10 @@ W: http://www.tazenda.demon.co.uk/phil/linux-hp 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 @@ -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 8c71daf94a59..9fee37e2596f 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -75,6 +75,7 @@ osf_set_program_attributes(unsigned long text_start, unsigned long text_len, 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 c107cc08daf4..78357798b6fd 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -71,25 +71,13 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82378, quirk_i 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 @@ pcibios_set_master(struct pci_dev *dev) 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 @@ pcibios_claim_one_bus(struct pci_bus *b) 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 @@ common_init_pci(void) } } - 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 920196bcbb61..a7f23b5ab814 100644 --- a/arch/alpha/kernel/sys_nautilus.c +++ b/arch/alpha/kernel/sys_nautilus.c @@ -187,6 +187,7 @@ nautilus_machine_check(unsigned long vector, unsigned long la_ptr) } 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 @@ nautilus_init_pci(void) /* 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 5235f64f235b..8508a0db3eaf 100644 --- a/arch/arm/mach-integrator/time.c +++ b/arch/arm/mach-integrator/time.c @@ -124,8 +124,11 @@ static int rtc_probe(struct amba_device *dev, void *id) 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 6d26661d99f6..2ef7d0097b38 100644 --- a/arch/arm/mach-lh7a40x/arch-kev7a400.c +++ b/arch/arm/mach-lh7a40x/arch-kev7a400.c @@ -75,10 +75,9 @@ static void kev7a400_cpld_handler (unsigned int irq, struct irq_desc *desc) { 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 746cbb7c8e95..1b8229d9c9d5 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -32,6 +32,7 @@ config CPU_ARM7TDMI 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 @@ config CPU_ARM740T 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 @@ config CPU_ARM9TDMI 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 @@ config CPU_ARM940T 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 @@ config CPU_ARM946E 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 @@ config CPU_XSC3 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 32fd7ea533f2..5673f4d6113b 100644 --- a/arch/arm/mm/proc-arm1020.S +++ b/arch/arm/mm/proc-arm1020.S @@ -471,6 +471,7 @@ arm1020_crval: .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 @@ arm1020_processor_functions: .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 fe2b0ae70274..4343fdb0e9e5 100644 --- a/arch/arm/mm/proc-arm1020e.S +++ b/arch/arm/mm/proc-arm1020e.S @@ -452,6 +452,7 @@ arm1020e_crval: .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 @@ arm1020e_processor_functions: .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 06dde678e19d..2a4ea1659e96 100644 --- a/arch/arm/mm/proc-arm1022.S +++ b/arch/arm/mm/proc-arm1022.S @@ -435,6 +435,7 @@ arm1022_crval: .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 @@ arm1022_processor_functions: .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 f5506e6e681e..77a1babd421c 100644 --- a/arch/arm/mm/proc-arm1026.S +++ b/arch/arm/mm/proc-arm1026.S @@ -430,6 +430,7 @@ arm1026_crval: .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 @@ arm1026_processor_functions: .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 14b6a95c8d45..c371fc87776e 100644 --- a/arch/arm/mm/proc-arm6_7.S +++ b/arch/arm/mm/proc-arm6_7.S @@ -293,6 +293,7 @@ __arm7_setup: mov r0, #0 .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 @@ ENTRY(arm6_processor_functions) .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 @@ ENTRY(arm6_processor_functions) .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 @@ ENTRY(arm7_processor_functions) .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 ca5e7aac2da7..d64f8e6f75ab 100644 --- a/arch/arm/mm/proc-arm720.S +++ b/arch/arm/mm/proc-arm720.S @@ -198,6 +198,7 @@ arm720_crval: .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 @@ ENTRY(arm720_processor_functions) .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 7069f495cf9b..3a57376c8bc9 100644 --- a/arch/arm/mm/proc-arm740.S +++ b/arch/arm/mm/proc-arm740.S @@ -126,6 +126,7 @@ __arm740_setup: .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 d091c2571823..7b3ecdeb5370 100644 --- a/arch/arm/mm/proc-arm7tdmi.S +++ b/arch/arm/mm/proc-arm7tdmi.S @@ -64,6 +64,7 @@ __arm7tdmi_setup: .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 0170d4f466ea..28cdb060df45 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -417,6 +417,7 @@ arm920_crval: .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 @@ arm920_processor_functions: .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 b7952493d404..94ddcb4a4b76 100644 --- a/arch/arm/mm/proc-arm922.S +++ b/arch/arm/mm/proc-arm922.S @@ -421,6 +421,7 @@ arm922_crval: .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 @@ arm922_processor_functions: .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 e2988eba4cf6..065087afb772 100644 --- a/arch/arm/mm/proc-arm925.S +++ b/arch/arm/mm/proc-arm925.S @@ -484,6 +484,7 @@ arm925_crval: .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 @@ arm925_processor_functions: .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 62f7d1dfe016..997db8472b5c 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -437,6 +437,7 @@ arm926_crval: .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 @@ arm926_processor_functions: .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 786c593778f0..44ead902bd54 100644 --- a/arch/arm/mm/proc-arm940.S +++ b/arch/arm/mm/proc-arm940.S @@ -321,6 +321,7 @@ __arm940_setup: .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 a60c1421d450..2218b0c01330 100644 --- a/arch/arm/mm/proc-arm946.S +++ b/arch/arm/mm/proc-arm946.S @@ -376,6 +376,7 @@ __arm946_setup: .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 4848eeac86b6..c85c1f50e396 100644 --- a/arch/arm/mm/proc-arm9tdmi.S +++ b/arch/arm/mm/proc-arm9tdmi.S @@ -64,6 +64,7 @@ __arm9tdmi_setup: .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 2f169b28e938..90e7594e29b1 100644 --- a/arch/arm/mm/proc-feroceon.S +++ b/arch/arm/mm/proc-feroceon.S @@ -423,6 +423,7 @@ feroceon_crval: .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 @@ feroceon_processor_functions: .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 4db3d6299a2b..9818195dbf11 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -216,6 +216,7 @@ sa110_crval: .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 @@ ENTRY(sa110_processor_functions) .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 3cdef043760f..c5fe27ad2892 100644 --- a/arch/arm/mm/proc-sa1100.S +++ b/arch/arm/mm/proc-sa1100.S @@ -231,6 +231,7 @@ sa1100_crval: .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 @@ ENTRY(sa1100_processor_functions) .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 bf760ea2f789..5702ec58b2a2 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -219,6 +219,7 @@ v6_crval: .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 @@ ENTRY(v6_processor_functions) .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 a1d7331cd64c..b49f9a4c82c8 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -205,6 +205,7 @@ __v7_setup_stack: .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 @@ ENTRY(v7_processor_functions) .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 d95921a2ab99..3533741a76f6 100644 --- a/arch/arm/mm/proc-xsc3.S +++ b/arch/arm/mm/proc-xsc3.S @@ -450,6 +450,7 @@ xsc3_crval: .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 1a6d89823dff..2dd85273976f 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -527,6 +527,7 @@ xscale_crval: .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 @@ ENTRY(xscale_processor_functions) .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 135644f8add7..484c83d23eef 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1409,7 +1409,6 @@ syscall_is_too_hard: 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 1f730619a24a..3e849e8e3480 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c @@ -105,11 +105,6 @@ static int _sigpause_common(old_sigset_t set) 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 df3eacb5ca15..8acc5cc38621 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -250,6 +250,26 @@ endchoice 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 e1835868ad36..92f79680f70d 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_GENERIC_CALIBRATE_DELAY=y 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_I2C_ALGOBIT=y # 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 @@ CONFIG_SSB_POSSIBLE=y # Multifunction device drivers # # CONFIG_MFD_SM501 is not set +# CONFIG_HTC_PASIC3 is not set # # Multimedia devices @@ -1219,10 +1221,6 @@ CONFIG_USB_STORAGE=m # 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_SCHEDSTATS=y 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_CORE=m 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_PLIST=y 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 04ab81cb4f48..bc2632274840 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -396,6 +396,7 @@ static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_de 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 fb43c76bdc26..fd06e937ae1e 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -47,7 +47,7 @@ do_fpdis: 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 @@ fp_other_bounce: 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 @@ utrap_trap: /* %g3=handler,%g4=level */ 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 @@ __spitfire_cee_trap_continue: 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 @@ __spitfire_data_access_exception_tl1: 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 @@ __spitfire_data_access_exception: 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 @@ __spitfire_insn_access_exception_tl1: 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 @@ __spitfire_insn_access_exception: 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 @@ do_dcpe_tl1_fatal: 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 @@ do_icpe_tl1_fatal: 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 @@ __do_privact: call do_privact add %sp, PTREGS_OFF, %o0 ba,pt %xcc, rtrap - clr %l6 + nop .globl do_mna do_mna: @@ -1308,7 +1308,7 @@ do_mna: 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 @@ do_lddfmna: call handle_lddfmna add %sp, PTREGS_OFF, %o0 ba,pt %xcc, rtrap - clr %l6 + nop .globl do_stdfmna do_stdfmna: @@ -1344,7 +1344,7 @@ do_stdfmna: call handle_stdfmna add %sp, PTREGS_OFF, %o0 ba,pt %xcc, rtrap - clr %l6 + nop .globl breakpoint_trap breakpoint_trap: @@ -1424,13 +1424,13 @@ sys32_rt_sigreturn: 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_sparc_syscall32: /* 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 @@ ret_sys_call: 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 @@ ret_sys_call: 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 4a91e9c6d31b..32fbab620852 100644 --- a/arch/sparc64/kernel/entry.h +++ b/arch/sparc64/kernel/entry.h @@ -20,7 +20,6 @@ extern void timer_interrupt(int irq, struct pt_regs *regs); 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 4b2bf9eb447a..b49d3b60bc0c 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -53,7 +53,11 @@ etrap_irq: 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 756fa24eeefa..2a37a6ca2a16 100644 --- a/arch/sparc64/kernel/iommu.c +++ b/arch/sparc64/kernel/iommu.c @@ -173,9 +173,11 @@ void iommu_range_free(struct iommu *iommu, dma_addr_t dma_addr, unsigned long np } 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 @@ int iommu_table_init(struct iommu *iommu, int tsbsize, /* 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 @@ int iommu_table_init(struct iommu *iommu, int tsbsize, /* 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 inline void iommu_free_ctx(struct iommu *iommu, int ctx) static void *dma_4u_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addrp, gfp_t gfp) { + unsigned long flags, order, first_page; struct iommu *iommu; + struct page *page; + int npages, nid; iopte_t *iopte; - unsigned long flags, order, first_page; 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 b5f7b354084f..a2af5ed784c9 100644 --- a/arch/sparc64/kernel/isa.c +++ b/arch/sparc64/kernel/isa.c @@ -92,6 +92,7 @@ static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br) 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 910083589569..dde52bcf5c64 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 @@ static void mdesc_handle_init(struct mdesc_handle *hp, 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 void mdesc_bootmem_free(struct mdesc_handle *hp) } } -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 @@ void __init sun4v_mdesc_init(void) 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 0fd9db95b896..9e58e8cba1c3 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 @@ static unsigned int __init build_one_device_irq(struct of_device *op, 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 @@ static unsigned int __init build_one_device_irq(struct of_device *op, 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 @@ static unsigned int __init build_one_device_irq(struct of_device *op, 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 545356b00e2e..49f912766519 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -369,10 +369,12 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, 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 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 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 7571ed563147..d23bb6f53cda 100644 --- a/arch/sparc64/kernel/pci_fire.c +++ b/arch/sparc64/kernel/pci_fire.c @@ -71,7 +71,8 @@ static int pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm) */ 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 @@ static int __init pci_fire_pbm_init(struct pci_controller_info *p, 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 4a50da13ce48..218bac4ff79b 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -148,6 +148,8 @@ struct pci_pbm_info { 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 struct pci_pbm_info *pci_pbm_root; 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 d6d64b44af63..db5e8fd8f674 100644 --- a/arch/sparc64/kernel/pci_msi.c +++ b/arch/sparc64/kernel/pci_msi.c @@ -279,11 +279,17 @@ static int bringup_one_msi_queue(struct pci_pbm_info *pbm, 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 0bad96e5d184..994dbe0603da 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -848,7 +848,8 @@ static int psycho_iommu_init(struct pci_pbm_info *pbm) /* 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 @@ static void __init psycho_pbm_init(struct pci_controller_info *p, 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 1c5f5fa2339f..4c34195baf37 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -704,7 +704,7 @@ static int sabre_iommu_init(struct pci_pbm_info *pbm, * 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 @@ static void __init sabre_pbm_init(struct pci_controller_info *p, 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 e30609362322..615edd9c8e2a 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -1220,7 +1220,8 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) /* 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 @@ static int __init schizo_pbm_init(struct pci_controller_info *p, 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 01839706bd52..e2bb9790039c 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -127,10 +127,12 @@ static inline long iommu_batch_end(void) 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 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size, 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 @@ static void __init pci_sun4v_pbm_init(struct pci_controller_info *p, 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 @@ static void __init pci_sun4v_pbm_init(struct pci_controller_info *p, 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 68964ddcde1e..ed03a18d3b36 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 @@ int of_find_in_proplist(const char *list, const char *match, int len) } 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 079d18a11d24..ecf6753b204a 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 @@ __handle_user_windows: 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 @@ __handle_perfctrs: 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_userfpu: __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 @@ __handle_signal: 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 d1fb13ba02b5..fa2827c4a3ad 100644 --- a/arch/sparc64/kernel/sbus.c +++ b/arch/sparc64/kernel/sbus.c @@ -544,6 +544,7 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) 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 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) 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 6acb4c51cfe4..da5e6ee0c661 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -82,7 +82,7 @@ unsigned long cmdline_memory_size = 0; 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 @@ void __init setup_arch(char **cmdline_p) /* 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 1c47009eb5ec..77a3e8592cbc 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -510,15 +510,20 @@ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, * 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 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0, int restart_s #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 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0, int restart_s 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 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0, int restart_s } } -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 74e0512f135c..43cdec64d9c9 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -982,20 +982,16 @@ static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs * 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 @@ void do_signal32(sigset_t *oldset, struct pt_regs * regs, 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 59f020d69d4c..524b88920947 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 @@ EXPORT_SYMBOL(__per_cpu_shift); 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 @@ void __init real_setup_per_cpu_areas(void) 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 38736460b8db..66336590e830 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -68,8 +68,6 @@ extern void *__memscan_zero(void *, size_t); 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 84d39e873e88..01b52f561af4 100644 --- a/arch/sparc64/kernel/stacktrace.c +++ b/arch/sparc64/kernel/stacktrace.c @@ -20,6 +20,8 @@ void save_stack_trace(struct stack_trace *trace) 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 @@ void save_stack_trace(struct stack_trace *trace) 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 fd9430562e0b..e1fbf8c75787 100644 --- a/arch/sparc64/kernel/sun4v_tlb_miss.S +++ b/arch/sparc64/kernel/sun4v_tlb_miss.S @@ -262,7 +262,7 @@ sun4v_iacc: 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 @@ sun4v_iacc_tl1: 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 @@ sun4v_dacc: 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 @@ sun4v_dacc_tl1: 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 @@ sun4v_mna: 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 @@ sun4v_privact: 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 @@ sun4v_lddfmna: 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 @@ sun4v_stdfmna: 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 52816c7be0b9..e885034a6b73 100644 --- a/arch/sparc64/kernel/sysfs.c +++ b/arch/sparc64/kernel/sysfs.c @@ -273,10 +273,22 @@ static void __init check_mmu_stats(void) 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 96da847023f3..d9b8d46707d1 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -2091,9 +2091,8 @@ static void user_instruction_dump(unsigned int __user *pc) 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 @@ void show_stack(struct task_struct *tsk, unsigned long *_ksp) 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 10adb2fb8ffe..c499214b501d 100644 --- a/arch/sparc64/kernel/tsb.S +++ b/arch/sparc64/kernel/tsb.S @@ -275,7 +275,7 @@ sparc64_realfault_common: 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 c4aa110a10e5..a6b0863c27df 100644 --- a/arch/sparc64/kernel/winfixup.S +++ b/arch/sparc64/kernel/winfixup.S @@ -32,7 +32,7 @@ fill_fixup: 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 @@ spill_fixup_dax: 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 @@ fill_fixup_mna: 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 @@ fill_fixup_dax: 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 f37078d96407..177d8aaeec42 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 @@ extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES]; #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 @@ out: 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; + + if (strstr(p, "off")) + numa_enabled = 0; + + if (strstr(p, "debug")) + numa_debug = 1; + + return 0; +} +early_param("numa", early_numa); - bootmap_size = bootmem_bootmap_pages(end_pfn - start_pfn); - bootmap_size <<= PAGE_SHIFT; +#define numadbg(f, a...) \ +do { if (numa_debug) \ + printk(KERN_INFO f, ## a); \ +} while (0) - avoid_start = avoid_end = 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); + if (sparc_ramdisk_image || sparc_ramdisk_image64) { + unsigned long ramdisk_image; + + /* 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; + + /* 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; + + numadbg("Found ramdisk at physical address 0x%lx, size %u\n", + ramdisk_image, sparc_ramdisk_size); + + initrd_start = ramdisk_image; + initrd_end = ramdisk_image + sparc_ramdisk_size; + + lmb_reserve(initrd_start, initrd_end); + } #endif +} - for (i = 0; i < pavail_ents; i++) { - unsigned long start, end; +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; - start = pavail[i].phys_addr; - end = start + pavail[i].reg_size; +int numa_cpu_lookup_table[NR_CPUS]; +cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES]; - 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; - } +#ifdef CONFIG_NEED_MULTIPLE_NODES +static bootmem_data_t plat_node_bdata[MAX_NUMNODES]; - if ((end - start) < bootmap_size) - break; +struct mdesc_mblock { + u64 base; + u64 size; + u64 offset; /* RA-to-PA */ +}; +static struct mdesc_mblock *mblocks; +static int num_mblocks; - if (start < kern_base && - (start + bootmap_size) > kern_base) { - start = PAGE_ALIGN(kern_base + kern_size); - continue; - } +static unsigned long ra_to_pa(unsigned long addr) +{ + int i; - if (start < avoid_start && - (start + bootmap_size) > avoid_start) { - start = avoid_end; - continue; - } + for (i = 0; i < num_mblocks; i++) { + struct mdesc_mblock *m = &mblocks[i]; - /* OK, it doesn't overlap anything, use it. */ - return start >> PAGE_SHIFT; + if (addr >= m->base && + addr < (m->base + m->size)) { + addr += m->offset; + break; } } - - prom_printf("Cannot find free area for bootmap, aborting.\n"); - prom_halt(); + return addr; } -static void __init trim_pavail(unsigned long *cur_size_p, - unsigned long *end_of_phys_p) +static int find_node(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); + addr = ra_to_pa(addr); + for (i = 0; i < num_node_masks; i++) { + struct node_mem_mask *p = &node_masks[i]; - avoid_start = avoid_end = 0; -#ifdef CONFIG_BLK_DEV_INITRD - avoid_start = initrd_start; - avoid_end = PAGE_ALIGN(initrd_end); + 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; + +#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)); - kern_end = PAGE_ALIGN(kern_base + kern_size); + NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; +#endif - trim_low = start = pavail[i].phys_addr; - trim_high = end = start + pavail[i].reg_size; + p = NODE_DATA(nid); - 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; + 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; - if (trim_high <= trim_low) + 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; - - 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; - } - } + val = mdesc_get_property(md, target, "latency", NULL); + if (!val) + continue; + + if (*val < best_latency) { + candidate = target; + best_latency = *val; } + } + + 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 (!to_trim) + 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++; } - /* 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; - } + mdesc_release(md); + + return nid; } -/* 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 void add_node_ranges(void) { - unsigned long bootmap_size, end_pfn; - unsigned long end_of_phys_memory = 0UL; - unsigned long bootmap_pfn, bytes_avail, size; 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 < 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; + } } +} - /* 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 grab_mlgroups(struct mdesc_handle *md) +{ + 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); + } -#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; + 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; + + for (i = 0; i < num_mlgroups; i++) { + struct mdesc_mlgroup *m = &mlgroups[i]; + if (m->node == node) + return m; + } + return NULL; +} + +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; + + 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 (num_node_masks != index) { + printk(KERN_ERR "Inconsistent NUMA state, " + "index[%d] != num_node_masks[%d]\n", + index, num_node_masks); + return -EINVAL; + } - if (cmdline_memory_size && - bytes_avail > cmdline_memory_size) - trim_pavail(&bytes_avail, - &end_of_phys_memory); + n = &node_masks[num_node_masks++]; - *pages_avail = bytes_avail >> PAGE_SHIFT; + n->mask = candidate->mask; + n->val = candidate->match; - end_pfn = end_of_phys_memory >> PAGE_SHIFT; + numadbg("NUMA NODE[%d]: mask[%lx] val[%lx] (latency[%lx])\n", + index, n->mask, n->val, candidate->latency); - /* Initialize the boot-time allocator. */ - max_pfn = max_low_pfn = end_pfn; - min_low_pfn = (phys_base >> PAGE_SHIFT); + return 0; +} + +static int __init numa_parse_mdesc_group(struct mdesc_handle *md, u64 grp, + int index) +{ + cpumask_t mask; + int cpu; - bootmap_pfn = choose_bootmap_pfn(min_low_pfn, end_pfn); + numa_parse_mdesc_group_cpus(md, grp, &mask); - bootmap_size = init_bootmem_node(NODE_DATA(0), bootmap_pfn, - min_low_pfn, end_pfn); + for_each_cpu_mask(cpu, mask) + numa_cpu_lookup_table[cpu] = index; + numa_cpumask_lookup_table[index] = mask; - /* 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); + if (numa_debug) { + printk(KERN_INFO "NUMA GROUP[%d]: cpus [ ", index); + for_each_cpu_mask(cpu, mask) + printk("%d ", cpu); + printk("]\n"); + } -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start) { - size = initrd_end - initrd_start; + 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; +} - /* Reserve the initrd image area. */ - reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT); +static int __init bootmem_init_numa(void) +{ + int err = -1; - initrd_start += PAGE_OFFSET; - initrd_end += PAGE_OFFSET; + 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 - /* 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++) { +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; - 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); + 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); + + if (bootmem_init_numa() < 0) + bootmem_init_nonnuma(); + + /* XXX cpu notifier XXX */ + + for_each_online_node(nid) + bootmem_init_one_node(nid); sparse_init(); @@ -1289,7 +1688,7 @@ void __init setup_per_cpu_areas(void) 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 @@ void __init paging_init(void) 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 @@ void __init paging_init(void) 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 @@ void __init paging_init(void) 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 @@ void __init paging_init(void) 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 @@ static void __init taint_real_pages(void) 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 @@ 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_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 @@ void __init mem_init(void) 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 a3e6e4b635b3..fe70c8a557b5 100644 --- a/arch/sparc64/mm/tsb.c +++ b/arch/sparc64/mm/tsb.c @@ -321,7 +321,8 @@ retry_tsb_alloc: 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 2865c105b6a4..e686a67561af 100644 --- a/arch/sparc64/mm/ultra.S +++ b/arch/sparc64/mm/ultra.S @@ -476,7 +476,6 @@ xcall_sync_tick: #endif call smp_synchronize_tick_client nop - clr %l6 b rtrap_xcall ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 @@ -511,7 +510,6 @@ xcall_report_regs: #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 @@ __hypervisor_tlb_xcall_error: 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 37fe80df0b91..f282976daaac 100644 --- a/drivers/char/mwave/tp3780i.c +++ b/drivers/char/mwave/tp3780i.c @@ -97,24 +97,20 @@ static void EnableSRAM(THINKPAD_BD_DATA * pBDData) 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 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) 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 c5263d63aca3..92b683411d5a 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -15,6 +15,7 @@ if INPUT_MISC 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 316b106c3511..03feb5b49e1b 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -690,10 +690,8 @@ MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); 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 047add8f3010..ec6bdb9680a3 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -954,10 +954,8 @@ static void snd_saa7134_free(struct snd_card * card) 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 af66f4f28300..4edc120a6359 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 @@ struct pasic3_data { #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 4dc5b4b7a561..d3207c0da895 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -123,7 +123,6 @@ struct ppp { 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 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) 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 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) #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 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) drop: if (skb) kfree_skb(skb); - ++ppp->stats.tx_errors; + ++ppp->dev->stats.tx_errors; } /* @@ -1409,7 +1400,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) 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 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) 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 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) 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 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) 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 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) */ 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 @@ ppp_mp_reconstruct(struct ppp *ppp) /* 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 @@ ppp_mp_reconstruct(struct ppp *ppp) 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 @@ ppp_mp_reconstruct(struct ppp *ppp) 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 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) 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 @@ ppp_create_interface(int unit, int *retp) 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 d91856b19f6f..0ce07a339c7e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -668,16 +668,23 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, 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 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, 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 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, 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 70092191fc53..c2642bc1d49b 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -56,8 +56,7 @@ obj-$(CONFIG_RTL8187) += rtl8187.o 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 87e782291a01..5fb1ae6ad3e2 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -304,14 +304,20 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) 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 21c886a9a1d9..6dcbb3c87e72 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -980,6 +980,42 @@ void b43_dma_free(struct b43_wldev *dev) 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 @@ int b43_dma_init(struct b43_wldev *dev) 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 943cc851c504..4bf8a99099fe 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -84,6 +84,10 @@ int b43_modparam_qos = 1; 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 setup_struct_wldev_for_init(struct b43_wldev *dev) 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 @@ static void b43_bluetooth_coext_enable(struct b43_wldev *dev) 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 @@ static int b43_wireless_core_init(struct b43_wldev *dev) 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 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl) 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 @@ static void b43_sprom_fixup(struct ssb_bus *bus) 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 575c5436ebdf..de024dc03718 100644 --- a/drivers/net/wireless/b43/phy.c +++ b/drivers/net/wireless/b43/phy.c @@ -2043,7 +2043,7 @@ int b43_phy_init(struct b43_wldev *dev) 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 1a5678fe4224..a1a0b3c581f1 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -6907,7 +6907,6 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw, 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 d7e2358a213a..d0bbcaaeb94c 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -6473,7 +6473,6 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw, 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 e5b3c282009c..5b375b289036 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -1186,7 +1186,7 @@ prism54_get_encode(struct net_device *ndev, struct iw_request_info *info, 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 977751f372ff..d0b1fb15c709 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2402,7 +2402,6 @@ static int bcm4320_early_init(struct usbnet *dev) 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 @@ static int bcm4320_early_init(struct usbnet *dev) 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 ed8c06904807..8d8852651fd2 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -200,7 +200,6 @@ config PCMCIA_AU1X00 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_SA1111 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 cb2e40506379..3271379a36db 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -1015,6 +1015,7 @@ static struct uart_ops sunzilog_pops = { .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 zilog_irq = -1; 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 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m 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 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m 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 struct of_platform_driver zs_driver = { 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 904b1a8d0885..57c4ccfab1ee 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -484,6 +484,11 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, 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 680e51d87374..19790eb99cc6 100644 --- a/include/asm-sparc/device.h +++ b/include/asm-sparc/device.h @@ -16,6 +16,8 @@ struct dev_archdata { 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 df5dc4422483..fd55522481cd 100644 --- a/include/asm-sparc/prom.h +++ b/include/asm-sparc/prom.h @@ -77,6 +77,11 @@ extern int of_getintprop_default(struct device_node *np, 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 46325ddee23b..d7b9afcba08b 100644 --- a/include/asm-sparc64/iommu.h +++ b/include/asm-sparc64/iommu.h @@ -56,6 +56,7 @@ struct strbuf { }; 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 000000000000..ebf5986c12ed --- /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 017e7e74f5e7..000000000000 --- 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 6da197803efc..b4b951d570bb 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 @@ struct pt_regs { 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 @@ extern void __show_regs(struct pt_regs *); #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 77bcd2bfa53c..b99d4e4b6d28 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 c6b557034f68..001c04027c82 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 7208a777750e..d3cc4eff39a6 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 f27d11ab418b..529f301d9372 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -465,13 +465,19 @@ enum ieee80211_eid { 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 b9e174079002..44c81c744538 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -740,13 +740,13 @@ extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change); 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 01480581f825..049edc5e6461 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 4e80d3fe7381..d293d36a66b8 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -182,6 +182,7 @@ struct ak4114 { 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 6153b91cdc3e..891cf1aea8b1 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -68,7 +68,7 @@ struct snd_akm4xxx { 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 024ce62f7d16..a6e0facf8a37 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 e79baa63912f..3dc1291f52db 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -169,4 +169,11 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, 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 4fc0235ad784..695ee53488a3 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -277,8 +277,8 @@ int snd_minor_info_done(void); 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_add(struct snd_card *card, struct file *file); 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 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) * 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 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) * 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 d45218b44dfe..68b634b75068 100644 --- a/include/sound/mpu401.h +++ b/include/sound/mpu401.h @@ -103,6 +103,21 @@ struct snd_mpu401 { #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 fac66c49445a..ed6fb2eb1eac 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 896e2832099e..207147ab25e4 100644 --- a/lib/lmb.c +++ b/lib/lmb.c @@ -346,7 +346,7 @@ u64 __init __lmb_alloc_base(u64 size, u64 align, u64 max_addr) 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 bc39e417694a..cf857c4dc7b1 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -82,6 +82,11 @@ int rtnl_trylock(void) 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(rtnetlink_put_metrics); 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 02088deb0461..2e2fc3376ac9 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -1003,7 +1003,7 @@ static unsigned fib_flag_trans(int type, __be32 mask, struct fib_info *fi) 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 @@ static int fib_seq_show(struct seq_file *seq, void *v) 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 ea294fffb9ce..4b02d14e7ab9 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2602,15 +2602,16 @@ static int fib_route_seq_show(struct seq_file *seq, void *v) 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 @@ static int fib_route_seq_show(struct seq_file *seq, void *v) (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 780e9484c825..ce25a13f3430 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -367,10 +367,10 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) "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 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) 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 776615180b93..0e9bc120707d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2255,13 +2255,13 @@ void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo) } 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 @@ static void get_openreq4(struct sock *sk, struct request_sock *req, 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 @@ static void get_tcp4_sock(struct sock *sk, char *tmpbuf, int i) 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 @@ static void get_tcp4_sock(struct sock *sk, char *tmpbuf, int i) 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 @@ static void get_timewait4_sock(struct inet_timewait_sock *tw, 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 void get_timewait4_sock(struct inet_timewait_sock *tw, 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 @@ static int tcp4_seq_show(struct seq_file *seq, void *v) 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 b053ac795275..1f535e315188 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1619,7 +1619,8 @@ void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo) } /* ------------------------------------------------------------------------ */ -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 @@ static void udp4_format_sock(struct sock *sp, char *tmpbuf, int bucket) __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 @@ int udp4_seq_show(struct seq_file *seq, void *v) "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 6b75cb6c6300..a5e5c31c23ab 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2248,10 +2248,13 @@ static void ieee80211_rx_bss_put(struct net_device *dev, struct ieee80211_sta_bss *bss) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - if (!atomic_dec_and_test(&bss->users)) + + local_bh_disable(); + if (!atomic_dec_and_lock(&bss->users, &local->sta_bss_lock)) { + local_bh_enable(); return; + } - spin_lock_bh(&local->sta_bss_lock); __ieee80211_rx_bss_hash_del(dev, bss); list_del(&bss->list); spin_unlock_bh(&local->sta_bss_lock); @@ -2709,7 +2712,26 @@ static void ieee80211_rx_bss_info(struct net_device *dev, 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 52e4554fdde7..02f436a86061 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2170,7 +2170,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, 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 4e94e4026e78..64faa3dc488f 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -709,7 +709,7 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) 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 cfeb07ea1b04..f73ec0ea93ba 100644 --- a/net/sctp/objcnt.c +++ b/net/sctp/objcnt.c @@ -83,13 +83,12 @@ static sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = { */ 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 1454afcc06c4..e18cd3628db4 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2197,7 +2197,11 @@ static void __exit af_unix_exit(void) 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 8704e2825b10..5b3274b465eb 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -72,7 +72,7 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg 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 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigne 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_write(struct snd_ac97 *ac97, unsigned short reg, unsigne 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 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) 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 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) 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 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) 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 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) 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 @@ static struct platform_driver pxa2xx_ac97_driver = { .resume = pxa2xx_ac97_resume, .driver = { .name = "pxa2xx-ac97", + .owner = THIS_MODULE, }, }; @@ -443,3 +468,4 @@ module_exit(pxa2xx_ac97_exit); 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 829ca38b595e..a8d71c6c8e75 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -181,3 +181,7 @@ config SND_PCM_XRUN_DEBUG 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 267039a97bd5..da8e685eef9c 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 e3338d6071ef..ac0573416130 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -254,7 +254,7 @@ static int snd_disconnect_release(struct inode *inode, struct file *file) 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 @@ int snd_card_disconnect(struct snd_card *card) 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 @@ int snd_card_disconnect(struct snd_card *card) /* 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 @@ int snd_card_disconnect(struct snd_card *card) 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 @@ static int snd_card_do_free(struct snd_card *card) 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 @@ EXPORT_SYMBOL(snd_card_free_when_closed); 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 102d1c36cf26..38524f615d94 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -39,7 +39,7 @@ void snd_verbose_printk(const char *file, int line, const char *format, ...) { 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 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) { 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 75daed298a15..581aa2c60e65 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1257,6 +1257,8 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer) { 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 ab570a0a6183..558dadbf45f1 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -245,8 +245,13 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) 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 index 2da49d20a1fc..4cc57f902e2c 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 link_master { struct list_head slaves; struct link_ctl_info info; int val; /* the master value */ + unsigned int tlv[4]; }; /* @@ -253,6 +255,8 @@ int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) return 0; } +EXPORT_SYMBOL(snd_ctl_add_slave); + /* * ctl callbacks for master controls */ @@ -355,10 +359,13 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, 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 75d4fe09fdf3..fe85af1c5693 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -4,6 +4,24 @@ menu "Generic devices" 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 8e5530006e1f..d4a07f9ff2c7 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -20,4 +20,4 @@ obj-$(CONFIG_SND_MTS64) += snd-mts64.o 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 a240eaeb5c62..4e4c69e6cb4c 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -181,10 +181,10 @@ struct snd_dummy_pcm { 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 @@ static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dummy_pcm *dpcm = runtime->private_data; - unsigned int bps; + int bps; + + bps = snd_pcm_format_width(runtime->format) * runtime->rate * + runtime->channels / 8; - bps = runtime->rate * runtime->channels; - bps *= snd_pcm_format_width(runtime->format); - bps /= 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 @@ static void snd_card_dummy_pcm_timer_function(unsigned long data) 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 @@ static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *su 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 05a871aa7b81..ecdbeb6d3603 100644 --- a/sound/drivers/ml403-ac97cr.c +++ b/sound/drivers/ml403-ac97cr.c @@ -1191,8 +1191,6 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev, return err; } - snd_card_set_dev(card, &pfdev->dev); - *rml403_ac97cr = ml403_ac97cr; return 0; } @@ -1330,11 +1328,15 @@ static int snd_ml403_ac97cr_remove(struct platform_device *pfdev) 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 5993864acbd3..18cca2457d44 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -49,12 +49,10 @@ static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu); */ -#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_input_read(struct snd_mpu401 * mpu) 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 000000000000..b19555b440da --- /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 000000000000..59203511e77d --- /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 000000000000..f07cc1ee1fe7 --- /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 000000000000..cd9b83e7f7d1 --- /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 000000000000..e66738c78333 --- /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 000000000000..ac6238e93513 --- /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 000000000000..64a695fef74e --- /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 15061bd72776..d20d893b3b60 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 struct snd_kcontrol_new snd_ak4114_iec958_controls[] = { } }; + +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 @@ int snd_ak4114_build(struct ak4114 *ak4114, 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 @@ static void ak4114_stats(struct work_struct *work) 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 35fbbf2cb9fa..288926d2e205 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -70,7 +70,8 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state) } /* 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 @@ static void ak4355_reset(struct snd_akm4xxx *ak, int state) 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 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state) /* 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 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 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 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr, 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 bed29ca22239..f3fd7b4f4668 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -331,7 +331,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, 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 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, 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 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, 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 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, 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 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, 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 get_version(struct snd_sb *chip) 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 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int 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 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int 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 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel 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 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel 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 d63c1af550de..b432d9ae874b 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -51,7 +51,7 @@ int snd_sbdsp_command(struct snd_sb *chip, unsigned char val) 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 @@ int snd_sbdsp_get_byte(struct snd_sb *chip) 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 @@ int snd_sbdsp_reset(struct snd_sb *chip) 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 eaf69971bf92..1e90d769b62e 100644 --- a/sound/oss/dmabuf.c +++ b/sound/oss/dmabuf.c @@ -795,9 +795,9 @@ static int find_output_space(int dev, char **buf, int *size) #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 d6af9065d1c0..f43f91ef86c7 100644 --- a/sound/oss/trident.c +++ b/sound/oss/trident.c @@ -3076,8 +3076,7 @@ ali_ac97_get(struct trident_card *card, int secondary, u8 reg) 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 @@ ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val) 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 @@ ali_ac97_read(struct ac97_codec *codec, u8 reg) 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 @@ ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) 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 4713b49fc91d..ff30a1d7c2f1 100644 --- a/sound/oss/trident.h +++ b/sound/oss/trident.h @@ -322,7 +322,7 @@ enum miscint_bits { #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 d25249a932bf..2c5aaa58046d 100644 --- a/sound/oss/vwsnd.c +++ b/sound/oss/vwsnd.c @@ -194,11 +194,11 @@ static void dbgassert(const char *fcn, int line, const char *expr) * 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 812085d521f1..581debf37dcb 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -122,6 +122,21 @@ config SND_AU8830 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 @@ config SND_CA0106 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 @@ config SND_HDA_INTEL 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 @@ config SND_ICE1724 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 @@ config SND_VIA82XX_MODEM 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 2d42fd28f4e7..85ef14bc8056 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_SND) += \ ac97/ \ ali5451/ \ au88x0/ \ + aw2/ \ ca0106/ \ cs46xx/ \ cs5535audio/ \ diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 50c637e55ffa..39198e505b12 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -114,10 +114,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd 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 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e 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 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .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 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .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 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .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 @@ static inline int is_shared_micin(struct snd_ac97 *ac97) 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 @@ static int patch_alc655(struct snd_ac97 * ac97) #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 @@ static void alc850_update_jacks(struct snd_ac97 *ac97) /* 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 @@ static int patch_alc850(struct snd_ac97 *ac97) 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 @@ static int patch_alc850(struct snd_ac97 *ac97) 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 3674f35c4a79..48cbda9378c5 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -574,7 +574,6 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, 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 a66d5150bb7a..39ec55b57b1e 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -264,10 +264,10 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip) 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 @@ snd_ad1889_free(struct snd_ad1889 *chip) 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 6a905ed9cbd6..1a0fd65ec280 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1809,26 +1809,26 @@ static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol, 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 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, 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 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, 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 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, 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 @@ static int snd_ali_free(struct snd_ali * codec) { 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 0e990a735821..8df6824b51cd 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 4594186b83ee..457228fb22aa 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1553,7 +1553,7 @@ static int snd_atiixp_free(struct atiixp *chip) 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 a67a869180d4..d457a32a7939 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1197,7 +1197,7 @@ static int snd_atiixp_free(struct atiixp_modem *chip) 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 26819e2f5761..68368e490074 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -126,7 +126,6 @@ static int snd_vortex_dev_free(struct snd_device *device) 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 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) 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 526c6c5ecf7b..f9a58b4a30eb 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -498,14 +498,14 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = { }; /* 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 @@ static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr) 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 000000000000..842335d3b735 --- /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 000000000000..56f87cd33c19 --- /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 000000000000..6a3891ab69dd --- /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 000000000000..5b35e358937f --- /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 000000000000..459b0311ea31 --- /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 000000000000..ce0ab5f9ee9c --- /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 4e71a55120a0..5f63af6b88a2 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -157,8 +157,8 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); #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 @@ snd_azf3328_free(struct snd_azf3328 *chip) /* 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 176e0f0e8058..ecbe79b67e43 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -435,22 +435,22 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, 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 @@ static int snd_ca0106_free(struct snd_ca0106 *chip) * 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 @@ static int snd_ca0106_free(struct snd_ca0106 *chip) // 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 af736869d9b1..3025ed1b6e1e 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -650,19 +650,55 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch #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 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) } 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 135f30860753..9971b5b7735b 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2744,12 +2744,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic } 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 @@ static int snd_cmipci_free(struct cmipci *cm) /* 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 87ddffcd9d89..e214e567dec8 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2772,6 +2772,9 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip) 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 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip) 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 90ec090792ba..e16dc92e82fb 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1852,15 +1852,16 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) 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 9a9b977d3cf1..abde5b901884 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1249,11 +1249,6 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) 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 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) } 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 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) #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 5512abd98bd9..491a4a50f869 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -327,22 +327,22 @@ static void snd_emu10k1x_ptr_write(struct emu10k1x *emu, 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 @@ static int snd_emu10k1x_free(struct emu10k1x *chip) // 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 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id) // 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 f3caa3f890c6..216f9748aff5 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -412,7 +412,7 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, 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 72d85a5ae6a0..fbf1124f7c79 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1635,20 +1635,20 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq, 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 @@ static int snd_ensoniq_free(struct ensoniq *ensoniq) 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 1a314fa99c45..84fac1fbf103 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1488,7 +1488,6 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state) 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 @@ static int snd_es1938_free(struct es1938 *chip) 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 7d911a18c082..1bf298d214b9 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1827,6 +1827,22 @@ snd_es1968_pcm(struct es1968 *chip, int device) 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 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id) 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 @@ snd_es1968_mixer(struct es1968 *chip) { 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 @@ snd_es1968_mixer(struct es1968 *chip) 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 inline void snd_es1968_free_gameport(struct es1968 *chip) { } 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 4c300e6149fc..c129f9e2072c 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1285,7 +1285,6 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id, 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 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume) } 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 9e0d8a1268aa..ab0c726d648e 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o # 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 37c413923db8..a6be6e3e8716 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 @@ struct hda_vendor_id { /* 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 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, 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 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, } /* 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 @@ static struct snd_kcontrol_new dig_mixes[] = { { } /* 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 snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) 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 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) } /* + * 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 snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) 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 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, 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 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, 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_dig_close(struct hda_codec *codec, */ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, - struct snd_pcm_substream *substream) -{ - substream->runtime->hw.channels_max = mout->max_channels; + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo) +{ + 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 snd_hda_multi_out_analog_prepare(struct hda_codec *codec, 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 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, 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 snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, 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 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, } } + /* 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 f14871151be9..dcd390b2bbaa 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -590,11 +590,21 @@ struct hda_pcm_stream { 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 @@ int snd_hda_build_pcms(struct hda_bus *bus); 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 f9de7c467c25..59e4389c94a4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1007,8 +1007,8 @@ static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, { 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 4be36c84b36c..b3a618eb42cd 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 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* 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 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* 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 @@ enum { */ 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 { 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 { 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 @@ static void azx_update_rirb(struct azx *chip) 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 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) 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 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) /* * 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 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) /* 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 @@ static struct snd_pcm_hardware azx_pcm_hw = { 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 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) 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 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) 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 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) 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_prepare(struct snd_pcm_substream *substream) 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 @@ static struct snd_pcm_ops azx_pcm_ops = { .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 void azx_pcm_free(struct snd_pcm *pcm) } 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 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, 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 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, 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; - 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; - } - err = create_codec_pcm(chip, codec, - &codec->pcm_info[c], pcm_dev); - if (err < 0) - return err; - pcm_dev++; - } - } - - /* create modem PCMs */ - pcm_dev = AZX_MAX_AUDIO_PCMS; + 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; /* already created */ - if (pcm_dev >= AZX_MAX_PCMS) { - snd_printk(KERN_ERR SFX - "Too many modem 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; - chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM; - pcm_dev++; } } return 0; @@ -1502,10 +1594,7 @@ static int __devinit azx_init_stream(struct azx *chip) * 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 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) 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_resume(struct pci_dev *pci) */ 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 int azx_dev_free(struct snd_device *device) 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 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, 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 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, 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 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, 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 @@ static void __devexit azx_remove(struct pci_dev *pci) /* 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 ad0014ab71f9..5c9e578f7f2d 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -228,8 +228,18 @@ struct hda_multi_out { 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 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, 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 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, 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 f5c23bb16d7e..2fdf2358dbc2 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_atihdmi[]; 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 c8649282c2cf..e0a605adde42 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 @@ struct ad198x_spec { #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 @@ static int ad198x_build_controls(struct hda_codec *codec) 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 @@ static int ad198x_build_controls(struct hda_codec *codec) /* 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 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, 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 @@ static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 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 @@ static int ad198x_build_pcms(struct hda_codec *codec) 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_laptop_eapd_capture_source = { }, }; +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 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { { } /* 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_eapd_init_verbs[] = { {} }; +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 @@ static int patch_ad1986a(struct hda_codec *codec) 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 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol, 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 @@ static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = { { } /* 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 @@ static struct hda_verb ad1988_spdif_init_verbs[] = { { } }; +/* 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 @@ static int patch_ad1988(struct hda_codec *codec) 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 @@ static int patch_ad1884(struct hda_codec *codec) * 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 @@ static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, 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 @@ static int patch_ad1984(struct hda_codec *codec) /* + * 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 @@ static int patch_ad1882(struct hda_codec *codec) * 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 9a8bb4ce3f8d..12272508b112 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_build_controls(struct hda_codec *codec) 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 @@ static int atihdmi_build_pcms(struct hda_codec *codec) 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 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = { { .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 3d6097ba1d68..c73ce074a6ea 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 @@ static int cmi9880_build_controls(struct hda_codec *codec) 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 @@ static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo, 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 @@ static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { 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 @@ static int cmi9880_build_pcms(struct hda_codec *codec) 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 const char *cmi9880_models[CMI_MODELS] = { 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 7206b30cbf94..36fd85260035 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 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, 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 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 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 @@ static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 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 @@ static int conexant_build_pcms(struct hda_codec *codec) 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 @@ static int conexant_build_controls(struct hda_codec *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); @@ -511,6 +518,14 @@ static struct hda_input_mux cxt5045_capture_source_benq = { } }; +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_benq_mixers[] = { {} }; +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 @@ enum { CXT5045_LAPTOP_MICSENSE, CXT5045_LAPTOP_HPMICSENSE, CXT5045_BENQ, + CXT5045_LAPTOP_HP530, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -844,6 +891,7 @@ static const char *cxt5045_models[CXT5045_MODELS] = { [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 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = { 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 @@ static int patch_cxt5045(struct hda_codec *codec) 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 @@ static void cxt5051_portc_automic(struct hda_codec *codec) 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 33282f9c01c7..cdda64b02f46 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 @@ enum { 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 @@ enum { 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 @@ struct alc_spec { /* 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 @@ struct alc_spec { /* 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 @@ struct alc_config_preset { 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 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, 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 @@ static void setup_preset(struct alc_spec *spec, 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 struct hda_verb alc_gpio3_init_verbs[] = { 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 @@ static void alc_sku_automute(struct hda_codec *codec) 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 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = { 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 @@ static int alc_build_controls(struct hda_codec *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); @@ -1525,10 +1537,11 @@ static int alc_build_controls(struct hda_codec *codec) /* 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 @@ static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec) 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 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, * 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 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {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 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {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 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, 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 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { 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 @@ static int alc_build_pcms(struct hda_codec *codec) 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 @@ static void alc_free(struct hda_codec *codec) kfree(spec->kctl_alloc); } kfree(spec); + codec->spec = NULL; /* to be sure */ } /* @@ -3057,7 +3077,9 @@ static struct alc_config_preset alc880_presets[] = { .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 @@ static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, return 0; } -static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, + unsigned int pin_type) { - /* set as output */ 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) +{ + 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 @@ static int alc880_parse_auto_config(struct hda_codec *codec) /* 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 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, 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 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* 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 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* 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 @@ static hda_nid_t alc882_dac_nids[4] = { #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 @@ static int alc882_mux_enum_put(struct snd_kcontrol *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[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 @@ static struct alc_config_preset alc882_presets[] = { .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 @@ static struct alc_config_preset alc882_presets[] = { .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 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, 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 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) 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 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec) 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 @@ static int alc882_parse_auto_config(struct hda_codec *codec) /* 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 @@ static int patch_alc882(struct hda_codec *codec) 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 @@ static int patch_alc882(struct hda_codec *codec) 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 int patch_alc882(struct hda_codec *codec) 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 @@ static hda_nid_t alc883_adc_nids[2] = { 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_lenovo_nb0763_capture_source = { }, }; +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 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = { { } /* 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 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { 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 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { { } /* 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 @@ static struct hda_verb alc883_mitac_verbs[] = { { } /* 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 @@ static struct hda_verb alc883_haier_w66_verbs[] = { { } /* 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 @@ static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) 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 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [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 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { 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 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { 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 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { 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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { 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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static struct alc_config_preset alc883_presets[] = { .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 @@ static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, 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 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec) 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 @@ static int alc883_parse_auto_config(struct hda_codec *codec) /* 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 @@ static int patch_alc883(struct hda_codec *codec) 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 @@ static int patch_alc883(struct hda_codec *codec) #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 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec, /* * 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 void alc262_hippo1_unsol_event(struct hda_codec *codec, 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 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) 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 @@ static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { }, }; +/* 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 @@ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, 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 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { }, 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 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { /* 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 @@ static void alc262_ultra_unsol_event(struct hda_codec *codec, 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 @@ static int alc262_parse_auto_config(struct hda_codec *codec) /* 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 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { [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 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { 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,18 +9578,32 @@ static struct alc_config_preset alc262_presets[] = { .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, - .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, + .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_fujitsu_capture_source, + .unsol_event = alc262_lenovo_3000_unsol_event, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -9472,12 +9674,14 @@ static int patch_alc262(struct hda_codec *codec) 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 @@ static hda_nid_t alc268_adc_nids_alt[1] = { 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 @@ static struct snd_kcontrol_new alc268_base_mixer[] = { { } }; +/* 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 snd_kcontrol_new alc268_acer_mixer[] = { }; 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 @@ static void alc268_dell_unsol_event(struct hda_codec *codec, #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 @@ static struct hda_verb alc268_base_init_verbs[] = { {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 @@ static struct hda_verb alc268_volume_init_verbs[] = { {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_capture_source = { }, }; +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 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, case 0x1c: idx1 = 3; /* CD */ break; + case 0x12: + case 0x13: + idx1 = 6; /* digital mics */ + break; default: continue; } @@ -10073,6 +10357,9 @@ static int alc268_parse_auto_config(struct hda_codec *codec) 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 @@ static int alc268_parse_auto_config(struct hda_codec *codec) /* 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 const char *alc268_models[ALC268_MODEL_LAST] = { }; 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 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { 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 @@ static struct alc_config_preset alc268_presets[] = { .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 @@ static struct alc_config_preset alc268_presets[] = { .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 @@ static struct alc_config_preset alc268_presets[] = { .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 @@ static struct alc_config_preset alc268_presets[] = { .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 @@ static int patch_alc268(struct hda_codec *codec) 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 @@ static int patch_alc268(struct hda_codec *codec) 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 @@ static int alc269_parse_auto_config(struct hda_codec *codec) /* 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 @@ static void alc861_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, 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 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) 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 @@ static int alc861_parse_auto_config(struct hda_codec *codec) /* 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 @@ static hda_nid_t alc861vd_adc_nids[1] = { 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_capture_source = { }; 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 @@ static struct hda_input_mux alc861vd_hp_capture_source = { #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 @@ static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { { } /* 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 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { /*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 @@ static struct alc_config_preset alc861vd_presets[] = { 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 @@ static struct alc_config_preset alc861vd_presets[] = { .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 @@ static struct alc_config_preset alc861vd_presets[] = { 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 @@ static struct alc_config_preset alc861vd_presets[] = { .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 @@ static struct alc_config_preset alc861vd_presets[] = { .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 struct alc_config_preset alc861vd_presets[] = { 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 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec) 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 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) /* 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 @@ static int patch_alc861vd(struct hda_codec *codec) 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 @@ static hda_nid_t alc662_adc_nids[1] = { /* 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 @@ static struct hda_input_mux alc662_eeepc_capture_source = { #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 hda_channel_mode alc662_5stack_modes[2] = { 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_base_mixer[] = { 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_2ch_mixer[] = { 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 const char *alc662_models[ALC662_MODEL_LAST] = { }; 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 @@ static struct alc_config_preset alc662_presets[] = { .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 @@ static struct alc_config_preset alc662_presets[] = { .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 @@ static struct alc_config_preset alc662_presets[] = { .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 @@ static struct alc_config_preset alc662_presets[] = { .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 @@ static struct alc_config_preset alc662_presets[] = { .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 @@ static struct alc_config_preset alc662_presets[] = { 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 @@ static struct alc_config_preset alc662_presets[] = { 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 @@ static void alc662_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; @@ -13595,6 +13880,9 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) 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 @@ static int alc662_parse_auto_config(struct hda_codec *codec) /* 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 @@ static int patch_alc662(struct hda_codec *codec) 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 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .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 d22f5a6b850f..9332b63e406c 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 @@ static int si3054_build_pcms(struct hda_codec *codec) 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 caf48edaa921..b3a15d616873 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 @@ enum { STAC_9200_DELL_M26, STAC_9200_DELL_M27, STAC_9200_GATEWAY, + STAC_9200_PANASONIC, STAC_9200_MODELS }; @@ -63,11 +66,14 @@ enum { 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 @@ struct sigmatel_spec { 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 @@ struct sigmatel_spec { /* 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 @@ struct sigmatel_spec { /* 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 sigmatel_spec { 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 @@ static hda_nid_t stac92hd71bxx_dmux_nids[1] = { 0x1c, }; -static hda_nid_t stac92hd71bxx_dac_nids[2] = { +static hda_nid_t stac92hd71bxx_dac_nids[1] = { 0x10, /*0x11, */ }; @@ -290,6 +296,10 @@ static hda_nid_t stac927x_mux_nids[3] = { 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 @@ static hda_nid_t stac922x_pin_nids[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 stac92hd73xx_6ch_core_init[] = { {} }; +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 @@ static int stac92xx_build_controls(struct hda_codec *codec) 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 @@ static int stac92xx_build_controls(struct hda_codec *codec) /* 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 @@ static unsigned int dell9200_m27_pin_configs[8] = { 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 @@ static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { [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 @@ static const char *stac9200_models[STAC_9200_MODELS] = { [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 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { 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 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = { {} /* 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 @@ static unsigned int ref92hd71bxx_pin_configs[10] = { 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 @@ static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, 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 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { 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 @@ static int stac92xx_build_pcms(struct hda_codec *codec) 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 @@ static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int 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 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, 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 @@ enum { 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 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { 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 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_ 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 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, } } + 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 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, 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 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, */ 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 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, 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 @@ static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) 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 @@ static int stac92xx_init(struct hda_codec *codec) ? 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 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) { 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 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) 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 @@ static int stac92xx_resume(struct hda_codec *codec) 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 @@ static int patch_stac9200(struct hda_codec *codec) 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 @@ again: 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 @@ again: 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 @@ again: 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 @@ again: 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 @@ static int patch_stac927x(struct hda_codec *codec) 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 @@ static int patch_stac927x(struct hda_codec *codec) 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 @@ static int patch_stac927x(struct hda_codec *codec) 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 @@ static int patch_stac9205(struct hda_codec *codec) (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 @@ static int patch_stac9205(struct hda_codec *codec) 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 4e5dd4cf36f5..52b1d81a26f7 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 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 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 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 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 @@ static int via_build_controls(struct hda_codec *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); @@ -523,6 +528,7 @@ static int via_build_pcms(struct hda_codec *codec) 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 efd180b40e56..0ed96c178059 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 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice) 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 unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) /* 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 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) 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 26ea05a32f56..ea7116c304c0 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 064760d2a027..013fc4f04822 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -238,6 +238,7 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned 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 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice) 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 @@ struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = { .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 e4ed1b475b08..1c443718af03 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 cf5c7c0898fd..6914189073a4 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -208,6 +208,19 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) /* 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 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) (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 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = { .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 df292af67381..29d449d73c98 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1297,11 +1297,14 @@ static void snd_ice1712_update_volume(struct snd_ice1712 *ice, int index) 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_get(struct snd_kcontrol *kcontrol, struc 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_info(struct snd_kcontrol *kcontrol, stru 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_get(struct snd_kcontrol *kcontrol, struc 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 @@ static int snd_ice1712_free(struct snd_ice1712 *ice) 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 303cffe08bd8..3208901c740e 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -367,6 +367,15 @@ struct snd_ice1712 { /* 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 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu 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 f533850ec6e7..4490422fb930 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -106,15 +106,19 @@ static unsigned int PRO_RATE_DEFAULT = 44100; * 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 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice) } /* + * 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 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id) { 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; - +#ifdef CONFIG_SND_DEBUG + if (++timeout > 10) { + printk(KERN_ERR + "ice1724: Too long irq loop, status = 0x%x\n", + status); + break; + } +#endif 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 (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); - outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT)); - status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX); + 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 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) #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 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, 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 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, 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 @@ static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = /* * 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 @@ static int snd_vt1724_playback_pro_close(struct snd_pcm_substream *substream) 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 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream) 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 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream) 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 @@ static int snd_vt1724_playback_spdif_close(struct snd_pcm_substream *substream) 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 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream) 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 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream) 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 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream) 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 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, 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 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, 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_read_eeprom(struct snd_ice1712 *ice, -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 @@ static int snd_vt1724_free(struct snd_ice1712 *ice) 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 @@ static int __devinit snd_vt1724_create(struct snd_card *card, 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 @@ static int __devinit snd_vt1724_create(struct snd_card *card, } /* 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 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, * 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 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, 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 e8038c0ceb72..b4e0c16852a6 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 @@ struct juli_spec { #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 @@ struct juli_spec { #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 @@ static unsigned char juli_ak4114_read(void *private_data, unsigned char reg) } /* + * 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 @@ static void juli_akm_write(struct snd_akm4xxx *ak, int chip, } /* - * 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 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice) /* * 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 @@ static int __devinit juli_init(struct snd_ice1712 *ice) 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 @@ static int __devinit juli_init(struct snd_ice1712 *ice) 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 int __devinit juli_init(struct snd_ice1712 *ice) */ 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 4945c81e8a96..203cdc1bf8da 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -246,7 +246,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val wm_put(ice, WM_ADC_MUX, nval); } mutex_unlock(&ice->gpio_mutex); - return 0; + return change; } /* @@ -450,7 +450,7 @@ static int cs_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu 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 48cf40a8f32a..48d3679292a7 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -319,12 +319,11 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, /* * 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 @@ static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) 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 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice) 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 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) }; const unsigned short *p; int err = 0; - struct snd_akm4xxx *ak; struct prodigy192_spec *spec; /* prodigy 192 */ @@ -761,15 +756,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) 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 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = { .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 301bf929acd9..4d2631434dc8 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -322,17 +322,23 @@ static struct snd_pt2258 ptc_revo51_volume; 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 @@ static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = { .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 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice) 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 unsigned int ap192_4wire_start(struct snd_ice1712 *ice) 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 @@ static int __devinit ap192_ak4114_init(struct snd_ice1712 *ice) 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 @@ static int __devinit revo_init(struct snd_ice1712 *ice) 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 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) 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 c52abd0bf22e..048d99e25ab0 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -155,7 +155,8 @@ DEFINE_REGSET(SP, 0x60); /* SPDIF out */ #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 @@ struct intel8x0 { unsigned multi4: 1, multi6: 1, + multi8 :1, dra: 1, smp20bit: 1; unsigned in_ac97_init: 1, @@ -997,6 +999,8 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip, 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 @@ static struct snd_pcm_hw_constraint_list hw_constraints_channels6 = { .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 @@ static int snd_intel8x0_playback_open(struct snd_pcm_substream *substream) 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 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, } 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 @@ static int snd_intel8x0_free(struct intel8x0 *chip) 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 @@ static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state) 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 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) 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 cadda8d6b70f..faf674e671ac 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -985,17 +985,15 @@ static int snd_intel8x0_free(struct intel8x0m *chip) /* 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 @@ static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state) 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 10c713d9ac49..f4c85b52bde3 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2102,7 +2102,6 @@ snd_korg1212_free(struct snd_korg1212 *korg1212) 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 04fa0a68416c..a536c59fbea1 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2068,7 +2068,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) { 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 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) 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 @@ static int snd_m3_free(struct snd_m3 *chip) 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 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) { 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 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) 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 @@ static int m3_resume(struct pci_dev *pci) { 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 @@ static int m3_resume(struct pci_dev *pci) 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 7ac654e381da..7efb838d18a6 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1439,7 +1439,7 @@ static int snd_nm256_free(struct nm256 *chip) 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 @@ static int snd_nm256_free(struct nm256 *chip) 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 000000000000..6a4fedf5e1ec --- /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 000000000000..5faf5efc8826 --- /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 666f69a3312e..090dd4354a28 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -66,12 +66,12 @@ static void hifier_init(struct oxygen *chip) { 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 const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); 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 @@ static const struct oxygen_model model_hifier = { .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 @@ static int __devinit hifier_probe(struct pci_dev *pci, ++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 9a9941bb0460..63f185c1ed1e 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 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); #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 @@ static void ak4396_init(struct oxygen *chip) 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 @@ static void ak4396_init(struct oxygen *chip) 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 @@ static void wm8785_init(struct oxygen *chip) 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 @@ static void set_ak5385_params(struct oxygen *chip, 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 @@ static const struct oxygen_model model_meridian = { .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 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci, 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 ad50fb8b206b..a71c6e059260 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 oxygen_model { 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 @@ struct oxygen_model { /* 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 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, 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 74e23ef9c946..5569606ee87f 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -190,12 +190,31 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) --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 78c21155218e..897697d43506 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -221,7 +221,8 @@ static void oxygen_init(struct oxygen *chip) 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 @@ static void oxygen_init(struct oxygen *chip) 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 @@ static void oxygen_init(struct oxygen *chip) 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 @@ static void oxygen_init(struct oxygen *chip) (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_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_RATE_48000 | chip->model->dac_i2s_format | + OXYGEN_I2S_MCLK_256 | 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 @@ static void oxygen_init(struct oxygen *chip) (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 @@ static void oxygen_init(struct oxygen *chip) 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 @@ static void oxygen_card_free(struct snd_card *card) 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 @@ static void oxygen_card_free(struct snd_card *card) } 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 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, 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 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, 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 a8e4623415d9..cc0cddadd589 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -32,8 +32,8 @@ static int dac_volume_info(struct snd_kcontrol *ctl, 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 @@ static int spdif_loopback_put(struct snd_kcontrol *ctl, 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 @@ static int ac97_switch_get(struct snd_kcontrol *ctl, 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 @@ static int ac97_switch_put(struct snd_kcontrol *ctl, 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 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .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 @@ static const struct snd_kcontrol_new controls[] = { .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 snd_kcontrol_new controls[] = { }, }; +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 @@ static int add_controls(struct oxygen *chip, 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 @@ static int add_controls(struct oxygen *chip, 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 b70046aca657..b17c405e069d 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -119,7 +119,7 @@ static int oxygen_open(struct snd_pcm_substream *substream, 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 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, 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 oxygen_pcm_init(struct oxygen *chip) 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->used_channels & OXYGEN_CHANNEL_SPDIF); - ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C); + 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->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 @@ int oxygen_pcm_init(struct oxygen *chip) 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 000000000000..698bf46c710c --- /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 d163397b85cc..7f84fa5deca2 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 + * -------- + * + * CMI8788: + * + * I²C <-> CS4398 (front) + * <-> CS4362A (surround, center/LFE, back) + * + * GPI 0 <- external power present * - * CM9780: + * 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) * - * GPIO 0 -> enable AC'97 bypass (line in -> ADC) + * 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_PARM_DESC(id, "ID string"); 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_LINE_MUTE CM9780_GPO0 - -/* 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 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_D2X_EXT_POWER 0x0020 +#define GPIO_D2_ALT 0x0080 +#define GPIO_D2_OUTPUT_ENABLE 0x0100 + +#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 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec, (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 @@ static void update_pcm1796_mute(struct oxygen *chip) 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 @@ static void xonar_gpio_changed(struct oxygen *chip) 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 xonar_gpio_changed(struct oxygen *chip) } } -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 @@ static int alt_switch_put(struct snd_kcontrol *ctl, 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 @@ static const struct snd_kcontrol_new alt_switch = { .put = alt_switch_put, }; +static int front_panel_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_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_control_filter(struct snd_kcontrol_new *template) +static int xonar_d2_control_filter(struct snd_kcontrol_new *template) { - 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)) { + 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 @@ static int xonar_mixer_init(struct oxygen *chip) 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 @@ static int __devinit xonar_probe(struct pci_dev *pci, ++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 000000000000..8c23e315ae66 --- /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 9d5bb76229a8..7fdcdc8c6b64 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -458,7 +458,7 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream) 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 @@ static void pcxhr_trigger_tasklet(unsigned long arg) #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 @@ static int pcxhr_open(struct snd_pcm_substream *subs) 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 @@ static int pcxhr_open(struct snd_pcm_substream *subs) 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 c4e415d07380..78aa81feaa4a 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -897,7 +897,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m #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 @@ void pcxhr_msg_tasklet(unsigned long arg) 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 9408b1eeec40..979f7da641ce 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1630,14 +1630,14 @@ static int snd_riptide_playback_open(struct snd_pcm_substream *substream) 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 @@ static int snd_riptide_playback_close(struct snd_pcm_substream *substream) { 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 df184aabce84..e7ef3a1a25a8 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1350,7 +1350,8 @@ static int __devinit snd_rme32_create(struct rme32 * rme32) 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 fb0a4ee8bc02..3fdd488d0975 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1559,7 +1559,8 @@ snd_rme96_create(struct rme96 *rme96) 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 1be84f22d0de..4d6fbb36ab8a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -318,6 +318,10 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #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 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #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 @@ static int snd_hdsp_use_is_exclusive(struct hdsp *hdsp) 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 @@ static int hdsp_spdif_sample_rate(struct hdsp *hdsp) 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 9a19ae6a64d9..ab423bc82342 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -540,7 +540,8 @@ static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf, 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 @@ static inline int hdspm_read_pb_gain(struct hdspm * hdspm, unsigned int chan, 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 @@ static inline int hdspm_write_in_gain(struct hdspm * hdspm, unsigned int chan, 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 @@ static inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v) } /* 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 @@ static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm) } /* 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 @@ static inline void hdspm_stop_audio(struct hdspm * s) } /* 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 @@ static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, { /* 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 @@ static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id) 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 742f1180c39e..df2007e3be7c 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1194,7 +1194,6 @@ static int sis_suspend(struct pci_dev *pci, pm_message_t state) /* 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 71138ff9b310..bbcee2c09ae4 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3676,6 +3676,8 @@ static int snd_trident_free(struct snd_trident *trident) 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 @@ static int snd_trident_free(struct snd_trident *trident) 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 a756be661f9a..b585cc3e4c47 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2236,7 +2236,7 @@ static int snd_via82xx_free(struct via82xx *chip) /* 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 f5df1c79bee1..31f64ee39882 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1075,7 +1075,7 @@ static int snd_via82xx_free(struct via82xx_modem *chip) /* 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 42c1eb7d35f5..29b3056c5109 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2249,6 +2249,8 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) #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 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) 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 8441e780df00..566a6d0daf4a 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -141,7 +141,7 @@ static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol, 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 int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol, 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 @@ static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble) /* * 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 @@ static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_awacs_info_tone_amp(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_awacs_info_master_amp(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol, 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 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol, * 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_screamer_mixers2[] __initdata = { 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 @@ static void snd_pmac_awacs_resume(struct snd_pmac *chip) #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 @@ static void snd_pmac_awacs_resume(struct snd_pmac *chip) } #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 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify) } 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 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify) 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 @@ snd_pmac_awacs_init(struct snd_pmac *chip) 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 @@ snd_pmac_awacs_init(struct snd_pmac *chip) 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 @@ snd_pmac_awacs_init(struct snd_pmac *chip) 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 @@ snd_pmac_awacs_init(struct snd_pmac *chip) /* 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 @@ snd_pmac_awacs_init(struct snd_pmac *chip) */ 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 @@ snd_pmac_awacs_init(struct snd_pmac *chip) * 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 @@ snd_pmac_awacs_init(struct snd_pmac *chip) 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 1b2cc44eda57..c33e6a531cf7 100644 --- a/sound/ppc/awacs.h +++ b/sound/ppc/awacs.h @@ -116,6 +116,11 @@ struct awacs_regs { #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 @@ struct awacs_regs { #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 @@ struct awacs_regs { #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 @@ struct awacs_regs { /* 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 1a545ac0de04..f860d39af36b 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -102,7 +102,8 @@ snd_pmac_burgundy_rcw(struct snd_pmac *chip, unsigned addr) } 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 @@ snd_pmac_burgundy_rcb(struct snd_pmac *chip, unsigned int addr) 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 @@ snd_pmac_burgundy_read_volume(struct snd_pmac *chip, unsigned int address, 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 @@ static int snd_pmac_burgundy_get_volume(struct snd_kcontrol *kcontrol, 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,24 +201,163 @@ static int snd_pmac_burgundy_put_volume(struct snd_kcontrol *kcontrol, 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 */ +/* + * 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 lvolume, rvolume; -static int snd_pmac_burgundy_info_switch_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + 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 = 100; + return 0; +} + +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); + 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_volume_2b(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 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_VOLUME_2B(xname, xindex, addr, off) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .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)) } + +/* + * 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; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +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); + 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_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, val; + + 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] << 4; + else + val |= ucontrol->value.integer.value[0] << 4; + if (atten) + val = ~val & 0xff; + snd_pmac_burgundy_wcb(chip, addr, val); + return val != oval; +} + +#define BURGUNDY_VOLUME_B(xname, xindex, addr, stereo, atten) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .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; @@ -231,111 +367,207 @@ static int snd_pmac_burgundy_info_switch_out(struct snd_kcontrol *kcontrol, 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_switch_w(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; + 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_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + 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_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +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); - int lmask = kcontrol->private_value & 0xff; - int rmask = (kcontrol->private_value >> 8) & 0xff; + 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_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); - val = oval & ~(lmask | rmask); + 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_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val); + snd_pmac_burgundy_wcw(chip, addr, val); return val != oval; } -#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \ +#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_out,\ - .get = snd_pmac_burgundy_get_switch_out,\ - .put = snd_pmac_burgundy_put_switch_out,\ - .private_value = ((lmask) | ((rmask) << 8) | ((stereo) << 24)) } - -/* line/speaker output volume */ -static int snd_pmac_burgundy_info_volume_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + .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_INTEGER; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = stereo + 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 15; + uinfo->value.integer.max = 1; 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_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 & 0xff); + 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 oval; - - oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; - ucontrol->value.integer.value[0] = oval & 0xf; + int val = snd_pmac_burgundy_rcb(chip, addr); + ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; if (stereo) - ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; + ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; 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_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 & 0xff); + 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; - unsigned int oval, val; - - oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; - val = ucontrol->value.integer.value[0] & 15; - if (stereo) - val |= (ucontrol->value.integer.value[1] & 15) << 4; - else - val |= val << 4; - val = ~val & 0xff; + 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_OUTPUT_VOLUME(xname, xindex, addr, stereo) \ +#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_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_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 int snd_pmac_burgundy_detect_headphone(struct snd_pmac *chip) 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 @@ static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_noti */ 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 @@ int __init snd_pmac_burgundy_init(struct snd_pmac *chip) 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 @@ int __init snd_pmac_burgundy_init(struct snd_pmac *chip) 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 @@ int __init snd_pmac_burgundy_init(struct snd_pmac *chip) 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; + } + 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(&snd_pmac_burgundy_master_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + 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->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; - 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) + 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 ebb457a8342c..7a7f9cf3d299 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 613a565e04de..a38c0c790d2b 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -214,7 +214,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, 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 @@ static int snd_pmac_pcm_close(struct snd_pmac *chip, struct pmac_stream *rec, /* reset constraints */ astr->cur_freqs = chip->freqs_ok; astr->cur_formats = chip->formats_ok; - + return 0; } @@ -1300,9 +1300,9 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) 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 276585215160..a3b51df2bea1 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -29,6 +29,7 @@ source "sound/soc/pxa/Kconfig" 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 4869c9ae7a03..e489dbdde458 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 898a7d363284..3903ab7dfa4a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -18,6 +18,10 @@ config SND_SOC_WM9712 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 c6e5338c2666..4e1314c9d3ec 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -3,6 +3,7 @@ snd-soc-wm8731-objs := wm8731.o 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_WM8731) += snd-soc-wm8731.o 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 242130cf1abd..2a1ffe396908 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -40,7 +40,8 @@ static int ac97_prepare(struct snd_pcm_substream *substream) } #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 @@ static int ac97_soc_probe(struct platform_device *pdev) 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 @@ static int ac97_soc_probe(struct platform_device *pdev) /* 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 @@ static int ac97_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) + if (!codec) return 0; snd_soc_free_pcms(socdev); @@ -145,11 +146,10 @@ static int ac97_soc_remove(struct platform_device *pdev) 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 bf2ab72d49bf..e73fcfd9f5cd 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -372,7 +372,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream, 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 889a897d41ac..630684f4a0bc 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -660,33 +660,53 @@ struct aic3x_rate_divs { /* 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 @@ static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, 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 9c33fe874928..0cf9265fca8f 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -110,7 +110,7 @@ static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg, 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 @@ static int wm8731_add_controls(struct snd_soc_codec *codec) 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 @@ static int wm8731_add_widgets(struct snd_soc_codec *codec) { 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 @@ pcm_err: 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 @@ err: 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 @@ static int wm8731_probe(struct platform_device *pdev) 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 @@ static int wm8731_remove(struct platform_device *pdev) 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 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = { .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 77a857b997a2..16cd5d4d5ad9 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -110,7 +110,7 @@ static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg, 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 @@ static int wm8750_add_controls(struct snd_soc_codec *codec) 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 @@ static int wm8750_add_widgets(struct snd_soc_codec *codec) { 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 @@ static int wm8750_dapm_event(struct snd_soc_codec *codec, int event) } #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 @@ static int wm8750_resume(struct platform_device *pdev) 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 @@ pcm_err: 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 @@ static int wm8750_probe(struct platform_device *pdev) 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 @@ static int wm8750_remove(struct platform_device *pdev) 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 @@ struct snd_soc_codec_device soc_codec_dev_wm8750 = { .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 ddd9c71b3fde..76a5c7b05dfb 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -198,6 +198,7 @@ static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"}; 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_ADC, 4, 2, wm8753_adc_filter), 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_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0 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_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0), 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 524f7450804f..d2d79e182a45 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -581,22 +581,14 @@ static int wm9712_dapm_event(struct snd_soc_codec *codec, int event) 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 000000000000..1f241161445c --- /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 000000000000..d357b6c8134b --- /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 000000000000..20680c551aab --- /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 000000000000..ca772e5b4637 --- /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 000000000000..fcd165240333 --- /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 000000000000..c421774b33ee --- /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 000000000000..9592d17db320 --- /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 000000000000..6a76927c9971 --- /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 000000000000..8d6a45e75a6e --- /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 652514fc8142..78de7168d2ba 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 145ad13d52d1..b2a11b0d2e4c 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 1a70a6ac98ce..7f32a1167572 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -297,21 +297,19 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) /* 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 @@ static int __init corgi_init(void) { 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 4fbf8bba9627..7e830b218943 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -257,21 +257,19 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) /* 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 e17379998802..97ec2d90547c 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -61,7 +61,7 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, 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 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, 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 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 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 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 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 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) 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 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) 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_warm_reset(struct snd_ac97 *ac97) 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 @@ static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) 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 @@ static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) 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 daeaa4c8b876..01ad7bf716b7 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -64,8 +64,8 @@ static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) 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 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, /* 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 @@ struct snd_soc_platform pxa2xx_soc_platform = { .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 ecca39033fcc..d8b8372db00e 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -313,15 +313,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) } /* 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 6ee115ceb011..962cc20b1af5 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -659,6 +659,7 @@ static int __init neo1973_init(void) 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 0a3c630951be..4ebcd6a8bf28 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 @@ static void s3c24xx_snd_txctrl(int on) 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 @@ static void s3c24xx_snd_rxctrl(int on) 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 @@ static void s3c24xx_snd_rxctrl(int on) * 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 @@ static int s3c24xx_snd_lrsync(void) 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 int s3c24xx_snd_lrsync(void) */ 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 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, { 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 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, 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 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) { int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -320,7 +320,7 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, { 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 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai, { u32 reg; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); switch (div_id) { case S3C24XX_DIV_BCLK: @@ -381,13 +381,13 @@ EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate); 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 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev) } #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 @@ int s3c24xx_i2s_suspend(struct platform_device *pdev, 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 29a6c82f873a..49580fb481d5 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 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) 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 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) 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 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, 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 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, 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 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) { 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 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) 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 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 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 @@ static snd_pcm_uframes_t 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 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) 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 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) 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 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, { 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 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 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 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 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 @@ static int s3c24xx_pcm_new(struct snd_card *card, { 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 f03220d23e73..4c1e013381c9 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 9eb5479787c1..e148db940cfc 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -839,6 +839,7 @@ static int soc_remove(struct platform_device *pdev) static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", + .owner = THIS_MODULE, }, .probe = soc_probe, .remove = soc_remove, @@ -1601,3 +1602,4 @@ module_exit(snd_soc_exit); 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 620d7ea3c15f..af3326c63504 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -226,7 +226,7 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget) 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 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, 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 @@ int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, 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 89d6e9c35140..09802e8a6fb8 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -118,7 +118,7 @@ static struct snd_pcm_hardware snd_at73c213_playback_hw = { .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 struct snd_pcm_hardware snd_at73c213_playback_hw = { 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 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) { 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_close(struct snd_pcm_substream *substream) 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 @@ static int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream) 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 @@ static struct snd_pcm_ops at73c213_playback_ops = { .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 @@ static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) 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 @@ static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id) 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 @@ cleanup: /* * 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 @@ static int snd_at73c213_ssc_init(struct snd_at73c213 *chip) 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 @@ out: 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 9cc4cd8283f9..24970a5c888f 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 @@ static void 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 @@ static int stream_start(struct snd_usb_caiaqdev *dev) { int i, ret; - debug("stream_start(%p)\n", dev); - spin_lock_irq(&dev->spinlock); - if (dev->streaming) { - spin_unlock_irq(&dev->spinlock); + 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 @@ static int stream_start(struct snd_usb_caiaqdev *dev) 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 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) { 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 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) 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 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) 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 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) 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 @@ static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd) 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 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev, 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 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev, } } } - - spin_unlock(&dev->spinlock); } static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, @@ -358,8 +355,6 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, 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 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, } } } - - spin_unlock(&dev->spinlock); } static void read_in_urb(struct snd_usb_caiaqdev *dev, @@ -418,8 +411,6 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev, 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 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev, 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 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev, 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 @@ static void read_completed(struct urb *urb) return; dev = info->dev; + if (!dev->streaming) return; @@ -489,8 +476,12 @@ static void read_completed(struct urb *urb) 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 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) 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 7c44a2c7f963..e97d8b2ac16a 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 @@ static void snd_disconnect(struct usb_interface *intf) 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 f48838a078cb..410be4aff1ba 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -64,9 +64,10 @@ MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}"); 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 @@ static void snd_complete_urb(struct urb *urb) 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 @@ static void snd_complete_sync_urb(struct urb *urb) 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 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t s 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 @@ static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sl 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 @@ static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sl 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 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri 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 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri /* 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 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri 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 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) 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 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, 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 @@ static int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audiof 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 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, 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 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, } } - if (! changed) { + if (!changed) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; @@ -1674,7 +1675,7 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, 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 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, } } - if (! changed) { + if (!changed) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; @@ -1727,7 +1728,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, 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 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, 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 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) 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 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre 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 @@ static void proc_pcm_format_add(struct snd_usb_stream *stream) 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 @@ static void free_substream(struct snd_usb_substream *subs) { 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 @@ static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct aud 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 @@ static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct aud /* 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 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor } 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 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) 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 int snd_usb_create_quirk(struct snd_usb_audio *chip, 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 proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_ 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 void snd_usb_audio_create_proc(struct snd_usb_audio *chip) static int snd_usb_audio_free(struct snd_usb_audio *chip) { - usb_chip[chip->index] = NULL; kfree(chip); return 0; } @@ -3600,8 +3617,8 @@ static void *snd_usb_audio_probe(struct usb_device *dev, 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 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) 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 938dff5f9cef..82a8d14c26af 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 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, -{ - 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 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, /* 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 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, -{ - /* 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), |