From cd1d18cf2f1efd7ac45c1cae09ed01ba780c8e29 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 16 Jan 2003 10:22:36 -0800 Subject: [COMPAT]: compat_{old_}sigset_t sparc64. --- include/asm-sparc64/compat.h | 7 +++++++ include/asm-sparc64/signal.h | 19 +++++-------------- 2 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/asm-sparc64/compat.h b/include/asm-sparc64/compat.h index 6e6111e478fe..0d56a2a0cde1 100644 --- a/include/asm-sparc64/compat.h +++ b/include/asm-sparc64/compat.h @@ -77,4 +77,11 @@ struct compat_statfs { int f_spare[6]; }; +typedef u32 compat_old_sigset_t; + +#define _COMPAT_NSIG 64 +#define _COMPAT_NSIG_BPW 32 + +typedef u32 compat_sigset_word; + #endif /* _ASM_SPARC64_COMPAT_H */ diff --git a/include/asm-sparc64/signal.h b/include/asm-sparc64/signal.h index ae08da1d6ec7..c6b6feba590c 100644 --- a/include/asm-sparc64/signal.h +++ b/include/asm-sparc64/signal.h @@ -88,27 +88,21 @@ #define _NSIG_BPW 64 #define _NSIG_WORDS (__NEW_NSIG / _NSIG_BPW) -#define _NSIG_BPW32 32 -#define _NSIG_WORDS32 (__NEW_NSIG / _NSIG_BPW32) - #define SIGRTMIN 32 #define SIGRTMAX (__NEW_NSIG - 1) #if defined(__KERNEL__) || defined(__WANT_POSIX1B_SIGNALS__) #define _NSIG __NEW_NSIG #define __new_sigset_t sigset_t -#define __new_sigset_t32 sigset_t32 #define __new_sigaction sigaction #define __new_sigaction32 sigaction32 #define __old_sigset_t old_sigset_t -#define __old_sigset_t32 old_sigset_t32 #define __old_sigaction old_sigaction #define __old_sigaction32 old_sigaction32 #else #define _NSIG __OLD_NSIG #define NSIG _NSIG #define __old_sigset_t sigset_t -#define __old_sigset_t32 sigset_t32 #define __old_sigaction sigaction #define __old_sigaction32 sigaction32 #endif @@ -116,16 +110,11 @@ #ifndef __ASSEMBLY__ typedef unsigned long __old_sigset_t; /* at least 32 bits */ -typedef unsigned int __old_sigset_t32; typedef struct { unsigned long sig[_NSIG_WORDS]; } __new_sigset_t; -typedef struct { - unsigned int sig[_NSIG_WORDS32]; -} __new_sigset_t32; - /* A SunOS sigstack */ struct sigstack { /* XXX 32-bit pointers pinhead XXX */ @@ -213,14 +202,14 @@ struct __new_sigaction { __new_sigset_t sa_mask; }; +#ifdef __KERNEL__ struct __new_sigaction32 { unsigned sa_handler; unsigned int sa_flags; unsigned sa_restorer; /* not used by Linux/SPARC yet */ - __new_sigset_t32 sa_mask; + compat_sigset_t sa_mask; }; -#ifdef __KERNEL__ struct k_sigaction { struct __new_sigaction sa; void *ka_restorer; @@ -234,12 +223,14 @@ struct __old_sigaction { void (*sa_restorer)(void); /* not used by Linux/SPARC yet */ }; +#ifdef __KERNEL__ struct __old_sigaction32 { unsigned sa_handler; - __old_sigset_t32 sa_mask; + compat_old_sigset_t sa_mask; unsigned int sa_flags; unsigned sa_restorer; /* not used by Linux/SPARC yet */ }; +#endif typedef struct sigaltstack { void *ss_sp; -- cgit v1.2.3 From aae7d999e258ba7b26027251d99db728ea1622cf Mon Sep 17 00:00:00 2001 From: Kai Germaschewski Date: Thu, 16 Jan 2003 22:26:43 -0600 Subject: kbuild: Fix __start_SECTION, __stop_SECTION In a discussion with Sam Ravnborg, the following problem became apparent: Most vmlinux.lds.S (but the ARM ones) used the following construct: __start___ksymtab = .; __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { *(__ksymtab) } __stop___ksymtab = .; However, the link will align the beginning of the section __ksymtab according to the requirements for the input sections. If '.' (current location counter) wasn't sufficiently aligned before, it's possible that __ksymtab actually starts at an address after the one __start___ksymtab points to, which will confuse the users of __start___ksymtab badly. The fix is to follow what the ARM Makefiles did for this case, ie __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { __start___ksymtab = .; *(__ksymtab) __stop___ksymtab = .; } --- include/asm-generic/vmlinux.lds.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 3d97aae1c3e6..7563b5730aaa 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -13,18 +13,18 @@ } \ \ /* Kernel symbol table: Normal symbols */ \ - __start___ksymtab = .; \ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ + __start___ksymtab = .; \ *(__ksymtab) \ + __stop___ksymtab = .; \ } \ - __stop___ksymtab = .; \ \ /* Kernel symbol table: GPL-only symbols */ \ - __start___gpl_ksymtab = .; \ __gpl_ksymtab : AT(ADDR(__gpl_ksymtab) - LOAD_OFFSET) { \ + __start___gpl_ksymtab = .; \ *(__gpl_ksymtab) \ + __stop___gpl_ksymtab = .; \ } \ - __stop___gpl_ksymtab = .; \ \ /* Kernel symbol table: strings */ \ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ -- cgit v1.2.3 From 9247ef295a62f761d6ade312efc5db3de17d1d8f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 18 Jan 2003 08:08:43 -0800 Subject: [SPARC64]: Handle unchanging _TIF_32BIT properly in SET_PERSONALITY. --- include/asm-sparc64/elf.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/asm-sparc64/elf.h b/include/asm-sparc64/elf.h index 4ca2b010eac3..458f56b0095a 100644 --- a/include/asm-sparc64/elf.h +++ b/include/asm-sparc64/elf.h @@ -111,6 +111,8 @@ do { unsigned long new_flags = current_thread_info()->flags; \ if ((current_thread_info()->flags & _TIF_32BIT) \ != new_flags) \ set_thread_flag(TIF_ABI_PENDING); \ + else \ + clear_thread_flag(TIF_ABI_PENDING); \ /* flush_thread will update pgd cache */ \ if (ibcs2) \ set_personality(PER_SVR4); \ -- cgit v1.2.3 From cfc7729e170e225c0d78c5e621917154370a0c13 Mon Sep 17 00:00:00 2001 From: Jamie Lokier Date: Sat, 18 Jan 2003 08:10:45 -0800 Subject: [SPARC64]: Fix MAP_GROWSDOWN value, cannot be the same as MAP_LOCKED. --- include/asm-sparc/mman.h | 2 +- include/asm-sparc64/mman.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-sparc/mman.h b/include/asm-sparc/mman.h index fe0976a9df24..7fc6075d358e 100644 --- a/include/asm-sparc/mman.h +++ b/include/asm-sparc/mman.h @@ -21,7 +21,7 @@ #define MAP_LOCKED 0x100 /* lock the mapping */ #define _MAP_NEW 0x80000000 /* Binary compatibility is fun... */ -#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ +#define MAP_GROWSDOWN 0x0200 /* stack-like segment */ #define MAP_DENYWRITE 0x0800 /* ETXTBSY */ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ diff --git a/include/asm-sparc64/mman.h b/include/asm-sparc64/mman.h index ec42f85b2bb8..f5980a152936 100644 --- a/include/asm-sparc64/mman.h +++ b/include/asm-sparc64/mman.h @@ -21,7 +21,7 @@ #define MAP_LOCKED 0x100 /* lock the mapping */ #define _MAP_NEW 0x80000000 /* Binary compatibility is fun... */ -#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ +#define MAP_GROWSDOWN 0x0200 /* stack-like segment */ #define MAP_DENYWRITE 0x0800 /* ETXTBSY */ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ -- cgit v1.2.3 From a7280d6a0a5be2dc17942a3fe7611cf822937e42 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Mon, 20 Jan 2003 15:11:36 -0800 Subject: [IPV{4,6}]: Add ipfragok arg to ip_queue_xmit. --- include/net/ip.h | 2 +- include/net/tcp.h | 3 ++- net/ipv4/ip_output.c | 4 ++-- net/ipv4/tcp_output.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- net/netsyms.c | 2 ++ 6 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index f3c975e75409..1f5be0ecf807 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -96,7 +96,7 @@ extern int ip_mc_output(struct sk_buff *skb); extern int ip_fragment(struct sk_buff *skb, int (*out)(struct sk_buff*)); extern int ip_do_nat(struct sk_buff *skb); extern void ip_send_check(struct iphdr *ip); -extern int ip_queue_xmit(struct sk_buff *skb); +extern int ip_queue_xmit(struct sk_buff *skb, int ipfragok); extern void ip_init(void); extern int ip_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, diff --git a/include/net/tcp.h b/include/net/tcp.h index 2f564c25c4bd..d2ee14622b2c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -566,7 +566,8 @@ static inline void tcp_openreq_free(struct open_request *req) */ struct tcp_func { - int (*queue_xmit) (struct sk_buff *skb); + int (*queue_xmit) (struct sk_buff *skb, + int ipfragok); void (*send_check) (struct sock *sk, struct tcphdr *th, diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 4fa79edeb30a..171a96e87f30 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -280,7 +280,7 @@ int ip_output(struct sk_buff *skb) return ip_finish_output(skb); } -int ip_queue_xmit(struct sk_buff *skb) +int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; struct inet_opt *inet = inet_sk(sk); @@ -337,7 +337,7 @@ packet_routed: iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); *((__u16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); iph->tot_len = htons(skb->len); - if (ip_dont_fragment(sk, &rt->u.dst)) + if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok) iph->frag_off = htons(IP_DF); else iph->frag_off = 0; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f7b7ef83c308..c7acc4ccb978 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -276,7 +276,7 @@ int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb) TCP_INC_STATS(TcpOutSegs); - err = tp->af_specific->queue_xmit(skb); + err = tp->af_specific->queue_xmit(skb, 0); if (err <= 0) return err; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 11ee1fae4e09..838d190d257d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1746,7 +1746,7 @@ static int tcp_v6_rebuild_header(struct sock *sk) return 0; } -static int tcp_v6_xmit(struct sk_buff *skb) +static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; struct inet_opt *inet = inet_sk(sk); diff --git a/net/netsyms.c b/net/netsyms.c index dadfc1e8932a..5b2698af58ae 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -248,6 +248,8 @@ EXPORT_SYMBOL(inet_unregister_protosw); EXPORT_SYMBOL(ip_route_output_key); EXPORT_SYMBOL(ip_route_input); EXPORT_SYMBOL(icmp_send); +EXPORT_SYMBOL(icmp_statistics); +EXPORT_SYMBOL(icmp_err_convert); EXPORT_SYMBOL(ip_options_compile); EXPORT_SYMBOL(ip_options_undo); EXPORT_SYMBOL(arp_send); -- cgit v1.2.3 From fdb6c00f753c9c7419aef5f77a5318e3d7700f7d Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Thu, 23 Jan 2003 00:50:34 -0500 Subject: [PATCH] SAM-3 status codes The perverse CHECK_CONDITION in include/scsi/scsi.h seems to have struck again (see "Can't burn DVD under 2.5.59 with ide-cd" thread on the linux kernel list). Most users of CHECK_CONDITION found out to their surprise that it is shifted 1 bit (right) from those values found in the standards. The attachment marks the orginal list of SCSI status codes as deprecated and supplies defines taken from the most recent SAM-3 draft. --- include/scsi/scsi.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 137609a3cf0a..1f4c535362c3 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -104,7 +104,26 @@ extern const unsigned char scsi_command_size[8]; /* - * Status codes + * SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft + * T10/1561-D Revision 4 Draft dated 7th November 2002. + */ +#define SAM_STAT_GOOD 0x00 +#define SAM_STAT_CHECK_CONDITION 0x02 +#define SAM_STAT_CONDITION_MET 0x04 +#define SAM_STAT_BUSY 0x08 +#define SAM_STAT_IMMEDIATE 0x10 +#define SAM_STAT_IMMEDIATE_CONDITION_MET 0x14 +#define SAM_STAT_RESERVATION_CONFLICT 0x18 +#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ +#define SAM_STAT_TASK_SET_FULL 0x28 +#define SAM_STAT_ACA_ACTIVE 0x30 +#define SAM_STAT_TASK_ABORTED 0x40 + +/* + * Status codes. These are deprecated as they are shifted 1 bit right + * from those found in the SCSI standards. This causes confusion for + * applications that are ported to several OSes. Prefer SAM Status codes + * above. */ #define GOOD 0x00 -- cgit v1.2.3 From 9878e9dd9645b4a4b6f8e9446712e2cc27b76e6a Mon Sep 17 00:00:00 2001 From: Derek Atkins Date: Thu, 23 Jan 2003 18:28:38 -0800 Subject: [IPSEC]: Block on connect for IPSEC keying. --- include/net/route.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index ba15b5140798..ae62dc4e5683 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -167,7 +167,7 @@ static inline int ip_route_connect(struct rtable **rp, u32 dst, ip_rt_put(*rp); *rp = NULL; } - return ip_route_output_flow(rp, &fl, sk, 0); + return ip_route_output_flow(rp, &fl, sk, 1); } static inline int ip_route_newports(struct rtable **rp, u16 sport, u16 dport, -- cgit v1.2.3 From a6b9977667ca069f18ad85fab1c2445a0f2f4e78 Mon Sep 17 00:00:00 2001 From: Kai Germaschewski Date: Fri, 24 Jan 2003 02:27:06 -0600 Subject: kbuild: Add CONFIG_MODVERSIONING and __kcrctab This patch adds the new config option CONFIG_MODVERSIONING which will be the new way of checking for ABI changes between kernel and module code. This and the following patches are in part based on an initial implementation by Rusty Russell and I believe some of the ideas go back to discussions on linux-kbuild, Keith Owens and Rusty. though I'm not sure I think credit for the basic idea of storing version info in sections goes to Keith Owens and Rusty. o Rename __gpl_ksymtab to __ksymtab_gpl since that looks more consistent and appending _gpl instead of putting it into the middle simplifies sharing code for EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL() o Add CONFIG_MODVERSIONING o If CONFIG_MODVERSIONING is set, add a section __kcrctab{,_gpl}, which contains the ABI checksums for the exported symbols listed in __ksymtab{,_crc} Since we don't know the checksums yet at compilation time, just make them an unresolved symbol which gets filled in by the linker later. --- include/asm-generic/vmlinux.lds.h | 22 ++++++++++++++++++---- include/linux/module.h | 31 +++++++++++++++++++++---------- init/Kconfig | 13 +++++++++++++ kernel/module.c | 21 +++++++++++---------- scripts/per-cpu-check.awk | 2 +- 5 files changed, 64 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 7563b5730aaa..0f09f5b73a69 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -20,10 +20,24 @@ } \ \ /* Kernel symbol table: GPL-only symbols */ \ - __gpl_ksymtab : AT(ADDR(__gpl_ksymtab) - LOAD_OFFSET) { \ - __start___gpl_ksymtab = .; \ - *(__gpl_ksymtab) \ - __stop___gpl_ksymtab = .; \ + __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \ + __start___ksymtab_gpl = .; \ + *(__ksymtab_gpl) \ + __stop___ksymtab_gpl = .; \ + } \ + \ + /* Kernel symbol table: Normal symbols */ \ + __kcrctab : AT(ADDR(__kcrctab) - LOAD_OFFSET) { \ + __start___kcrctab = .; \ + *(__kcrctab) \ + __stop___kcrctab = .; \ + } \ + \ + /* Kernel symbol table: GPL-only symbols */ \ + __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) { \ + __start___kcrctab_gpl = .; \ + *(__kcrctab_gpl) \ + __stop___kcrctab_gpl = .; \ } \ \ /* Kernel symbol table: strings */ \ diff --git a/include/linux/module.h b/include/linux/module.h index 6dad1479105f..d017a67210c6 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -134,29 +134,40 @@ struct exception_table #ifdef CONFIG_MODULES + /* Get/put a kernel symbol (calls must be symmetric) */ void *__symbol_get(const char *symbol); void *__symbol_get_gpl(const char *symbol); #define symbol_get(x) ((typeof(&x))(__symbol_get(MODULE_SYMBOL_PREFIX #x))) +#ifdef CONFIG_MODVERSIONING +/* Mark the CRC weak since genksyms apparently decides not to + * generate a checksums for some symbols */ +#define __CRC_SYMBOL(sym, sec) \ + extern void *__crc_##sym __attribute__((weak)); \ + static const unsigned long __kcrctab_##sym \ + __attribute__((section("__kcrctab" sec))) \ + = (unsigned long) &__crc_##sym; +#else +#define __CRC_SYMBOL(sym, sec) +#endif + /* For every exported symbol, place a struct in the __ksymtab section */ -#define EXPORT_SYMBOL(sym) \ +#define __EXPORT_SYMBOL(sym, sec) \ + __CRC_SYMBOL(sym, sec) \ static const char __kstrtab_##sym[] \ __attribute__((section("__ksymtab_strings"))) \ = MODULE_SYMBOL_PREFIX #sym; \ static const struct kernel_symbol __ksymtab_##sym \ - __attribute__((section("__ksymtab"))) \ + __attribute__((section("__ksymtab" sec))) \ = { (unsigned long)&sym, __kstrtab_##sym } -#define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL(sym) __EXPORT_SYMBOL(sym, "") +#define EXPORT_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_gpl") -#define EXPORT_SYMBOL_GPL(sym) \ - static const char __kstrtab_##sym[] \ - __attribute__((section("__ksymtab_strings"))) \ - = MODULE_SYMBOL_PREFIX #sym; \ - static const struct kernel_symbol __ksymtab_##sym \ - __attribute__((section("__gpl_ksymtab"))) \ - = { (unsigned long)&sym, __kstrtab_##sym } +/* We don't mangle the actual symbol anymore, so no need for + * special casing EXPORT_SYMBOL_NOVERS */ +#define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym) struct module_ref { diff --git a/init/Kconfig b/init/Kconfig index fcdd9fdb7896..5541908be8c0 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -144,6 +144,19 @@ config OBSOLETE_MODPARM have not been converted to the new module parameter system yet. If unsure, say Y. +config MODVERSIONING + bool "Module versioning support (EXPERIMENTAL)" + depends on MODULES && EXPERIMENTAL + help + ---help--- + Usually, you have to use modules compiled with your kernel. + Saying Y here makes it sometimes possible to use modules + compiled for different kernels, by adding enough information + to the modules to (hopefully) spot any changes which would + make them incompatible with the kernel you are running. If + you say Y here, you will need a copy of genksyms. If + unsure, say N. + config KMOD bool "Kernel module loader" depends on MODULES diff --git a/kernel/module.c b/kernel/module.c index 864828099b36..b798fd62ad2f 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1040,6 +1040,11 @@ static struct module *load_module(void *umod, /* Exported symbols. */ DEBUGP("EXPORT table in section %u\n", i); exportindex = i; + } else if (strcmp(secstrings+sechdrs[i].sh_name, + "__ksymtab_gpl") == 0) { + /* Exported symbols. (GPL) */ + DEBUGP("GPL symbols found in section %u\n", i); + gplindex = i; } else if (strcmp(secstrings+sechdrs[i].sh_name, "__param") == 0) { /* Setup parameter info */ @@ -1060,11 +1065,6 @@ static struct module *load_module(void *umod, /* MODULE_LICENSE() */ DEBUGP("Licence found in section %u\n", i); licenseindex = i; - } else if (strcmp(secstrings+sechdrs[i].sh_name, - "__gpl_ksymtab") == 0) { - /* EXPORT_SYMBOL_GPL() */ - DEBUGP("GPL symbols found in section %u\n", i); - gplindex = i; } else if (strcmp(secstrings+sechdrs[i].sh_name, "__vermagic") == 0) { /* Version magic. */ @@ -1492,8 +1492,8 @@ int module_text_address(unsigned long addr) /* Provided by the linker */ extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[]; -extern const struct kernel_symbol __start___gpl_ksymtab[]; -extern const struct kernel_symbol __stop___gpl_ksymtab[]; +extern const struct kernel_symbol __start___ksymtab_gpl[]; +extern const struct kernel_symbol __stop___ksymtab_gpl[]; static struct kernel_symbol_group kernel_symbols, kernel_gpl_symbols; @@ -1504,9 +1504,10 @@ static int __init symbols_init(void) kernel_symbols.syms = __start___ksymtab; kernel_symbols.gplonly = 0; list_add(&kernel_symbols.list, &symbols); - kernel_gpl_symbols.num_syms = (__stop___gpl_ksymtab - - __start___gpl_ksymtab); - kernel_gpl_symbols.syms = __start___gpl_ksymtab; + + kernel_gpl_symbols.num_syms = (__stop___ksymtab_gpl + - __start___ksymtab_gpl); + kernel_gpl_symbols.syms = __start___ksymtab_gpl; kernel_gpl_symbols.gplonly = 1; list_add(&kernel_gpl_symbols.list, &symbols); diff --git a/scripts/per-cpu-check.awk b/scripts/per-cpu-check.awk index f1b34c42df4b..3be9f0d25ebd 100644 --- a/scripts/per-cpu-check.awk +++ b/scripts/per-cpu-check.awk @@ -6,7 +6,7 @@ IN_PER_CPU=0 } -/__per_cpu$$/ && ! ( / __ksymtab_/ || / __kstrtab_/ ) { +/__per_cpu$$/ && ! ( / __ksymtab_/ || / __kstrtab_/ || / __kcrctab_/ ) { if (!IN_PER_CPU) { print $$3 " not in per-cpu section" > "/dev/stderr"; FOUND=1; -- cgit v1.2.3 From 09369f635011eb6d520925f375f364f4273163e1 Mon Sep 17 00:00:00 2001 From: Kai Germaschewski Date: Fri, 24 Jan 2003 09:45:27 -0600 Subject: kbuild: Generate versions for exported symbols Up to now, we had a way to store the checksums associated with the exported symbols, but they were not filled in yet. This is done with this patch, using the linker to actually do that for us. The comment added with this patch explains what magic exactly is going on. --- include/linux/module.h | 16 +++++++++++++-- scripts/Makefile.build | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index d017a67210c6..7475c1791529 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -140,6 +140,13 @@ void *__symbol_get(const char *symbol); void *__symbol_get_gpl(const char *symbol); #define symbol_get(x) ((typeof(&x))(__symbol_get(MODULE_SYMBOL_PREFIX #x))) +#ifdef __GENKSYMS__ + +/* genksyms doesn't handle GPL-only symbols yet */ +#define EXPORT_SYMBOL_GPL EXPORT_SYMBOL + +#else + #ifdef CONFIG_MODVERSIONING /* Mark the CRC weak since genksyms apparently decides not to * generate a checksums for some symbols */ @@ -162,8 +169,13 @@ void *__symbol_get_gpl(const char *symbol); __attribute__((section("__ksymtab" sec))) \ = { (unsigned long)&sym, __kstrtab_##sym } -#define EXPORT_SYMBOL(sym) __EXPORT_SYMBOL(sym, "") -#define EXPORT_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_gpl") +#define EXPORT_SYMBOL(sym) \ + __EXPORT_SYMBOL(sym, "") + +#define EXPORT_SYMBOL_GPL(sym) \ + __EXPORT_SYMBOL(sym, "_gpl") + +#endif /* We don't mangle the actual symbol anymore, so no need for * special casing EXPORT_SYMBOL_NOVERS */ diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 1fe8acad16fe..522c40663372 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -50,6 +50,55 @@ __build: $(if $(KBUILD_BUILTIN),$(O_TARGET) $(L_TARGET) $(EXTRA_TARGETS)) \ $(subdir-ym) $(build-targets) @: +# Module versioning +# --------------------------------------------------------------------------- + +ifdef CONFIG_MODVERSIONING + +# $(call if_changed_rule,vcc_o_c) does essentially the same as the +# normal $(call if_changed_dep,cc_o_c), i.e. compile an object file +# from a C file, keeping track of the command line and dependencies. +# +# However, actually it does: +# o compile a .tmp_.o from .c +# o if .tmp_.o doesn't contain a __ksymtab version, i.e. does +# not export symbols, we just rename .tmp_.o to .o and +# are done. +# o otherwise, we calculate symbol versions using the good old +# genksyms on the preprocessed source and postprocess them in a way +# that they are usable as a linker script +# o generate .o from .tmp_.o using the linker to +# replace the unresolved symbols __crc_exported_symbol with +# the actual value of the checksum generated by genksyms + +quiet_cmd_vcc_o_c = CC $(quiet_modtag) $@ +cmd_vcc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $< + +define rule_vcc_o_c + $(if $($(quiet)cmd_vcc_o_c),echo ' $($(quiet)cmd_vcc_o_c)';) \ + $(cmd_vcc_o_c); \ + \ + if ! $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \ + mv $(@D)/.tmp_$(@F) $@; \ + else \ + $(CPP) -D__GENKSYMS__ $(c_flags) $< \ + | $(GENKSYMS) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) \ + | grep __ver \ + | sed 's/\#define __ver_\([^ ]*\)[ ]*\([^ ]*\)/__crc_\1 = 0x\2 ;/g' \ + > $(@D)/.tmp_$(@F:.o=.ver); \ + \ + $(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \ + -T $(@D)/.tmp_$(@F:.o=.ver); \ + rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \ + fi; + \ + scripts/fixdep $(depfile) $@ '$(cmd_vcc_o_c)' > $(@D)/.$(@F).tmp; \ + rm -f $(depfile); \ + mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd +endef + +endif + # Compile C sources (.c) # --------------------------------------------------------------------------- @@ -97,7 +146,11 @@ quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< %.o: %.c FORCE +ifdef CONFIG_MODVERSIONING + $(call if_changed_rule,vcc_o_c) +else $(call if_changed_dep,cc_o_c) +endif quiet_cmd_cc_lst_c = MKLST $@ cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && \ -- cgit v1.2.3 From 1336f151165fd8a2da10a5d8e1a633e3b72e3fd7 Mon Sep 17 00:00:00 2001 From: Kai Germaschewski Date: Fri, 24 Jan 2003 09:49:33 -0600 Subject: kbuild/modules: Track versions of exported symbols Store the information on the checksum alongside the rest of the information on exported symbols. To actually use them, we need something to check them against first, though ;) Also, fix some conditional debug code to actually compile. --- include/linux/module.h | 1 + kernel/module.c | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index 7475c1791529..af4272910b27 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -119,6 +119,7 @@ struct kernel_symbol_group unsigned int num_syms; const struct kernel_symbol *syms; + const unsigned long *crcs; }; /* Given an address, look for it in the exception tables */ diff --git a/kernel/module.c b/kernel/module.c index f54e6ada3d26..c9dc1996d136 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -916,7 +916,7 @@ static void layout_sections(struct module *mod, || strstr(secstrings + s->sh_name, ".init")) continue; s->sh_entsize = get_offset(&mod->core_size, s); - DEBUGP("\t%s\n", name); + DEBUGP("\t%s\n", secstrings + s->sh_name); } } @@ -932,7 +932,7 @@ static void layout_sections(struct module *mod, continue; s->sh_entsize = (get_offset(&mod->init_size, s) | INIT_OFFSET_MASK); - DEBUGP("\t%s\n", name); + DEBUGP("\t%s\n", secstrings + s->sh_name); } } } @@ -976,7 +976,8 @@ static struct module *load_module(void *umod, Elf_Shdr *sechdrs; char *secstrings, *args; unsigned int i, symindex, exportindex, strindex, setupindex, exindex, - modindex, obsparmindex, licenseindex, gplindex, vmagindex; + modindex, obsparmindex, licenseindex, gplindex, vmagindex, + crcindex, gplcrcindex; long arglen; struct module *mod; long err = 0; @@ -1012,7 +1013,8 @@ static struct module *load_module(void *umod, /* May not export symbols, or have setup params, so these may not exist */ - exportindex = setupindex = obsparmindex = gplindex = licenseindex = 0; + exportindex = setupindex = obsparmindex = gplindex = licenseindex + = crcindex = gplcrcindex = 0; /* And these should exist, but gcc whinges if we don't init them */ symindex = strindex = exindex = modindex = vmagindex = 0; @@ -1045,6 +1047,16 @@ static struct module *load_module(void *umod, /* Exported symbols. (GPL) */ DEBUGP("GPL symbols found in section %u\n", i); gplindex = i; + } else if (strcmp(secstrings+sechdrs[i].sh_name, "__kcrctab") + == 0) { + /* Exported symbols CRCs. */ + DEBUGP("CRC table in section %u\n", i); + crcindex = i; + } else if (strcmp(secstrings+sechdrs[i].sh_name, "__kcrctab_gpl") + == 0) { + /* Exported symbols CRCs. (GPL)*/ + DEBUGP("CRC table in section %u\n", i); + gplcrcindex = i; } else if (strcmp(secstrings+sechdrs[i].sh_name, "__param") == 0) { /* Setup parameter info */ @@ -1192,9 +1204,11 @@ static struct module *load_module(void *umod, mod->symbols.num_syms = (sechdrs[exportindex].sh_size / sizeof(*mod->symbols.syms)); mod->symbols.syms = (void *)sechdrs[exportindex].sh_addr; + mod->symbols.crcs = (void *)sechdrs[crcindex].sh_addr; mod->gpl_symbols.num_syms = (sechdrs[gplindex].sh_size / sizeof(*mod->symbols.syms)); mod->gpl_symbols.syms = (void *)sechdrs[gplindex].sh_addr; + mod->gpl_symbols.crcs = (void *)sechdrs[gplcrcindex].sh_addr; /* Set up exception table */ if (exindex) { @@ -1497,6 +1511,10 @@ extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[]; extern const struct kernel_symbol __start___ksymtab_gpl[]; extern const struct kernel_symbol __stop___ksymtab_gpl[]; +extern const unsigned long __start___kcrctab[]; +extern const unsigned long __stop___kcrctab[]; +extern const unsigned long __start___kcrctab_gpl[]; +extern const unsigned long __stop___kcrctab_gpl[]; static struct kernel_symbol_group kernel_symbols, kernel_gpl_symbols; @@ -1505,12 +1523,14 @@ static int __init symbols_init(void) /* Add kernel symbols to symbol table */ kernel_symbols.num_syms = (__stop___ksymtab - __start___ksymtab); kernel_symbols.syms = __start___ksymtab; + kernel_symbols.crcs = __start___kcrctab; kernel_symbols.gplonly = 0; list_add(&kernel_symbols.list, &symbols); kernel_gpl_symbols.num_syms = (__stop___ksymtab_gpl - __start___ksymtab_gpl); kernel_gpl_symbols.syms = __start___ksymtab_gpl; + kernel_gpl_symbols.crcs = __start___kcrctab_gpl; kernel_gpl_symbols.gplonly = 1; list_add(&kernel_gpl_symbols.list, &symbols); -- cgit v1.2.3 From dcc38eae49e06d798497373771ae03f5508a2ec7 Mon Sep 17 00:00:00 2001 From: Kai Germaschewski Date: Fri, 24 Jan 2003 09:54:04 -0600 Subject: kbuild/modules: Record versions for unresolved symbols In the case of CONFIG_MODVERSIONING, the build step will only generate preliminary .o objects, and an additional postprocessing step is necessary to record the versions of the unresolved symbols and add them into the final .ko The version information for unresolved symbols is again recorded into a special section, "__versions", which contains an array of symbol name strings and checksum (struct modversion_info). Size is here not an issue, since this section will not be stored permanently in kernel memory. Makefile.modver takes care of the following steps: o Collect the version information for all exported symbols from vmlinux and all modules which export symbols. o For each module, generate a C file which contains the modversion information for all unresolved symbols in that module. o For each module, compile that C file to an object file o Finally, link the .ko using the preliminary + the version information above. The first two steps are currently done by not very efficient scripting, so there's room for performance improvement using some helper C code. --- Makefile | 15 +++++++-- include/linux/module.h | 7 ++++ scripts/Makefile.modver | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index 308673b8b2e5..3387d56da0b0 100644 --- a/Makefile +++ b/Makefile @@ -262,7 +262,7 @@ endif # When we're building modules with modversions, we need to consider # the built-in objects during the descend as well, in order to -# make sure the checksums are uptodate before we use them. +# make sure the checksums are uptodate before we record them. ifdef CONFIG_MODVERSIONING ifeq ($(KBUILD_MODULES),1) @@ -508,8 +508,16 @@ ifdef CONFIG_MODULES # Build modules -.PHONY: modules -modules: $(SUBDIRS) +.PHONY: modules __modversions +modules: $(SUBDIRS) __modversions + +ifdef CONFIG_MODVERSIONING + +__modversions: vmlinux $(SUBDIRS) + @echo ' Recording module symbol versions.'; + $(Q)$(MAKE) -rR -f scripts/Makefile.modver + +endif # Install modules @@ -690,6 +698,7 @@ MRPROPER_FILES += \ # Directories removed with 'make mrproper' MRPROPER_DIRS += \ + $(MODVERDIR) \ .tmp_export-objs \ include/config \ include/linux/modules diff --git a/include/linux/module.h b/include/linux/module.h index af4272910b27..e8726d9909d7 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -33,12 +33,19 @@ #endif #define MODULE_NAME_LEN (64 - sizeof(unsigned long)) + struct kernel_symbol { unsigned long value; const char *name; }; +struct modversion_info +{ + unsigned long crc; + char name[MODULE_NAME_LEN]; +}; + /* These are either module local, or the kernel's dummy ones. */ extern int init_module(void); extern void cleanup_module(void); diff --git a/scripts/Makefile.modver b/scripts/Makefile.modver index 5405cbce2170..372d6aff5a32 100644 --- a/scripts/Makefile.modver +++ b/scripts/Makefile.modver @@ -2,3 +2,92 @@ # Module versions # =========================================================================== +.PHONY: __modversions +__modversions: + +include scripts/Makefile.lib + +# + +modules := $(patsubst ./%,%,$(shell cd $(MODVERDIR); find . -name \*.ko)) + +__modversions: $(modules) + @: + +quiet_cmd_ld_ko_o = LD [M] $@ + cmd_ld_ko_o = $(LD) $(LDFLAGS) $(LDFLAGS_MODULE) -o $@ \ + $(filter-out FORCE,$^) + +init/vermagic.o: ; + +$(modules): %.ko :%.o $(MODVERDIR)/%.o init/vermagic.o FORCE + $(call if_changed,ld_ko_o) + +targets += $(modules) + + +quiet_cmd_cc_o_c = CC $@ + cmd_cc_o_c = $(CC) $(CFLAGS) -c -o $@ $< + +$(addprefix $(MODVERDIR)/,$(modules:.ko=.o)): %.o: %.c FORCE + $(call if_changed,cc_o_c) + +targets += $(addprefix $(MODVERDIR)/,$(modules:.ko=.o)) + +define rule_mkver_o_c + echo ' MKVER $@'; \ + ( echo "#include "; \ + echo ""; \ + echo "static const struct modversion_info ____versions[]"; \ + echo "__attribute__((section(\"__versions\"))) = {"; \ + for sym in `nm -u $<`; do \ + grep "\"$$sym\"" .tmp_all-versions \ + || echo "*** Warning: $(<:.o=.ko): \"$$sym\" unresolved!" >&2;\ + done; \ + echo "};"; \ + ) > $@ +endef + +$(addprefix $(MODVERDIR)/,$(modules:.ko=.c)): \ +$(MODVERDIR)/%.c: %.o .tmp_all-versions FORCE + $(call if_changed_rule,mkver_o_c) + +targets += $(addprefix $(MODVERDIR)/,$(modules:.ko=.o.c)) + +export-objs := $(shell for m in vmlinux $(modules:.ko=.o); do objdump -h $$m | grep -q __ksymtab && echo $$m; done) + +cmd_gen-all-versions = mksyms $(export-objs) +define rule_gen-all-versions + echo ' MKSYMS $@'; \ + for mod in $(export-objs); do \ + modname=`basename $$mod`; \ + nm $$mod \ + | grep ' __crc_' \ + | sed "s/\([^ ]*\) A __crc_\(.*\)/{ 0x\1, \"\2\" }, \/* $$modname *\//g;s/.* w __crc_\(.*\)/{ 0x0 , \"\1\" }, \/* $$modname *\//g"; \ + done > $@; \ + echo 'cmd_$@ := $(cmd_$(1))' > $(@D)/.$(@F).cmd +endef + +.tmp_all-versions: $(export-objs) FORCE + $(call if_changed_rule,gen-all-versions) + +targets += .tmp_all-versions + +# Add FORCE to the prequisites of a target to force it to be always rebuilt. +# --------------------------------------------------------------------------- + +.PHONY: FORCE + +FORCE: + +# Read all saved command lines and dependencies for the $(targets) we +# may be building above, using $(if_changed{,_dep}). As an +# optimization, we don't need to read them if the target does not +# exist, we will rebuild anyway in that case. + +targets := $(wildcard $(sort $(targets))) +cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) + +ifneq ($(cmd_files),) + include $(cmd_files) +endif -- cgit v1.2.3 From e9aff3178f09e5281ddc4bb1220a9acc64888421 Mon Sep 17 00:00:00 2001 From: Kai Germaschewski Date: Sat, 25 Jan 2003 08:20:44 -0600 Subject: kbuild: gcc-3.3 warns about 2.5.59 EXPORT_SYMBOL When building linux-2.5.59 with gcc-3.3 (on s390, if that matters), I get a warning like "warning: `__ksymtab___foo' defined but not used" each time that EXPORT_SYMBOL is used. by Arnd Bergmann --- include/linux/module.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index e8726d9909d7..1be44624ec3a 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -99,7 +99,7 @@ extern const struct gtype##_id __mod_##gtype##_table \ */ #define MODULE_LICENSE(license) \ static const char __module_license[] \ - __attribute__((section(".init.license"))) = license + __attribute__((section(".init.license"), unused)) = license #else /* !MODULE */ @@ -161,7 +161,7 @@ void *__symbol_get_gpl(const char *symbol); #define __CRC_SYMBOL(sym, sec) \ extern void *__crc_##sym __attribute__((weak)); \ static const unsigned long __kcrctab_##sym \ - __attribute__((section("__kcrctab" sec))) \ + __attribute__((section("__kcrctab" sec), unused)) \ = (unsigned long) &__crc_##sym; #else #define __CRC_SYMBOL(sym, sec) @@ -174,7 +174,7 @@ void *__symbol_get_gpl(const char *symbol); __attribute__((section("__ksymtab_strings"))) \ = MODULE_SYMBOL_PREFIX #sym; \ static const struct kernel_symbol __ksymtab_##sym \ - __attribute__((section("__ksymtab" sec))) \ + __attribute__((section("__ksymtab" sec), unused)) \ = { (unsigned long)&sym, __kstrtab_##sym } #define EXPORT_SYMBOL(sym) \ -- cgit v1.2.3 From 15c2e5448ff3c917af325daf9b49a62e399dba42 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 25 Jan 2003 18:26:32 +0000 Subject: [ARM] Fix KSTK_EIP and KSTK_ESP macros These two macros got missed when converting from the task-struct on stack to thread_info-struct on stack. --- include/asm-arm/proc-armv/processor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-arm/proc-armv/processor.h b/include/asm-arm/proc-armv/processor.h index 853f411083db..390e887d2037 100644 --- a/include/asm-arm/proc-armv/processor.h +++ b/include/asm-arm/proc-armv/processor.h @@ -23,7 +23,7 @@ #define KERNEL_STACK_SIZE PAGE_SIZE #define INIT_EXTRA_THREAD_INFO \ - cpu_domain: domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ + .cpu_domain = domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT) @@ -45,7 +45,7 @@ regs->ARM_r0 = stack[0]; /* r0 (argc) */ \ }) -#define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1019]) -#define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1017]) +#define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)->thread_info))[1019]) +#define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)->thread_info))[1017]) #endif -- cgit v1.2.3 From a7c4fb648cdfc8d8332d6486fa7b522b7279169c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 25 Jan 2003 18:33:32 +0000 Subject: [ARM] Add extra IO functionality. Add {read,write}[bwl] functionality to Acorn RISC PC. Add {read,write}s[bwl] functionality for all. --- include/asm-arm/arch-rpc/io.h | 5 +++++ include/asm-arm/io.h | 8 ++++++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/asm-arm/arch-rpc/io.h b/include/asm-arm/arch-rpc/io.h index 3a1c18ad63bb..3f7a2366cad3 100644 --- a/include/asm-arm/arch-rpc/io.h +++ b/include/asm-arm/arch-rpc/io.h @@ -247,4 +247,9 @@ DECLARE_IO(int,l,"") #define outsb(p,d,l) __raw_writesb(__ioaddr(p),d,l) #define outsw(p,d,l) __raw_writesw(__ioaddr(p),d,l) +/* + * 1:1 mapping for ioremapped regions. + */ +#define __mem_pci(x) (x) + #endif diff --git a/include/asm-arm/io.h b/include/asm-arm/io.h index 1dc1dd5d611a..8bb0d5e1cc9a 100644 --- a/include/asm-arm/io.h +++ b/include/asm-arm/io.h @@ -150,10 +150,18 @@ extern void _memset_io(unsigned long, int, size_t); #define readw(c) ({ unsigned int __v = le16_to_cpu(__raw_readw(__mem_pci(c))); __v; }) #define readl(c) ({ unsigned int __v = le32_to_cpu(__raw_readl(__mem_pci(c))); __v; }) +#define readsb(p,d,l) __raw_readsb((unsigned int)__mem_pci(p),d,l) +#define readsw(p,d,l) __raw_readsw((unsigned int)__mem_pci(p),d,l) +#define readsl(p,d,l) __raw_readsl((unsigned int)__mem_pci(p),d,l) + #define writeb(v,c) __raw_writeb(v,__mem_pci(c)) #define writew(v,c) __raw_writew(cpu_to_le16(v),__mem_pci(c)) #define writel(v,c) __raw_writel(cpu_to_le32(v),__mem_pci(c)) +#define writesb(p,d,l) __raw_writesb((unsigned int)__mem_pci(p),d,l) +#define writesw(p,d,l) __raw_writesw((unsigned int)__mem_pci(p),d,l) +#define writesl(p,d,l) __raw_writesl((unsigned int)__mem_pci(p),d,l) + #define memset_io(c,v,l) _memset_io(__mem_pci(c),(v),(l)) #define memcpy_fromio(a,c,l) _memcpy_fromio((a),__mem_pci(c),(l)) #define memcpy_toio(c,a,l) _memcpy_toio(__mem_pci(c),(a),(l)) -- cgit v1.2.3 From 299c2e8667b2b79a78a3f7f681e443b1a6d0410c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 26 Jan 2003 14:45:19 +0000 Subject: [ARM] Convert ecard to allow use of ioremap + {read,write}[bwl] --- arch/arm/kernel/ecard.c | 104 +++++++++++++++++++++++++++++++++--- include/asm-arm/arch-rpc/hardware.h | 11 ++++ include/asm-arm/ecard.h | 17 +++++- 3 files changed, 124 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index a6c0c1993306..2ce63481432b 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -881,6 +881,90 @@ static void ecard_proc_init(void) get_ecard_dev_info); } +#define ec_set_resource(ec,nr,st,sz,flg) \ + do { \ + (ec)->resource[nr].name = ec->dev.name; \ + (ec)->resource[nr].start = st; \ + (ec)->resource[nr].end = (st) + (sz) - 1; \ + (ec)->resource[nr].flags = flg; \ + } while (0) + +static void __init ecard_init_resources(struct expansion_card *ec) +{ + unsigned long base = PODSLOT_IOC4_BASE; + unsigned int slot = ec->slot_no; + int i; + + if (slot < 4) { + ec_set_resource(ec, ECARD_RES_MEMC, + PODSLOT_MEMC_BASE + (slot << 14), + PODSLOT_MEMC_SIZE, IORESOURCE_MEM); + base = PODSLOT_IOC0_BASE; + } + +#ifdef CONFIG_ARCH_RPC + if (slot < 8) { + ec_set_resource(ec, ECARD_RES_EASI, + PODSLOT_EASI_BASE + (slot << 24), + PODSLOT_EASI_SIZE, IORESOURCE_MEM); + } + + if (slot == 8) { + ec_set_resource(ec, ECARD_RES_MEMC, NETSLOT_BASE, + NETSLOT_SIZE, IORESOURCE_MEM); + } else +#endif + + for (i = 0; i < ECARD_RES_IOCSYNC - ECARD_RES_IOCSLOW; i++) { + ec_set_resource(ec, i + ECARD_RES_IOCSLOW, + base + (slot << 14) + (i << 19), + PODSLOT_IOC_SIZE, IORESOURCE_MEM); + } + + for (i = 0; i < ECARD_NUM_RESOURCES; i++) { + if (ec->resource[i].start && + request_resource(&iomem_resource, &ec->resource[i])) { + printk(KERN_ERR "%s: resource(s) not available\n", + ec->dev.bus_id); + ec->resource[i].end -= ec->resource[i].start; + ec->resource[i].start = 0; + } + } +} + +static ssize_t ecard_show_irq(struct device *dev, char *buf) +{ + struct expansion_card *ec = ECARD_DEV(dev); + return sprintf(buf, "%u\n", ec->irq); +} + +static DEVICE_ATTR(irq, S_IRUGO, ecard_show_irq, NULL); + +static ssize_t ecard_show_dma(struct device *dev, char *buf) +{ + struct expansion_card *ec = ECARD_DEV(dev); + return sprintf(buf, "%u\n", ec->dma); +} + +static DEVICE_ATTR(dma, S_IRUGO, ecard_show_dma, NULL); + +static ssize_t ecard_show_resources(struct device *dev, char *buf) +{ + struct expansion_card *ec = ECARD_DEV(dev); + char *str = buf; + int i; + + for (i = 0; i < ECARD_NUM_RESOURCES; i++) + str += sprintf(str, "%08lx %08lx %08lx\n", + ec->resource[i].start, + ec->resource[i].end, + ec->resource[i].flags); + + return str - buf; +} + +static DEVICE_ATTR(resource, S_IRUGO, ecard_show_resources, NULL); + /* * Probe for an expansion card. * @@ -949,6 +1033,16 @@ ecard_probe(int slot, card_type_t type) break; } + snprintf(ec->dev.bus_id, sizeof(ec->dev.bus_id), "ecard%d", slot); + snprintf(ec->dev.name, sizeof(ec->dev.name), "ecard %04x:%04x", + ec->cid.manufacturer, ec->cid.product); + ec->dev.parent = NULL; + ec->dev.bus = &ecard_bus_type; + ec->dev.dma_mask = &ec->dma_mask; + ec->dma_mask = (u64)0xffffffff; + + ecard_init_resources(ec); + /* * hook the interrupt handlers */ @@ -974,14 +1068,10 @@ ecard_probe(int slot, card_type_t type) *ecp = ec; slot_to_expcard[slot] = ec; - snprintf(ec->dev.bus_id, sizeof(ec->dev.bus_id), "ecard%d", slot); - strcpy(ec->dev.name, "fixme!"); - ec->dev.parent = NULL; - ec->dev.bus = &ecard_bus_type; - ec->dev.dma_mask = &ec->dma_mask; - ec->dma_mask = (u64)0xffffffff; - device_register(&ec->dev); + device_create_file(&ec->dev, &dev_attr_dma); + device_create_file(&ec->dev, &dev_attr_irq); + device_create_file(&ec->dev, &dev_attr_resource); return 0; diff --git a/include/asm-arm/arch-rpc/hardware.h b/include/asm-arm/arch-rpc/hardware.h index a25d8394ffc5..9dc5f5d4b912 100644 --- a/include/asm-arm/arch-rpc/hardware.h +++ b/include/asm-arm/arch-rpc/hardware.h @@ -63,6 +63,17 @@ #define IO_EC_MEMC8_BASE 0x8000ac00 #define IO_EC_MEMC_BASE 0x80000000 +#define NETSLOT_BASE 0x0302b000 +#define NETSLOT_SIZE 0x00001000 + +#define PODSLOT_IOC0_BASE 0x03240000 +#define PODSLOT_IOC4_BASE 0x03270000 +#define PODSLOT_IOC_SIZE (1 << 14) +#define PODSLOT_MEMC_BASE 0x03000000 +#define PODSLOT_MEMC_SIZE (1 << 14) +#define PODSLOT_EASI_BASE 0x08000000 +#define PODSLOT_EASI_SIZE (1 << 24) + #define EXPMASK_STATUS (EXPMASK_BASE + 0x00) #define EXPMASK_ENABLE (EXPMASK_BASE + 0x04) diff --git a/include/asm-arm/ecard.h b/include/asm-arm/ecard.h index 602dc63b9dfa..89c1d1db4b7b 100644 --- a/include/asm-arm/ecard.h +++ b/include/asm-arm/ecard.h @@ -130,6 +130,20 @@ typedef struct { /* Card handler routines */ int (*fiqpending)(ecard_t *ec); } expansioncard_ops_t; +#define ECARD_NUM_RESOURCES (6) + +#define ECARD_RES_IOCSLOW (0) +#define ECARD_RES_IOCMEDIUM (1) +#define ECARD_RES_IOCFAST (2) +#define ECARD_RES_IOCSYNC (3) +#define ECARD_RES_MEMC (4) +#define ECARD_RES_EASI (5) + +#define ecard_resource_start(ec,nr) ((ec)->resource[nr].start) +#define ecard_resource_end(ec,nr) ((ec)->resource[nr].end) +#define ecard_resource_len(ec,nr) ((ec)->resource[nr].end - \ + (ec)->resource[nr].start + 1) + /* * This contains all the info needed on an expansion card */ @@ -137,6 +151,7 @@ struct expansion_card { struct expansion_card *next; struct device dev; + struct resource resource[ECARD_NUM_RESOURCES]; /* Public data */ volatile unsigned char *irqaddr; /* address of IRQ register */ @@ -147,7 +162,7 @@ struct expansion_card { void *irq_data; /* Data for use for IRQ by card */ void *fiq_data; /* Data for use for FIQ by card */ - expansioncard_ops_t *ops; /* Enable/Disable Ops for card */ + const expansioncard_ops_t *ops; /* Enable/Disable Ops for card */ CONST unsigned int slot_no; /* Slot number */ CONST unsigned int dma; /* DMA number (for request_dma) */ -- cgit v1.2.3 From aa2cbc19b92e37e54e5af342699e2bc51e67c1dc Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 27 Jan 2003 00:10:48 +0000 Subject: [ARM] Remove 200Hz -> 100Hz conversion for ebsa110 timer. We now really run the ebsa110 kernel timer at 200Hz, and convert where necessary to 100Hz for user space. --- arch/arm/mach-ebsa110/Makefile | 2 +- arch/arm/mach-ebsa110/time.c | 112 ----------------------------------- include/asm-arm/arch-ebsa110/param.h | 1 + include/asm-arm/arch-ebsa110/time.h | 86 ++++++++++++++++++++++++--- 4 files changed, 80 insertions(+), 121 deletions(-) delete mode 100644 arch/arm/mach-ebsa110/time.c (limited to 'include') diff --git a/arch/arm/mach-ebsa110/Makefile b/arch/arm/mach-ebsa110/Makefile index 2f0104c4f10e..1504bbe04749 100644 --- a/arch/arm/mach-ebsa110/Makefile +++ b/arch/arm/mach-ebsa110/Makefile @@ -4,7 +4,7 @@ # Object file lists. -obj-y := core.o io.o time.o +obj-y := core.o io.o obj-m := obj-n := obj- := diff --git a/arch/arm/mach-ebsa110/time.c b/arch/arm/mach-ebsa110/time.c deleted file mode 100644 index 989ab98a8d97..000000000000 --- a/arch/arm/mach-ebsa110/time.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * linux/arch/arm/mach-ebsa110/time.c - * - * Copyright (C) 2001 Russell King - * - * 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 - -#include - -#define PIT_CTRL (PIT_BASE + 0x0d) -#define PIT_T2 (PIT_BASE + 0x09) -#define PIT_T1 (PIT_BASE + 0x05) -#define PIT_T0 (PIT_BASE + 0x01) - -/* - * This is the rate at which your MCLK signal toggles (in Hz) - * This was measured on a 10 digit frequency counter sampling - * over 1 second. - */ -#define MCLK 47894000 - -/* - * This is the rate at which the PIT timers get clocked - */ -#define CLKBY7 (MCLK / 7) - -/* - * If CLKBY7 is larger than this, then we must do software - * division of the timer interrupt. - */ -#if CLKBY7 > 6553500 -#define DIVISOR 2 -#else -#define DIVISOR 1 -#endif - -/* - * This is the counter value - */ -#define COUNT ((CLKBY7 + (DIVISOR * HZ / 2)) / (DIVISOR * HZ)) - -extern unsigned long (*gettimeoffset)(void); - -static unsigned long divisor; - -/* - * Get the time offset from the system PIT. Note that if we have missed an - * interrupt, then the PIT counter will roll over (ie, be negative). - * This actually works out to be convenient. - */ -static unsigned long ebsa110_gettimeoffset(void) -{ - unsigned long offset, count; - - __raw_writeb(0x40, PIT_CTRL); - count = __raw_readb(PIT_T1); - count |= __raw_readb(PIT_T1) << 8; - - /* - * If count > COUNT, make the number negative. - */ - if (count > COUNT) - count |= 0xffff0000; - - offset = COUNT * (DIVISOR - divisor); - offset -= count; - - /* - * `offset' is in units of timer counts. Convert - * offset to units of microseconds. - */ - offset = offset * (1000000 / HZ) / (COUNT * DIVISOR); - - return offset; -} - -int ebsa110_reset_timer(void) -{ - u32 count; - - /* latch and read timer 1 */ - __raw_writeb(0x40, PIT_CTRL); - count = __raw_readb(PIT_T1); - count |= __raw_readb(PIT_T1) << 8; - - count += COUNT; - - __raw_writeb(count & 0xff, PIT_T1); - __raw_writeb(count >> 8, PIT_T1); - - if (divisor == 0) - divisor = DIVISOR; - divisor -= 1; - return divisor; -} - -void __init ebsa110_setup_timer(void) -{ - /* - * Timer 1, mode 2, LSB/MSB - */ - __raw_writeb(0x70, PIT_CTRL); - __raw_writeb(COUNT & 0xff, PIT_T1); - __raw_writeb(COUNT >> 8, PIT_T1); - divisor = DIVISOR - 1; - - gettimeoffset = ebsa110_gettimeoffset; -} diff --git a/include/asm-arm/arch-ebsa110/param.h b/include/asm-arm/arch-ebsa110/param.h index f077b717193d..13a9fc1b7a25 100644 --- a/include/asm-arm/arch-ebsa110/param.h +++ b/include/asm-arm/arch-ebsa110/param.h @@ -1,3 +1,4 @@ /* * linux/include/asm-arm/arch-ebsa110/param.h */ +#define __KERNEL_HZ 200 diff --git a/include/asm-arm/arch-ebsa110/time.h b/include/asm-arm/arch-ebsa110/time.h index 278c8e3632a1..30c90e607546 100644 --- a/include/asm-arm/arch-ebsa110/time.h +++ b/include/asm-arm/arch-ebsa110/time.h @@ -17,17 +17,80 @@ */ #include +#include -extern int ebsa110_reset_timer(void); -extern void ebsa110_setup_timer(void); +extern unsigned long (*gettimeoffset)(void); + +#define PIT_CTRL (PIT_BASE + 0x0d) +#define PIT_T2 (PIT_BASE + 0x09) +#define PIT_T1 (PIT_BASE + 0x05) +#define PIT_T0 (PIT_BASE + 0x01) + +/* + * This is the rate at which your MCLK signal toggles (in Hz) + * This was measured on a 10 digit frequency counter sampling + * over 1 second. + */ +#define MCLK 47894000 + +/* + * This is the rate at which the PIT timers get clocked + */ +#define CLKBY7 (MCLK / 7) + +/* + * This is the counter value. We tick at 200Hz on this platform. + */ +#define COUNT ((CLKBY7 + (HZ / 2)) / HZ) + +/* + * Get the time offset from the system PIT. Note that if we have missed an + * interrupt, then the PIT counter will roll over (ie, be negative). + * This actually works out to be convenient. + */ +static unsigned long ebsa110_gettimeoffset(void) +{ + unsigned long offset, count; + + __raw_writeb(0x40, PIT_CTRL); + count = __raw_readb(PIT_T1); + count |= __raw_readb(PIT_T1) << 8; + + /* + * If count > COUNT, make the number negative. + */ + if (count > COUNT) + count |= 0xffff0000; + + offset = COUNT; + offset -= count; + + /* + * `offset' is in units of timer counts. Convert + * offset to units of microseconds. + */ + offset = offset * (1000000 / HZ) / COUNT; + + return offset; +} static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - if (ebsa110_reset_timer()) { - do_leds(); - do_timer(regs); - do_profile(regs); - } + u32 count; + + /* latch and read timer 1 */ + __raw_writeb(0x40, PIT_CTRL); + count = __raw_readb(PIT_T1); + count |= __raw_readb(PIT_T1) << 8; + + count += COUNT; + + __raw_writeb(count & 0xff, PIT_T1); + __raw_writeb(count >> 8, PIT_T1); + + do_leds(); + do_timer(regs); + do_profile(regs); } /* @@ -35,7 +98,14 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) */ void __init time_init(void) { - ebsa110_setup_timer(); + /* + * Timer 1, mode 2, LSB/MSB + */ + __raw_writeb(0x70, PIT_CTRL); + __raw_writeb(COUNT & 0xff, PIT_T1); + __raw_writeb(COUNT >> 8, PIT_T1); + + gettimeoffset = ebsa110_gettimeoffset; timer_irq.handler = timer_interrupt; -- cgit v1.2.3 From 7b2acd9741028fac89fbc17f4bf297d139d1392b Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Sun, 26 Jan 2003 18:32:56 -0800 Subject: [SPARC]: Kill smp_found_cpus declaration. --- include/asm-sparc/smp.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/asm-sparc/smp.h b/include/asm-sparc/smp.h index c283f5157617..f32a11f8a113 100644 --- a/include/asm-sparc/smp.h +++ b/include/asm-sparc/smp.h @@ -48,7 +48,6 @@ extern unsigned long cpu_offset[NR_CPUS]; * Private routines/data */ -extern int smp_found_cpus; extern unsigned char boot_cpu_id; extern unsigned long cpu_present_map; #define cpu_online_map cpu_present_map -- cgit v1.2.3 From 3ae4c190021a0521a268a416a9e2e01d24dbe4f5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 26 Jan 2003 23:56:14 -0800 Subject: [TCP]: Add tcp_low_latency sysctl. Currently it turns of prequeue processing, but more decisions may be guided by it in the future. Based upon a patch from Andi Kleen. --- Documentation/networking/ip-sysctl.txt | 8 ++++++++ include/linux/sysctl.h | 3 ++- include/net/tcp.h | 3 ++- net/ipv4/sysctl_net_ipv4.c | 2 ++ net/ipv4/tcp_ipv4.c | 1 + net/netsyms.c | 1 + 6 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 84f59f3782b7..df7e278b65bb 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -281,6 +281,14 @@ tcp_rfc1337 - BOOLEAN assassination. Default: 0 +tcp_low_latency - BOOLEAN + If set, the TCP stack makes decisions that prefer lower + latency as opposed to higher throughput. By default, this + option is not set meaning that higher throughput is preferred. + An example of an application where this default should be + changed would be a Beowulf compute cluster. + Default: 0 + ip_local_port_range - 2 INTEGERS Defines the local port range that is used by TCP and UDP to choose the local port. The first number is the first, the diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index b115623159e9..780c78ac1c5b 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -305,7 +305,8 @@ enum NET_IPV4_ICMP_RATELIMIT=89, NET_IPV4_ICMP_RATEMASK=90, NET_TCP_TW_REUSE=91, - NET_TCP_FRTO=92 + NET_TCP_FRTO=92, + NET_TCP_LOW_LATENCY=93 }; enum { diff --git a/include/net/tcp.h b/include/net/tcp.h index d2ee14622b2c..7c12128650d6 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -474,6 +474,7 @@ extern int sysctl_tcp_app_win; extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_tw_reuse; extern int sysctl_tcp_frto; +extern int sysctl_tcp_low_latency; extern atomic_t tcp_memory_allocated; extern atomic_t tcp_sockets_allocated; @@ -1349,7 +1350,7 @@ static __inline__ int tcp_prequeue(struct sock *sk, struct sk_buff *skb) { struct tcp_opt *tp = tcp_sk(sk); - if (tp->ucopy.task) { + if (!sysctl_tcp_low_latency && tp->ucopy.task) { __skb_queue_tail(&tp->ucopy.prequeue, skb); tp->ucopy.memory += skb->truesize; if (tp->ucopy.memory > sk->rcvbuf) { diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 03ed6d44d5cf..217b41a89f40 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -223,6 +223,8 @@ ctl_table ipv4_table[] = { &sysctl_tcp_tw_reuse, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_TCP_FRTO, "tcp_frto", &sysctl_tcp_frto, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_TCP_LOW_LATENCY, "tcp_low_latency", + &sysctl_tcp_low_latency, sizeof(int), 0644, NULL, &proc_dointvec}, {0} }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2eaff3e85d43..116a748cd875 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -75,6 +75,7 @@ extern int sysctl_ip_dynaddr; extern int sysctl_ip_default_ttl; int sysctl_tcp_tw_reuse; +int sysctl_tcp_low_latency; /* Check TCP sequence numbers in ICMP packets. */ #define ICMP_MIN_LENGTH 8 diff --git a/net/netsyms.c b/net/netsyms.c index 5b2698af58ae..2eb67c344256 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -429,6 +429,7 @@ EXPORT_SYMBOL(sysctl_tcp_wmem); EXPORT_SYMBOL(sysctl_tcp_ecn); EXPORT_SYMBOL(tcp_cwnd_application_limited); EXPORT_SYMBOL(tcp_sendpage); +EXPORT_SYMBOL(sysctl_tcp_low_latency); EXPORT_SYMBOL(tcp_write_xmit); -- cgit v1.2.3 From 76a1ee06ce2edfb7cad2d30afa026e3ad122e939 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 27 Jan 2003 10:01:45 +0000 Subject: [ARM] Include ARM architecture version in module "version" string --- include/asm-arm/module.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/asm-arm/module.h b/include/asm-arm/module.h index 5b4d1a3f3679..24b168dc31a3 100644 --- a/include/asm-arm/module.h +++ b/include/asm-arm/module.h @@ -10,4 +10,9 @@ struct mod_arch_specific #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +/* + * Include the ARM architecture version. + */ +#define MODULE_ARCH_VERMAGIC "ARMv" __stringify(__LINUX_ARM_ARCH__) " " + #endif /* _ASM_ARM_MODULE_H */ -- cgit v1.2.3 From 191c011abbb58966ab1ec30421e082f9802f2c8d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 27 Jan 2003 19:43:41 +0100 Subject: ALSA update - removed some 2.2 code - PCM - fixed memory leak for 24-bit samples - gameport cleanups (CS4231, ENS1370/1371, SonicVibes, Trident) - VIA82xx - fixed current pointer calculation - sound_firmware - fixed errno problem - USB - moved out compatibility code --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 +- Documentation/sound/alsa/CMIPCI.txt | 15 +++-- include/sound/version.h | 2 +- sound/core/control.c | 11 +--- sound/core/info.c | 9 --- sound/core/init.c | 6 +- sound/core/ioctl32/ioctl32.c | 7 ++- sound/core/oss/mixer_oss.c | 9 --- sound/core/oss/pcm_oss.c | 11 +--- sound/core/pcm_misc.c | 1 + sound/core/pcm_native.c | 11 +--- sound/core/rawmidi.c | 9 --- sound/core/rtctimer.c | 2 - sound/core/seq/oss/seq_oss_init.c | 6 -- sound/core/seq/oss/seq_oss_midi.c | 1 - sound/core/seq/seq_clientmgr.c | 7 --- sound/core/timer.c | 6 -- sound/drivers/serial-u16550.c | 29 +++++----- sound/isa/gus/gus_main.c | 6 +- sound/isa/wavefront/wavefront_fx.c | 6 +- sound/isa/wavefront/wavefront_synth.c | 6 +- sound/pci/Kconfig | 4 +- sound/pci/ac97/ac97_codec.c | 13 ++++- sound/pci/ali5451/ali5451.c | 2 +- sound/pci/cs4281.c | 14 ++--- sound/pci/cs46xx/cs46xx_lib.c | 12 ++-- sound/pci/ens1370.c | 10 +++- sound/pci/es1938.c | 10 ++-- sound/pci/fm801.c | 74 +++++++++++++++---------- sound/pci/intel8x0.c | 7 ++- sound/pci/rme9652/hammerfall_mem.c | 7 ++- sound/pci/sonicvibes.c | 10 ++-- sound/pci/trident/trident_main.c | 10 ++-- sound/pci/via82xx.c | 66 +++++++++++++++++----- sound/ppc/keywest.c | 1 + sound/sound_firmware.c | 21 +++---- sound/synth/emux/emux_seq.c | 7 +-- sound/usb/usbaudio.c | 42 ++------------ sound/usb/usbaudio.h | 4 ++ sound/usb/usbmidi.c | 24 ++------ 40 files changed, 224 insertions(+), 277 deletions(-) (limited to 'include') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 9acb5a3ef5d5..264bb1368ec8 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -748,7 +748,8 @@ Module parameters Module snd-rme32 ---------------- - Module for RME Digi32, Digi32/8 and Digi32 PRO soundcards. + Module for RME Digi32, Digi32 Pro and Digi32/8 (Sek'd Prodif32, + Prodif96 and Prodif Gold) soundcards. Module supports up to 8 cards. diff --git a/Documentation/sound/alsa/CMIPCI.txt b/Documentation/sound/alsa/CMIPCI.txt index 582781b64b13..dc717523f865 100644 --- a/Documentation/sound/alsa/CMIPCI.txt +++ b/Documentation/sound/alsa/CMIPCI.txt @@ -82,9 +82,15 @@ as you like and set the format S16LE. For example, for playback with and use the interleaved 4 channel data. -There is a control switch, "Line-In As Bass". As you can imagine from -its name, the line-in jack is used for the bass (5th and 6th channels) -output. +There are some control switchs affecting to the speaker connections: + +"Line-In As Rear" - As mentioned above, the line-in jack is used + for the rear (3th and 4th channels) output. +"Line-In As Bass" - The line-in jack is used for the bass (5th + and 6th channels) output. +"Mic As Center/LFE" - The mic jack is used for the bass output. + If this switch is on, you cannot use a microphone as a capture + source, of course. Digital I/O @@ -134,8 +140,7 @@ Additionally there are relevant control switches: (see the next section). "IEC958 In Select" - Select SPDIF input, the internal CD-in (false) - and the external input (true). This switch appears only on - the chip models 039 or later. + and the external input (true). "IEC958 Loop" - SPDIF input data is loop back into SPDIF output (aka bypass) diff --git a/include/sound/version.h b/include/sound/version.h index a0166739358d..a4c7d9a7cd3a 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc6" -#define CONFIG_SND_DATE " (Tue Dec 17 19:01:13 2002 UTC)" +#define CONFIG_SND_DATE " (Thu Dec 26 11:57:42 2002 UTC)" diff --git a/sound/core/control.c b/sound/core/control.c index 85a0a656c5b8..31173c043e10 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -48,9 +48,6 @@ static int snd_ctl_open(struct inode *inode, struct file *file) snd_ctl_file_t *ctl; int err; -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif card = snd_cards[cardnum]; if (!card) { err = -ENODEV; @@ -82,13 +79,10 @@ static int snd_ctl_open(struct inode *inode, struct file *file) return 0; __error: - module_put(card->module); + module_put(card->module); __error2: snd_card_file_remove(card, file); __error1: -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return err; } @@ -131,9 +125,6 @@ static int snd_ctl_release(struct inode *inode, struct file *file) snd_magic_kfree(ctl); module_put(card->module); snd_card_file_remove(card, file); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return 0; } diff --git a/sound/core/info.c b/sound/core/info.c index 45bf36503596..b4e85fb69c21 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -293,9 +293,6 @@ static int snd_info_entry_open(struct inode *inode, struct file *file) up(&info_mutex); return -ENODEV; } -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif if (!try_module_get(entry->module)) { err = -EFAULT; goto __error1; @@ -403,9 +400,6 @@ static int snd_info_entry_open(struct inode *inode, struct file *file) __error: module_put(entry->module); __error1: -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif up(&info_mutex); return err; } @@ -445,9 +439,6 @@ static int snd_info_entry_release(struct inode *inode, struct file *file) break; } module_put(entry->module); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif snd_magic_kfree(data); return 0; } diff --git a/sound/core/init.c b/sound/core/init.c index bc320cb1d2bd..26aa3d323c12 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -292,15 +292,17 @@ int snd_card_free(snd_card_t * card) static void snd_card_free_thread(void * __card) { snd_card_t *card = __card; - struct module * module; + struct module * module = card->module; - if (!try_module_get(module = card->module)) { + if (!try_module_get(module)) { snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number); module = NULL; } wait_event(card->shutdown_sleep, card->files == NULL); + snd_card_free(card); + module_put(module); } diff --git a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c index c8183837f8b0..4c2e37a01ee4 100644 --- a/sound/core/ioctl32/ioctl32.c +++ b/sound/core/ioctl32/ioctl32.c @@ -249,14 +249,17 @@ static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id) ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + read_lock(&ctl->card->control_rwlock); kctl = snd_ctl_find_id(ctl->card, id); - if (! kctl) + if (! kctl) { + read_unlock(&ctl->card->control_rwlock); return -ENXIO; - + } info.id = *id; err = kctl->info(kctl, &info); if (err >= 0) err = info.type; + read_unlock(&ctl->card->control_rwlock); return err; } diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index c4c6d4d3ff59..63c69c5519a4 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -56,14 +56,8 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) fmixer->card = card; fmixer->mixer = card->mixer_oss; file->private_data = fmixer; -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif if (!try_module_get(card->module)) { kfree(fmixer); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif snd_card_file_remove(card, file); return -EFAULT; } @@ -77,9 +71,6 @@ static int snd_mixer_oss_release(struct inode *inode, struct file *file) if (file->private_data) { fmixer = (snd_mixer_oss_file_t *) file->private_data; module_put(fmixer->card->module); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif snd_card_file_remove(fmixer->card, file); kfree(fmixer); } diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 4c7d5c33f134..ac284d748d3d 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1540,9 +1540,6 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ? adsp_map[cardnum] : dsp_map[cardnum]; -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device]; if (pcm == NULL) { err = -ENODEV; @@ -1611,13 +1608,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) return err; __error: - module_put(pcm->card->module); + module_put(pcm->card->module); __error2: snd_card_file_remove(pcm->card, file); __error1: -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return err; } @@ -1640,9 +1634,6 @@ static int snd_pcm_oss_release(struct inode *inode, struct file *file) wake_up(&pcm->open_wait); module_put(pcm->card->module); snd_card_file_remove(pcm->card, file); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return 0; } diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index cb6a8ca8895d..38a87b6d69eb 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -497,6 +497,7 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int #endif } } + break; } case 32: { u_int32_t silence = snd_pcm_format_silence_64(format); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 405fe698896e..ce21615f6c92 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -76,6 +76,8 @@ static inline void snd_leave_user(mm_segment_t fs) set_fs(fs); } + + int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info) { snd_pcm_runtime_t * runtime; @@ -1779,9 +1781,6 @@ int snd_pcm_open(struct inode *inode, struct file *file) snd_pcm_file_t *pcm_file; wait_queue_t wait; -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif snd_runtime_check(device >= SNDRV_MINOR_PCM_PLAYBACK && device < SNDRV_MINOR_DEVICES, return -ENXIO); pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + (device % SNDRV_MINOR_PCMS)]; if (pcm == NULL) { @@ -1829,9 +1828,6 @@ int snd_pcm_open(struct inode *inode, struct file *file) __error2: snd_card_file_remove(pcm->card, file); __error1: -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return err; } @@ -1857,9 +1853,6 @@ int snd_pcm_release(struct inode *inode, struct file *file) wake_up(&pcm->open_wait); module_put(pcm->card->module); snd_card_file_remove(pcm->card, file); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return 0; } diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 301445309d17..061cb55fa1e1 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -181,9 +181,6 @@ int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, if (rfile) rfile->input = rfile->output = NULL; -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; if (rmidi == NULL) { err = -ENODEV; @@ -342,9 +339,6 @@ int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, module_put(rmidi->card->module); up(&rmidi->open_mutex); __error1: -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return err; } @@ -499,9 +493,6 @@ int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile) } up(&rmidi->open_mutex); module_put(rmidi->card->module); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return 0; } diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c index a2ef3839cb19..7b30c7ac513b 100644 --- a/sound/core/rtctimer.c +++ b/sound/core/rtctimer.c @@ -83,7 +83,6 @@ rtctimer_open(snd_timer_t *t) if (err < 0) return err; t->private_data = &rtc_task; - MOD_INC_USE_COUNT; return 0; } @@ -95,7 +94,6 @@ rtctimer_close(snd_timer_t *t) rtc_unregister(rtc); t->private_data = NULL; } - MOD_DEC_USE_COUNT; return 0; } diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index efc14b72a4c0..2d632ecd64c1 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -275,9 +275,6 @@ snd_seq_oss_open(struct file *file, int level) client_table[dp->index] = dp; num_clients++; -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif debug_printk(("open done\n")); @@ -434,9 +431,6 @@ snd_seq_oss_release(seq_oss_devinfo_t *dp) if (dp->queue >= 0) delete_seq_queue(dp); -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif debug_printk(("release done\n")); } diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index a1d481e483db..8c98a6b4ef38 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -217,7 +217,6 @@ snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo) midi_devs[mdev->seq_device] = mdev; spin_unlock_irqrestore(®ister_lock, flags); - /*MOD_INC_USE_COUNT;*/ return 0; } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 7b5aa6687266..6df90a100613 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -339,10 +339,6 @@ static int snd_seq_open(struct inode *inode, struct file *file) /* make others aware this new client */ snd_seq_system_client_ev_client_start(c); -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif - return 0; } @@ -358,9 +354,6 @@ static int snd_seq_release(struct inode *inode, struct file *file) kfree(client); } -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return 0; } diff --git a/sound/core/timer.c b/sound/core/timer.c index b386bdeb48c8..e39e4f22ab43 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -942,9 +942,6 @@ static int snd_timer_user_open(struct inode *inode, struct file *file) return -ENOMEM; } file->private_data = tu; -#ifdef LINUX_2_2 - MOD_INC_USE_COUNT; -#endif return 0; } @@ -961,9 +958,6 @@ static int snd_timer_user_release(struct inode *inode, struct file *file) kfree(tu->queue); snd_magic_kfree(tu); } -#ifdef LINUX_2_2 - MOD_DEC_USE_COUNT; -#endif return 0; } diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 60e64ff05563..b99f5154f00a 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -322,17 +322,20 @@ static void snd_uart16550_buffer_timer(unsigned long data) * return 0 if found * return negative error if not found */ -static int __init snd_uart16550_detect(unsigned int io_base) +static int __init snd_uart16550_detect(snd_uart16550_t *uart) { + unsigned long io_base = uart->base; int ok; unsigned char c; - if (check_region(io_base, 8)) - return -EBUSY; - /* Do some vague tests for the presence of the uart */ - if (io_base == 0) + if (io_base == 0) { return -ENODEV; /* Not configured */ + } + + uart->res_base = request_region(io_base, 8, "Serial MIDI"); + if (uart->res_base == NULL) + return -EBUSY; ok = 1; /* uart detected unless one of the following tests should fail */ /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */ @@ -766,11 +769,13 @@ static int __init snd_uart16550_create(snd_card_t * card, uart->card = card; spin_lock_init(&uart->open_lock); uart->irq = -1; - if ((uart->res_base = request_region(iobase, 8, "Serial MIDI")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", iobase, iobase + 8 - 1); - return -EBUSY; - } uart->base = iobase; + + if ((err = snd_uart16550_detect(uart)) <= 0) { + printk(KERN_ERR "no UART detected at 0x%lx\n", iobase); + return err; + } + if (irq >= 0) { if (request_irq(irq, snd_uart16550_interrupt, SA_INTERRUPT, "Serial MIDI", (void *) uart)) { @@ -888,12 +893,6 @@ static int __init snd_serial_probe(int dev) strcpy(card->driver, "Serial"); strcpy(card->shortname, "Serial midi (uart16550A)"); - if ((err = snd_uart16550_detect(port[dev])) <= 0) { - snd_card_free(card); - printk(KERN_ERR "no UART detected at 0x%lx\n", (long)port[dev]); - return err; - } - if ((err = snd_uart16550_create(card, port[dev], irq[dev], diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index 7a9adec95248..7ff31280b1f9 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -41,18 +41,14 @@ static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches); int snd_gus_use_inc(snd_gus_card_t * gus) { - MOD_INC_USE_COUNT; - if (!try_module_get(gus->card->module)) { - MOD_DEC_USE_COUNT; + if (!try_module_get(gus->card->module)) return 0; - } return 1; } void snd_gus_use_dec(snd_gus_card_t * gus) { module_put(gus->card->module); - MOD_DEC_USE_COUNT; } static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index a9de8cb5dff2..b1b1962fc55a 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -144,11 +144,8 @@ int snd_wavefront_fx_open (snd_hwdep_t *hw, struct file *file) { - MOD_INC_USE_COUNT; - if (!try_module_get(hw->card->module)) { - MOD_DEC_USE_COUNT; + if (!try_module_get(hw->card->module)) return -EFAULT; - } file->private_data = hw; return 0; } @@ -158,7 +155,6 @@ snd_wavefront_fx_release (snd_hwdep_t *hw, struct file *file) { module_put(hw->card->module); - MOD_DEC_USE_COUNT; return 0; } diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 88b88d23c3fd..359457d549dd 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -1603,11 +1603,8 @@ int snd_wavefront_synth_open (snd_hwdep_t *hw, struct file *file) { - MOD_INC_USE_COUNT; - if (!try_module_get(hw->card->module)) { - MOD_DEC_USE_COUNT; + if (!try_module_get(hw->card->module)) return -EFAULT; - } file->private_data = hw; return 0; } @@ -1617,7 +1614,6 @@ snd_wavefront_synth_release (snd_hwdep_t *hw, struct file *file) { module_put(hw->card->module); - MOD_DEC_USE_COUNT; return 0; } diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index d675b5103c8b..d5bca86c637e 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -51,8 +51,8 @@ config SND_RME32 tristate "RME Digi32, 32/8, 32 PRO" depends on SND help - Say 'Y' or 'M' to include support for RME Digi32, Digi32/8 and - Digi32 PRO audio devices. + Say 'Y' or 'M' to include support for RME Digi32, Digi32 PRO and + Digi32/8 (Sek'd Prodif32, Prodif96 and Prodif Gold) audio devices. config SND_RME96 tristate "RME Digi96, 96/8, 96/8 PRO" diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 19f155712df4..a02b6e8ef8f5 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -671,6 +671,11 @@ AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), }; +static const snd_kcontrol_new_t snd_ac97_sigmatel_surround[2] = { +AC97_SINGLE("Sigmatel Surround Playback Switch", AC97_HEADPHONE, 15, 1, 1), +AC97_DOUBLE("Sigmatel Surround Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1) +}; + static const snd_kcontrol_new_t snd_ac97_sigmatel_controls[] = { AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0), AC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0) @@ -1236,6 +1241,7 @@ static snd_kcontrol_t *snd_ac97_cnew(const snd_kcontrol_new_t *_template, ac97_t static int snd_ac97_mixer_build(snd_card_t * card, ac97_t * ac97) { snd_kcontrol_t *kctl; + const snd_kcontrol_new_t *knew; int err, idx; unsigned char max; @@ -1291,10 +1297,11 @@ static int snd_ac97_mixer_build(snd_card_t * card, ac97_t * ac97) } /* build headphone controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_headphone[0], ac97))) < 0) + if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE) || ac97->id == AC97_ID_STAC9708) { + knew = ac97->id == AC97_ID_STAC9708 ? snd_ac97_sigmatel_surround : snd_ac97_controls_headphone; + if ((err = snd_ctl_add(card, snd_ac97_cnew(knew, ac97))) < 0) return err; - if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_headphone[1], ac97))) < 0) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(knew + 1, ac97))) < 0) return err; snd_ac97_change_volume_params1(ac97, AC97_HEADPHONE, &max); kctl->private_value &= ~(0xff << 16); diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 2f426d67dd05..f05aeb5fbb5a 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -67,7 +67,7 @@ MODULE_PARM_DESC(pcm_channels, "PCM Channels"); MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); /* - * Debug part definitions + * Debug part definations */ //#define ALI_DEBUG diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 3185a948c546..a9468e9e127c 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -35,9 +36,6 @@ #define SNDRV_GET_ID #include -#ifndef LINUX_2_2 -#include -#endif MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Cirrus Logic CS4281"); @@ -1309,7 +1307,7 @@ static void snd_cs4281_proc_done(cs4281_t * chip) * joystick support */ -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) typedef struct snd_cs4281_gameport { struct gameport info; @@ -1399,7 +1397,9 @@ static void __devinit snd_cs4281_gameport(cs4281_t *chip) gameport_register_port(&gp->info); } -#endif /* !LINUX_2_2 */ +#else +#define snd_cs4281_gameport(chip) /*NOP*/ +#endif /* CONFIG_GAMEPORT || CONFIG_GAMEPORT_MODULE */ /* @@ -1408,7 +1408,7 @@ static void __devinit snd_cs4281_gameport(cs4281_t *chip) static int snd_cs4281_free(cs4281_t *chip) { -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) if (chip->gameport) { gameport_unregister_port(&chip->gameport->info); kfree(chip->gameport); @@ -2041,9 +2041,7 @@ static int __devinit snd_cs4281_probe(struct pci_dev *pci, snd_card_free(card); return err; } -#ifndef LINUX_2_2 snd_cs4281_gameport(chip); -#endif strcpy(card->driver, "CS4281"); strcpy(card->shortname, "Cirrus Logic CS4281"); sprintf(card->longname, "%s at 0x%lx, irq %d", diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 03bd93443ec5..d4a97f58c4dd 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -52,14 +52,12 @@ #include #include #include +#include #include #include #include #include -#ifndef LINUX_2_2 -#include -#endif #include @@ -2740,7 +2738,7 @@ int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmi * gameport interface */ -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) typedef struct snd_cs46xx_gameport { struct gameport info; @@ -2824,13 +2822,13 @@ void __devinit snd_cs46xx_gameport(cs46xx_t *chip) gameport_register_port(&gp->info); } -#else /* LINUX_2_2 */ +#else void __devinit snd_cs46xx_gameport(cs46xx_t *chip) { } -#endif /* !LINUX_2_2 */ +#endif /* CONFIG_GAMEPORT */ /* * proc interface @@ -2972,7 +2970,7 @@ static int snd_cs46xx_free(cs46xx_t *chip) if (chip->active_ctrl) chip->active_ctrl(chip, 1); -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) if (chip->gameport) { gameport_unregister_port(&chip->gameport->info); kfree(chip->gameport); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 8ae6d3756ea5..1b3c1f7b3036 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -405,8 +405,10 @@ struct _snd_ensoniq { dma_addr_t bugbuf_addr; #endif +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) struct gameport gameport; struct semaphore joy_sem; // gameport configuration semaphore +#endif }; static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -1576,6 +1578,7 @@ static int __devinit snd_ensoniq_1370_mixer(ensoniq_t * ensoniq) * General Switches... */ +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) /* MQ: gameport driver connectivity */ #define ENSONIQ_JOY_CONTROL(xname, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \ @@ -1693,6 +1696,7 @@ static snd_kcontrol_new_t snd_es1371_joystick_addr __devinitdata = ES1371_JOYSTICK_ADDR("Joystick Address"); #endif /* CHIP1371 */ +#endif /* CONFIG_GAMEPORT */ /* @@ -1749,8 +1753,10 @@ static void snd_ensoniq_proc_done(ensoniq_t * ensoniq) static int snd_ensoniq_free(ensoniq_t *ensoniq) { +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) if (ensoniq->ctrl & ES_JYSTK_EN) snd_ensoniq_joy_disable(ensoniq); +#endif snd_ensoniq_proc_done(ensoniq); if (ensoniq->irq < 0) goto __hw_end; @@ -1831,7 +1837,6 @@ static int __devinit snd_ensoniq_create(snd_card_t * card, if (ensoniq == NULL) return -ENOMEM; spin_lock_init(&ensoniq->reg_lock); - init_MUTEX(&ensoniq->joy_sem); ensoniq->card = card; ensoniq->pci = pci; ensoniq->irq = -1; @@ -1946,11 +1951,14 @@ static int __devinit snd_ensoniq_create(snd_card_t * card, outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL)); outb(0x00, ES_REG(ensoniq, UART_RES)); outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) + init_MUTEX(&ensoniq->joy_sem); #ifdef CHIP1371 snd_ctl_add(card, snd_ctl_new1(&snd_es1371_joystick_addr, ensoniq)); #endif snd_ctl_add(card, snd_ctl_new1(&snd_ensoniq_control_joystick, ensoniq)); ensoniq->gameport.io = 0x200; // FIXME: is ES1371 configured like this above ? +#endif synchronize_irq(ensoniq->irq); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) { diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 268e4ac18f48..0b91028e2768 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -59,9 +60,6 @@ #include #define SNDRV_GET_ID #include -#ifndef LINUX_2_2 -#include -#endif #include @@ -248,7 +246,7 @@ struct _snd_es1938 { spinlock_t mixer_lock; snd_info_entry_t *proc_entry; -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) struct gameport gameport; #endif }; @@ -1328,7 +1326,7 @@ ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0) static int snd_es1938_free(es1938_t *chip) { -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) if (chip->gameport.io) gameport_unregister_port(&chip->gameport); #endif @@ -1645,7 +1643,7 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci, chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) { printk(KERN_ERR "es1938: unable to initialize MPU-401\n"); } -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) chip->gameport.io = chip->game_port; gameport_register_port(&chip->gameport); #endif diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 1b9216c6b758..88cd1dac6d1b 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -312,48 +312,66 @@ static int snd_fm801_playback_trigger(snd_pcm_substream_t * substream, int cmd) { fm801_t *chip = snd_pcm_substream_chip(substream); - int result = 0; spin_lock(&chip->reg_lock); - if (cmd == SNDRV_PCM_TRIGGER_START) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: chip->ply_ctrl &= ~(FM801_BUF1_LAST | FM801_BUF2_LAST | FM801_PAUSE); chip->ply_ctrl |= FM801_START | FM801_IMMED_STOP; - outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { - chip->ply_ctrl &= ~FM801_START; - outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); - } else { - result = -EINVAL; + break; + case SNDRV_PCM_TRIGGER_STOP: + chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + chip->ply_ctrl |= FM801_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + chip->ply_ctrl &= ~FM801_PAUSE; + break; + default: + spin_unlock(&chip->reg_lock); + snd_BUG(); + return -EINVAL; } + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); spin_unlock(&chip->reg_lock); - return result; + return 0; } static int snd_fm801_capture_trigger(snd_pcm_substream_t * substream, int cmd) { fm801_t *chip = snd_pcm_substream_chip(substream); - int result = 0; spin_lock(&chip->reg_lock); - if (cmd == SNDRV_PCM_TRIGGER_START) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: chip->cap_ctrl &= ~(FM801_BUF1_LAST | FM801_BUF2_LAST | FM801_PAUSE); chip->cap_ctrl |= FM801_START | FM801_IMMED_STOP; - outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { - chip->cap_ctrl &= ~FM801_START; - outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); - } else { - result = -EINVAL; + break; + case SNDRV_PCM_TRIGGER_STOP: + chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + chip->cap_ctrl |= FM801_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + chip->cap_ctrl &= ~FM801_PAUSE; + break; + default: + spin_unlock(&chip->reg_lock); + snd_BUG(); + return -EINVAL; } + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); spin_unlock(&chip->reg_lock); - return result; + return 0; } static int snd_fm801_hw_params(snd_pcm_substream_t * substream, @@ -470,8 +488,11 @@ static void snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs) unsigned int tmp; status = inw(FM801_REG(chip, IRQ_STATUS)); - if ((status & (FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME)) == 0) + status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME; + if (! status) return; + /* ack first */ + outw(status, FM801_REG(chip, IRQ_STATUS)); if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) { spin_lock(&chip->reg_lock); chip->ply_buf++; @@ -483,7 +504,6 @@ static void snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs) (chip->ply_buf & 1) ? FM801_REG(chip, PLY_BUF1) : FM801_REG(chip, PLY_BUF2)); - outw(FM801_IRQ_PLAYBACK, FM801_REG(chip, IRQ_STATUS)); spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(chip->playback_substream); } @@ -498,24 +518,20 @@ static void snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs) (chip->cap_buf & 1) ? FM801_REG(chip, CAP_BUF1) : FM801_REG(chip, CAP_BUF2)); - outw(FM801_IRQ_CAPTURE, FM801_REG(chip, IRQ_STATUS)); spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(chip->capture_substream); } - if ((status & FM801_IRQ_MPU) && chip->rmidi != NULL) { + if (chip->rmidi && (status & FM801_IRQ_MPU)) snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); - outw(FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); - } - if (status & FM801_IRQ_VOLUME) { - /* TODO */ - outw(FM801_IRQ_VOLUME, FM801_REG(chip, IRQ_STATUS)); - } + if (status & FM801_IRQ_VOLUME) + ;/* TODO */ } static snd_pcm_hardware_t snd_fm801_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, @@ -535,6 +551,7 @@ static snd_pcm_hardware_t snd_fm801_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, @@ -822,6 +839,7 @@ FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0), FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0), FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0), FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0), +FM801_SINGLE("IEC958 Playback Switch", FM801_GEN_CTRL, 2, 1, 0), }; static void snd_fm801_mixer_free_ac97(ac97_t *ac97) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index bf051b78385c..392178381d5e 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -131,6 +131,9 @@ MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog #ifndef PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO #define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a #endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da +#endif enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI }; @@ -388,6 +391,7 @@ static struct pci_device_id snd_intel8x0_ids[] __devinitdata = { { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE */ { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE2 */ + { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE3 */ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ @@ -2247,7 +2251,7 @@ static int __devinit snd_intel8x0_create(snd_card_t * card, chip->bdbars_count = 3; if (device_type == DEVICE_INTEL_ICH4 || device_type == DEVICE_ALI) chip->bdbars_count = 6; - chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); + chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); if (chip->bdbars == NULL) { snd_intel8x0_free(chip); return -ENOMEM; @@ -2304,6 +2308,7 @@ static struct shortname_table { { PCI_DEVICE_ID_SI_7012, "SiS SI7012" }, { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" }, { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia NForce2" }, + { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia NForce3" }, { 0x746d, "AMD AMD8111" }, { 0x7445, "AMD AMD768" }, { 0x5455, "ALi M5455" }, diff --git a/sound/pci/rme9652/hammerfall_mem.c b/sound/pci/rme9652/hammerfall_mem.c index 2705ca915220..067ce2c18fb0 100644 --- a/sound/pci/rme9652/hammerfall_mem.c +++ b/sound/pci/rme9652/hammerfall_mem.c @@ -25,7 +25,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - $Id: hammerfall_mem.c,v 1.5 2002/11/04 09:11:42 perex Exp $ + $Id: hammerfall_mem.c,v 1.6 2002/12/19 15:59:18 tiwai Exp $ Tue Oct 17 2000 Jaroslav Kysela @@ -150,9 +150,10 @@ void *snd_hammerfall_get_buffer (struct pci_dev *pcidev, dma_addr_t *dmaaddr) for (i = 0; i < NBUFS; i++) { rbuf = &hammerfall_buffers[i]; if (rbuf->flags == HAMMERFALL_BUF_ALLOCATED) { + if (! try_module_get(THIS_MODULE)) + return NULL; rbuf->flags |= HAMMERFALL_BUF_USED; rbuf->pci = pcidev; - MOD_INC_USE_COUNT; *dmaaddr = rbuf->addr; return rbuf->buf; } @@ -169,8 +170,8 @@ void snd_hammerfall_free_buffer (struct pci_dev *pcidev, void *addr) for (i = 0; i < NBUFS; i++) { rbuf = &hammerfall_buffers[i]; if (rbuf->buf == addr && rbuf->pci == pcidev) { - MOD_DEC_USE_COUNT; rbuf->flags &= ~HAMMERFALL_BUF_USED; + module_put(THIS_MODULE); return; } } diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 7a883d517208..5f1767c27cf0 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -37,9 +38,6 @@ #include #define SNDRV_GET_ID #include -#ifndef LINUX_2_2 -#include -#endif #include @@ -257,7 +255,7 @@ struct _snd_sonicvibes { snd_kcontrol_t *master_mute; snd_kcontrol_t *master_volume; -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) struct gameport gameport; #endif }; @@ -1208,7 +1206,7 @@ SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0); static int snd_sonicvibes_free(sonicvibes_t *sonic) { -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) if (sonic->gameport.io) gameport_unregister_port(&sonic->gameport); #endif @@ -1512,7 +1510,7 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci, snd_card_free(card); return err; } -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) sonic->gameport.io = sonic->game_port; gameport_register_port(&sonic->gameport); #endif diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index c0e7811e086a..0e3496c3c7e0 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -34,15 +34,13 @@ #include #include #include +#include #include #include #include #include #include -#ifndef LINUX_2_2 -#include -#endif #include @@ -2987,7 +2985,7 @@ static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device /* * gameport interface */ -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) typedef struct snd_trident_gameport { struct gameport info; @@ -3076,7 +3074,7 @@ void __devinit snd_trident_gameport(trident_t *chip) void __devinit snd_trident_gameport(trident_t *chip) { } -#endif +#endif /* CONFIG_GAMEPORT */ /* * SiS reset routine @@ -3482,7 +3480,7 @@ int __devinit snd_trident_create(snd_card_t * card, int snd_trident_free(trident_t *trident) { -#ifndef LINUX_2_2 +#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) if (trident->gameport) { gameport_unregister_port(&trident->gameport->info); kfree(trident->gameport); diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8e431d5066fb..8a0f92d265fd 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -40,6 +40,10 @@ #define SNDRV_GET_ID #include +#if 0 +#define POINTER_DEBUG +#endif + MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("VIA VT82xx audio"); MODULE_LICENSE("GPL"); @@ -198,6 +202,9 @@ typedef struct { u32 *table; /* physical address + flag */ dma_addr_t table_addr; struct snd_via_sg_table *idx_table; + unsigned int lastpos; + unsigned int bufsize; + unsigned int bufsize2; } viadev_t; @@ -208,9 +215,9 @@ typedef struct { */ static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, struct pci_dev *pci, - int periods, int fragsize) + unsigned int periods, unsigned int fragsize) { - int i, idx, ofs, rest; + unsigned int i, idx, ofs, rest; struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); if (! dev->table) { @@ -237,8 +244,13 @@ static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, * over page boundary. */ do { - int r; + unsigned int r; unsigned int flag; + + if (idx >= VIA_TABLE_SIZE) { + snd_printk(KERN_ERR "via82xx: too much table size!\n"); + return -EINVAL; + } dev->table[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); r = PAGE_SIZE - (ofs % PAGE_SIZE); if (rest < r) @@ -257,13 +269,11 @@ static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, dev->idx_table[idx].size = r; ofs += r; idx++; - if (idx >= VIA_TABLE_SIZE) { - snd_printk(KERN_ERR "via82xx: too much table size!\n"); - return -EINVAL; - } } while (rest > 0); } dev->tbl_entries = idx; + dev->bufsize = periods * fragsize; + dev->bufsize2 = dev->bufsize / 2; return 0; } @@ -439,6 +449,7 @@ static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev) outb(0x03, port + VIA_REG_OFFSET_STATUS); outb(0x00, port + VIA_REG_OFFSET_TYPE); /* for via686 */ outl(0, port + VIA_REG_OFFSET_CURR_PTR); + viadev->lastpos = 0; } static int snd_via82xx_trigger(via82xx_t *chip, viadev_t *viadev, int cmd) @@ -641,19 +652,22 @@ static int snd_via82xx_capture_prepare(snd_pcm_substream_t * substream) static inline unsigned int snd_via82xx_cur_ptr(via82xx_t *chip, viadev_t *viadev) { - unsigned int val, ptr, count; - + unsigned int val, ptr, count, res; + snd_assert(viadev->tbl_entries, return 0); if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) return 0; - count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; + spin_lock(&chip->reg_lock); + switch (chip->chip_type) { case TYPE_VIA686: + count &= 0xffffff; /* The via686a does not have the current index register, * so we need to calculate the index from CURR_PTR. */ ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset); + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; if (ptr <= (unsigned int)viadev->table_addr) val = 0; else /* CURR_PTR holds the address + 8 */ @@ -662,14 +676,37 @@ static inline unsigned int snd_via82xx_cur_ptr(via82xx_t *chip, viadev_t *viadev case TYPE_VIA8233: default: - /* ah, this register makes life easier for us here. */ - val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->tbl_entries; + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset); + val = count >> 24; + count &= 0xffffff; break; } /* convert to the linear position */ - return viadev->idx_table[val].offset + - viadev->idx_table[val].size - count; + ptr = viadev->idx_table[val].size; + res = viadev->idx_table[val].offset + ptr - count; + + if (ptr < count || (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))) { +#ifdef POINTER_DEBUG + printk("fail: val = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", val, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[val].offset, viadev->idx_table[val].size, count); +#endif + /* VIA8233 count register returns full size when end of buffer is reached */ + if (ptr != count) { + snd_printk("invalid via82xx_cur_ptr, using last valid pointer\n"); + res = viadev->lastpos; + } else { + res = viadev->idx_table[val].offset + ptr; + if (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2)) { + snd_printk("invalid via82xx_cur_ptr (2), using last valid pointer\n"); + res = viadev->lastpos; + } + } + } + + viadev->lastpos = res; + spin_unlock(&chip->reg_lock); + + return res; } static snd_pcm_uframes_t snd_via82xx_playback_pointer(snd_pcm_substream_t * substream) @@ -1224,7 +1261,6 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci, return err; } - if (snd_via82xx_mixer(chip) < 0) { snd_card_free(card); return err; diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index b9f3e96741ce..52b95a8079d3 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c index cfd9a322e463..f6e57a3e18f6 100644 --- a/sound/sound_firmware.c +++ b/sound/sound_firmware.c @@ -9,39 +9,40 @@ static int do_mod_firmware_load(const char *fn, char **fp) { - int fd; + struct file* filp; long l; char *dp; + loff_t pos; - fd = open(fn, 0, 0); - if (fd == -1) + filp = filp_open(fn, 0, 0); + if (IS_ERR(filp)) { printk(KERN_INFO "Unable to load '%s'.\n", fn); return 0; } - l = lseek(fd, 0L, 2); + l = filp->f_dentry->d_inode->i_size; if (l <= 0 || l > 131072) { printk(KERN_INFO "Invalid firmware '%s'\n", fn); - sys_close(fd); + filp_close(filp, current->files); return 0; } - lseek(fd, 0L, 0); dp = vmalloc(l); if (dp == NULL) { printk(KERN_INFO "Out of memory loading '%s'.\n", fn); - sys_close(fd); + filp_close(filp, current->files); return 0; } - if (read(fd, dp, l) != l) + pos = 0; + if (vfs_read(filp, dp, l, &pos) != l) { printk(KERN_INFO "Failed to read '%s'.\n", fn); vfree(dp); - sys_close(fd); + filp_close(filp, current->files); return 0; } - close(fd); + filp_close(filp, current->files); *fp = dp; return (int) l; } diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c index a06961482fa5..5855b356fd53 100644 --- a/sound/synth/emux/emux_seq.c +++ b/sound/synth/emux/emux_seq.c @@ -58,9 +58,6 @@ static snd_midi_op_t emux_ops = { SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE) -/* - */ - /* * Initialise the EMUX Synth by creating a client and registering * a series of ports. @@ -292,11 +289,11 @@ snd_emux_inc_count(snd_emux_t *emu) void snd_emux_dec_count(snd_emux_t *emu) { - module_put(emu->ops.owner); + module_put(emu->card->module); emu->used--; if (emu->used <= 0) snd_emux_terminate_all(emu); - module_put(emu->card->module); + module_put(emu->ops.owner); } diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index bc1d7f7b18de..66e2c95adb32 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -526,11 +526,7 @@ static struct snd_urb_ops audio_urb_ops[2] = { /* * complete callback from data urb */ -#ifndef OLD_USB static void snd_complete_urb(struct urb *urb, struct pt_regs *regs) -#else -static void snd_complete_urb(struct urb *urb) -#endif { snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; snd_usb_substream_t *subs = ctx->subs; @@ -555,11 +551,7 @@ static void snd_complete_urb(struct urb *urb) /* * complete callback from sync urb */ -#ifndef OLD_USB static void snd_complete_sync_urb(struct urb *urb, struct pt_regs *regs) -#else -static void snd_complete_sync_urb(struct urb *urb) -#endif { snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; snd_usb_substream_t *subs = ctx->subs; @@ -881,7 +873,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *run u->urb->transfer_flags = URB_ISO_ASAP | UNLINK_FLAGS; u->urb->number_of_packets = u->packets; u->urb->context = u; - u->urb->complete = (usb_complete_t)snd_complete_urb; + u->urb->complete = snd_usb_complete_callback(snd_complete_urb); } if (subs->syncpipe) { @@ -903,7 +895,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *run u->urb->transfer_flags = URB_ISO_ASAP | UNLINK_FLAGS; u->urb->number_of_packets = u->packets; u->urb->context = u; - u->urb->complete = (usb_complete_t)snd_complete_sync_urb; + u->urb->complete = snd_usb_complete_callback(snd_complete_sync_urb); } } return 0; @@ -1325,11 +1317,7 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype * entry point for linux usb interface */ -#ifdef OLD_USB -static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id); -static void usb_audio_disconnect(struct usb_device *dev, void *ptr); -#else +#ifndef OLD_USB static int usb_audio_probe(struct usb_interface *intf, const struct usb_device_id *id); static void usb_audio_disconnect(struct usb_interface *intf); @@ -2295,25 +2283,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) } } - -#ifdef OLD_USB - -/* - * 2.4 USB kernel API - */ -static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id) -{ - return snd_usb_audio_probe(dev, usb_ifnum_to_if(dev, ifnum), id); -} - -static void usb_audio_disconnect(struct usb_device *dev, void *ptr) -{ - snd_usb_audio_disconnect(dev, ptr); -} - -#else - +#ifndef OLD_USB /* * new 2.5 USB kernel API */ @@ -2323,7 +2293,7 @@ static int usb_audio_probe(struct usb_interface *intf, void *chip; chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); if (chip) { - usb_set_intfdata(intf, chip); + dev_set_drvdata(&intf->dev, chip); return 0; } else return -EIO; @@ -2332,7 +2302,7 @@ static int usb_audio_probe(struct usb_interface *intf, static void usb_audio_disconnect(struct usb_interface *intf) { snd_usb_audio_disconnect(interface_to_usbdev(intf), - usb_get_intfdata(intf)); + dev_get_drvdata(&intf->dev)); } #endif diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index de9bdb965a7c..2160937c9036 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -208,4 +208,8 @@ int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *i #define usb_pipe_needs_resubmit(pipe) 1 #endif +#ifndef snd_usb_complete_callback +#define snd_usb_complete_callback(x) (x) +#endif + #endif /* __USBAUDIO_H */ diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index a8a184b922bc..10e71356d5de 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -172,11 +172,7 @@ static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep, /* * Processes the data read from the device. */ -#ifndef OLD_USB static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs) -#else -static void snd_usbmidi_in_urb_complete(struct urb* urb) -#endif { snd_usb_midi_in_endpoint_t* ep = snd_magic_cast(snd_usb_midi_in_endpoint_t, urb->context, return); @@ -201,11 +197,7 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb) /* * Converts the data read from a Midiman device to standard USB MIDI packets. */ -#ifndef OLD_USB static void snd_usbmidi_in_midiman_complete(struct urb* urb, struct pt_regs *regs) -#else -static void snd_usbmidi_in_midiman_complete(struct urb* urb) -#endif { if (urb->status == 0) { uint8_t* buffer = (uint8_t*)urb->transfer_buffer; @@ -231,18 +223,10 @@ static void snd_usbmidi_in_midiman_complete(struct urb* urb) } } } -#ifndef OLD_USB snd_usbmidi_in_urb_complete(urb, regs); -#else - snd_usbmidi_in_urb_complete(urb); -#endif } -#ifndef OLD_USB static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs) -#else -static void snd_usbmidi_out_urb_complete(struct urb* urb) -#endif { snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, urb->context, return); @@ -606,11 +590,11 @@ static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi, } if (int_epd) usb_fill_int_urb(ep->urb, umidi->chip->dev, pipe, buffer, length, - (usb_complete_t)snd_usbmidi_in_urb_complete, + snd_usb_complete_callback(snd_usbmidi_in_urb_complete), ep, int_epd->bInterval); else usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, length, - (usb_complete_t)snd_usbmidi_in_urb_complete, + snd_usb_complete_callback(snd_usbmidi_in_urb_complete), ep); rep->in = ep; @@ -677,7 +661,7 @@ static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi, } usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, ep->max_transfer, - (usb_complete_t)snd_usbmidi_out_urb_complete, ep); + snd_usb_complete_callback(snd_usbmidi_out_urb_complete), ep); spin_lock_init(&ep->buffer_lock); tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); @@ -967,7 +951,7 @@ static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, err = snd_usbmidi_in_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); if (err < 0) return err; - umidi->endpoints[0].in->urb->complete = (usb_complete_t)snd_usbmidi_in_midiman_complete; + umidi->endpoints[0].in->urb->complete = snd_usb_complete_callback(snd_usbmidi_in_midiman_complete); if (endpoint->out_cables > 0x0001) { ep_info.epnum = get_endpoint(hostif, 4)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; -- cgit v1.2.3 From b8038484bf9c60e6e8669d098bb705deda099445 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 27 Jan 2003 20:31:11 +0100 Subject: ALSA update - added DocBook documentation - added many source comments - simplified proc style interface (per card) - updated PCM scatter-gather routines - moved PM locking outside callbacks --- .../sound/alsa/DocBook/alsa-driver-api.tmpl | 99 + .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 5262 ++++++++++++++++++++ include/sound/ac97_codec.h | 4 +- include/sound/ad1848.h | 1 + include/sound/ak4531_codec.h | 1 - include/sound/core.h | 3 + include/sound/cs46xx.h | 2 - include/sound/emu10k1.h | 21 +- include/sound/gus.h | 8 - include/sound/info.h | 16 + include/sound/pcm.h | 12 +- include/sound/pcm_sgbuf.h | 3 + include/sound/sb.h | 1 + include/sound/sb16_csp.h | 1 - include/sound/sndmagic.h | 56 +- include/sound/trident.h | 6 +- include/sound/version.h | 2 +- sound/core/control.c | 120 +- sound/core/device.c | 66 + sound/core/hwdep.c | 13 + sound/core/info.c | 189 +- sound/core/init.c | 57 +- sound/core/isadma.c | 21 +- sound/core/memory.c | 237 +- sound/core/pcm.c | 33 +- sound/core/pcm_lib.c | 327 +- sound/core/pcm_memory.c | 342 +- sound/core/pcm_misc.c | 86 + sound/core/pcm_native.c | 15 + sound/core/pcm_sgbuf.c | 133 +- sound/core/rawmidi.c | 73 + sound/core/seq/Makefile | 1 + sound/core/sound.c | 33 + sound/drivers/mpu401/mpu401_uart.c | 31 +- sound/i2c/l3/uda1341.c | 50 +- sound/isa/ad1848/ad1848_lib.c | 7 + sound/isa/cmi8330.c | 152 +- sound/isa/cs423x/cs4231_lib.c | 21 +- sound/isa/es18xx.c | 17 +- sound/isa/gus/gus_irq.c | 22 +- sound/isa/gus/gus_mem.c | 17 +- sound/isa/gus/gus_mem_proc.c | 41 +- sound/isa/gus/gus_reset.c | 4 - sound/isa/sb/sb16_csp.c | 23 +- sound/isa/sb/sb16_main.c | 7 + sound/isa/sb/sb_mixer.c | 9 +- sound/pci/ac97/ac97_codec.c | 173 +- sound/pci/ac97/ak4531_codec.c | 24 +- sound/pci/cmipci.c | 39 +- sound/pci/cs4281.c | 59 +- sound/pci/cs46xx/cs46xx_lib.c | 68 +- sound/pci/emu10k1/emu10k1.c | 1 + sound/pci/emu10k1/emu10k1_main.c | 1 - sound/pci/emu10k1/emupcm.c | 2 +- sound/pci/emu10k1/emuproc.c | 94 +- sound/pci/emu10k1/memory.c | 14 +- sound/pci/ens1370.c | 42 +- sound/pci/es1938.c | 40 +- sound/pci/es1968.c | 10 +- sound/pci/ice1712/ice1712.c | 28 +- sound/pci/intel8x0.c | 47 +- sound/pci/korg1212/korg1212.c | 25 +- sound/pci/maestro3.c | 10 +- sound/pci/nm256/nm256.c | 10 +- sound/pci/rme32.c | 37 +- sound/pci/rme96.c | 172 +- sound/pci/rme9652/hammerfall_mem.c | 4 +- sound/pci/rme9652/hdsp.c | 25 +- sound/pci/rme9652/rme9652.c | 25 +- sound/pci/sonicvibes.c | 24 +- sound/pci/trident/trident_main.c | 603 ++- sound/pci/trident/trident_memory.c | 42 +- sound/pci/ymfpci/ymfpci_main.c | 34 +- sound/ppc/pmac.c | 10 +- sound/usb/usbaudio.c | 138 +- 75 files changed, 7972 insertions(+), 1474 deletions(-) create mode 100644 Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl create mode 100644 Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl (limited to 'include') diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl new file mode 100644 index 000000000000..6421246ddac0 --- /dev/null +++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl @@ -0,0 +1,99 @@ + + + + + + + + + + The ALSA Driver API + + + + This document is free; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + + + Management of Cards and Devices + Card Managment +!Esound/core/init.c + + Device Components +!Esound/core/device.c + + KMOD and Device File Entries +!Esound/core/sound.c + + Memory Management Helpers +!Esound/core/memory.c +!Iinclude/sound/sndmagic.h + + + PCM API + PCM Core +!Esound/core/pcm.c +!Esound/core/pcm_lib.c +!Esound/core/pcm_native.c + + PCM Format Helpers +!Esound/core/pcm_misc.c + + PCM Memory Managment +!Esound/core/pcm_memory.c + + SG-Buffer Helpers +!Esound/core/pcm_sgbuf.c + + + Control/Mixer API + General Control Interface +!Esound/core/control.c + + AC97 Codec API +!Esound/pci/ac97/ac97_codec.c + + + MIDI API + Raw MIDI API +!Esound/core/rawmidi.c + + MPU401-UART API +!Esound/drivers/mpu401/mpu401_uart.c + + + Proc Info API + Proc Info Interface +!Esound/core/info.c + + + Miscellaneous Functions + Hardware-Dependent Devices API +!Esound/core/hwdep.c + + ISA DMA Helpers +!Esound/core/isadma.c + + + + diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl new file mode 100644 index 000000000000..8cf02dfa80d2 --- /dev/null +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -0,0 +1,5262 @@ + + + + + + + + + + Writing an ALSA Driver + + Takashi + Iwai + +
+ tiwai@suse.de +
+
+
+ + Dec. 27, 2002 + 0.2 (reborn at Christmas) + + + + This document describes how to write an ALSA (Advanced Linux + Sound Architecture) driver. + + + + + + Copyright (c) 2002 Takashi Iwai tiwai@suse.de + + + + This document is free; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + +
+ + + + + + Preface + + This document describes how to write an + + ALSA (Advanced Linux Sound Architecture) + driver. The document focuses mainly on the PCI soundcard. + In the case of other device types, the API might + be different, too. However, at least the ALSA kernel API is + consistent, and therefore it would be still a bit help for + writing them. + + + + The target of this document is ones who already have enough + skill of C language and have the basic knowledge of linux + kernel programming. This document doesn't explain the general + topics of linux kernel codes and doesn't cover the detail of + implementation of each low-level driver. It describes only how is + the standard way to write a PCI sound driver on ALSA. + + + + If you are already familiar with the older ALSA ver.0.5.x, you + can check the drivers such as es1938.c or + maestro3.c which have also almost the same + code-base in the ALSA 0.5.x tree, so you can compare the differences. + + + + This document is still a draft version. Any feedbacks and + corrections, please!! + + + + + + + + + File Tree Structure + +
+ General + + The ALSA drivers are provided in the two ways. + + + + One is the the trees provided as a tarball or via cvs from the + ALSA's ftp site, and another is the 2.5 (or later) Linux kernel + tree. To synchronize both, the ALSA driver tree is split to + two different trees: alsa-kernel and alsa-driver. The former + contains purely the source codes for the Linux 2.5 (or later) + tree. This tree is designed only for compilation on 2.5 or + later environment. The latter, alsa-driver, contains many subtle + files for compiling the ALSA driver on the outside of Linux + kernel like configure script, the wrapper functions for older, + 2.2 and 2.4 kernels, to adapt the latest kernel API, + and additional drivers which are still in development or in + tests. The drivers in alsa-driver tree will be moved to + alsa-kernel (eventually 2.5 kernel tree) once when they are + finished and confirmed to work fine. + + + + The file tree structure of ALSA driver is depicted below. Both + alsa-kernel and alsa-driver have almost the same file + structure, except for core directory. It's + named as acore in alsa-driver tree. + + + ALSA File Tree Structure + + sound + /core + /oss + /seq + /oss + /instr + /ioctl32 + /include + /drivers + /mpu401 + /opl3 + /i2c + /l3 + /synth + /emux + /pci + /(cards) + /isa + /(cards) + /arm + /ppc + /sparc + /usb + /pcmcia /(cards) + /oss + + + +
+ +
+ core directory + + This directory contains the middle layer, that is, the heart + of ALSA drivers. In this directory, the native ALSA modules are + stored. The sub-directories contain different modules and are + dependent upon the kernel config. + + +
+ core/oss + + + The codes for PCM and mixer OSS emulation modules are stored + in this directory. The rawmidi OSS emulation is included in + the ALSA rawmidi code since it's quite small. The sequencer + code is stored in core/seq/oss directory (see + + below). + +
+ +
+ core/ioctl32 + + + This directory contains the 32bit-ioctl wrappers for 64bit + architectures such like x86-64, ppc64 and sparc64. For 32bit + and alpha architectures, these are not compiled. + +
+ +
+ core/seq + + This and its sub-directories are for the ALSA + sequencer. This directory contains the sequencer core and + primary sequencer modules such like snd-seq-midi, + snd-seq-virmidi, etc. They are compiled only when + CONFIG_SND_SEQUENCER is set in the kernel + config. + +
+ +
+ core/seq/oss + + This contains the OSS sequencer emulation codes. + +
+ +
+ core/seq/instr + + This directory contains the modules for the sequencer + instrument layer. + +
+
+ +
+ include directory + + This is the place for the public header files of ALSA drivers, + which are to be exported to the user-space, or included by + several files at different directories. Basically, the private + header files should not be placed in this directory, but you may + still find files there, due to historical reason :) + +
+ +
+ drivers directory + + This directory contains the non-architecture-specific + codes. For example, the dummy pcm driver and the serial MIDI + driver are found in this directory. In the sub-directories, + there are the codes for components which are independent from + bus and cpu architectures. + + +
+ drivers/mpu401 + + The MPU401 and MPU401-UART modules are stored here. + +
+ +
+ drivers/opl3 + + The OPL3 FM-synth stuff is found here. + +
+
+ +
+ i2c directory + + This contains the ALSA i2c components. + + + + Although there is a standard i2c layer on Linux, ALSA uses its + own i2c codes for some cards, because the soundcard needs only a + simple operation and the standard API is too complicated for + such a purpose. + + +
+ i2c/l3 + + This is a sub-directory for ARM L3 i2c. + +
+
+ +
+ synth directory + + This contains the synth middle-level modules. + + + + So far, there is only Emu8000/Emu10k1 synth driver under + synth/emux sub-directory. + +
+ +
+ pci directory + + This and its sub-directories hold the top-level card modules + for PCI soundcards. + + + + The drivers compiled from a single file is stored directly on + pci directory, while the drivers with several source files are + stored on its own sub-directory (e.g. emu10k1, ice1712). + +
+ +
+ isa directory + + This and its sub-directories hold the top-level card modules + for ISA soundcards. + +
+ +
+ arm, ppc, and sparc directories + + These are for the top-level card modules which are + architecture specific. + +
+ +
+ usb directory + + This contains the USB-audio driver. On the latest version, the + USB MIDI driver is integrated together with usb-audio driver. + +
+ +
+ pcmcia directory + + The PCMCIA, especially PCCard drivers will go here. CardBus + drivers will be on pci directory, because its API is identical + with the standard PCI cards. + + + + At this moment, only VX-pocket driver exists. + +
+ +
+ oss directory + + The OSS/Lite source files are stored here on Linux 2.5 (or + later) tree. (In the ALSA driver tarball, it's empty, of course :) + +
+
+ + + + + + + Basic Flow for PCI Drivers + +
+ Outline + + The minimum flow of PCI soundcard is like the following: + + + define the PCI ID table (see the section + PCI Entries + ). + create probe() callback. + create remove() callback. + create pci_driver table which contains the three pointers above. + create init() function just calling pci_module_init() to register the pci_driver table defined above. + create exit() function to call pci_unregister_driver() function. + + +
+ +
+ Full Code Example + + The code example is shown below. Some parts are kept + unimplemented at this moment but will be filled in the + succeeding sections. The numbers in comment lines of + snd_mychip_probe() function are the + markers. + + + Basic Flow for PCI Drivers Example + + + #include + #include + #include + #include + #define SNDRV_GET_ID + #include + + // module parameters (see "Module Parameters") + 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; + + // definition of the chip-specific record + typedef struct snd_mychip mychip_t; + struct snd_mychip { + snd_card_t *card; + // rest of implementation will be in the section + // "PCI Resource Managements" + }; + + // this should be go into + // (see "Management of Cards and Components") + #define mychip_t_magic 0xa15a4501 + + // chip-specific destructor + // (see "PCI Resource Managements") + static int snd_mychip_free(mychip_t *chip) + { + // will be implemented later... + } + + // component-destructor + // (see "Management of Cards and Components") + static int snd_mychip_dev_free(snd_device_t *device) + { + mychip_t *chip = snd_magic_cast(mychip_t, + device->device_data, return -ENXIO); + return snd_mychip_free(chip); + } + + // chip-specific constructor + // (see "Management of Cards and Components") + static int __devinit snd_mychip_create(snd_card_t *card, + struct pci_device *pci, + mychip_t *rchip) + { + mychip_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + // check PCI availability here + // (see "PCI Resource Managements") + + // allocate a chip-specific data with magic-alloc + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + + // rest of initialization here; will be implemented + // later, see "PCI Resource Managements" + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &ops)) < 0) { + snd_mychip_free(chip); + return err; + } + *rchip = chip; + return 0; + } + + // constructor -- see "Constructor" sub-section + static int __devinit snd_mychip_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) + { + static int dev; + snd_card_t *card; + mychip_t *chip; + int err; + + // (1) + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + // (2) + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + // (3) + if ((err = snd_mychip_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + // (4) + // implemented later + + // (5) + strcpy(card->driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); + + // (6) + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + // (7) + pci_set_drvdata(pci, chip); + dev++; + return 0; + } + + // destructor -- see "Destructor" sub-section + static void __devexit snd_mychip_remove(struct pci_dev *pci) + { + mychip_t *chip = snd_magic_cast(mychip_t, + pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); + } +]]> + + + +
+ +
+ Constructor + + The real constructor of PCI drivers is probe callback. The + probe callback and other component-constructors which are called + from probe callback should be defined with + __devinit prefix. You + cannot use __init prefix for them, + because any PCI device could be a hotplug device. + + + + In the probe callback, the following scheme is often used. + + +
+ 1) Check and increment the device index. + + + += SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } +]]> + + + + where enable[dev] is the module option. + + + + At each time probe callback is called, check the + availability of the device. If not available, simply increment + the device index and returns. dev will be incremented also + later (step + 7). + +
+ +
+ 2) Create a card instance + + + + + + + + + + The detail will be explained in the section + + Management of Cards and Components. + +
+ +
+ 3) Create a main component + + In this part, the PCI resources are allocated. + + + + + + + + The detail will be explained in the section PCI Resource + Managements. + +
+ +
+ 4) Create other components, such as mixer, MIDI, etc. + + Here you define the basic components such as + PCM, + mixer (e.g. AC97), + MIDI (e.g. MPU-401), + and other interfaces. + Also, if you want a proc + file, define it here, too. + +
+ +
+ 5) Set the driver ID and name strings. + + + +driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); +]]> + + + + The driver field holds the minimal ID string of the + chip. This is referred by alsa-lib's configurator, so keep it + simple but unique. + Even the same driver can have different driver IDs to + distinguish the functionality of each chip type. + + + + The shortname field is a string shown as more verbose + name. The longname field contains the information which is + shown in /proc/asound/cards. + +
+ +
+ 6) Register the card instance. + + + + + + + + + + Will be explained in the section Management + of Cards and Components, too. + +
+ +
+ 7) Set the PCI driver data and return zero. + + + + + + + + In the above, the chip record is stored. This pointer is + referred in the remove callback and power-management + callbacks, too. + If the card doesn't support the suspend/resume, you can store + the card pointer instead of the chip pointer, so that + snd_card_free can be called directly + without cast in the remove callback. But anyway, be sure + which pointer is used. + +
+
+ +
+ Destructor + + The destructor, remove callback, simply releases the card + instance. Then the ALSA middle layer will release all the + attached components automatically. + + + + It would be typically like the following: + + + +card); + pci_set_drvdata(pci, NULL); + } +]]> + + + + The above code assumes that the chip is allocated + with snd_magic stuff and + has the field to hold the card pointer (see the next + section). + +
+ +
+ Header Files + + For the above example, at least the following include files + are necessary. + + + + + #include + #include + #include + #include + #define SNDRV_GET_ID + #include +]]> + + + + where the last twos are necessary only when module options are + defined in the source file. If the codes are split to several + files, the file without module options don't need them. + + + + In addition to them, you'll need + <linux/interrupt.h> for the interrupt + handling, and <asm/io.h> for the i/o + access. If you use mdelay() or + udelay() functions, you'll need to include + <linux/delay.h>, too. + + + + The ALSA interfaces like PCM or control API are define in other + header files as <sound/xxx.h>. + They have to be included after + <sound/core.h>. + + +
+
+ + + + + + + Management of Cards and Components + +
+ Card Instance + + For each soundcard, a card record must be allocated. + + + + A card record is the headquarters of the soundcard. It manages + the list of whole devices (components) on the soundcard, such as + PCM, mixers, MIDI, synthesizer, and so on. Also, the card + record holds the ID and the name strings of the card, manages + the root of proc files, and controls the power-management states + and hotplug disconnections. The component list on the card + record is used to manage the proper releases of resources at + destruction. + + + + As mentioned above, to create a card instance, call + snd_card_new(). + + + + + + + + + + The function takes four arguments, the card-index number, the + id string, the module pointer (usually + THIS_MODULE), + and the size of extra-data space. The last argument is used to + allocate card->private_data for the + chip-specific data. Note that this data + is allocated by + snd_card_new(). + +
+ +
+ Components + + After the card is created, you can attach the components + (devices) to the card instance. On ALSA driver, a component is + represented as a snd_device_t object. + A component can be a PCM instance, a control interface, a raw + MIDI interface, etc. Each of such instances has one component + entry. + + + + A component can be created via + snd_device_new() function. + + + + + + + + + + This takes the card pointer, the device-level + (SNDRV_DEV_XXX), the data pointer, and the + callback pointers (&ops). The + device-level defines the type of components and the order of + registration and de-registration. For most of components, the + device-level is already defined. For a user-defined component, + you can use SNDRV_DEV_LOWLEVEL. + + + + This function itself doesn't allocate the data space. The data + must be allocated manually beforehand, and its pointer is passed + as the argument. This pointer is used as the identifier + (chip in the above example) for the + instance. + + + + Each ALSA pre-defined component such as ac97 or pcm calls + snd_device_new() inside its + constructor. The destructor for each component is defined in the + callback pointers. Hence, you don't need to take care of + calling a destructor for such a component. + + + + If you would like to create your own component, you need to + set the destructor function to dev_free callback in + ops, so that it can be released + automatically via snd_card_free(). The + example will be shown later as an implementation of a + chip-specific data. + +
+ +
+ Chip-Specific Data + + The chip-specific information, e.g. the i/o port address, its + resource pointer, or the irq number, is stored in the + chip-specific record. + Usually, the chip-specific record is typedef'ed as + xxx_t like the following: + + + + + + + + + + You might have objections against such a typedef, but this + typedef is necessary if you use a magic-cast + (explained later). + + + + In general, there are two ways to allocate the chip record. + + +
+ 1. Allocating via <function>snd_card_new()</function>. + + As mentioned above, you can pass the extra-data-length to the 4th argument of snd_card_new(), i.e. + + + + + + + + whether mychip_t is the type of the chip record. + + + + In return, the allocated record can be accessed as + + + +private_data; +]]> + + + + With this method, you don't have to allocate twice. But you + cannot use magic-cast for this record pointer, + instead. + +
+ +
+ 2. Allocating an extra device. + + + After allocating a card instance via + snd_card_new() (with + NULL on the 4th arg), call + snd_magic_kcalloc(). + + + + + + + + Once when the record is allocated via snd_magic stuff, you + can use magic-cast for the void pointer. + + + + The chip record should have the field to hold the card + pointer at least, + + + + + + + + + + Then, set the card pointer in the returned chip instance. + + + +card = card; +]]> + + + + + + Also, you need to define a magic-value for mychip_t. + + + + + + + (the detail will be described in the + + next subsection). + + + + Next, initialize the fields, and register this chip + record as a low-level device with a specified + ops, + + + + + + + + snd_mychip_dev_free() is the + device-destructor function, which will call the real + destructor. + + + + + +device_data, + return -ENXIO); + return snd_mychip_free(chip); + } +]]> + + + + where snd_mychip_free() is the real destructor. + +
+ +
+ Not a magic but a logic + + Now, you might have a question: What is the advantage of the + second method? Obviously, it looks far more complicated. + + As I wrote many times, the second method allows a + magic-cast for mychip_t. If you + have a void pointer (such as + pcm->private_data), the pointer type + is unknown at the compile time, and you cannot know even if a + wrong pointer type is passed. The compiler would accept + it. The magic-cast checks the pointer type at the runtime (and + whether it's a null pointer, too). Hence, the cast will be + much safer and good for debugging. + + + + As you have already seen, allocation with a magic-header can + be done via snd_magic_kmalloc() or + snd_magic_kcalloc(). + + + + + + + + The difference of these two functions is whether the area is + zero-cleared (kcalloc) or not + (kmalloc). + + + + The first argument of the allocator is the type of the + record. The magic-constant has to be defined for this type + beforehand. In this case, we'll need to define + mychip_t_magic, for example, as already + seen, + + + + + + + + The value is arbitrary but should be unique. + This is usually defined in + <include/sndmagic.h> or + <include/amagic.h> for alsa-driver tree, + but you may define it locally in the code at the early + development stage, since changing + sndmagic.h will lead to the recompilation + of the whole driver codes. + + + + The second argument is the extra-data length. It is usually + zero. The third argument is the flags to be passed to kernel + memory allocator, GFP_XXX. Normally, + GFP_KERNEL is passed. + + + + For casting a pointer, use + snd_magic_cast() macro: + + + + + + + + where source_pointer is the pointer to + be casted (e.g. pcm->private_data), and + action is the action to do if the cast + fails (e.g. return -EINVAL). + + + + For releasing the magic-allocated data, you need to call + snd_magic_kfree() function instead of + kfree(). + + + + + + + + + + If you call kfree() for the + magic-allocated value, it will lead to memory leaks. + When the ALSA drivers are compiled with + CONFIG_SND_DEBUG_MEMORY kernel config (or + configured with ), the + non-matching free will be checked and you'll see warning + messages. + + + + If you are 100% sure that your code is bug-free, you can + compile the driver without + CONFIG_SND_DEBUG_MEMORY kernel config, + so that the magic-allocator and the magic-cast will be + replaced to the normal kmalloc and cast. + +
+
+ +
+ Registration and Release + + After all components are assigned, register the card instance + by calling snd_card_register(). The access + to the device files are enabled at this point. That is, before + snd_card_register() is called, the + components are safely inaccessible from external side. If this + call fails, exit the probe function after releasing the card via + snd_card_free(). + + + + For releasing the card instance, you can call simply + snd_card_free(). As already mentioned, all + components are released automatically by this call. + + + + As further notes, the destructors (both + snd_mychip_dev_free and + snd_mychip_free) cannot be defined with + __devexit prefix, because they may be + called from the constructor, too, at the false path. + + + + For a device which allows hotplugging, you can use + snd_card_free_in_thread. This one will + postpone the destruction and wait in a kernel-thread until all + devices are closed. + + +
+ +
+ + + + + + + PCI Resource Managements + +
+ Full Code Example + + In this section, we'll finish the chip-specific constructor, + destructor and PCI entries. The example code is shown first, + below. + + + PCI Resource Managements Example + +res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + // release the irq + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + // release the data + snd_magic_kfree(chip); + return 0; + } + + // chip-specific constructor + static int __devinit snd_mychip_create(snd_card_t *card, + struct pci_dev *pci, + mychip_t **rchip) + { + mychip_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + // check PCI availability (28bit DMA) + if ((err = pci_enable_device(pci)) < 0) + return err; + if (!pci_dma_supported(pci, 0x0fffffff)) { + printk(KERN_ERR "error to set 28bit mask DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + // initialize the stuff + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + // (1) PCI resource allocation + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 8, + "My Chip")) == NULL) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot allocate the port\n"); + return -EBUSY; + } + if (request_irq(pci->irq, snd_mychip_interrupt, + SA_INTERRUPT|SA_SHIRQ, "My Chip", + (void *)chip)) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot grab irq\n"); + return -EBUSY; + } + chip->irq = pci->irq; + + // (2) initialization of the chip hardware + // (not implemented in this document) + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &ops)) < 0) { + snd_mychip_free(chip); + return err; + } + *rchip = chip; + return 0; + } + + // PCI IDs + static struct pci_device_id snd_mychip_ids[] __devinitdata = { + { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + .... + { 0, } + }; + MODULE_DEVICE_TABLE(pci, snd_mychip_ids); + + // pci_driver definition + static struct pci_driver driver = { + .name = "My Own Chip", + .id_table = snd_mychip_ids, + .probe = snd_mychip_probe, + .remove = __devexit_p(snd_mychip_remove), + }; + + // initialization of the module + static int __init alsa_card_mychip_init(void) + { + int err; + + if ((err = pci_module_init(&driver)) < 0) { + #ifdef MODULE + printk(KERN_ERR "My chip soundcard not found " + "or device busy\n"); + #endif + return err; + } + return 0; + } + + // clean up the module + static void __exit alsa_card_mychip_exit(void) + { + pci_unregister_driver(&driver); + } + + module_init(alsa_card_mychip_init) + module_exit(alsa_card_mychip_exit) + + EXPORT_NO_SYMBOLS; /* for old kernels only */ +]]> + + + +
+ +
+ Some Hafta's + + The allocation of PCI resources is done in the + probe() function, and usually an extra + xxx_create() function is written for this + purpose. + + + + In the case of PCI devices, you have to call at first + pci_enable_device() function before + allocating resources. Also, you need to set the proper PCI DMA + mask to limit the accessed i/o range. In some cases, you might + need to call pci_set_master() function, + too. + + + + Suppose the 28bit mask, and the code to be added would be like: + + + + + + + +
+ +
+ Resource Allocation + + The allocation of ports and irqs are done via standard kernel + functions. Unlike ALSA ver.0.5.x., there are no helpers for + that. And these resources must be released in the destructor + function (see below). Also, on ALSA 0.9.x, you don't need + allocate (pseudo-)DMA for PCI like 0.5.x. + + + + Now assume that this PCI device has an I/O port with 8 bytes + and an interrupt. Then mychip_t will have the + following fields: + + + + + + + + + + For an i/o port (and also a memory region), you need to have + the resource pointer for the standard resource management. For + an irq, you have to keep only the irq number (integer). But you + need to initialize this number as -1 before actual allocation, + since irq 0 is valid. The port address and its resource pointer + can be initialized as null by + snd_magic_kcalloc() automatically, so you + don't have to take care of it. + + + + The allocation of an i/o port is done like this: + + + +port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 8, + "My Chip")) == NULL) { + printk(KERN_ERR "cannot allocate the port 0x%lx\n", + chip->port); + snd_mychip_free(chip); + return -EBUSY; + } +]]> + + + + + + It will reserve the i/o port region of 8 bytes of the given + PCI device. The returned value, chip->res_port, is allocated + via kmalloc() by + request_region(). The pointer must be + released via kfree(), but there is some + problem regarding this. This issue will be explained more below. + + + + The allocation of an interrupt source is done like this: + + + +irq, snd_mychip_interrupt, + SA_INTERRUPT|SA_SHIRQ, "My Chip", + (void *)chip)) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot grab irq %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; +]]> + + + + where snd_mychip_interrupt() is the + interrupt handler defined later. + Note that chip->irq should be defined + only when request_irq() succeeded. + + + + On the PCI bus, the interrupts can be shared. Thus, + SA_SHIRQ is given as the interrupt flag of + request_irq(). + + + + The last argument of request_irq() is the + data pointer passed to the interrupt handler. Usually, the + chip-specific record is used for that, but you can use what you + like, too. + + + + I won't define the detail of the interrupt handler at this + point, but at least its appearance can be explained now. The + interrupt handler looks usually like the following: + + + + + + + + Again the magic-cast is used here to get the correct pointer + from the second argument. + + + + Now let's write the corresponding destructor for the resources + above. The role of destructor is simple: disable the hardware + (if already activated) and release the resources. So far, we + have no hardware part, so the disabling is not written here. + + + + For releasing the resources, check-and-release + method is a safer way. For the i/o port, do like this: + + + +res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } +]]> + + + + + + As you can see, the i/o resource pointer is also to be freed + via kfree_nocheck() after + release_resource() is called. You + cannot use kfree() here, because on ALSA, + kfree() may be a wrapper to its own + allocator with the memory debugging. Since the resource pointer + is allocated externally outside the ALSA, it must be released + via the native + kfree(). + kfree_nocheck() is used for that; it calls + the native kfree() without wrapper. + + + + For releasing the interrupt, do like this: + + + +irq >= 0) + free_irq(chip->irq, (void *)chip); +]]> + + + + And finally, release the chip-specific record. + + + + + + + + + + The chip instance is freed via + snd_magic_kfree(). Please use this function + for the object allocated by + snd_magic_kmalloc(). If you free it with + kfree(), it won't work properly and will + result in the memory leak. Also, again, remember that you cannot + set __devexit prefix for this destructor. + + + + We didn't implement the hardware-disabling part in the above. + If you need to do this, please note that the destructor may be + called even before the initialization of the chip is completed. + It would be better to have a flag to skip the hardware-disabling + if the hardware was not initialized yet. + + + + When the chip-data is assigned to the card using + snd_device_new() with + SNDRV_DEV_LOWLELVEL , its destructor is + called at the last. that is, it is assured that all other + components like PCMs and controls have been already released. + You don't have to call stopping PCMs, etc. explicitly, but just + stop the hardware in the low-level. + + + + The management of a memory-mapped region is almost as same as + the management of an i/o port. You'll need three fields like + the following: + + + + + + + + and the allocation would be (assuming its size is 512 bytes): + + + +iobase_phys = pci_resource_start(pci, 0); + chip->iobase_virt = (unsigned long) + ioremap_nocache(chip->iobase_phys, 512); + if ((chip->res_port = request_mem_region(chip->port, 512, + "My Chip")) == NULL) { + printk(KERN_ERR "cannot allocate the memory region\n"); + snd_mychip_free(chip); + return -EBUSY; + } +]]> + + + + and the corresponding destructor would be: + + + +iobase_virt) + iounmap((void *)chip->iobase_virt); + if (chip->res_iobase) { + release_resource(chip->res_iobase); + kfree_nocheck(chip->res_iobase); + } + .... + } +]]> + + + + +
+ +
+ PCI Entries + + So far, so good. Let's finish the rest of missing PCI + stuffs. At first, we need a + pci_device_id table for this + chipset. It's a table of PCI vendor/device ID number, and some + masks. + + + + For example, + + + + + + + + + + The first and second fields of + pci_device_id struct are the vendor and + device IDs. If you have nothing special to filter the matching + devices, you can use the rest of fields like above. The last + field of pci_device_id struct is a + private data for this entry. You can specify any value here, for + example, to tell the type of different operations per each + device IDs. Such an example is found in intel8x0 driver. + + + + The last entry of this list is the terminator. You must + specify this all-zero entry. + + + + Then, prepare the pci_driver record: + + + + + + + + + + The probe and + remove functions are what we already + defined in + the previous sections. The remove should + be defined with + __devexit_p() macro, so that it's not + defined for built-in (and non-hot-pluggable) case. The + name + field is the name string of this device. Note that you must not + use a slash / in this string. + + + + And at last, the module entries: + + + + + + + + + + Note that these module entries are tagged with + __init and + __exit prefixes, not + __devinit nor + __devexit. + + + + Oh, one thing was forgotten. If you have no exported symbols, + you need to declare it on 2.2 or 2.4 kernels (on 2.5 kernels + it's not necessary, though). + + + + + + + + That's all! + +
+
+ + + + + + + PCM Interface + +
+ General + + The PCM middle layer of ALSA is quite powerful and it is only + necessary for each driver to implement the low-level functions + to access its hardware. + + + + For accessing to the PCM layer, you need to include + <sound/pcm.h> above all. In addition, + <sound/pcm_params.h> might be needed + if you access to some functions related with hw_param. + + + + Each card device can have up to four pcm instances. A pcm + instance corresponds to a pcm device file. The limitation of + number of instances comes only from the available bit size of + the linux's device number. Once when 64bit device number is + used, we'll have more available pcm instances. + + + + A pcm instance consists of pcm playback and capture streams, + and each pcm stream consists of one or more pcm substreams. Some + soundcard supports the multiple-playback function. For example, + emu10k1 has a PCM playback of 32 stereo substreams. In this case, at + each open, a free substream is (usually) automatically chosen + and opened. Meanwhile, when only one substream exists and it was + already opened, the succeeding open will result in the blocking + or the error with EAGAIN according to the + file open mode. But you don't have to know the detail in your + driver. The PCM middle layer will take all such jobs. + +
+ +
+ Full Code Example + + The example code below does not include any hardware access + routines but shows only the skeleton, how to build up the PCM + interfaces. + + + PCM Example Code + + + .... + + #define chip_t mychip_t + .... + + /* hardware definition */ + static snd_pcm_hardware_t snd_mychip_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_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, + }; + + /* open callback */ + static int snd_mychip_pcm_open(snd_pcm_substream_t *subs) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->hw = snd_mychip_playback_hw; + // more hardware-initialization will be done here + return 0; + } + + /* close callback */ + static int snd_mychip_pcm_close(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + // the hardware-specific codes will be here + return 0; + + } + + /* hw_params callback */ + static int snd_mychip_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t * hw_params) + { + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + } + + /* hw_free callback */ + static int snd_mychip_pcm_hw_free(snd_pcm_substream_t *substream) + { + return snd_pcm_lib_free_pages(substream); + } + + /* prepare callback */ + static int snd_mychip_pcm_prepare(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + // set up the hardware with the current configuration + // for example... + mychip_set_sample_format(chip, runtime->format); + mychip_set_sample_rate(chip, runtime->rate); + mychip_set_channels(chip, runtime->channels); + mychip_set_dma_setup(chip, runtime->dma_area, + chip->buffer_size, + chip->period_size); + return 0; + } + + /* trigger callback */ + static int snd_mychip_pcm_trigger(snd_pcm_substream_t *substream, + int cmd) + { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + // do something to start the PCM engine + break; + case SNDRV_PCM_TRIGGER_STOP: + // do something to stop the PCM engine + break; + default: + return -EINVAL; + } + } + + /* pointer callback */ + static snd_pcm_uframes_t + snd_mychip_pcm_pointer(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + unsigned int current_ptr; + + // get the current hardware pointer + current_ptr = mychip_get_hw_pointer(chip); + return current_ptr; + } + + /* operators */ + static snd_pcm_ops_t snd_mychip_playback_ops = { + .open = snd_mychip_playback_open, + .close = snd_mychip_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mychip_pcm_hw_params, + .hw_free = snd_mychip_pcm_hw_free, + .prepare = snd_mychip_pcm_prepare, + .trigger = snd_mychip_pcm_trigger, + .pointer = snd_mychip_pcm_pointer, + }; + + /* + * definitions of capture are omitted here... + */ + + /* create a pcm device */ + static int __devinit snd_mychip_new_pcm(mychip_t *chip) + { + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, + &pcm)) < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_mychip_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_mychip_capture_ops); + /* pre-allocation of buffers */ + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, + 64*1024, 64*1024); + return 0; + } +]]> + + + +
+ +
+ Constructor + + A pcm instance is allocated snd_pcm_new() + function. It would be better to create a constructor for pcm, + namely, + + + +card, "My Chip", 0, 1, 1, + &pcm)) < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + .... + return 0; + } +]]> + + + + + + The snd_pcm_new() function takes the four + arguments. The first argument is the card pointer to which this + pcm is assigned, and the second is the ID string. + + + + The third argument (index, 0 in the + above) is the index of this new pcm. It begins from zero. When + you will create more than one pcm instances, specify the + different numbers in this argument. For example, + index = 1 for the second PCM device. + + + + The fourth and fifth arguments are the number of substreams + for playback and capture, respectively. Here both 1 are given in + the above example. When no playback or no capture is available, + pass 0 to the corresponding argument. + + + + If a chip supports multiple playbacks or captures, you can + specify more numbers, but they must be handled properly in + open/close, etc. callbacks. When you need to know which + substream you are referring to, then it can be obtained from + snd_pcm_substream_t data passed to each callback + as follows: + + + +number; +]]> + + + + + + After the pcm is created, you need to set operators for each + pcm stream. + + + + + + + + + + The operators are defined typically like this: + + + + + + + + Each of callbacks is explained in the subsection + + Operators. + + + + After setting the operators, most likely you'd like to + pre-allocate the buffer. For the pre-allocation, simply call + the following: + + + +pci, pcm, + 64*1024, 64*1024); +]]> + + + + It will allocate up to 64kB buffer as default. The details of + buffer management will be described in the later section Buffer and Memory + Management. + + + + Additionally, you can set some extra information for this pcm + in pcm->info_flags. + The available values are defined as + SNDRV_PCM_INFO_XXX in + <sound/asound.h>, which is used for + the hardware definition (described later). When your soundchip + supports only half-duplex, specify like this: + + + +info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; +]]> + + + +
+ +
+ ... And the Destructor? + + The destructor for a pcm instance is not always + necessary. Since the pcm device will be released by the middle + layer code automatically, you don't have to call destructor + explicitly. + + + + The destructor would be necessary when you created some + special records internally and need to release them. In such a + case, set the destructor function to + pcm->private_free: + + + PCM Instance with a Destructor + +private_data, return); + // free your own data + kfree(chip->my_private_pcm_data); + // do what you like else... + } + + static int __devinit snd_mychip_new_pcm(mychip_t *chip) + { + snd_pcm_t *pcm; + .... + // allocate your own data + chip->my_private_pcm_data = kmalloc(...); + // set the destructor + pcm->private_data = chip; + pcm->private_free = mychip_pcm_free; + .... + } +]]> + + + +
+ +
+ Operators + + OK, now let me explain the detail of each pcm callback + (ops). In general, every callback must + return 0 if successful, or a negative number with the error + number such as -EINVAL at any + error. + + + + The callback function takes at least the argument with + snd_pcm_substream_t pointer. For retrieving the + chip record from the given substream instance, you can use the + following macro. + + + + + + + + + + It's expanded with a magic-cast, so the cast-error is + automatically checked. You should define chip_t at + the beginning of the code, since this will be referred in many + places of pcm and control interfaces. + + +
+ open callback + + + + + + + + This is called when a pcm substream is opened. + + + + At least, here you have to initialize the runtime hardware + record. Typically, this is done by like this: + + + +runtime; + + runtime->hw = snd_mychip_playback_hw; + return 0; + } +]]> + + + + where snd_mychip_playback_hw is the + pre-defined hardware record. + + + + + + + + + + The similar struct exists on ALSA 0.5.x driver, so you can + guess the values if you already wrote a driver. + + + + The info field contains the type and + capabilities of this pcm. The bit flags are defined in + <sound/asound.h> as + SNDRV_PCM_INFO_XXX. Here, at least, you + have to specify whether the mmap is supported and which + interleaved format is supported. + When the mmap is supported, add + SNDRV_PCM_INFO_MMAP flag here. When the + hardware supports the interleaved or the non-interleaved + format, SNDRV_PCM_INFO_INTERLEAVED or + SNDRV_PCM_INFO_NONINTERLEAVED flag must + be set, respectively. If both are supported, you can set both, + too. + + + + In the above example, MMAP_VALID and + BLOCK_TRANSFER are specified for OSS mmap + mode. Usually both are set. Of course, + MMAP_VALID is set only if the mmap is + really supported. + + + + The other possible flags are + SNDRV_PCM_INFO_PAUSE and + SNDRV_PCM_INFO_RESUME. The + PAUSE bit means that the pcm supports the + pause operation, while the + RESUME bit means that the pcm supports + the suspend/resume operation. If these flags + are set, the trigger callback below + must handle the corresponding commands. + + + + formats field contains the bit-flags + of supported formats (SNDRV_PCM_FMTBIT_XXX). + If the hardware supports more than one format, give all or'ed + bits. In the example above, the signed 16bit little-endian + format is specified. + + + + rates field contains the bit-flags of + supported rates (SNDRV_PCM_RATE_XXX). + When the chip supports continuous rates, pass + CONTINUOUS bit additionally. + The pre-defined rate bits are only for typical rates. If your + chip supports unconventional rates, you need to add + KNOT bit and set up the + constraint manually (explained later). + + + + There have been many changes of terminology between + ALSA 0.5.x and 0.9.x. + On the ALSA 0.9.x world, period means what is + known as fragment in the OSS. It's the least + size of (a part of) the buffer to generate an interrupt. + + + + Now, taking the new terminology into account, the other fields + are self-explanatory (I hope). Please note that here, both + min/max buffer and period sizes are specified in bytes. + + + + Some drivers allocate the private instance for each pcm + substream. It can be stored in + substream->runtime->private_data. + Since it's a void pointer, you + should use magic-kmalloc and magic-cast for such an object. + + + +runtime->private_data = data; + .... + } +]]> + + + + + + The allocated object must be released in the close callback below. + +
+ +
+ close callback + + + + + + + + Obviously, this is called when a pcm substream is closed. + + + + Any private instance for a pcm substream allocated in the + open callback will be released here. + + + +runtime->private_data); + .... + } +]]> + + + +
+ +
+ ioctl callback + + This is used for any special action to pcm ioctls. But + usually you can pass a generic ioctl callback, + snd_pcm_lib_ioctl. + +
+ +
+ hw_params callback + + + + + + + + This and hw_free callbacks exist + only on ALSA 0.9.x. + + + + This is called when the hardware parameter + (hw_params) is set + up by the application, + that is, once when the buffer size, the period size, the + format, etc. are defined for the pcm substream. + + + + Many hardware set-up should be done in this callback, + including the allocation of buffers. + + + + Parameters to be initialized are retrieved by + params_xxx() macros. For allocating a + buffer, you can call a helper function, + + + + + + + + snd_pcm_lib_malloc_pages() is available + only when the DMA buffers have been pre-allocated. + See the section + Buffer Types for more details. + + + + Note that this and prepare callbacks + may be called multiple times per initialization. + For example, the OSS emulation may + call these callbacks at each change via its ioctl. + + + + Thus, you need to take care not to allocate the same buffers + many times, which will lead to memory leak! Calling the + helper function above many times is OK. It will release the + previous buffer automatically when it was already allocated. + + + + Another note is that this callback is non-atomic + (schedulable). This is important, because the + prepare callback + is atomic (non-schedulable). That is, mutex or any + schedule-related functions are available only in + hw_params callback. + Please see the subsection + + Atomicity for details. + +
+ +
+ hw_free callback + + + + + + + + + + This is called to release the resources allocated via + hw_params. For example, releasing the + buffer via + snd_pcm_lib_malloc_pages() is done by + calling the following: + + + + + + + + + + This callback may be called multiple times, too. + Keep track whether the resource was already released. + +
+ +
+ prepare callback + + + + + + + + + + This callback is called when the pcm is + prepared. You can set the format type, sample + rate, etc. here. The difference from + hw_params is that the + prepare callback will be called at each + time + snd_pcm_prepare() is called, i.e. when + recovered after underruns, etc. + + + + As mentioned above, this callback is atomic. + + + + In this and the following callbacks, you can refer to the + values via the runtime record, + substream->runtime. + For example, to get the current + rate, format or channels, access to + runtime->rate, + runtime->format or + runtime->channels, respectively. + The physical address of the allocated buffer is set to + runtime->dma_area. The buffer and period sizes are + in runtime->buffer_size and runtime->period_size, + respectively. + + + + Note that the period and the buffer sizes are stored in + frames. In the ALSA world, 1 frame = channels + * samples-size. For conversion between frames and bytes, you + can use the helper functions, + frames_to_bytes() and + bytes_to_frames(). + + + +period_size); +]]> + + + + + + Be careful that this callback will be called many times at + each set up, too. + +
+ +
+ trigger callback + + + + + + + + This is called when the pcm is started, stopped or paused. + + + + Which action is specified in the second argument, + SNDRV_PCM_TRIGGER_XXX in + <sound/pcm.h>. At least, + START and STOP + commands must be defined in this callback. + + + + + + + + + + When the pcm supports the pause operation (given in info + field of the hardware table), PAUSE_PUSE + and PAUSE_RELEASE commands must be + handled here, too. The former is the command to pause the pcm, + and the latter to restart the pcm again. + + + + When the pcm supports the suspend/resume operation, + SUSPEND and RESUME + commands must be handled, too. Obviously it does suspend and + resume of the pcm substream. Usually, the + SUSPEND is identical with + STOP command and the + RESUME is identical with + START command. + + + + This callback is also atomic. + +
+ +
+ pointer callback + + + + + + + + This callback is called when the PCM middle layer inquires + the current hardware position on the buffer. The position must + be returned in frames (which was in bytes on ALSA 0.5.x), + ranged from 0 to buffer_size - 1. + + + + This is called usually from the buffer-update routine in the + pcm middle layer, which is invoked when + snd_pcm_period_elapsed() is called in the + interrupt routine. Then the pcm middle layer updates the + position and calculates the available space, and wakes up the + sleeping poll threads, etc. + + + + This callback is also atomic. + +
+ +
+ copy and silence callbacks + + These callbacks are not mandatory, and can be omitted in + most cases. These callbacks are used when the hardware buffer + cannot be on the normal memory space. Some chips have their + own buffer on the hardware which is not mappable. In such a + case, you have to transfer the data manually from the memory + buffer to the hardware buffer. Or, if the buffer is + non-contiguous on both physical and virtual memory spaces, + these callbacks must be defined, too. + + + + If these two callbacks are defined, copy and set-silence + operations are done by them. The detailed will be described in + the later section Buffer and Memory + Management. + +
+ +
+ page callback + + + This callback is also not mandatory. This callback is used + mainly for the non-contiguous buffer. The mmap calls this + callback to get the page address. Some examples will be + explained in the later section Buffer and Memory + Management, too. + +
+
+ +
+ Interrupt Handler + + The rest of pcm stuff is the PCM interrupt handler. The + role of PCM interrupt handler in the sound driver is to update + the buffer position and to tell the PCM middle layer when the + buffer position goes across the prescribed period size. To + inform this, call snd_pcm_period_elapsed() + function. + + + + There are several types of sound chips to generate the interrupts. + + +
+ Interrupts at the period (fragment) boundary + + This is the most frequently found type: the hardware + generates an interrupt at each period boundary. + In this case, you can call + snd_pcm_period_elapsed() at each + interrupt. + + + + snd_pcm_period_elapsed() takes the + substream pointer as its argument. Thus, you need to keep the + substream pointer accessible from the chip instance. For + example, define substream field in the chip record to hold the + current running substream pointer, and set the pointer value + at open callback (and reset at close callback). + + + + If you aquire a spinlock in the interrupt handler, and the + lock is used in other pcm callbacks, too, then you have to + release the lock before calling + snd_pcm_period_elapsed(), because + snd_pcm_period_elapsed() calls other pcm + callbacks inside. + + + + A typical coding would be like: + + + Interrupt Handler Case #1 + +lock); + .... + if (pcm_irq_invoked(chip)) { + // call updater, unlock before it + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(chip->substream); + spin_lock(&chip->lock); + } + .... + spin_unlock(&chip->lock); + } +]]> + + + +
+ +
+ High-frequent timer interrupts + + This is the case when the hardware doesn't generate interrupts + at the period boundary but do timer-interrupts at the fixed + timer rate (e.g. es1968 or ymfpci drivers). + In this case, you need to check the current hardware + position and accumulates the processed sample length at each + interrupt. When the accumulated size overcomes the period + size, call + snd_pcm_period_elapsed() and reset the + accumulator. + + + + A typical coding would be like the following. + + + Interrupt Handler Case #2 + +lock); + .... + if (pcm_irq_invoked(chip)) { + unsigned int last_ptr, size; + // get the current hardware pointer (in frames) + last_ptr = get_hw_ptr(chip); + // calculate the processed frames since the + // last update + if (last_ptr < chip->last_ptr) + size = runtime->buffer_size + last_ptr + - chip->last_ptr; + else + size = last_ptr - chip->last_ptr; + // remember the last updated point + chip->last_ptr = last_ptr; + // accumulate the size + chip->size += size; + // over the period boundary? + if (chip->size >= runtime->period_size) { + // reset the accumulator + chip->size %= runtime->period_size; + // call updater + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->lock); + } + } + .... + spin_unlock(&chip->lock); + } +]]> + + + +
+ +
+ On calling <function>snd_pcm_period_elapsed()</function> + + In both cases, even if more than one period are elapsed, you + don't have to call + snd_pcm_period_elapsed() many times. Call + only once. And the pcm layer will check the current hardware + pointer and update to the latest status. + +
+
+ +
+ Atomicity + + One of the most important (and thus difficult to debug) problem + on the kernel programming is the race condition. + On linux kernel, usually it's solved via spin-locks or + semaphores. In general, if the race condition may + happen in the interrupt handler, it's handled as atomic, and you + have to use spinlock for protecting the critical session. If it + never happens in the interrupt and it may take relatively long + time, you should use semaphore. + + + + As already seen, some pcm callbacks are atomic and some are + not. For example, hw_params callback is + non-atomic, while prepare callback is + atomic. This means, the latter is called already in a spinlock + held by the PCM middle layer. Please take this atomicity into + account when you use a spinlock or a semaphore in the callbacks. + + + + In the atomic callbacks, you cannot use functions which may call + schedule or go to + sleep. The semaphore and mutex do sleep, + and hence they cannot be used inside the atomic callbacks + (e.g. prepare callback). + For taking a certain delay in such a callback, please use + udelay() or mdelay(). + + + + This atomicity problem appears also in the initialization of the + hardware when the power-management is supported. The functions + for suspending and resuming the chip must be atomic, i.e. no + mutex nor sleep can be used in them. + + +
+
+ Constraints + + If your chip supports unconventional sample rates, or only the + limited samples, you need to set a constraint for the + condition. + + + + For example, in order to restrict the sample rates in the some + supported values, use + snd_pcm_hw_constraint_list(). + You need to call this function in the open callback. + + + Example of Hardware Constraints + +runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + if (err < 0) + return err; + .... + } +]]> + + + + + + There are many different constraints. You can even define your + own constraint rules. I won't explain the details here, rather I + would like to say, Luke, use the source. + +
+ +
+ + + + + + + Control Interface + +
+ General + + The control interface is used widely for many switches, + sliders, etc. which are accessed from the user-space. Its most + important use is the mixer interface. In other words, on ALSA + 0.9.x, all the mixer stuff is implemented on the control kernel + API (while there was an independent mixer kernel API on 0.5.x). + + + + ALSA has a well-defined AC97 control module. If your chip + supports only the AC97 and nothing else, you can skip this + section. + + + + The control API is defined in + <sound/control.h>. + Include this file if you add your own controls. + +
+ +
+ Definition of Controls + + For creating a new control, you need to define the three + callbacks: info, + get and + put. Then, define a + snd_kcontrol_new_t record, such as: + + + Definition of a Control + + + + + + + + Most likely the control is created via + snd_ctl_new1(), and in such a case, you can + add __devinitdata prefix to the + definition like above. + + + + The iface field specifies the type of + the control, + SNDRV_CTL_ELEM_IFACE_XXX. There are + MIXER, PCM, + CARD, etc. + + + + The name is the name identifier + string. On ALSA 0.9.x, the control name is very important, + because its role is classified from its name. There are + pre-defined standard control names. The details are described in + the subsection + + Control Names. + + + + The index field holds the index number + of this control. If there are several different controls with + the same name, they can be distinguished by the index + number. This is the case when + several codecs exist on the card. If the index is zero, you can + omit the definition above. + + + + The access field contains the access + type of this control. Give the combination of bit masks, + SNDRV_CTL_ELEM_ACCESS_XXX, there. + The detailed will be explained in the subsection + + Access Flags. + + + + The private_values field contains + an arbitrary long integer value for this record. When using + generic info, + get and + put callbacks, you can pass a value + through this field. If several small numbers are necessary, you can + combine them in bitwise. Or, it's possible to give a pointer + (casted to unsigned long) of some record to this field, too. + + + + The other three are + + callback functions. + +
+ +
+ Control Names + + There are some standards for defining the control names. A + control is usually defined from the three parts as + SOURCE DIRECTION FUNCTION. + + + + The first, SOURCE, specifies the source + of the control, and is a string such as Master, + PCM, CD or + Line. There are many pre-defined sources. + + + + The second, DIRECTION, is one of the + following strings according to the direction of the control: + Playback, Capture, Bypass + Playback and Bypass Capture. Or, it can + be omitted, meaning both playback and capture directions. + + + + The third, FUNCTION, is one of the + following strings according to the function of the control: + Switch, Volume and + Route. + + + + The example of control names are, thus, Master Capture + Switch or PCM Playback Volume. + + + + There are some exceptions: + + +
+ Global capture and playback + + Capture Source, Capture Switch + and Capture Volume are used for the global + capture (input) source, switch and volume. Similarly, + Playback Switch and Playback + Volume are used for the global output gain switch and + volume. + +
+ +
+ Tone-controls + + tone-control switch and volumes are specified like + Tone Control - XXX, e.g. Tone Control - + Switch, Tone Control - Bass, + Tone Control - Center. + +
+ +
+ 3D controls + + 3D-control switches and volumes are specified like 3D + Control - XXX, e.g. 3D Control - + Switch, 3D Control - Center, 3D + Control - Space. + +
+ +
+ Mic boost + + Mic-boost switch is set as Mic Boost or + Mic Boost (6dB). + + + + More precise information can be found in + alsa-kernel/Documentation/ControlNames.txt. + +
+
+ +
+ Access Flags + + + The access flag is the bit-flags which specifies the access type + of the given control. The default access type is + SNDRV_CTL_ELEM_ACCESS_READWRITE, + which means both read and write are allowed to this control. + When the access flag is omitted (i.e. = 0), it is + regarded as READWRITE access as default. + + + + When the control is read-only, pass + SNDRV_CTL_ELEM_ACCESS_READ instead. + In this case, you don't have to define + put callback. + Similarly, when the control is write-only (although it's a rare + case), you can use WRITE flag instead, and + you don't need get callback. + + + + If the control value changes frequently (e.g. the VU meter), + VOLATILE flag should be given. This means + that the control may be changed without + + notification. Applications should poll such + a control constantly. + + + + When the control is inactive, set + INACTIVE flag, too. + There are LOCK and + OWNER flags for changing the write + permissions. + + +
+ +
+ Callbacks + +
+ info callback + + The info callback is used to get + the detailed information of this control. This must store the + values of the given snd_ctl_elem_info_t + object. For example, for a boolean control with a single + element will be: + + + Example of info callback + +type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; + } +]]> + + + + + + The type field specifies the type + of the control. There are BOOLEAN, + INTEGER, ENUMERATED, + BYTES, IEC958 and + INTEGER64. The + count field specifies the + number of elements in this control. For example, a stereo + volume would have count = 2. The + value field is a union, and + the values stored are depending on the type. The boolean and + integer are identical. + + + + The enumerated type is a bit different from others. You'll + need to set the string for the currently given item index. + + + +value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); +]]> + + + +
+ +
+ get callback + + + This callback is used to read the current value of the + control and to return to the user-space. + + + + For example, + + + Example of get callback + +value.integer.value[0] = get_some_value(chip); + return 0; + } +]]> + + + + + + Here, the chip instance is retrieved via + snd_kcontrol_chip() macro. This macro + converts from kcontrol->private_data to the type defined by + chip_t. The + kcontrol->private_data field is + given as the argument of snd_ctl_new() + (see the later subsection + Constructor). + + + + The value field is depending on + the type of control as well as on info callback. For example, + the sb driver uses this field to store the register offset, + the bit-shift and the bit-mask. The + private_value is set like + + + + + + and is retrieved in callbacks like + + +private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0xff; + int mask = (kcontrol->private_value >> 24) & 0xff; + .... + } +]]> + + + + + + In get callback, you have to fill all the elements if the + control has more than one elements, + i.e. count > 1. + In the example above, we filled only one element + (value.integer.value[0]) since it's + assumed as count = 1. + +
+ +
+ put callback + + + This callback is used to write a value from the user-space. + + + + For example, + + + Example of put callback + +current_value != + ucontrol->value.integer.value[0]) { + change_current_value(chip, + ucontrol->value.integer.value[0]); + changed = 1; + } + return changed; + } +]]> + + + + As seen above, you have to return 1 if the value is + changed. If the value is not changed, return 0 instead. + If any fatal error happens, return a negative error code as + usual. + + + + Like get callback, + when the control has more than one elements, + all elemehts must be evaluated in this callback, too. + +
+ +
+ Callbacks are not atomic + + All these three callbacks are basically not atomic. + +
+
+ +
+ Constructor + + When everything is ready, finally we can create a new + control. For creating a control, there are two functions to be + called, snd_ctl_new1() and + snd_ctl_add(). + + + + In the simplest way, you can do like this: + + + + + + + + where my_control is the + snd_kcontrol_new_t object defined above, and chip + is the object pointer to be passed to + kcontrol->private_data + which can be referred in callbacks. + + + + snd_ctl_new1() allocates a new + snd_kcontrol_t instance (that's why the definition + of my_control can be with + __devinitdata + prefix), and snd_ctl_add assigns the given + control component to the card. + +
+ +
+ Change Notification + + If you need to change and update a control in the interrupt + routine, you can call snd_ctl_notify(). For + example, + + + + + + + + This function takes the card pointer, the event-mask, and the + control id pointer for the notification. The event-mask + specifies the types of notification, for example, in the above + example, the change of control values is notified. + The id pointer is the pointer of snd_ctl_elem_id_t + to be notified. + You can find some examples in es1938.c or + es1968.c for hardware volume interrupts. + +
+ +
+ + + + + + + API for AC97 Codec + +
+ General + + The ALSA AC97 codec layer is a well-defined one, and you don't + have to write many codes to control it. Only low-level control + routines are necessary. The AC97 codec API is defined in + <sound/ac97_codec.h>. + +
+ +
+ Full Code Example + + + Example of AC97 Interface + +private_data, return 0); + .... + // read a register value here from the codec + return the_register_value; + } + + static void snd_mychip_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) + { + mychip_t *chip = snd_magic_cast(mychip_t, + ac97->private_data, return 0); + .... + // write the given register value to the codec + } + + static int snd_mychip_ac97(mychip_t *chip) + { + ac97_t ac97; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_mychip_ac97_write; + ac97.read = snd_mychip_ac97_read; + ac97.private_data = chip; + return snd_ac97_mixer(card, &ac97, &chip->ac97); + } + +]]> + + + +
+ +
+ Constructor + + For creating an ac97 instance, call + snd_ac97_mixer() with an ac97_t + record, in which the callbacks and the private_data is set. + + + +ac97); +]]> + + + + where chip->ac97 is the pointer of a newly created + ac97_t instance. + This instance is not necessarily stored in the chip + record. When you need to change the register values from the + driver, or need the suspend/resume of ac97 codecs, keep this + pointer to pass to the corresponding functions. + +
+ +
+ Callbacks + + The standard callbacks are read and + write. Obviously they + correspond to the functions for read and write accesses to the + hardware low-level codes. + + + + The read callback returns the + register value specified in the argument. + + + +private_data, return 0); + .... + return the_register_value; + } +]]> + + + + Here, the chip can be cast from ac97->private_data. + + + + Meanwhile, the write callback is + used to set the register value. + + + + + + + + + + Note that these callbacks are atomic unlike the callbacks of + control API since the ac97 middle-layer routine takes a + spinlock before calling these callbacks. Therefore you cannot + use mutex or schedule in them. However, these are never called + from interrupts unless you do by yourself. + Thus it's not necessarily protected with + spin_lock_irqsave() usually. + + + + There are also other callbacks: + reset, + wait and + init. + + + + The reset callback is used to reset + the codec. If the chip requires a special way of reset, you can + define this callback. + This callback is atomic, since it can be called in the suspend + phase, too. + + + + The wait callback is used for a + certain wait at the standard initialization of the codec. If the + chip requires the extra wait-time, define this callback. + This callback is non-atomic. + + + + The init callback is used for + additional initialization of the codec. This callback is called + after the reset phase, and should be atomic, since it's called + from the resume handler, too. + +
+ +
+ Updating Registers in The Driver + + If you need to access to the codec from the driver, you can + call the following functions: + snd_ac97_write(), + snd_ac97_read(), + snd_ac97_update() and + snd_ac97_update_bits(). + + + + Both snd_ac97_write() and + snd_ac97_update() functions are used to + set a value to the given register + (AC97_XXX). The different between them is + that snd_ac97_update() doesn't write a + value if the given value has been already set, while + snd_ac97_write() always rewrites the + value. + + + + + + + + + + snd_ac97_read() is used to read the value + of the given register. For example, + + + + + + + + + + snd_ac97_update_bits() is used to update + some bits of the given register. + + + + + + + + + + Also, there is a function to change the sample rate (of a + certain register such as + AC97_PCM_FRONT_DAC_RATE) when VRA is + supported by the codec: + snd_ac97_set_rate(). + + + + + + + + + + The following registers are available for setting the rate: + AC97_PCM_MIC_ADC_RATE, + AC97_PCM_FRONT_DAC_RATE, + AC97_PCM_LR_ADC_RATE, + AC97_SPDIF. When the + AC97_SPDIF is specified, the register is + not really changed but the corresponding IEC958 status bits will + be updated. + +
+ +
+ Clock Adjustment + + On some chip, the clock of the codec isn't 48000 but using a + PCI clock (to save a quartz!). In this case, change the field + ac97->clock to the corresponding + value. For example, intel8x0 + and es1968 drivers have the auto-measurement function of the + clock. + +
+ +
+ Proc Files + + The ALSA AC97 interface will create a proc file such as + /proc/asound/card0/ac97#0 and + ac97#0regs. You can refer to these files to + see the current status and registers of the codec. + +
+ +
+ Multiple Codecs + + When there are several codecs on the same card, you need to + call snd_ac97_new() multiple times with + ac97.num=1 or greater. The num field + specifies the codec + number. + + + + If you have set up multiple codecs, you need to either write + different callbacks for each codec or check + ac97->num in the + callback routines. + +
+ +
+ + + + + + + MIDI (MPU401-UART) Interface + +
+ General + + Many soundcards have built-in MIDI (MPU401-UART) + interfaces. When the soundcard supports the standard MPU401-UART + interface, most likely you can use the ALSA MPU401-UART API. The + MPU401-UART API is defined in + <sound/mpu401.h>. + + + + Some soundchips have similar but a little bit different + implementation of mpu401 stuff. For example, emu10k1 has its own + mpu401 routines. + + + + In this document, I won't explain the rawmidi interface API, + which is the basis of MPU401-UART implementation. + + + + For details, please check the source, + core/rawmidi.c, and examples such as + drivers/mpu401/mpu401_uart.c or + usb/usbmidi.c. + +
+ +
+ Constructor + + For creating a rawmidi object, call + snd_mpu401_uart_new(). + + + + + + + + + + The first argument is the card pointer, and the second is the + index of this component. You can create up to 8 rawmidi + devices. + + + + The third argument is the type of the hardware, + MPU401_HW_XXX. If it's not a special one, + you can use MPU401_HW_MPU401. + + + + The 4th argument is the i/o port address. Many + backward-compatible MPU401 has an i/o port such as 0x330. Or, it + might be a part of its own PCI i/o region. It depends on the + chip design. + + + + When the i/o port address above is a part of the PCI i/o + region, the MPU401 i/o port might have been already allocated + (reserved) by the driver itself. In such a case, pass non-zero + to the 5th argument + (integrated). Otherwise, pass 0 to it, + and + the mpu401-uart layer will allocate the i/o ports by itself. + + + + Usually, the port address corresponds to the command port and + port + 1 corresponds to the data port. If not, you may change + the cport field of + mpu401_t manually + afterward. However, mpu401_t pointer is not + returned explicitly by + snd_mpu401_uart_new(). You need to cast + rmidi->private_data to + mpu401_t explicitly, + + + +private_data, ); +]]> + + + + and reset the cport as you like: + + + +cport = my_own_control_port; +]]> + + + + + + The 6th argument specifies the irq number for UART. If the irq + is already allocated, pass 0 to the 7th argument + (irq_flags). Otherwise, pass the flags + for irq allocation + (SA_XXX bits) to it, and the irq will be + reserved by the mpu401-uart layer. If the card doesn't generates + UART interrupts, pass -1 as the irq number. Then a timer + interrupt will be invoked for polling. + +
+ +
+ Interrupt Handler + + When the interrupt is allocated in + snd_mpu401_uart_new(), the private + interrupt handler is used, hence you don't have to do nothing + else than creating the mpu401 stuff. Otherwise, you have to call + snd_mpu401_uart_interrupt() explicitly when + a UART interrupt is invoked and checked in your own interrupt + handler. + + + + In this case, you need to pass the private_data of the + returned rawmidi object from + snd_mpu401_uart_new() as the second + argument of snd_mpu401_uart_interrupt(). + + + +private_data, regs); +]]> + + + +
+ +
+ + + + + + + Miscellaneous Devices + +
+ FM OPL3 + + The FM OPL3 is still used on many chips (mainly for backward + compatibility). ALSA has a nice OPL3 FM control layer, too. The + OPL3 API is defined in + <sound/opl3.h>. + + + + FM registers can be directly accessed through direct-FM API, + defined in <sound/asound_fm.h>. In + ALSA native mode, FM registers are accessed through + Hardware-Dependant Device direct-FM extension API, whereas in + OSS compatible mode, FM registers can be accessed with OSS + direct-FM compatible API on /dev/dmfmX device. + + + + For creating the OPL3 component, you have two functions to + call. The first one is a constructor for opl3_t + instance. + + + + + + + + + + The first argument is the card pointer, the second one is the + left port address, and the third is the right port address. In + most cases, the right port is placed at the left port + 2. + + + + The fourth argument is the hardware type. + + + + When the left and right ports have been already allocated by + the card driver, pass non-zero to the fifth argument + (integrated). Otherwise, opl3 module will + allocate the specified ports by itself. + + + + If this function returns successfully with 0, then create a + hwdep device for this opl3. + + + + + + + + + + The first argument is the opl3_t instance you + created, and the second is the index number, usually 0. + + + + The third argument is the index-offset for the sequencer + client assigned to the OPL3 port. When there is an MPU401-UART, + give 1 for here (UART always takes 0). + +
+ +
+ Hardware-Dependent Devices + + Some chips need the access from the user-space for special + controls or for loading the micro code. In such a case, you can + create a hwdep (hardware-dependent) device. The hwdep API is + defined in <sound/hwdep.h>. You can + find examples in opl3 driver or + isa/sb/sb16_csp.c. + + + + Creation of the hwdep instance is done via + snd_hwdep_new(). + + + + + + + + where the third argument is the index number. + + + + You can then pass any pointer value to the + private_data. Again, it should be a + magic-allocated record, so that the cast can be checked more + safely. If you assign a private data, you should define the + destructor, too. The destructor function is set to + private_free field. + + + +private_data = p; + hw->private_free = mydata_free; +]]> + + + + and the implementation of destructor would be: + + + +private_data, return); + snd_magic_kfree(p); + } +]]> + + + + + + The arbitrary file operations can be defined for this + instance. The file operators are defined in + ops table. For example, assume that + this chip needs an ioctl. + + + +ops.open = mydata_open; + hw->ops.ioctl = mydata_ioctl; + hw->ops.release = mydata_release; +]]> + + + + And implement the callback functions as you like. + +
+ +
+ IEC958 (S/PDIF) + + Usually the controls for IEC958 devices are implemented via + control interface. There is a macro to compose a name string for + IEC958 controls, SNDRV_CTL_NAME_IEC958() + defined in <include/asound.h>. + + + + There are some standard controls for IEC958 status bits. These + controls use the type SNDRV_CTL_ELEM_TYPE_IEC958, + and the size of element is fixed as 4 bytes array + (value.iec958.status[x]). For info + callback, you don't specify + the value field for this type (the count field must be set, + though). + + + + IEC958 Playback Con Mask is used to return the + bit-mask for the IEC958 status bits of consumer mode. Similarly, + IEC958 Playback Pro Mask returns the bitmask for + professional mode. They are read-only controls, and are defined + as MIXER controls (iface = + SNDRV_CTL_ELEM_IFACE_MIXER). + + + + Meanwhile, IEC958 Playback Default control is + defined for getting and setting the current default IEC958 + bits. Note that this one is usually defined as a PCM control + (iface = SNDRV_CTL_ELEM_IFACE_PCM), + although in some places it's defined as a MIXER control. + + + + In addition, you can define the control switches to + enable/disable or to set the raw bit mode. The implementation + will depend on the chip, but the control should be named as + IEC958 xxx, preferably using + SNDRV_CTL_NAME_IEC958() macro. + + + + You can find several cases, for example, + pci/emu10k1, + pci/ice1712, or + pci/cmipci.c. + +
+ +
+ + + + + + + Buffer and Memory Management + +
+ Buffer Types + + ALSA provides several different buffer allocation functions + depending on the bus and the architecture. All these have a + consistent API. The allocation of physically-contiguous pages is + done via + snd_malloc_xxx_pages() function, where xxx + is the bus type. + + + + The allocation of pages with fallback is + snd_malloc_xxx_pages_fallback(). This + function tries to allocate the specified pages but if the pages + are not available, it tries to reduce the page sizes until the + enough space is found. + + + + For releasing the space, call + snd_free_xxx_pages() function. + + + + Usually, ALSA drivers try to allocate and reserve + a large contiguous physical space + at the time the module is loaded for the later use. + This is called pre-allocation. + As already written, you can call the following function at the + construction of pcm instance (in the case of PCI bus). + + + + + + + + where size is the byte size to be + pre-allocated and the max is the maximal + size to be changed via prealloc proc file. + The allocator will try to get as the large area as possible + within the given size. + There are different versions of pre-allocator for different + buses. + + + + Once when the buffer is pre-allocated, you can use the + allocator in the hw_params callback + + + + + + + + Note that you have to pre-allocate to use this function + (i.e. you cannot use this function for + + a scatter-gather buffer). + +
+ +
+ External Hardware Buffers + + Some chips have their own hardware buffers and the DMA + transfer from the host memory is not available. In such a case, + you need to either 1) copy/set the audio data directly to the + external hardware buffer, or 2) make an intermediate buffer and + copy/set the data from it to the external hardware buffer in + interrupts (or in tasklets, preferably). + + + + The first case works fine if the external hardware buffer is enough + large. This method doesn't need any extra buffers and thus is + more effective. You need to define the + copy and + silence callbacks for + the data transfer. However, there is a drawback: it cannot + be mmapped. The examples are GUS's GF1 PCM or emu8000's + wavetable PCM. + + + + The second case allows the mmap of the buffer, although you have + to handle an interrupt or a tasklet for transferring the data + from the intermediate buffer to the hardware buffer. You can find an + example in vxpocket driver. + + + + Another case is that the chip uses a PCI memory-map + region for the buffer instead of the host memory. In this case, + mmap is available only on certain architectures like intel. In + non-mmap mode, the data cannot be transferred as the normal + way. Thus you need to define copy and + silence callbacks as well + as in the cases above. The examples are found in + rme32.c and rme96.c. + + + + The implementation of copy and + silence callbacks depends upon + whether the hardware supports interleaved or non-interleaved + samples. The copy callback is + defined like below, a bit + differently depending whether the direction is playback or + capture: + + + + + + + + + + In the case of interleaved samples, the second argument + (channel) is not used. The third argument + (pos) points the + current position offset in frames. + + + + The meaning of the fourth argument is different between + playback and capture. For playback, it holds the source data + pointer, and for capture, it's the destination data pointer. + + + + The last argument is the number of frames to be copied. + + + + What you have to do in this callback is again different + between playback and capture directions. In the case of + playback, you do: copy the given amount of data + (count) at the specified pointer + (src) to the specified offset + (pos) on the hardware buffer. When + coded like memcpy-like way, the copy would be like: + + + + + + + + + + For the capture direction, you do: copy the given amount of + data (count) at the specified offset + (pos) on the hardware buffer to the + specified pointer (dst). + + + + + + + + Note that both of the position and the data amount are given + in frames. + + + + In the case of non-interleaved samples, the implementation + will be a bit more complicated. + + + + You need to check the channel argument, and if it's -1, copy + the whole channels. Otherwise, you have to copy only the + specified channel. Please check + isa/gus/gus_pcm.c as an example. + + + + The silence callback is also + implemented in a similar way. + + + + + + + + + + The meanings of arguments are identical with the + copy + callback, although there is no src/dst + argument. In the case of interleaved samples, the channel + argument has no meaning, as well as on + copy callback. + + + + The role of silence callback is to + set the given amount + (count) of silence data at the + specified offset (pos) on the hardware + buffer. Suppose that the data format is signed (that is, the + silent-data is 0), and the implementation using a memset-like + function would be like: + + + + + + + + + + In the case of non-interleaved samples, again, the + implementation becomes a bit more complicated. See, for example, + isa/gus/gus_pcm.c. + +
+ +
+ Non-Contiguous Buffers + + If your hardware supports the page table like emu10k1 or the + buffer descriptors like via82xx, you can use the scatter-gather + (SG) DMA. ALSA provides an interface for handling SG-buffers. + The API is provided in <sound/pcm_sgbuf.h>. + + + + For creating the SG-buffer handler, call + snd_pcm_sgbuf_init() in the + open callback + of a pcm substream (or in the constructor of the pcm). + You need to pass the + pci_dev struct pointer of the chip + and the default table size (which can be changed + dynamically). The snd_sg_buf_t instance is created as + substream->dma_private. You can cast + the pointer like: + + + +dma_private, return -EINVAL); +]]> + + + + + + Then call snd_pcm_sgbuf_alloc() instead + of normal snd_pcm_lib_malloc_pages() in + hw_params callback. The SG-handler + will allocate single pages + and build the table on sgbuf->table. The pointer and the + physical address of each page is stored in this table. You can + get the physical address at a certain offset via + snd_pcm_sgbuf_get_addr(). + + + + When a SG-handler is used, you need to set + snd_pcm_sgbuf_ops_copy_playback and + snd_pcm_ops_silence as the + copy and the + silence callbacks, respectively. + All the jobs described in the previous section will be done + by these helper functions. + Also, the page callback must be set as + snd_pcm_sgbuf_ops_page. + + + + For releasing the data, call + snd_pcm_sgbuf_free() in the + hw_free callback, and call + snd_pcm_sgbuf_delete() in the + close callback (or in the destructor, + if snd_pcm_sgbuf_init() was called in the + constructor). + + + + Note that you must not do pre-allocation if a SG-handler is used. + They conflict with each other. + +
+ +
+ Vmalloc'ed Buffers + + It's possible to use a buffer allocated via + vmalloc, for example, for an intermediate + buffer. Since the allocated pages are not contiguous, you need + to set the page callback to obtain + the physical address at every offset. + + + + The implementation of page callback + would be like this: + + + + + #include + + /* get the physical page pointer on the given offset */ + static struct page *mychip_page(snd_pcm_substream_t *subs, + unsigned long offset) + { + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long lpage; + void *pageptr = subs->runtime->dma_area + offset; + struct page *page = NOPAGE_SIGBUS; + + lpage = VMALLOC_VMADDR(pageptr); + spin_lock(&init_mm.page_table_lock); + pgd = pgd_offset(&init_mm, lpage); + pmd = pmd_offset(pgd, lpage); + pte = pte_offset(pmd, lpage); + page = pte_page(*pte); + spin_unlock(&init_mm.page_table_lock); + + return page; + } +]]> + + + + + + You don't need copy and + silence callbacks in this case, + because the copy/set operations are available on the virtual + address. + +
+ +
+ + + + + + + Proc Interface + + ALSA provides an easy interface for procfs. The proc files are + very useful for debugging. I recommend you set up proc files if + you write a driver and want to get a running status or register + dumps. The API is found in + <sound/info.h>. + + + + For creating a proc file, call + snd_card_proc_new(). + + + + + + + + where the second argument specifies the proc-file name to be + created. The above example will create a file + my-file under the card directory, + e.g. /proc/asound/card0/my-file. + + + + Like other components, the proc entry created via + snd_card_proc_new() will be registered and + released automatically in the card registration and release + functions. + + + + When the creation is successful, the function stores a new + instance at the pointer given in the third argument. + It is initialized as a text proc file for read only. For using + this proc file as a read-only text file as it is, set the read + callback with a private data via + snd_info_set_text_ops(). + + + + + + + + where the second argument (chip) is the + private data to be used in the callbacks and the third + (my_proc_read) is the callback function, which + is defined like + + + + + + + + + + + In the read callback, use snd_iprintf() for + output strings, which works just like normal + printf(). For example, + + + +private_data, return); + + snd_iprintf(buffer, "This is my chip!\n"); + snd_iprintf(buffer, "Port = %ld\n", chip->port); + } +]]> + + + + + + The file permission can be changed afterwards. As default, it's + set as read only for all users. If you want to add the write + permission to the user (root as default), set like below: + + + +mode = S_IFREG | S_IRUGO | S_IWUSR; +]]> + + + + and set the write buffer size and the callback + + + +c.text.write_size = 256; + entry->c.text.write = my_proc_write; +]]> + + + + + + The buffer size for read is set to 1024 implicitly by + snd_info_set_text_ops(). It should suffice + in most cases (the size will be aligned to + PAGE_SIZE anyway), but if you need to handle + very large text files, you can set it explicitly, too. + + + +c.text.read_size = 65536; +]]> + + + + + + For the write callback, you can use + snd_info_get_line() to get a text line, and + snd_info_get_str() to retrieve a string from + the line. Some examples are found in + core/oss/mixer_oss.c, core/oss/and + pcm_oss.c. + + + + For a raw-data proc-file, set the attributes like the following: + + + +content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &my_file_io_ops; + entry->size = 4096; + entry->mode = S_IFREG | S_IRUGO; +]]> + + + + + + The callback is much more complicated than the text-file + version. You need to use a low-level i/o functions such as + copy_from/to_user() to transfer the + data. Also, you have to keep tracking the file position, too. + + + +f_pos + size > local_max_size) + size = local_max_size - file->f_pos; + if (copy_to_user(buf, local_data + file->f_pos, size)) + return -EFAULT; + file->f_pos += size; + return size; + } +]]> + + + + + + + + + + + + Power Management + + If the chip is supposed to work with with suspend/resume + functions, you need to add the power-management codes to the + driver. The additional codes for the power-management should be + ifdef'ed with + CONFIG_PM. + + + + Basic jobs of suspend/resume are done in + suspend and + resume callbacks of + pci_driver struct. Unfortunately, the + API of these callbacks was changed at the middle time of Linux + 2.4.x, if you want to keep the support for older kernels, you + have to write two different callbacks. The example below is the + skeleton callbacks which just call the real suspend and resume + functions. + + + + + + + + + + The scheme of the real suspend job is as following. + + + Check whether the power-state is already D3hot. If yes, skip the job. + Call snd_pcm_suspend_all() to suspend the running PCM streams. + Save the register values if necessary. + Stop the hardware if necessary. + Set the power-state as D3hot by calling snd_power_change_state(). + + + + + A typical code would be like: + + + +card; + // (1) + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + // (2) + snd_pcm_suspend_all(chip->pcm); + // (3) + snd_mychip_save_registers(chip); + // (4) + snd_mychip_stop_hardware(chip); + // (5) + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + } +]]> + + + + + + The scheme of the real resume job is as following. + + + Check whether the power-state is already D0. + If yes, skip the job. + Enable the pci device again by calling + pci_enable_device(). + Re-initialize the chip. + Restore the saved registers if necessary. + Resume the mixer, e.g. calling + snd_ac97_resume(). + Restart the hardware (if any). + Set the power-state as D0 by calling + snd_power_change_state(). + + + + + A typical code would be like: + + + +card; + // (1) + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + // (2) + pci_enable_device(chip->pci); + // (3) + snd_mychip_reinit_chip(chip); + // (4) + snd_mychip_restore_registers(chip); + // (5) + snd_ac97_resume(chip->ac97); + // (6) + snd_mychip_restart_chip(chip); + // (7) + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + } +]]> + + + + + + In addition to the callbacks above, you should define a callback + for the changes via the ALSA control interface. It's defined + like below: + + + +power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + mychip_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + mychip_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; + } +]]> + + + + + + OK, we have all callbacks now. Let's set up them now. In the + initialization of the card, add the following: + + + +set_power_state = snd_mychip_set_power_state; + card->power_state_private_data = chip; + #endif + .... + } +]]> + + + + + + If you need a space for saving the registers, you'll need to + allocate the buffer for it here, too, since you cannot call + kmalloc() with + GFP_KERNEL flag or + vmalloc() in the suspend callback. + The allocated buffer should be released in the corresponding + destructor. + + + + And next, set suspend/resume callbacks to the pci_driver, + + + + + + + + + + Last but not least: Please keep in mind that you cannot call + schedule() during the suspend and the resume + callbacks. If any delay is necessary, you have to use + mdelay() or udelay() + instead of schedule_timeout()! + Of course, semaphores cannot be used, too, which will invoke sleep + inside. + + + + + + + + + Module Parameters + + There are standard module options for ALSA. At least, each + module should have index, + id and enable + options. + + + + If the module supports multiple cards (usually up to + 8 = SNDRV_CARDS cards), they should be + arrays. The default initial values are defined already as + constants for ease of programming: + + + + + + + + + + If the module supports only a single card, they could be single + variables, instead. enable option is not + always necessary in this case, but it wouldn't be so bad to have a + dummy option for compatibility. + + + + The module parameters must be declared with the standard + MODULE_PARM() and + MODULE_PARM_DESC() macros. The ALSA provides + an additional macro, MODULE_PARM_SYNTAX(), + for describing its syntax. The strings will be written to + /lib/modules/XXX/modules.generic_string + file. + + + + For convenience, the typical string arguments given to + MODULE_PARM_SYNTAX() are defined in + <sound/initval.h>, such as + SNDRV_ID_DESC or + SNDRV_ENABLED. + + + + The typical coding would be like below: + + + + + + + + + + Also, don't forget to define the module description, classes, + license and devices. Especially, the recent modprobe requires to + define the module license as GPL, etc., otherwise the system is + shown as tainted. + + + + + + + + + + For building the driver into kernel, you should define the + setup() function in addition, too. + ALSA provides get_id() function to retrieve + a string argument from the kernel boot parameters. + + + += SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; + } + + __setup("snd-mychip=", alsa_card_mychip_setup); + + #endif /* ifndef MODULE */ +]]> + + + + + + + + + + + Useful Functions + +
+ <function>snd_printk()</function> and friends + + ALSA provides a verbose version of + printk() function. If a kernel config + CONFIG_SND_VERBOSE_PRINTK is set, this + function prints the given message together with the file name + and the line of the caller. The KERN_XXX + prefix is processed as + well as the original printk() does, so it's + recommended to add this prefix, e.g. + + + + + + + + + + There are also printk()'s for + debugging. snd_printd() can be used for + general debugging purposes. If + CONFIG_SND_DEBUG is set, this function is + compiled, and works just like + snd_printk(). If the ALSA is compiled + without the debugging flag, it's ignored. + + + + snd_printdd() is compiled in only when + CONFIG_SND_DEBUG_DETECT is set. Please note + that DEBUG_DETECT is not set as default + even if you configure the alsa-driver with + option. You need to give + explicitly option instead. + +
+ +
+ <function>snd_assert()</function> + + snd_assert() macro is similar with the + normal assert() macro. For example, + + + + + + + + + + The first argument is the expression to evaluate, and the + second argument is the action if it fails. When + CONFIG_SND_DEBUG, is set, it will show an + error message such as BUG? (xxx) (called from + yyy). When no debug flag is set, this is + ignored. + +
+ +
+ <function>snd_runtime_check()</function> + + This macro is quite similar with + snd_assert(). Unlike + snd_assert(), the expression is always + evaluated regardless of + CONFIG_SND_DEBUG. When + CONFIG_SND_DEBUG is set, the macro will + show a message like ERROR (xx) (called from + yyy). + +
+ +
+ <function>snd_BUG()</function> + + It calls snd_assert(0,) -- that is, just + prints the error message at the point. It's useful to show that + a fatal error happens there. + +
+
+ + + + + + + Acknowledgments + + I would like to thank Phil Kerr for his help for improvement and + corrections of this document. + + + Kevin Conder reformatted the original plain-text to the + DocBook format. + + + + +
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index fd6c403d0828..47e5e67e990e 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -196,6 +196,8 @@ #define AC97_CXR_SPDIF_AC3 0x2 /* specific - ALC */ +#define AC97_ALC650_SURR_DAC_VOL 0x64 +#define AC97_ALC650_LFE_DAC_VOL 0x66 #define AC97_ALC650_MULTICH 0x6a #define AC97_ALC650_CLOCK 0x7a @@ -235,8 +237,6 @@ struct _snd_ac97 { unsigned short (*read) (ac97_t *ac97, unsigned short reg); void (*wait) (ac97_t *ac97); void (*init) (ac97_t *ac97); - snd_info_entry_t *proc_entry; - snd_info_entry_t *proc_regs_entry; void *private_data; void (*private_free) (ac97_t *ac97); /* --- */ diff --git a/include/sound/ad1848.h b/include/sound/ad1848.h index 9ae8eb32db5f..18f1fec77e2d 100644 --- a/include/sound/ad1848.h +++ b/include/sound/ad1848.h @@ -162,6 +162,7 @@ int snd_ad1848_create(snd_card_t * card, ad1848_t ** chip); int snd_ad1848_pcm(ad1848_t * chip, int device, snd_pcm_t **rpcm); +const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction); int snd_ad1848_mixer(ad1848_t * chip); void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs); diff --git a/include/sound/ak4531_codec.h b/include/sound/ak4531_codec.h index bf8294d6722c..ad7b52bc2627 100644 --- a/include/sound/ak4531_codec.h +++ b/include/sound/ak4531_codec.h @@ -68,7 +68,6 @@ typedef struct _snd_ak4531 ak4531_t; struct _snd_ak4531 { void (*write) (ak4531_t *ak4531, unsigned short reg, unsigned short val); - snd_info_entry_t *proc_entry; void *private_data; void (*private_free) (ak4531_t *ak4531); /* --- */ diff --git a/include/sound/core.h b/include/sound/core.h index 9d705453eca1..68b3a692eff7 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -55,6 +55,7 @@ typedef enum { SNDRV_DEV_TIMER, SNDRV_DEV_SEQUENCER, SNDRV_DEV_HWDEP, + SNDRV_DEV_INFO, SNDRV_DEV_LOWLEVEL = (2*SNDRV_DEV_TYPE_RANGE_SIZE) } snd_device_type_t; @@ -281,6 +282,8 @@ void snd_free_pages(void *ptr, unsigned long size); void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr); void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size); void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr); +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr); +#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr) #endif #ifdef CONFIG_SBUS void *snd_malloc_sbus_pages(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr); diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h index dc849a634aec..f60ea070bacd 100644 --- a/include/sound/cs46xx.h +++ b/include/sound/cs46xx.h @@ -1673,7 +1673,6 @@ typedef struct { unsigned long remap_addr; unsigned long size; struct resource *resource; - void *proc_entry; } snd_cs46xx_region_t; struct _snd_cs46xx { @@ -1726,7 +1725,6 @@ struct _snd_cs46xx { spinlock_t reg_lock; unsigned int midcr; unsigned int uartm; - snd_info_entry_t *proc_entry; int amplifier; void (*amplifier_ctrl)(cs46xx_t *, int); diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 7d6b34144c0d..afdb277148c0 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -25,12 +25,11 @@ #ifdef __KERNEL__ -#include "pcm.h" -#include "pcm_sgbuf.h" -#include "rawmidi.h" -#include "hwdep.h" -#include "ac97_codec.h" -#include "util_mem.h" +#include +#include +#include +#include +#include #include #ifndef PCI_VENDOR_ID_CREATIVE @@ -993,13 +992,6 @@ struct _snd_emu10k1 { emu10k1_midi_t midi2; /* for audigy */ unsigned int efx_voices_mask[2]; - - snd_info_entry_t *proc_entry; - snd_info_entry_t *proc_entry_fx8010_gpr; - snd_info_entry_t *proc_entry_fx8010_tram_data; - snd_info_entry_t *proc_entry_fx8010_tram_addr; - snd_info_entry_t *proc_entry_fx8010_code; - snd_info_entry_t *proc_entry_fx8010_iblocks; }; int snd_emu10k1_create(snd_card_t * card, @@ -1045,7 +1037,7 @@ unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate); unsigned char snd_emu10k1_sum_vol_attn(unsigned int value); /* memory allocation */ -snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf); +snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream); int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk); snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size); int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk); @@ -1063,7 +1055,6 @@ int snd_emu10k1_audigy_midi(emu10k1_t * emu); /* proc interface */ int snd_emu10k1_proc_init(emu10k1_t * emu); -int snd_emu10k1_proc_done(emu10k1_t * emu); #endif /* __KERNEL__ */ diff --git a/include/sound/gus.h b/include/sound/gus.h index 2e84f2687588..a896bf9ff626 100644 --- a/include/sound/gus.h +++ b/include/sound/gus.h @@ -210,7 +210,6 @@ typedef struct _snd_gf1_mem { snd_gf1_bank_info_t banks_16[4]; snd_gf1_mem_block_t *first; snd_gf1_mem_block_t *last; - snd_info_entry_t *info_entry; struct semaphore memory_mutex; } snd_gf1_mem_t; @@ -332,8 +331,6 @@ struct _snd_gf1 { unsigned int rom_banks; /* GUS's ROM banks */ snd_gf1_mem_t mem_alloc; - snd_info_entry_t *ram_entries[4]; - snd_info_entry_t *rom_entries[4]; /* registers */ unsigned short reg_page; @@ -452,9 +449,6 @@ struct _snd_gus_card { int timer_dev; /* timer device */ struct _snd_gf1 gf1; /* gf1 specific variables */ -#ifdef CONFIG_SND_DEBUG - snd_info_entry_t *irq_entry; -#endif snd_pcm_t *pcm; snd_pcm_substream_t *pcm_cap_substream; unsigned int c_dma_size; @@ -601,7 +595,6 @@ int snd_gf1_mem_done(snd_gus_card_t * gus); /* gus_mem_proc.c */ int snd_gf1_mem_proc_init(snd_gus_card_t * gus); -int snd_gf1_mem_proc_done(snd_gus_card_t * gus); /* gus_dma.c */ @@ -676,7 +669,6 @@ int snd_gus_initialize(snd_gus_card_t * gus); void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs); #ifdef CONFIG_SND_DEBUG void snd_gus_irq_profile_init(snd_gus_card_t *gus); -void snd_gus_irq_profile_done(snd_gus_card_t *gus); #endif /* gus_uart.c */ diff --git a/include/sound/info.h b/include/sound/info.h index 6984c6e736dc..c9bb51aedb04 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -141,6 +141,19 @@ struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, void snd_remove_proc_entry(struct proc_dir_entry *parent, struct proc_dir_entry *de); +/* for card drivers */ +int snd_card_proc_new(snd_card_t *card, const char *name, snd_info_entry_t **entryp); + +inline static void snd_info_set_text_ops(snd_info_entry_t *entry, + void *private_data, + void (*read)(snd_info_entry_t *, snd_info_buffer_t *)) +{ + entry->private_data = private_data; + entry->c.text.read_size = 1024; + entry->c.text.read = read; +} + + #else #define snd_seq_root NULL @@ -169,6 +182,9 @@ static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mod static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, struct proc_dir_entry *de) { ; } +#define snd_card_proc_new(card,name,entryp) 0 /* always success */ +#define snd_info_set_text_ops(entry,private_data,read) /*NOP*/ + #endif /* diff --git a/include/sound/pcm.h b/include/sound/pcm.h index d97f7e751d9a..0bc5fef9456e 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -50,7 +50,7 @@ typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t; typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t; typedef struct sndrv_mask snd_mask_t; -#define _snd_pcm_substream_chip(substream) ((substream)->pcm->private_data) +#define _snd_pcm_substream_chip(substream) ((substream)->private_data) #define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO) #define _snd_pcm_chip(pcm) ((pcm)->private_data) #define snd_pcm_chip(pcm) snd_magic_cast1(chip_t, _snd_pcm_chip(pcm), return -ENXIO) @@ -120,10 +120,11 @@ typedef struct _snd_pcm_ops { #define SNDRV_PCM_TRIGGER_SUSPEND 5 #define SNDRV_PCM_TRIGGER_RESUME 6 -#define SNDRV_PCM_DMA_TYPE_CONTINUOUS 0 /* continuous no-DMA memory */ -#define SNDRV_PCM_DMA_TYPE_ISA 1 /* ISA continuous */ -#define SNDRV_PCM_DMA_TYPE_PCI 2 /* PCI continuous */ -#define SNDRV_PCM_DMA_TYPE_SBUS 3 /* SBUS continuous */ +#define SNDRV_PCM_DMA_TYPE_UNKNOWN 0 /* not defined */ +#define SNDRV_PCM_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ +#define SNDRV_PCM_DMA_TYPE_ISA 2 /* ISA continuous */ +#define SNDRV_PCM_DMA_TYPE_PCI 3 /* PCI continuous */ +#define SNDRV_PCM_DMA_TYPE_SBUS 4 /* SBUS continuous */ /* If you change this don't forget to change rates[] table in pcm_native.c */ #define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ @@ -363,6 +364,7 @@ struct _snd_pcm_runtime { struct _snd_pcm_substream { snd_pcm_t *pcm; snd_pcm_str_t *pstr; + void *private_data; /* copied from pcm->private_data */ int number; char name[32]; /* substream name */ int stream; /* stream (direction) */ diff --git a/include/sound/pcm_sgbuf.h b/include/sound/pcm_sgbuf.h index bc6474559c57..e5dda1d053af 100644 --- a/include/sound/pcm_sgbuf.h +++ b/include/sound/pcm_sgbuf.h @@ -63,5 +63,8 @@ int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count); struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); +#define _snd_pcm_substream_sgbuf(substream) ((substream)->dma_private) +#define snd_pcm_substream_sgbuf(substream) snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return -ENXIO) + #endif /* __SOUND_PCM_SGBUF_H */ diff --git a/include/sound/sb.h b/include/sound/sb.h index 53f243b7bf64..4b9b902e9ea7 100644 --- a/include/sound/sb.h +++ b/include/sound/sb.h @@ -305,6 +305,7 @@ int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi); /* sb16_init.c */ int snd_sb16dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction); int snd_sb16dsp_configure(sb_t *chip); /* sb16.c */ void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs); diff --git a/include/sound/sb16_csp.h b/include/sound/sb16_csp.h index dfdb71dc6a7d..eb8368b56b16 100644 --- a/include/sound/sb16_csp.h +++ b/include/sound/sb16_csp.h @@ -159,7 +159,6 @@ struct snd_sb_csp { snd_kcontrol_t *qsound_space; struct semaphore access_mutex; /* locking */ - snd_info_entry_t *proc; /* proc interface */ }; int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep); diff --git a/include/sound/sndmagic.h b/include/sound/sndmagic.h index 83495c4ce9e5..560f248b9cdf 100644 --- a/include/sound/sndmagic.h +++ b/include/sound/sndmagic.h @@ -27,10 +27,45 @@ void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags); void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags); -void snd_magic_kfree(void *ptr); -#define snd_magic_kcalloc(type, extra, flags) (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) -#define snd_magic_kmalloc(type, extra, flags) (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) +/** + * snd_magic_kmalloc - allocate a record with a magic-prefix + * @type: the type to allocate a record (like xxx_t) + * @extra: the extra size to allocate in bytes + * @flags: the allocation condition (GFP_XXX) + * + * Allocates a record of the given type with the extra space and + * returns its pointer. The allocated record has a secret magic-key + * to be checked via snd_magic_cast() for safe casts. + * + * The allocated pointer must be released via snd_magic_kfree(). + * + * The "struct xxx" style cannot be used as the type argument + * because the magic-key constant is generated from the type-name + * string. + */ +#define snd_magic_kmalloc(type, extra, flags) \ + (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) +/** + * snd_magic_kcalloc - allocate a record with a magic-prefix and initialize + * @type: the type to allocate a record (like xxx_t) + * @extra: the extra size to allocate in bytes + * @flags: the allocation condition (GFP_XXX) + * + * Works like snd_magic_kmalloc() but this clears the area with zero + * automatically. + */ +#define snd_magic_kcalloc(type, extra, flags) \ + (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) + +/** + * snd_magic_kfree - release the allocated area + * @ptr: the pointer allocated via snd_magic_kmalloc() or snd_magic_kcalloc() + * + * Releases the memory area allocated via snd_magic_kmalloc() or + * snd_magic_kcalloc() function. + */ +void snd_magic_kfree(void *ptr); static inline unsigned long _snd_magic_value(void *obj) { @@ -44,7 +79,19 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) #define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd) -#define snd_magic_cast(type, ptr, action...) (type *) ({\ +/** + * snd_magic_cast - check and cast the magic-allocated pointer + * @type: the type of record to cast + * @ptr: the magic-allocated pointer + * @action...: the action to do if failed + * + * This macro provides a safe cast for the given type, which was + * allocated via snd_magic_kmalloc() or snd_magic_kcallc(). + * If the pointer is invalid, i.e. the cast-type doesn't match, + * the action arguments are called with a debug message. + */ +#define snd_magic_cast(type, ptr, action...) \ + (type *) ({\ void *__ptr = ptr;\ unsigned long __magic = _snd_magic_value(__ptr);\ if (__magic != type##_magic) {\ @@ -64,6 +111,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) #define snd_pcm_sgbuf_t_magic 0xa15a0107 #define snd_info_private_data_t_magic 0xa15a0201 +#define snd_info_entry_t_magic 0xa15a0202 #define snd_ctl_file_t_magic 0xa15a0301 #define snd_kcontrol_t_magic 0xa15a0302 #define snd_rawmidi_t_magic 0xa15a0401 diff --git a/include/sound/trident.h b/include/sound/trident.h index bf4ed45f1a63..f4f4042f6e7f 100644 --- a/include/sound/trident.h +++ b/include/sound/trident.h @@ -431,7 +431,8 @@ struct _snd_trident { int ChanPCM; /* max number of PCM channels */ int ChanPCMcnt; /* actual number of PCM channels */ - int ac97_detect; /* 1 = AC97 in detection phase */ + unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */ + unsigned int in_suspend: 1; /* 1 during suspend/resume */ struct _snd_4dwave synth; /* synth specific variables */ @@ -452,7 +453,6 @@ struct _snd_trident { snd_trident_pcm_mixer_t pcm_mixer[32]; spinlock_t reg_lock; - snd_info_entry_t *proc_entry; struct snd_trident_gameport *gameport; }; @@ -479,7 +479,7 @@ void snd_trident_write_voice_regs(trident_t * trident, snd_trident_voice_t *voic void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max); /* TLB memory allocation */ -snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size); +snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, snd_pcm_substream_t *substream); int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk); snd_util_memblk_t *snd_trident_synth_alloc(trident_t *trident, unsigned int size); int snd_trident_synth_free(trident_t *trident, snd_util_memblk_t *blk); diff --git a/include/sound/version.h b/include/sound/version.h index a4c7d9a7cd3a..323f4e184ed5 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc6" -#define CONFIG_SND_DATE " (Thu Dec 26 11:57:42 2002 UTC)" +#define CONFIG_SND_DATE " (Wed Jan 08 17:04:59 2003 UTC)" diff --git a/sound/core/control.c b/sound/core/control.c index 31173c043e10..da7471dca9a4 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -169,6 +169,15 @@ void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id) read_unlock(&card->ctl_files_rwlock); } +/** + * snd_ctl_new - create a control instance from the template + * @control: the control template + * + * Allocates a new snd_kcontrol_t instance and copies the given template + * to the new instance. + * + * Returns the pointer of the new instance, or NULL on failure. + */ snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control) { snd_kcontrol_t *kctl; @@ -181,6 +190,17 @@ snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control) return kctl; } +/** + * snd_ctl_new1 - create a control instance from the template + * @ncontrol: the initialization record + * @private_data: the private data to set + * + * Allocates a new snd_kcontrol_t instance and initialize from the given + * template. When the access field of ncontrol is 0, it's assumed as + * READWRITE access. + * + * Returns the pointer of the newly generated instance, or NULL on failure. + */ snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) { snd_kcontrol_t kctl; @@ -203,6 +223,14 @@ snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) return snd_ctl_new(&kctl); } +/** + * snd_ctl_free_one - release the control instance + * @kcontrol: the control instance + * + * Releases the control instance created via snd_ctl_new() + * or snd_ctl_new1(). + * Don't call this after the control was added to the card. + */ void snd_ctl_free_one(snd_kcontrol_t * kcontrol) { if (kcontrol) { @@ -212,6 +240,16 @@ void snd_ctl_free_one(snd_kcontrol_t * kcontrol) } } +/** + * snd_ctl_add - add the control instance to the card + * @card: the card instance + * @kcontrol: the control instance to add + * + * Adds the control instance created via snd_ctl_new() or + * snd_ctl_new1() to the given card. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) { snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); @@ -227,6 +265,16 @@ int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) return 0; } +/** + * snd_ctl_remove - remove the control from the card and release it + * @card: the card instance + * @kcontrol: the control instance to remove + * + * Removes the control from the card and then releases the instance. + * You don't need to call snd_ctl_free_one(). + * + * Returns 0 if successful, or a negative error code on failure. + */ int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) { snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); @@ -239,6 +287,16 @@ int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) return 0; } +/** + * snd_ctl_remove_id - remove the control of the given id and release it + * @card: the card instance + * @id: the control id to remove + * + * Finds the control instance with the given id, removes it from the + * card list and releases it. + * + * Returns 0 if successful, or a negative error code on failure. + */ int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) { snd_kcontrol_t *kctl; @@ -251,6 +309,17 @@ int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) static snd_kcontrol_t *_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id); /* w/o lock */ +/** + * snd_ctl_rename_id - replace the id of a control on the card + * @card: the card instance + * @src_id: the old id + * @dst_id: the new id + * + * Finds the control with the old id from the card, and replaces the + * id with the new one. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id) { snd_kcontrol_t *kctl; @@ -306,7 +375,15 @@ static snd_kcontrol_t *_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) return NULL; } -/* exported: with read lock */ +/** + * snd_ctl_find_id - find the control instance with the given id + * @card: the card instance + * @id: the id to search + * + * Finds the control instance with the given id from the card. + * + * Returns the pointer of the instance if found, or NULL if not. + */ snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) { snd_kcontrol_t *kctl; @@ -316,7 +393,15 @@ snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) return kctl; } -/* exported: with read lock */ +/** + * snd_ctl_find_numid - find the control instance with the given number-id + * @card: the card instance + * @numid: the number-id to search + * + * Finds the control instance with the given number-id from the card. + * + * Returns the pointer of the instance if found, or NULL if not. + */ snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid) { snd_kcontrol_t *kctl; @@ -628,13 +713,15 @@ static int snd_ctl_ioctl(struct inode *inode, struct file *file, return -EFAULT; if (!capable(CAP_SYS_ADMIN)) return -EPERM; + err = -ENOPROTOOPT; #ifdef CONFIG_PM - if (card->set_power_state == NULL) - return -ENOPROTOOPT; - return card->set_power_state(card, err); -#else - return -ENOPROTOOPT; + if (card->set_power_state) { + snd_power_lock(card); + err = card->set_power_state(card, err); + snd_power_unlock(card); + } #endif + return err; case SNDRV_CTL_IOCTL_POWER_STATE: #ifdef CONFIG_PM return put_user(card->power_state, (int *)arg) ? -EFAULT : 0; @@ -727,6 +814,10 @@ static unsigned int snd_ctl_poll(struct file *file, poll_table * wait) return mask; } +/* + * register the device-specific control-ioctls. + * called from each device manager like pcm.c, hwdep.c, etc. + */ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) { snd_kctl_ioctl_t *pn; @@ -742,6 +833,9 @@ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) return 0; } +/* + * de-register the device-specific control-ioctls. + */ int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) { struct list_head *list; @@ -797,6 +891,10 @@ static snd_minor_t snd_ctl_reg = .f_ops = &snd_ctl_f_ops, }; +/* + * registration of the control device: + * called from init.c + */ int snd_ctl_register(snd_card_t *card) { int err, cardnum; @@ -812,6 +910,10 @@ int snd_ctl_register(snd_card_t *card) return 0; } +/* + * disconnection of the control device: + * called from init.c + */ int snd_ctl_disconnect(snd_card_t *card) { struct list_head *flist; @@ -827,6 +929,10 @@ int snd_ctl_disconnect(snd_card_t *card) return 0; } +/* + * de-registration of the control device: + * called from init.c + */ int snd_ctl_unregister(snd_card_t *card) { int err, cardnum; diff --git a/sound/core/device.c b/sound/core/device.c index cb23f8a3eb7d..2665fb1e4974 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -25,6 +25,22 @@ #include #include +/** + * snd_device_new - create an ALSA device component + * @card: the card instance + * @type: the device type, SNDRV_DEV_TYPE_XXX + * @device_data: the data pointer of this device + * @ops: the operator table + * + * Creates a new device component for the given data pointer. + * The device will be assigned to the card and managed together + * by the card. + * + * The data pointer plays a role as the identifier, too, so the + * pointer address must be unique and unchanged. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_device_new(snd_card_t *card, snd_device_type_t type, void *device_data, snd_device_ops_t *ops) { @@ -43,6 +59,18 @@ int snd_device_new(snd_card_t *card, snd_device_type_t type, return 0; } +/** + * snd_device_free - release the device from the card + * @card: the card instance + * @device_data: the data pointer to release + * + * Removes the device from the list on the card and invokes the + * callback, dev_unregister or dev_free, corresponding to the state. + * Then release the device. + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ int snd_device_free(snd_card_t *card, void *device_data) { struct list_head *list; @@ -73,6 +101,19 @@ int snd_device_free(snd_card_t *card, void *device_data) return -ENXIO; } +/** + * snd_device_free - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ int snd_device_disconnect(snd_card_t *card, void *device_data) { struct list_head *list; @@ -95,6 +136,19 @@ int snd_device_disconnect(snd_card_t *card, void *device_data) return -ENXIO; } +/** + * snd_device_register - register the device + * @card: the card instance + * @device_data: the data pointer to register + * + * Registers the device which was already created via + * snd_device_new(). Usually this is called from snd_card_register(), + * but it can be called later if any new devices are created after + * invokation of snd_card_register(). + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ int snd_device_register(snd_card_t *card, void *device_data) { struct list_head *list; @@ -118,6 +172,10 @@ int snd_device_register(snd_card_t *card, void *device_data) return -ENXIO; } +/* + * register all the devices on the card. + * called from init.c + */ int snd_device_register_all(snd_card_t *card) { struct list_head *list; @@ -136,6 +194,10 @@ int snd_device_register_all(snd_card_t *card) return 0; } +/* + * disconnect all the devices on the card. + * called from init.c + */ int snd_device_disconnect_all(snd_card_t *card) { snd_device_t *dev; @@ -151,6 +213,10 @@ int snd_device_disconnect_all(snd_card_t *card) return err; } +/* + * release all the devices on the card. + * called from init.c + */ int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd) { snd_device_t *dev; diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 36a217c48e2c..5826ea7d5419 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -256,6 +256,19 @@ static snd_minor_t snd_hwdep_reg = .f_ops = &snd_hwdep_f_ops, }; +/** + * snd_hwdep_new - create a new hwdep instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero-based) + * @rhwdep: the pointer to store the new hwdep instance + * + * Creates a new hwdep instance with the given index on the card. + * The callbacks (hwdep->ops) must be set on the returned instance + * after this call manually by the caller. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep) { snd_hwdep_t *hwdep; diff --git a/sound/core/info.c b/sound/core/info.c index b4e85fb69c21..b37093a9df78 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -83,10 +83,16 @@ typedef struct _snd_info_private_data { static int snd_info_version_init(void); static int snd_info_version_done(void); -/* +/** + * snd_iprintf - printf on the procfs buffer + * @buffer: the procfs buffer + * @fmt: the printf format + * + * Outputs the string on the procfs buffer just like printf(). + * + * Returns the size of output string. */ - int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { va_list args; @@ -591,6 +597,17 @@ struct inode_operations snd_info_card_link_inode_operations = .follow_link = snd_info_card_followlink, }; +/** + * snd_create_proc_entry - create a procfs entry + * @name: the name of the proc file + * @mode: the file permission bits, S_Ixxx + * @parent: the parent proc-directory entry + * + * Creates a new proc file entry with the given name and permission + * on the given directory. + * + * Returns the pointer of new instance or NULL on failure. + */ struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { @@ -682,6 +699,10 @@ int __exit snd_info_done(void) */ +/* + * create a card proc file + * called from init.c + */ int snd_info_card_create(snd_card_t * card) { char str[8]; @@ -701,6 +722,10 @@ int snd_info_card_create(snd_card_t * card) return 0; } +/* + * register the card proc file + * called from init.c + */ int snd_info_card_register(snd_card_t * card) { char *s; @@ -728,6 +753,10 @@ int snd_info_card_register(snd_card_t * card) return 0; } +/* + * de-register the card proc file + * called from init.c + */ int snd_info_card_free(snd_card_t * card) { void *data; @@ -747,10 +776,17 @@ int snd_info_card_free(snd_card_t * card) return 0; } -/* +/** + * snd_info_get_line - read one line from the procfs buffer + * @buffer: the procfs buffer + * @line: the buffer to store + * @len: the max. buffer size - 1 + * + * Reads one line from the buffer and stores the string. + * + * Returns zero if successful, or 1 if error or EOF. */ - int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) { int c = -1; @@ -781,6 +817,18 @@ int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) return 0; } +/** + * snd_info_get_line - parse a string token + * @dest: the buffer to store the string token + * @src: the original string + * @len: the max. length of token - 1 + * + * Parses the original string and copy a token to the given + * string buffer. + * + * Returns the updated pointer of the original string so that + * it can be used for the next call. + */ char *snd_info_get_str(char *dest, char *src, int len) { int c; @@ -805,15 +853,27 @@ char *snd_info_get_str(char *dest, char *src, int len) return src; } +/** + * snd_info_create_entry - create an info entry + * @name: the proc file name + * + * Creates an info entry with the given file name and initializes as + * the default state. + * + * Usually called from other functions such as + * snd_info_create_card_entry(). + * + * Returns the pointer of the new instance, or NULL on failure. + */ static snd_info_entry_t *snd_info_create_entry(const char *name) { snd_info_entry_t *entry; - entry = (snd_info_entry_t *) snd_kcalloc(sizeof(snd_info_entry_t), GFP_KERNEL); + entry = snd_magic_kcalloc(snd_info_entry_t, 0, GFP_KERNEL); if (entry == NULL) return NULL; entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); if (entry->name == NULL) { - kfree(entry); + snd_magic_kfree(entry); return NULL; } entry->mode = S_IFREG | S_IRUGO; @@ -822,6 +882,16 @@ static snd_info_entry_t *snd_info_create_entry(const char *name) return entry; } +/** + * snd_info_create_module_entry - create an info entry for the given module + * @module: the module pointer + * @name: the file name + * @parent: the parent directory + * + * Creates a new info entry and assigns it to the given module. + * + * Returns the pointer of the new instance, or NULL on failure. + */ snd_info_entry_t *snd_info_create_module_entry(struct module * module, const char *name, snd_info_entry_t *parent) @@ -834,6 +904,16 @@ snd_info_entry_t *snd_info_create_module_entry(struct module * module, return entry; } +/** + * snd_info_create_card_entry - create an info entry for the given card + * @card: the card instance + * @name: the file name + * @parent: the parent directory + * + * Creates a new info entry and assigns it to the given card. + * + * Returns the pointer of the new instance, or NULL on failure. + */ snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, const char *name, snd_info_entry_t * parent) @@ -847,6 +927,75 @@ snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, return entry; } +static int snd_info_dev_free_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + snd_info_free_entry(entry); + return 0; +} + +static int snd_info_dev_register_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + return snd_info_register(entry); +} + +static int snd_info_dev_unregister_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + return snd_info_unregister(entry); +} + +/** + * snd_card_proc_new - create an info entry for the given card + * @card: the card instance + * @name: the file name + * @entryp: the pointer to store the new info entry + * + * Creates a new info entry and assigns it to the given card. + * Unlike snd_info_create_card_entry(), this function registers the + * info entry as an ALSA device component, so that it can be + * unregistered/released without explicit call. + * Also, you don't have to register this entry via snd_info_register(), + * since this will be registered by snd_card_register() automatically. + * + * The parent is assumed as card->proc_root. + * + * For releasing this entry, use snd_device_free() instead of + * snd_info_free_entry(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_card_proc_new(snd_card_t *card, const char *name, + snd_info_entry_t **entryp) +{ + static snd_device_ops_t ops = { + .dev_free = snd_info_dev_free_entry, + .dev_register = snd_info_dev_register_entry, + // .dev_disconnect = snd_info_dev_disconnect_entry, + .dev_unregister = snd_info_dev_unregister_entry + }; + snd_info_entry_t *entry; + int err; + + entry = snd_info_create_card_entry(card, name, card->proc_root); + if (! entry) + return -ENOMEM; + if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { + snd_info_free_entry(entry); + return err; + } + if (entryp) + *entryp = entry; + return 0; +} + +/** + * snd_info_free_entry - release the info entry + * @entry: the info entry + * + * Releases the info entry. Don't call this after registered. + */ void snd_info_free_entry(snd_info_entry_t * entry) { if (entry == NULL) @@ -855,7 +1004,7 @@ void snd_info_free_entry(snd_info_entry_t * entry) kfree((char *)entry->name); if (entry->private_free) entry->private_free(entry); - kfree(entry); + snd_magic_kfree(entry); } #ifdef LINUX_2_2 @@ -892,11 +1041,11 @@ static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_ } #endif /* LINUX_2_2 */ +/* + * create a procfs device file + */ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, unsigned int mode) { -#ifdef CONFIG_DEVFS_FS - char dname[32]; -#endif unsigned short _major = number >> 16; unsigned short minor = (unsigned short) number; snd_info_entry_t *entry; @@ -933,6 +1082,7 @@ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, up(&info_mutex); #ifdef CONFIG_DEVFS_FS if (strncmp(name, "controlC", 8)) { /* created in sound.c */ + char dname[32]; sprintf(dname, "snd/%s", name); devfs_register(NULL, dname, DEVFS_FL_DEFAULT, major, minor, mode, @@ -942,6 +1092,9 @@ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, return entry; } +/* + * release a procfs device file + */ void snd_info_free_device(snd_info_entry_t * entry) { snd_runtime_check(entry, return); @@ -953,6 +1106,14 @@ void snd_info_free_device(snd_info_entry_t * entry) snd_info_free_entry(entry); } +/** + * snd_info_register - register the info entry + * @entry: the info entry + * + * Registers the proc info entry. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_info_register(snd_info_entry_t * entry) { struct proc_dir_entry *root, *p = NULL; @@ -982,6 +1143,14 @@ int snd_info_register(snd_info_entry_t * entry) return 0; } +/** + * snd_info_unregister - de-register the info entry + * @entry: the info entry + * + * De-registers the info entry and releases the instance. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_info_unregister(snd_info_entry_t * entry) { struct proc_dir_entry *root; diff --git a/sound/core/init.c b/sound/core/init.c index 26aa3d323c12..30e217be051d 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -51,14 +51,16 @@ static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer } /** - * snd_card_new: create and initialize a soundcard structure + * snd_card_new - create and initialize a soundcard structure * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] * @xid: card identification (ASCII string) * @module: top level module for locking * @extra_size: allocate this extra size after the main soundcard structure * + * Creates and initializes a soundcard structure. + * * Returns kmallocated snd_card_t structure. Creates the ALSA control interface - * (which is blocked until #snd_card_register function is called). + * (which is blocked until snd_card_register function is called). */ snd_card_t *snd_card_new(int idx, const char *xid, struct module *module, int extra_size) @@ -136,10 +138,12 @@ static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) } /** - * snd_card_disconnect: disconnect all APIs from the file-operations (user space) + * snd_card_disconnect - disconnect all APIs from the file-operations (user space) * @card: soundcard structure * - * Returns - zero, otherwise a negative error code. + * Disconnects all APIs from the file-operations (user space). + * + * Returns zero, otherwise a negative error code. * * Note: The current implementation replaces all active file->f_op with special * dummy file operations (they do nothing except release). @@ -219,19 +223,18 @@ int snd_card_disconnect(snd_card_t * card) } /** - * snd_card_free: frees given soundcard structure + * snd_card_free - frees given soundcard structure * @card: soundcard structure * * This function releases the soundcard structure and the all assigned * devices automatically. That is, you don't have to release the devices * by yourself. * - * Returns - zero. Frees all associated devices and frees the control + * Returns zero. Frees all associated devices and frees the control * interface associated to given soundcard. */ int snd_card_free(snd_card_t * card) { - wait_queue_t wait; struct snd_shutdown_f_ops *s_f_ops; if (card == NULL) @@ -242,13 +245,7 @@ int snd_card_free(snd_card_t * card) write_unlock(&snd_card_rwlock); /* wait, until all devices are ready for the free operation */ - init_waitqueue_entry(&wait, current); - add_wait_queue(&card->shutdown_sleep, &wait); - while (card->files) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(30 * HZ); - } - remove_wait_queue(&card->shutdown_sleep, &wait); + wait_event(card->shutdown_sleep, card->files == NULL); #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) if (snd_mixer_oss_notify_callback) @@ -299,24 +296,22 @@ static void snd_card_free_thread(void * __card) module = NULL; } - wait_event(card->shutdown_sleep, card->files == NULL); - snd_card_free(card); module_put(module); } /** - * snd_card_free_in_thread: call snd_card_free() in thread + * snd_card_free_in_thread - call snd_card_free() in thread * @card: soundcard structure * - * This function schedules the call of #snd_card_free function in a + * This function schedules the call of snd_card_free() function in a * work queue. When all devices are released (non-busy), the work - * is woken up and calls #snd_card_free. + * is woken up and calls snd_card_free(). * * When a card can be disconnected at any time by hotplug service, * this function should be used in disconnect (or detach) callback - * instead of calling #snd_card_free directly. + * instead of calling snd_card_free() directly. * * Returns - zero otherwise a negative error code if the start of thread failed. */ @@ -392,7 +387,7 @@ static void choose_default_id(snd_card_t * card) } /** - * snd_card_register: register the soundcard + * snd_card_register - register the soundcard * @card: soundcard structure * * This function registers all the devices assigned to the soundcard. @@ -400,7 +395,7 @@ static void choose_default_id(snd_card_t * card) * external accesses. Thus, you should call this function at the end * of the initialization of the card. * - * Returns - zero otherwise a negative error code if the registrain failed. + * Returns zero otherwise a negative error code if the registrain failed. */ int snd_card_register(snd_card_t * card) { @@ -517,14 +512,14 @@ int __exit snd_card_info_done(void) } /** - * snd_component_add: add a component string + * snd_component_add - add a component string * @card: soundcard structure * @component: the component id string * * This function adds the component id string to the supported list. * The component can be referred from the alsa-lib. * - * Returns - zero otherwise a negative error code. + * Returns zero otherwise a negative error code. */ int snd_component_add(snd_card_t *card, const char *component) @@ -548,7 +543,7 @@ int snd_component_add(snd_card_t *card, const char *component) } /** - * snd_card_file_add: add the file to the file list of the card + * snd_card_file_add - add the file to the file list of the card * @card: soundcard structure * @file: file pointer * @@ -580,15 +575,15 @@ int snd_card_file_add(snd_card_t *card, struct file *file) } /** - * snd_card_file_remove: remove the file from the file list + * snd_card_file_remove - remove the file from the file list * @card: soundcard structure * @file: file pointer * * This function removes the file formerly added to the card via - * #snd_card_file_add function. + * snd_card_file_add() function. * If all files are removed and the release of the card is - * scheduled, it will wake up the the thread to call #snd_card_free - * (see #snd_card_free_in_thread function). + * scheduled, it will wake up the the thread to call snd_card_free() + * (see snd_card_free_in_thread() function). * * Returns zero or a negative error code. */ @@ -623,9 +618,11 @@ int snd_card_file_remove(snd_card_t *card, struct file *file) #ifdef CONFIG_PM /** - * snd_power_wait: wait until the power-state is changed. + * snd_power_wait - wait until the power-state is changed. * @card: soundcard structure * + * Waits until the power-state is changed. + * * Note: the power lock must be active before call. */ void snd_power_wait(snd_card_t *card) diff --git a/sound/core/isadma.c b/sound/core/isadma.c index 561c1111962f..48f355397df2 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -30,10 +30,15 @@ #include #include -/* +/** + * snd_dma_program - program an ISA DMA transfer + * @dma: the dma number + * @addr: the physical address of the buffer + * @size: the DMA transfer size + * @mode: the DMA transfer mode, DMA_MODE_XXX * + * Programs an ISA DMA transfer for the given buffer. */ - void snd_dma_program(unsigned long dma, unsigned long addr, unsigned int size, unsigned short mode) @@ -51,6 +56,12 @@ void snd_dma_program(unsigned long dma, release_dma_lock(flags); } +/** + * snd_dma_disable - stop the ISA DMA transfer + * @dma: the dma number + * + * Stops the ISA DMA transfer. + */ void snd_dma_disable(unsigned long dma) { unsigned long flags; @@ -61,6 +72,12 @@ void snd_dma_disable(unsigned long dma) release_dma_lock(flags); } +/** + * snd_dma_residue - return the residue count of the given DMA + * @dma: the dma number + * + * Returns the residue count of the given DMA transfer. + */ unsigned int snd_dma_residue(unsigned long dma) { unsigned long flags; diff --git a/sound/core/memory.c b/sound/core/memory.c index c12a28ab2ecd..5a45ec66893d 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -264,7 +264,15 @@ int __exit snd_memory_info_done(void) #endif /* CONFIG_SND_DEBUG_MEMORY */ - +/** + * snd_malloc_pages - allocate pages with the given size + * @size: the size to allocate in bytes + * @dma_flags: the allocation conditions, GFP_XXX + * + * Allocates the physically contiguous pages with the given size. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pages(unsigned long size, unsigned int dma_flags) { int pg; @@ -285,6 +293,19 @@ void *snd_malloc_pages(unsigned long size, unsigned int dma_flags) return res; } +/** + * snd_malloc_pages_fallback - allocate pages with the given size with fallback + * @size: the requested size to allocate in bytes + * @dma_flags: the allocation conditions, GFP_XXX + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size) { void *res; @@ -301,6 +322,13 @@ void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsi return NULL; } +/** + * snd_free_pages - release the pages + * @ptr: the buffer pointer to release + * @size: the allocated buffer size + * + * Releases the buffer allocated via snd_malloc_pages(). + */ void snd_free_pages(void *ptr, unsigned long size) { int pg; @@ -321,6 +349,16 @@ void snd_free_pages(void *ptr, unsigned long size) #if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) +/** + * snd_malloc_isa_pages - allocate pages for ISA bus with the given size + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * ISA bus. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr) { void *dma_area; @@ -329,6 +367,19 @@ void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr) return dma_area; } +/** + * snd_malloc_isa_pages_fallback - allocate pages with the given size with fallback for ISA bus + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for PCI bus. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_isa_pages_fallback(unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size) @@ -343,6 +394,17 @@ void *snd_malloc_isa_pages_fallback(unsigned long size, #ifdef CONFIG_PCI +/** + * snd_malloc_pci_pages - allocate pages for PCI bus with the given size + * @pci: the pci device pointer + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * PCI bus. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr) @@ -366,6 +428,20 @@ void *snd_malloc_pci_pages(struct pci_dev *pci, return res; } +/** + * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for PCI bus + * @pci: pci device pointer + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for PCI bus. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, @@ -384,6 +460,15 @@ void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, return NULL; } +/** + * snd_free_pci_pages - release the pages + * @pci: pci device pointer + * @size: the allocated buffer size + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_pages(). + */ void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, @@ -409,6 +494,17 @@ void snd_free_pci_pages(struct pci_dev *pci, #ifdef CONFIG_SBUS +/** + * snd_malloc_sbus_pages - allocate pages for SBUS with the given size + * @sdev: sbus device pointer + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * SBUS. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_sbus_pages(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr) @@ -432,6 +528,20 @@ void *snd_malloc_sbus_pages(struct sbus_dev *sdev, return res; } +/** + * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for SBUS + * @sdev: sbus device pointer + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for SBUS. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr, @@ -450,6 +560,15 @@ void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, return NULL; } +/** + * snd_free_sbus_pages - release the pages + * @sdev: sbus device pointer + * @size: the allocated buffer size + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_pages(). + */ void snd_free_sbus_pages(struct sbus_dev *sdev, unsigned long size, void *ptr, @@ -473,6 +592,15 @@ void snd_free_sbus_pages(struct sbus_dev *sdev, #endif /* CONFIG_SBUS */ +/** + * snd_kcalloc - memory allocation and zero-clear + * @size: the size to allocate in bytes + * @flags: allocation conditions, GFP_XXX + * + * Allocates a memory chunk via kmalloc() and initializes it to zero. + * + * Returns the pointer, or NULL if no enoguh memory. + */ void *snd_kcalloc(size_t size, int flags) { void *ptr; @@ -483,6 +611,15 @@ void *snd_kcalloc(size_t size, int flags) return ptr; } +/** + * snd_kmalloc_strdup - copy the string + * @string: the original string + * @flags: allocation conditions, GFP_XXX + * + * Allocates a memory chunk via kmalloc() and copies the string to it. + * + * Returns the pointer, or NULL if no enoguh memory. + */ char *snd_kmalloc_strdup(const char *string, int flags) { size_t len; @@ -497,6 +634,16 @@ char *snd_kmalloc_strdup(const char *string, int flags) return ptr; } +/** + * copy_to_user_fromio - copy data from mmio-space to user-space + * @dst: the destination pointer on user-space + * @src: the source pointer on mmio + * @count: the data size to copy in bytes + * + * Copies the data from mmio-space to user-space. + * + * Returns zero if successful, or non-zero on failure. + */ int copy_to_user_fromio(void *dst, unsigned long src, size_t count) { #if defined(__i386__) || defined(CONFIG_SPARC32) @@ -518,6 +665,16 @@ int copy_to_user_fromio(void *dst, unsigned long src, size_t count) #endif } +/** + * copy_from_user_toio - copy data from user-space to mmio-space + * @dst: the destination pointer on mmio-space + * @src: the source pointer on user-space + * @count: the data size to copy in bytes + * + * Copies the data from user-space to mmio-space. + * + * Returns zero if successful, or non-zero on failure. + */ int copy_from_user_toio(unsigned long dst, const void *src, size_t count) { #if defined(__i386__) || defined(CONFIG_SPARC32) @@ -538,3 +695,81 @@ int copy_from_user_toio(unsigned long dst, const void *src, size_t count) return 0; #endif } + + +#ifdef CONFIG_PCI + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(__i386__) +/* + * on ix86, we allocate a page with GFP_KERNEL to assure the + * allocation. the code is almost same with kernel/i386/pci-dma.c but + * it allocates only a single page and checks the validity of the + * page address with the given pci dma mask. + */ + +/** + * snd_malloc_pci_page - allocate a page in the valid pci dma mask + * @pci: pci device pointer + * @addrp: the pointer to store the physical address of the buffer + * + * Allocates a single page for the given PCI device and returns + * the virtual address and stores the physical address on addrp. + * + * This function cannot be called from interrupt handlers or + * within spinlocks. + */ +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) +{ + void *ptr; + dma_addr_t addr; + unsigned long rmask; + + rmask = ~(unsigned long)(pci ? pci->dma_mask : 0x00ffffff); + ptr = (void *)__get_free_page(GFP_KERNEL); + if (ptr) { + addr = virt_to_phys(ptr); + if (((unsigned long)addr + PAGE_SIZE - 1) & rmask) { + /* try to reallocate with the GFP_DMA */ + free_page((unsigned long)ptr); + ptr = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); + if (ptr) /* ok, the address must be within lower 16MB... */ + addr = virt_to_phys(ptr); + else + addr = 0; + } + } else + addr = 0; + if (ptr) { + struct page *page = virt_to_page(ptr); + memset(ptr, 0, PAGE_SIZE); + SetPageReserved(page); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages++; +#endif + } + *addrp = addr; + return ptr; +} +#else +/* on other architectures, call snd_malloc_pci_pages() helper function + * which uses pci_alloc_consistent(). + */ +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) +{ + return snd_malloc_pci_pages(pci, PAGE_SIZE, addrp); +} +#endif /* 2.4 && i386 */ + +#if 0 /* for kernel-doc */ +/** + * snd_free_pci_page - release a page + * @pci: pci device pointer + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_page(). + */ +void snd_free_pci_page(struct pci_dev *pci, void *ptr, dma_addr_t dma_addr); +#endif /* for kernel-doc */ + +#endif /* CONFIG_PCI */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 562f4f4ca6a2..d9383e69f19c 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -542,6 +542,19 @@ static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream) return 0; } +/** + * snd_pcm_new_stream - create a new PCM stream + * @pcm: the pcm instance + * @stream: the stream direction, SNDRV_PCM_STREAM_XXX + * @substream_count: the number of substreams + * + * Creates a new stream for the pcm. + * The corresponding stream on the pcm must have been empty before + * calling this, i.e. zero must be given to the argument of + * snd_pcm_new(). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) { int idx, err; @@ -582,7 +595,7 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) snd_magic_kfree(substream); return err; } - substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA; + substream->dma_type = SNDRV_PCM_DMA_TYPE_UNKNOWN; substream->dma_private = NULL; spin_lock_init(&substream->timer_lock); prev = substream; @@ -590,6 +603,22 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) return 0; } +/** + * snd_pcm_new - create a new PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new PCM instance. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_new(snd_card_t * card, char *id, int device, int playback_count, int capture_count, snd_pcm_t ** rpcm) @@ -660,6 +689,7 @@ static int snd_pcm_free(snd_pcm_t *pcm) snd_assert(pcm != NULL, return -ENXIO); if (pcm->private_free) pcm->private_free(pcm); + snd_pcm_lib_preallocate_free_for_all(pcm); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); snd_magic_kfree(pcm); @@ -774,6 +804,7 @@ int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, runtime->status->state = SNDRV_PCM_STATE_OPEN; substream->runtime = runtime; + substream->private_data = pcm->private_data; pstr->substream_opened++; *rsubstream = substream; return 0; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e5c4d15e55c9..1c3a6ec1dc50 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -214,10 +214,14 @@ int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) return 0; } -/* - * Operations +/** + * snd_pcm_set_ops - set the PCM operators + * @pcm: the pcm instance + * @direction: stream direction, SNDRV_PCM_STREAM_XXX + * @ops: the operator table + * + * Sets the given PCM operators to the pcm instance. */ - void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) { snd_pcm_str_t *stream = &pcm->streams[direction]; @@ -227,10 +231,13 @@ void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) substream->ops = ops; } -/* - * Sync + +/** + * snd_pcm_sync - set the PCM sync id + * @substream: the pcm substream + * + * Sets the PCM sync identifier for the card. */ - void snd_pcm_set_sync(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; @@ -354,7 +361,17 @@ int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) return changed; } -/* r <- v */ +/** + * snd_interval_refine - refine the interval value of configurator + * @i: the interval value to refine + * @v: the interval value to refer to + * + * Refines the interval value with the reference value. + * The interval is changed to the range satisfying both intervals. + * The interval status (min, max, integer, etc.) are evaluated. + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) { int changed = 0; @@ -445,6 +462,13 @@ void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_inte c->integer = (a->integer && b->integer); } +/** + * snd_interval_div - refine the interval value with division + * + * c = a / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) { unsigned int r; @@ -469,7 +493,13 @@ void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_inte c->integer = 0; } -/* a * b / k */ +/** + * snd_interval_muldivk - refine the interval value + * + * c = a * b / k + * + * Returns non-zero if the value is changed, zero if not changed. + */ void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, unsigned int k, snd_interval_t *c) { @@ -490,7 +520,13 @@ void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, c->integer = 0; } -/* a * k / b */ +/** + * snd_interval_mulkdiv - refine the interval value + * + * c = a * k / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, const snd_interval_t *b, snd_interval_t *c) { @@ -520,6 +556,11 @@ void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, /* ---- */ +/** + * snd_interval_ratnum - refine the interval value + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_ratnum(snd_interval_t *i, unsigned int rats_count, ratnum_t *rats, unsigned int *nump, unsigned int *denp) @@ -612,6 +653,11 @@ int snd_interval_ratnum(snd_interval_t *i, return err; } +/** + * snd_interval_ratden - refine the interval value + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_ratden(snd_interval_t *i, unsigned int rats_count, ratden_t *rats, unsigned int *nump, unsigned int *denp) @@ -698,6 +744,19 @@ int snd_interval_ratden(snd_interval_t *i, return err; } +/** + * snd_interval_list - refine the interval value from the list + * @i: the interval value to refine + * @count: the number of elements in the list + * @list: the value list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask) { unsigned int k; @@ -762,6 +821,17 @@ int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step) /* Info constraints helpers */ +/** + * snd_pcm_hw_rule_add - add the hw-constraint rule + * @runtime: the pcm runtime instance + * @cond: condition bits + * @var: the variable to evaluate + * @func: the evaluation function + * @private: the private data pointer passed to function + * @dep: the dependent variables + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, int var, snd_pcm_hw_rule_func_t func, void *private, @@ -808,6 +878,9 @@ int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, return 0; } +/** + * snd_pcm_hw_constraint_mask + */ int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, u_int32_t mask) { @@ -820,6 +893,9 @@ int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t va return 0; } +/** + * snd_pcm_hw_constraint_mask64 + */ int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, u_int64_t mask) { @@ -833,12 +909,18 @@ int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t return 0; } +/** + * snd_pcm_hw_constraint_integer + */ int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var) { snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; return snd_interval_setinteger(constrs_interval(constrs, var)); } +/** + * snd_pcm_hw_constraint_minmax + */ int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, unsigned int min, unsigned int max) { @@ -859,6 +941,9 @@ static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params, } +/** + * snd_pcm_hw_constraint_list + */ int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -884,6 +969,9 @@ static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params, return err; } +/** + * snd_pcm_hw_constraint_ratnums + */ int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -908,6 +996,9 @@ static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params, return err; } +/** + * snd_pcm_hw_constraint_ratdens + */ int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -930,6 +1021,9 @@ static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params, return 0; } +/** + * snd_pcm_hw_constraint_msbits + */ int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, unsigned int cond, unsigned int width, @@ -949,6 +1043,9 @@ static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params, return snd_interval_step(hw_param_interval(params, rule->var), 0, step); } +/** + * snd_pcm_hw_constraint_step + */ int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -971,6 +1068,9 @@ static int snd_pcm_hw_rule_pow2(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t * sizeof(pow2_sizes)/sizeof(int), pow2_sizes, 0); } +/** + * snd_pcm_hw_constraint_pow2 + */ int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var) @@ -1004,6 +1104,9 @@ void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) snd_BUG(); } +/** + * snd_pcm_hw_param_any + */ int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { @@ -1022,16 +1125,23 @@ void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) params->info = ~0U; } -/* Fill PARAMS with full configuration space boundaries */ +/** + * snd_pcm_hw_params_any + * + * Fill PARAMS with full configuration space boundaries + */ int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { _snd_pcm_hw_params_any(params); return snd_pcm_hw_refine(pcm, params); } -/* Return the value for field PAR if it's fixed in configuration space - defined by PARAMS. Return -EINVAL otherwise -*/ +/** + * snd_pcm_hw_param_value + * + * Return the value for field PAR if it's fixed in configuration space + * defined by PARAMS. Return -EINVAL otherwise + */ int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) { @@ -1055,7 +1165,11 @@ int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, return -EINVAL; } -/* Return the minimum value for field PAR. */ +/** + * snd_pcm_hw_param_value_min + * + * Return the minimum value for field PAR. + */ unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) { @@ -1074,7 +1188,11 @@ unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, return -EINVAL; } -/* Return the maximum value for field PAR. */ +/** + * snd_pcm_hw_param_value_max + * + * Return the maximum value for field PAR. + */ unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) { @@ -1122,10 +1240,13 @@ int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - non integer values. Reduce configuration space accordingly. - Return -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_setinteger + * + * Inside configuration space defined by PARAMS remove from PAR all + * non integer values. Reduce configuration space accordingly. + * Return -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) @@ -1161,10 +1282,13 @@ int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params, } -/* Inside configuration space defined by PARAMS remove from PAR all - values > minimum. Reduce configuration space accordingly. - Return the minimum. -*/ +/** + * snd_pcm_hw_param_first + * + * Inside configuration space defined by PARAMS remove from PAR all + * values > minimum. Reduce configuration space accordingly. + * Return the minimum. + */ int snd_pcm_hw_param_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) @@ -1199,10 +1323,13 @@ int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params, } -/* Inside configuration space defined by PARAMS remove from PAR all - values < maximum. Reduce configuration space accordingly. - Return the maximum. -*/ +/** + * snd_pcm_hw_param_last + * + * Inside configuration space defined by PARAMS remove from PAR all + * values < maximum. Reduce configuration space accordingly. + * Return the maximum. + */ int snd_pcm_hw_param_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) @@ -1247,10 +1374,13 @@ int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - values < VAL. Reduce configuration space accordingly. - Return new minimum or -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_min + * + * Inside configuration space defined by PARAMS remove from PAR all + * values < VAL. Reduce configuration space accordingly. + * Return new minimum or -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int *dir) { @@ -1297,10 +1427,13 @@ int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - values >= VAL + 1. Reduce configuration space accordingly. - Return new maximum or -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_max + * + * Inside configuration space defined by PARAMS remove from PAR all + * values >= VAL + 1. Reduce configuration space accordingly. + * Return new maximum or -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int *dir) { @@ -1364,10 +1497,13 @@ int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - values != VAL. Reduce configuration space accordingly. - Return VAL or -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_set + * + * Inside configuration space defined by PARAMS remove from PAR all + * values != VAL. Reduce configuration space accordingly. + * Return VAL or -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int dir) { @@ -1395,13 +1531,16 @@ int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all values - not contained in MASK. Reduce configuration space accordingly. - This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. - Return 0 on success or -EINVAL - if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_mask + * + * Inside configuration space defined by PARAMS remove from PAR all values + * not contained in MASK. Reduce configuration space accordingly. + * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, + * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + * Return 0 on success or -EINVAL + * if the configuration space is empty + */ int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, const snd_mask_t *val) { @@ -1464,12 +1603,15 @@ static int boundary_nearer(int min, int mindir, return boundary_lt(dmin, dmindir, dmax, dmaxdir); } -/* Inside configuration space defined by PARAMS set PAR to the available value - nearest to VAL. Reduce configuration space accordingly. - This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. - Return the value found. - */ +/** + * snd_pcm_hw_param_near + * + * Inside configuration space defined by PARAMS set PAR to the available value + * nearest to VAL. Reduce configuration space accordingly. + * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, + * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + * Return the value found. + */ int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int best, int *dir) { @@ -1537,17 +1679,14 @@ int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, return v; } -/* Choose one configuration from configuration space defined by PARAMS - The configuration choosen is that obtained fixing in this order: - first access - first format - first subformat - min channels - min rate - min period time - max buffer size - min tick time -*/ +/** + * snd_pcm_hw_param_choose + * + * Choose one configuration from configuration space defined by PARAMS + * The configuration choosen is that obtained fixing in this order: + * first access, first format, first subformat, min channels, + * min rate, min period time, max buffer size, min tick time + */ int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { int err; @@ -1630,6 +1769,17 @@ static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream, return 0; } +/** + * snd_pcm_lib_ioctl - a generic PCM ioctl callback + * @substream: the pcm substream instance + * @cmd: ioctl command + * @arg: ioctl argument + * + * Processes the generic ioctl commands for PCM. + * Can be passed as the ioctl callback for PCM ops. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg) { @@ -1648,30 +1798,70 @@ int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, * Conditions */ +/** + * snd_pcm_playback_ready - check whether the playback buffer is available + * @substream: the pcm substream instance + * + * Checks whether enough free space is available on the playback buffer. + * + * Returns non-zero if available, or zero if not. + */ int snd_pcm_playback_ready(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min; } +/** + * snd_pcm_capture_ready - check whether the capture buffer is available + * @substream: the pcm substream instance + * + * Checks whether enough capture data is available on the capture buffer. + * + * Returns non-zero if available, or zero if not. + */ int snd_pcm_capture_ready(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min; } +/** + * snd_pcm_playback_data - check whether any data exists on the playback buffer + * @substream: the pcm substream instance + * + * Checks whether any data exists on the playback buffer. + * + * Returns non-zero if exists, or zero if not. + */ int snd_pcm_playback_data(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_playback_avail(runtime) < runtime->buffer_size; } +/** + * snd_pcm_playback_empty - check whether the playback buffer is empty + * @substream: the pcm substream instance + * + * Checks whether the playback buffer is empty. + * + * Returns non-zero if empty, or zero if not. + */ int snd_pcm_playback_empty(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_playback_avail(runtime) >= runtime->buffer_size; } +/** + * snd_pcm_capture_empty - check whether the capture buffer is empty + * @substream: the pcm substream instance + * + * Checks whether the capture buffer is empty. + * + * Returns non-zero if empty, or zero if not. + */ int snd_pcm_capture_empty(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; @@ -1766,6 +1956,17 @@ void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream) spin_unlock_irq(&runtime->lock); } +/** + * snd_pcm_period_elapsed - update the pcm status for the next period + * @substream: the pcm substream instance + * + * This function is called from the interrupt handler when the + * PCM has processed the period size. It will update the current + * pointer, set up the tick, wake up sleepers, etc. + * + * Even if more than one periods have elapsed since the last call, you + * have to call this only once. + */ void snd_pcm_period_elapsed(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime; diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 4cc1750197dd..b2da702f0d7e 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -37,36 +37,120 @@ MODULE_PARM(maximum_substreams, "i"); MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory."); MODULE_PARM_SYNTAX(maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC); -static int snd_minimum_buffer = 16384; +const static int snd_minimum_buffer = 16384; -static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) +/* + * allocate pages on the specified bus + */ +static int alloc_pcm_pages(snd_pcm_substream_t *substream, size_t size, + void **dma_area, dma_addr_t *dma_addr) +{ + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + *dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); + *dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + *dma_area = snd_malloc_isa_pages(size, dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + *dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, dma_addr); + break; +#endif +#ifdef CONFIG_SBUS + case SNDRV_PCM_DMA_TYPE_SBUS: + *dma_area = snd_malloc_sbus_pages((struct sbus_dev *)substream->dma_private, size, dma_addr); + break; +#endif + default: + *dma_area = NULL; + *dma_addr = 0; + return -ENXIO; + } + return 0; +} + +/* + * try to allocate as the large pages as possible. + * stores the resultant memory size in *res_size. + * + * the minimum size is snd_minimum_buffer. it should be power of 2. + */ +static void *alloc_pcm_pages_fallback(snd_pcm_substream_t *substream, + size_t size, dma_addr_t *addrp, + size_t *res_size) +{ + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if (alloc_pcm_pages(substream, size, &res, addrp) < 0) + return NULL; + if (res) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= snd_minimum_buffer); + *res_size = 0; /* tell error */ + return NULL; +} + +/* + * release the pages on the specified bus + */ +static void free_pcm_pages(snd_pcm_substream_t *substream, size_t size, + void *dma_area, dma_addr_t dma_addr) { - if (substream->dma_area == NULL) - return; switch (substream->dma_type) { case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - snd_free_pages(substream->dma_area, substream->dma_bytes); + snd_free_pages(dma_area, size); break; #ifdef CONFIG_ISA case SNDRV_PCM_DMA_TYPE_ISA: - snd_free_isa_pages(substream->dma_bytes, substream->dma_area, substream->dma_addr); + snd_free_isa_pages(size, dma_area, dma_addr); break; #endif #ifdef CONFIG_PCI case SNDRV_PCM_DMA_TYPE_PCI: - snd_free_pci_pages((struct pci_dev *)substream->dma_private, substream->dma_bytes, substream->dma_area, substream->dma_addr); + snd_free_pci_pages((struct pci_dev *)substream->dma_private, + size, dma_area, dma_addr); break; #endif #ifdef CONFIG_SBUS case SNDRV_PCM_DMA_TYPE_SBUS: - snd_free_sbus_pages((struct sbus_dev *)substream->dma_private, substream->dma_bytes, substream->dma_area, substream->dma_addr); + snd_free_sbus_pages((struct sbus_dev *)substream->dma_private, + size, dma_area, dma_addr); break; #endif } +} + +/* + * release the preallocated buffer if not yet done. + */ +static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) +{ + if (substream->dma_area == NULL) + return; + free_pcm_pages(substream, substream->dma_bytes, + substream->dma_area, substream->dma_addr); substream->dma_area = NULL; } +/** + * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream. + * @substream: the pcm substream instance + * + * Releases the pre-allocated buffer of the given substream. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) { snd_pcm_lib_preallocate_dma_free(substream); @@ -77,6 +161,14 @@ int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) return 0; } +/** + * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm + * @pcm: the pcm instance + * + * Releases all the pre-allocated buffers on the given pcm. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) { snd_pcm_substream_t *substream; @@ -88,6 +180,11 @@ int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) return 0; } +/* + * read callback for prealloc proc file + * + * prints the current allocated size in kB. + */ static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { @@ -95,6 +192,11 @@ static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_bytes / 1024); } +/* + * write callback for prealloc proc file + * + * accepts the preallocation size in kB. + */ static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { @@ -118,27 +220,7 @@ static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, if (substream->dma_bytes == size) return; if (size > 0) { - switch (substream->dma_type) { - case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); - dma_addr = 0UL; /* not valid */ - break; -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - dma_area = snd_malloc_isa_pages(size, &dma_addr); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - dma_area = snd_malloc_sbus_pages((struct sbus_dev *)substream->dma_private, size, &dma_addr); - break; -#endif - default: + if (alloc_pcm_pages(substream, size, &dma_area, &dma_addr) < 0) { dma_area = NULL; dma_addr = 0UL; } @@ -160,45 +242,19 @@ static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, } } +/* + * pre-allocate the buffer and create a proc file for the substream + */ static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, size_t size, size_t max) { - unsigned long rsize = 0; + size_t rsize = 0; void *dma_area = NULL; dma_addr_t dma_addr = 0UL; snd_info_entry_t *entry; - if (!size || !preallocate_dma || substream->number >= maximum_substreams) { - size = 0; - } else { - switch (substream->dma_type) { - case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - dma_area = snd_malloc_pages_fallback(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff), &rsize); - dma_addr = 0UL; /* not valid */ - break; -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - dma_area = snd_malloc_isa_pages_fallback(size, &dma_addr, &rsize); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - dma_area = snd_malloc_pci_pages_fallback((struct pci_dev *)substream->dma_private, size, &dma_addr, &rsize); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - dma_area = snd_malloc_sbus_pages_fallback((struct sbus_dev *)substream->dma_private, size, &dma_addr, &rsize); - break; -#endif - default: - size = 0; - } - if (rsize < snd_minimum_buffer) { - snd_pcm_lib_preallocate_dma_free(substream); - size = 0; - } - } + if (size > 0 && preallocate_dma && substream->number < maximum_substreams) + dma_area = alloc_pcm_pages_fallback(substream, size, &dma_addr, &rsize); substream->dma_area = dma_area; substream->dma_addr = dma_addr; substream->dma_bytes = rsize; @@ -220,6 +276,17 @@ static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, return 0; } +/** + * snd_pcm_lib_preallocate_pages - pre-allocation for the continuous memory type + * @substream: the pcm substream instance + * @size: the requested pre-allocation size in bytes + * @max: the max. allowed pre-allocation size + * @flags: allocation condition, GFP_XXX + * + * Do pre-allocation for the continuous memory type. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, size_t size, size_t max, unsigned int flags) @@ -229,6 +296,18 @@ int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/** + * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * @flags: allocation condition, GFP_XXX + * + * Do pre-allocation to all substreams of the given pcm for the + * continuous memory type. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, size_t size, size_t max, unsigned int flags) @@ -244,6 +323,16 @@ int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, } #ifdef CONFIG_ISA +/** + * snd_pcm_lib_preallocate_isa_pages - pre-allocation for the ISA bus + * @substream: substream to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the ISA bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, size_t size, size_t max) { @@ -252,6 +341,18 @@ int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_isa_pages_for_all - pre-allocation for the ISA bus (all substreams) + * @pcm: pcm to assign the buffer + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * ISA bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, size_t size, size_t max) { @@ -266,12 +367,24 @@ int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, } #endif /* CONFIG_ISA */ +/** + * snd_pcm_lib_malloc_pages - allocate the DMA buffer + * @substream: the substream to allocate the DMA buffer to + * @size: the requested buffer size in bytes + * + * Allocates the DMA buffer on the BUS type given by + * snd_pcm_lib_preallocate_xxx_pages(). + * + * Returns 1 if the buffer is changed, 0 if not changed, or a negative + * code on failure. + */ int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) { snd_pcm_runtime_t *runtime; void *dma_area = NULL; dma_addr_t dma_addr = 0UL; + snd_assert(substream->dma_type != SNDRV_PCM_DMA_TYPE_UNKNOWN, return -EINVAL); snd_assert(substream != NULL, return -EINVAL); runtime = substream->runtime; snd_assert(runtime != NULL, return -EINVAL); @@ -287,29 +400,7 @@ int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) dma_area = substream->dma_area; dma_addr = substream->dma_addr; } else { - switch (substream->dma_type) { - case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); - dma_addr = 0UL; /* not valid */ - break; -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - dma_area = snd_malloc_isa_pages(size, &dma_addr); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - dma_area = snd_malloc_sbus_pages((struct sbus_dev *)substream->dma_private, size, &dma_addr); - break; -#endif - default: - return -ENXIO; - } + alloc_pcm_pages(substream, size, &dma_area, &dma_addr); } if (! dma_area) return -ENOMEM; @@ -319,6 +410,14 @@ int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) return 1; /* area was changed */ } +/** + * snd_pcm_lib_free_pages - release the allocated DMA buffer. + * @substream: the substream to release the DMA buffer + * + * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages(). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime; @@ -328,25 +427,9 @@ int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) snd_assert(runtime != NULL, return -EINVAL); if (runtime->dma_area == NULL) return 0; - if (runtime->dma_area != substream->dma_area) { - switch (substream->dma_type) { -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - snd_free_isa_pages(runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - snd_free_pci_pages((struct pci_dev *)substream->dma_private, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - snd_free_sbus_pages((struct sbus_dev *)substream->dma_private, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); - break; -#endif - } - } + if (runtime->dma_area != substream->dma_area) + free_pcm_pages(substream, runtime->dma_bytes, + runtime->dma_area, runtime->dma_addr); runtime->dma_area = NULL; runtime->dma_addr = 0UL; runtime->dma_bytes = 0; @@ -354,7 +437,18 @@ int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) } #ifdef CONFIG_PCI - +/** + * snd_pcm_lib_preallocate_pci_pages - pre-allocation for the PCI bus + * + * @pci: pci device + * @substream: substream to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, snd_pcm_substream_t *substream, size_t size, size_t max) @@ -364,6 +458,20 @@ int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_pci_pages_for_all - pre-allocation for the PCI bus (all substreams) + * @pci: pci device + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, snd_pcm_t *pcm, size_t size, size_t max) @@ -381,7 +489,17 @@ int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, #endif /* CONFIG_PCI */ #ifdef CONFIG_SBUS - +/** + * snd_pcm_lib_preallocate_sbus_pages - pre-allocation for the SBUS bus + * + * @sbus: SBUS device + * @substream: substream to assign the buffer + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the SBUS. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, snd_pcm_substream_t *substream, size_t size, size_t max) @@ -391,6 +509,20 @@ int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_sbus_pages_for_all - pre-allocation for the SBUS bus (all substreams) + * @sbus: SBUS device + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * SUBS. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, snd_pcm_t *pcm, size_t size, size_t max) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 38a87b6d69eb..310f1f1c4006 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -30,6 +30,13 @@ #define snd_enum_to_int(v) (v) #define snd_int_to_enum(v) (v) +/** + * snd_pcm_format_signed - Check the PCM format is signed linear + * @format: the format to check + * + * Returns 1 if the given PCM format is signed linear, 0 if unsigned + * linear, and a negative error code for non-linear formats. + */ int snd_pcm_format_signed(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -66,6 +73,13 @@ int snd_pcm_format_signed(snd_pcm_format_t format) } } +/** + * snd_pcm_format_unsigned - Check the PCM format is unsigned linear + * @format: the format to check + * + * Returns 1 if the given PCM format is unsigned linear, 0 if signed + * linear, and a negative error code for non-linear formats. + */ int snd_pcm_format_unsigned(snd_pcm_format_t format) { int val; @@ -76,11 +90,24 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format) return !val; } +/** + * snd_pcm_format_linear - Check the PCM format is linear + * @format: the format to check + * + * Returns 1 if the given PCM format is linear, 0 if not. + */ int snd_pcm_format_linear(snd_pcm_format_t format) { return snd_pcm_format_signed(format) >= 0; } +/** + * snd_pcm_format_little_endian - Check the PCM format is little-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is little-endian, 0 if + * big-endian, or a negative error code if endian not specified. + */ int snd_pcm_format_little_endian(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -121,6 +148,13 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format) } } +/** + * snd_pcm_format_big_endian - Check the PCM format is big-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is big-endian, 0 if + * little-endian, or a negative error code if endian not specified. + */ int snd_pcm_format_big_endian(snd_pcm_format_t format) { int val; @@ -131,6 +165,13 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format) return !val; } +/** + * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is CPU-endian, 0 if + * opposite, or a negative error code if endian not specified. + */ int snd_pcm_format_cpu_endian(snd_pcm_format_t format) { #ifdef SNDRV_LITTLE_ENDIAN @@ -140,6 +181,13 @@ int snd_pcm_format_cpu_endian(snd_pcm_format_t format) #endif } +/** + * snd_pcm_format_width - return the bit-width of the format + * @format: the format to check + * + * Returns the bit-width of the format, or a negative error code + * if unknown format. + */ int snd_pcm_format_width(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -193,6 +241,13 @@ int snd_pcm_format_width(snd_pcm_format_t format) } } +/** + * snd_pcm_format_physical_width - return the physical bit-width of the format + * @format: the format to check + * + * Returns the physical bit-width of the format, or a negative error code + * if unknown format. + */ int snd_pcm_format_physical_width(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -243,6 +298,13 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format) } } +/** + * snd_pcm_format_size - return the byte size of samples on the given format + * @format: the format to check + * + * Returns the byte size of the given samples for the format, or a + * negative error code if unknown format. + */ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) { switch (snd_enum_to_int(format)) { @@ -296,6 +358,12 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) } } +/** + * snd_pcm_format_silence_64 - return the silent data in 64bit integer + * @format: the format to check + * + * Returns the silent data in 64bit integer for the given format. + */ u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -451,6 +519,16 @@ u_int8_t snd_pcm_format_silence(snd_pcm_format_t format) return (u_int8_t)snd_pcm_format_silence_64(format); } +/** + * snd_pcm_format_set_silence - set the silence data on the buffer + * @format: the PCM format + * @data: the buffer pointer + * @samples: the number of samples to set silence + * + * Sets the silence data on the buffer for the given samples. + * + * Returns zero if sucessful, or a negative error code on failure. + */ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) { if (samples == 0) @@ -544,6 +622,14 @@ static int linear_formats[4*2*2] = { SNDRV_PCM_FORMAT_U32_BE }; +/** + * snd_pcm_build_linear_format - return the suitable linear format for the given condition + * @width: the bit-width + * @unsignd: 1 if unsigned, 0 if signed. + * @big_endian: 1 if big-endian, 0 if little-endian + * + * Returns the suitable linear format for the given condition. + */ snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) { switch (width) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ce21615f6c92..c12bad5d92b4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -676,6 +676,9 @@ static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state) snd_pcm_tick_prepare(substream); } +/** + * snd_pcm_sart + */ int snd_pcm_start(snd_pcm_substream_t *substream) { SND_PCM_ACTION(start, substream, 0); @@ -705,6 +708,9 @@ static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state) wake_up(&runtime->sleep); } +/** + * snd_pcm_stop + */ int snd_pcm_stop(snd_pcm_substream_t *substream, int state) { SND_PCM_ACTION(stop, substream, state); @@ -781,11 +787,17 @@ static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int stat wake_up(&runtime->sleep); } +/** + * snd_pcm_suspend + */ int snd_pcm_suspend(snd_pcm_substream_t *substream) { SND_PCM_ACTION(suspend, substream, 0); } +/** + * snd_pcm_suspend_all + */ int snd_pcm_suspend_all(snd_pcm_t *pcm) { snd_pcm_substream_t *substream; @@ -978,6 +990,9 @@ static inline void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int sta runtime->status->state = SNDRV_PCM_STATE_PREPARED; } +/** + * snd_pcm_prepare + */ int snd_pcm_prepare(snd_pcm_substream_t *substream) { int res; diff --git a/sound/core/pcm_sgbuf.c b/sound/core/pcm_sgbuf.c index 2511b99b3ede..914373a2a7e9 100644 --- a/sound/core/pcm_sgbuf.c +++ b/sound/core/pcm_sgbuf.c @@ -41,16 +41,24 @@ static void sgbuf_shrink(struct snd_sg_buf *sgbuf, int pages) return; while (sgbuf->pages > pages) { sgbuf->pages--; - snd_free_pci_pages(sgbuf->pci, PAGE_SIZE, - sgbuf->table[sgbuf->pages].buf, + snd_free_pci_page(sgbuf->pci, sgbuf->table[sgbuf->pages].buf, sgbuf->table[sgbuf->pages].addr); } } -/* - * initialize the sg buffer - * assigned to substream->dma_private. - * initialize the table with the given size. +/** + * snd_pcm_sgbuf_init - initialize the sg buffer + * @substream: the pcm substream instance + * @pci: pci device pointer + * @tblsize: the default table size + * + * Initializes the SG-buffer instance and assigns it to + * substream->dma_private. The SG-table is initialized with the + * given size. + * + * Call this function in the open callback. + * + * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize) { @@ -73,8 +81,15 @@ int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int return 0; } -/* - * release all pages and free the sgbuf instance +/** + * snd_pcm_sgbuf_delete - release all pages and free the sgbuf instance + * @substream: the pcm substream instance + * + * Releaes all pages and free the sgbuf instance. + * + * Call this function in the close callback. + * + * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream) { @@ -92,60 +107,19 @@ int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream) return 0; } -/* - * snd_pci_alloc_page - allocate a page in the valid pci dma mask +/** + * snd_pcm_sgbuf_alloc - allocate the pages for the SG buffer + * @substream: the pcm substream instance + * @size: the requested buffer size in bytes * - * returns the virtual address and stores the physical address on - * addrp. this function cannot be called from interrupt handlers or - * within spinlocks. - */ -#ifdef __i386__ -/* - * on ix86, we allocate a page with GFP_KERNEL to assure the - * allocation. the code is almost same with kernel/i386/pci-dma.c but - * it allocates only a single page and checkes the validity of the - * page address with the given pci dma mask. - */ -inline static void *snd_pci_alloc_page(struct pci_dev *pci, dma_addr_t *addrp) -{ - void *ptr; - dma_addr_t addr; - unsigned long rmask; - - if (pci) - rmask = ~(unsigned long)pci->dma_mask; - else - rmask = 0; - ptr = (void *)__get_free_page(GFP_KERNEL); - if (ptr) { - addr = virt_to_phys(ptr); - if (((unsigned long)addr + PAGE_SIZE - 1) & rmask) { - /* try to reallocate with the GFP_DMA */ - free_page((unsigned long)ptr); - ptr = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); - if (ptr) /* ok, the address must be within lower 16MB... */ - addr = virt_to_phys(ptr); - else - addr = 0; - } - } else - addr = 0; - if (ptr) - memset(ptr, 0, PAGE_SIZE); - *addrp = addr; - return ptr; -} -#else -/* on other architectures, call snd_malloc_pci_pages() helper function - * which uses pci_alloc_consistent(). - */ -#define snd_pci_alloc_page(pci, addrp) snd_malloc_pci_pages(pci, PAGE_SIZE, addrp) -#endif - -/* - * allocate sg buffer table with the given byte size. - * if the buffer table already exists, try to resize it. - * call this from hw_params callback. + * Allocates the buffer pages for the given size and updates the + * sg buffer table. If the buffer table already exists, try to resize + * it. + * + * Call this function from hw_params callback. + * + * Returns 1 if the buffer is changed, 0 if not changed, or a negative + * code on failure. */ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size) { @@ -178,7 +152,7 @@ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size) while (sgbuf->pages < pages) { void *ptr; dma_addr_t addr; - ptr = snd_pci_alloc_page(sgbuf->pci, &addr); + ptr = snd_malloc_pci_page(sgbuf->pci, &addr); if (! ptr) return -ENOMEM; sgbuf->table[sgbuf->pages].buf = ptr; @@ -192,10 +166,15 @@ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size) return changed; } -/* - * free the sg buffer - * the table is kept. - * call this from hw_free callback. +/** + * snd_pcm_sgbuf_free - free the sg buffer + * @substream: the pcm substream instance + * + * Releases the pages. The SG-table itself is still kept. + * + * Call this function from hw_free callback. + * + * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream) { @@ -221,9 +200,13 @@ static void *sgbuf_get_addr(snd_pcm_substream_t *substream, unsigned long offset return sgbuf->table[idx].buf; } -/* - * get the page struct at the given offset - * used as the page callback of pcm ops +/** + * snd_pcm_sgbuf_ops_page - get the page struct at the given offset + * @substream: the pcm substream instance + * @offset: the buffer offset + * + * Returns the page struct at the given buffer offset. + * Used as the page callback of PCM ops. */ struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) { @@ -324,7 +307,9 @@ static int set_silence_sg_buf(snd_pcm_substream_t *substream, return 0; } -/* +/** + * snd_pcm_sgbuf_ops_copy_playback - copy callback for playback pcm ops + * * copy callback for playback pcm ops */ int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, @@ -340,7 +325,9 @@ int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, } } -/* +/** + * snd_pcm_sgbuf_ops_copy_capture - copy callback for capture pcm ops + * * copy callback for capture pcm ops */ int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, @@ -356,7 +343,9 @@ int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, } } -/* +/** + * snd_pcm_sgbuf_ops_silence - silence callback for pcm ops + * * silence callback for pcm ops */ int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 061cb55fa1e1..17376907b20f 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -815,6 +815,16 @@ int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, return -ENOIOCTLCMD; } +/** + * snd_rawmidi_receive - receive the input data from the device + * @substream: the rawmidi substream + * @buffer: the buffer pointer + * @count: the data size to read + * + * Reads the data from the internal buffer. + * + * Returns the size of read data, or a negative error code on failure. + */ int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count) { unsigned long flags; @@ -959,6 +969,12 @@ static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff return result; } +/** + * snd_rawmidi_transmit_empty - check whether the output buffer is empty + * @substream: the rawmidi substream + * + * Returns 1 if the internal output buffer is empty, 0 if not. + */ int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) { snd_rawmidi_runtime_t *runtime = substream->runtime; @@ -977,6 +993,20 @@ int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) return result; } +/** + * snd_rawmidi_transmit_peek - copy data from the internal buffer + * @substream: the rawmidi substream + * @buffer: the buffer pointer + * @count: data size to transfer + * + * Copies data from the internal output buffer to the given buffer. + * + * Call this in the interrupt handler when the midi output is ready, + * and call snd_rawmidi_transmit_ack() after the transmission is + * finished. + * + * Returns the size of copied data, or a negative error code on failure. + */ int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) { unsigned long flags; @@ -1014,6 +1044,17 @@ int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char return result; } +/** + * snd_rawmidi_transmit_ack - acknowledge the transmission + * @substream: the rawmidi substream + * @count: the tranferred count + * + * Advances the hardware pointer for the internal output buffer with + * the given size and updates the condition. + * Call after the transmission is finished. + * + * Returns the advanced size if successful, or a negative error code on failure. + */ int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) { unsigned long flags; @@ -1041,6 +1082,16 @@ int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) return count; } +/** + * snd_rawmidi_transmit - copy from the buffer to the device + * @substream: the rawmidi substream + * @buf: the buffer pointer + * @count: the data size to transfer + * + * Copies data from the buffer to the device and advances the pointer. + * + * Returns the copied size if successful, or a negative error code on failure. + */ int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) { count = snd_rawmidi_transmit_peek(substream, buffer, count); @@ -1304,6 +1355,20 @@ static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi, return 0; } +/** + * snd_rawmidi_new - create a rawmidi instance + * @card: the card instance + * @id: the id string + * @device: the device index + * @output_count: the number of output streams + * @input_count: the number of input streams + * @rrawmidi: the pointer to store the new rawmidi instance + * + * Creates a new rawmidi instance. + * Use snd_rawmidi_set_ops() to set the operators to the new instance. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_rawmidi_new(snd_card_t * card, char *id, int device, int output_count, int input_count, snd_rawmidi_t ** rrawmidi) @@ -1513,6 +1578,14 @@ static int snd_rawmidi_dev_unregister(snd_device_t *device) return snd_rawmidi_free(rmidi); } +/** + * snd_rawmidi_set_ops - set the rawmidi operators + * @rmidi: the rawmidi instance + * @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX + * @ops: the operator table + * + * Sets the rawmidi operators for the given stream direction. + */ void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops) { struct list_head *list; diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 59b386b1f74a..1a8b6335f5cf 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -77,5 +77,6 @@ obj-$(CONFIG_SND_EMU10K1) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-virmidi obj-$(CONFIG_SND_TRIDENT) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_YMFPCI) += $(RAWMIDI_OBJS) $(OPL3_OBJS) obj-$(CONFIG_SND_USB_AUDIO) += $(RAWMIDI_OBJS) +obj-$(CONFIG_SND_HDSP) += $(RAWMIDI_OBJS) obj-m := $(sort $(obj-m)) diff --git a/sound/core/sound.c b/sound/core/sound.c index 0a18ccae9fd1..3f6a4b68cce4 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -73,6 +73,13 @@ static devfs_handle_t devfs_handle = NULL; #ifdef CONFIG_KMOD +/** + * snd_request_card - try to load the card module + * @card: the card number + * + * Tries to load the module "snd-card-X" for the given card number + * via KMOD. Returns immediately if already loaded. + */ void snd_request_card(int card) { char str[32]; @@ -188,6 +195,19 @@ static int snd_kernel_minor(int type, snd_card_t * card, int dev) return minor; } +/** + * snd_register_device - Register the ALSA device file for the card + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card: the card instance + * @dev: the device index + * @reg: the snd_minor_t record + * @name: the device file name + * + * Registers an ALSA device file for the given card. + * The operators have to be set in reg parameter. + * + * Retrurns zero if successful, or a negative error code on failure. + */ int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) { int minor = snd_kernel_minor(type, card, dev); @@ -215,6 +235,17 @@ int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, return 0; } +/** + * snd_unregister_device - unregister the device on the given card + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card: the card instance + * @dev: the device index + * + * Unregisters the device file already registered via + * snd_register_device(). + * + * Returns zero if sucecessful, or a negative error code on failure + */ int snd_unregister_device(int type, snd_card_t * card, int dev) { int minor = snd_kernel_minor(type, card, dev); @@ -411,6 +442,7 @@ EXPORT_SYMBOL(snd_malloc_isa_pages_fallback); #ifdef CONFIG_PCI EXPORT_SYMBOL(snd_malloc_pci_pages); EXPORT_SYMBOL(snd_malloc_pci_pages_fallback); +EXPORT_SYMBOL(snd_malloc_pci_page); EXPORT_SYMBOL(snd_free_pci_pages); #endif #ifdef CONFIG_SBUS @@ -463,6 +495,7 @@ EXPORT_SYMBOL(snd_info_create_device); EXPORT_SYMBOL(snd_info_free_device); EXPORT_SYMBOL(snd_info_register); EXPORT_SYMBOL(snd_info_unregister); +EXPORT_SYMBOL(snd_card_proc_new); #endif /* info_oss.c */ #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 88e3f72c89be..33aab157bf99 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -74,6 +74,14 @@ static void _snd_mpu401_uart_interrupt(mpu401_t *mpu) snd_mpu401_uart_output_write(mpu); } +/** + * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler + * @irq: the irq number + * @dev_id: mpu401 instance + * @regs: the reigster + * + * Processes the interrupt for MPU401-UART i/o. + */ void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) { mpu401_t *mpu = snd_magic_cast(mpu401_t, dev_id, return); @@ -375,6 +383,25 @@ static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi) snd_magic_kfree(mpu); } +/** + * snd_mpu401_uart_new - create an MPU401-UART instance + * @card: the card instance + * @device: the device index, zero-based + * @hardware: the hardware type, MPU401_HW_XXXX + * @port: the base address of MPU401 port + * @integrated: non-zero if the port was already reserved by the chip + * @irq: the irq number, -1 if no interrupt for mpu + * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved. + * @rrawmidi: the pointer to store the new rawmidi instance + * + * Creates a new MPU-401 instance. + * + * Note that the rawmidi instance is returned on the rrawmidi argument, + * not the mpu401 instance itself. To access to the mpu401 instance, + * cast from rawmidi->private_data (with mpu401_t magic-cast). + * + * Returns zero if successful, or a negative error code. + */ int snd_mpu401_uart_new(snd_card_t * card, int device, unsigned short hardware, unsigned long port, int integrated, @@ -418,9 +445,9 @@ int snd_mpu401_uart_new(snd_card_t * card, int device, snd_device_free(card, rmidi); return -EBUSY; } - mpu->irq = irq; - mpu->irq_flags = irq_flags; } + mpu->irq = irq; + mpu->irq_flags = irq_flags; strcpy(rmidi->name, "MPU-401 (UART)"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c index 51ae6acf0b85..caa103d9d33a 100644 --- a/sound/i2c/l3/uda1341.c +++ b/sound/i2c/l3/uda1341.c @@ -16,7 +16,7 @@ * 2002-04-12 Tomas Kasparek Proc interface update, code cleanup */ -/* $Id: uda1341.c,v 1.5 2002/11/09 13:12:19 perex Exp $ */ +/* $Id: uda1341.c,v 1.6 2003/01/07 10:36:28 tiwai Exp $ */ #include #include @@ -128,9 +128,6 @@ struct uda1341{ snd_card_t *card; - snd_info_entry_t *proc_entry; - snd_info_entry_t *proc_regs_entry; - uda1341_cfg cfg; }; @@ -410,46 +407,10 @@ static void __devinit snd_uda1341_proc_init(snd_card_t *card, struct l3_client * DEBUG_NAME(KERN_DEBUG "proc_init\n"); - if ((entry = snd_info_create_card_entry(card, "uda1341", card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = clnt; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; - entry->c.text.read = snd_uda1341_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - uda->proc_entry = entry; - if ((entry = snd_info_create_card_entry(card, "uda1341-regs", card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = clnt; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 1024; - entry->c.text.read = snd_uda1341_proc_regs_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - uda->proc_regs_entry = entry; -} - -static void snd_uda1341_proc_done(struct l3_client *clnt) -{ - struct uda1341 *uda = clnt->driver_data; - - DEBUG_NAME(KERN_DEBUG "proc_done\n"); - - if (uda->proc_regs_entry) { - snd_info_unregister(uda->proc_regs_entry); - uda->proc_regs_entry = NULL; - } - if (uda->proc_entry) { - snd_info_unregister(uda->proc_entry); - uda->proc_entry = NULL; - } + if (! snd_card_proc_new(card, "uda1341", &entry)) + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_read); + if (! snd_card_proc_new(card, "uda1341-regs", &entry)) { + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_regs_read); } /* }}} */ @@ -731,7 +692,6 @@ void __init snd_chip_uda1341_mixer_del(snd_card_t *card) { DEBUG_NAME(KERN_DEBUG "uda1341 mixer_del\n"); - snd_uda1341_proc_done(uda1341); l3_detach_client(uda1341); snd_magic_kfree(uda1341); diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index d36d57a2efa9..a46d5a4613ad 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -936,6 +936,12 @@ int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm) return 0; } +const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction) +{ + return direction == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_ad1848_playback_ops : &snd_ad1848_capture_ops; +} + /* * MIXER part */ @@ -1160,6 +1166,7 @@ EXPORT_SYMBOL(snd_ad1848_mce_down); EXPORT_SYMBOL(snd_ad1848_interrupt); EXPORT_SYMBOL(snd_ad1848_create); EXPORT_SYMBOL(snd_ad1848_pcm); +EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); EXPORT_SYMBOL(snd_ad1848_mixer); EXPORT_SYMBOL(snd_ad1848_info_single); EXPORT_SYMBOL(snd_ad1848_get_single); diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 093420d84783..cf41048fbf65 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -147,6 +147,15 @@ struct snd_cmi8330 { struct isapnp_dev *cap; struct isapnp_dev *play; #endif + snd_card_t *card; + ad1848_t *wss; + sb_t *sb; + + snd_pcm_t *pcm; + snd_pcm_ops_t playback_ops; + int (*playback_open)(snd_pcm_substream_t *); + snd_pcm_ops_t capture_ops; + int (*capture_open)(snd_pcm_substream_t *); }; static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; @@ -294,6 +303,79 @@ static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard) } #endif +/* + * PCM interface + * + * since we call the different chip interfaces for playback and capture + * directions, we need a trick. + * + * - copy the ops for each direction into a local record. + * - replace the open callback with the new one, which replaces the + * substream->private_data with the corresponding chip instance + * and calls again the original open callback of the chip. + * + */ + +static int snd_cmi8330_playback_open(snd_pcm_substream_t * substream) +{ + struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + + /* replace the private_data and call the original open callback */ + substream->private_data = chip->sb; + return chip->playback_open(substream); +} + +static int snd_cmi8330_capture_open(snd_pcm_substream_t * substream) +{ + struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + + /* replace the private_data and call the original open callback */ + substream->private_data = chip->wss; + return chip->capture_open(substream); +} + +static void snd_cmi8330_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init snd_cmi8330_pcm(snd_card_t *card, struct snd_cmi8330 *chip) +{ + snd_pcm_t *pcm; + const snd_pcm_ops_t *ops; + int err; + + if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0) + return err; + strcpy(pcm->name, "CMI8330"); + pcm->private_data = chip; + pcm->private_free = snd_cmi8330_pcm_free; + + /* playback - SB16 */ + ops = snd_sb16dsp_get_pcm_ops(SNDRV_PCM_STREAM_PLAYBACK); + chip->playback_ops = *ops; + chip->playback_open = ops->open; + chip->playback_ops.open = snd_cmi8330_playback_open; + + /* capture - AD1848 */ + ops = snd_ad1848_get_pcm_ops(SNDRV_PCM_STREAM_CAPTURE); + chip->capture_ops = *ops; + chip->capture_open = ops->open; + chip->capture_ops.open = snd_cmi8330_capture_open; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->capture_ops); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + chip->pcm = pcm; + + return 0; +} + + +/* + */ + static void snd_cmi8330_free(snd_card_t *card) { struct snd_cmi8330 *acard = (struct snd_cmi8330 *)card->private_data; @@ -309,12 +391,8 @@ static int __init snd_cmi8330_probe(int dev) { snd_card_t *card; struct snd_cmi8330 *acard; - ad1848_t *chip_wss; - sb_t *chip_sb; unsigned long flags; int i, err; - snd_pcm_t *pcm, *wss_pcm, *sb_pcm; - snd_pcm_str_t *pstr; #ifdef __ISAPNP__ if (!isapnp[dev]) { @@ -337,6 +415,7 @@ static int __init snd_cmi8330_probe(int dev) return -ENOMEM; } acard = (struct snd_cmi8330 *)card->private_data; + acard->card = card; card->private_free = snd_cmi8330_free; #ifdef __ISAPNP__ @@ -352,83 +431,60 @@ static int __init snd_cmi8330_probe(int dev) wssirq[dev], wssdma[dev], AD1848_HW_DETECT, - &chip_wss)) < 0) { + &acard->wss)) < 0) { snd_printk("(AD1848) device busy??\n"); snd_card_free(card); return err; } - if (chip_wss->hardware != AD1848_HW_CMI8330) { + if (acard->wss->hardware != AD1848_HW_CMI8330) { snd_printk("(AD1848) not found during probe\n"); snd_card_free(card); return -ENODEV; } - if ((err = snd_ad1848_pcm(chip_wss, 0, &wss_pcm)) < 0) { - snd_printk("(AD1848) no enough memory??\n"); - snd_card_free(card); - return err; - } if ((err = snd_sbdsp_create(card, sbport[dev], sbirq[dev], snd_sb16dsp_interrupt, sbdma8[dev], sbdma16[dev], - SB_HW_AUTO, &chip_sb)) < 0) { + SB_HW_AUTO, &acard->sb)) < 0) { snd_printk("(SB16) device busy??\n"); snd_card_free(card); return err; } - if ((err = snd_sb16dsp_pcm(chip_sb, 1, &sb_pcm)) < 0) { - snd_printk("(SB16) no enough memory??\n"); - snd_card_free(card); - return err; - } - - if (chip_sb->hardware != SB_HW_16) { + if (acard->sb->hardware != SB_HW_16) { snd_printk("(SB16) not found during probe\n"); snd_card_free(card); return -ENODEV; } + memcpy(&acard->wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image)); - memcpy(&chip_wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image)); + spin_lock_irqsave(&acard->wss->reg_lock, flags); + snd_ad1848_out(acard->wss, AD1848_MISC_INFO, /* switch on MODE2 */ + acard->wss->image[AD1848_MISC_INFO] |= 0x40); + spin_unlock_irqrestore(&acard->wss->reg_lock, flags); - spin_lock_irqsave(&chip_wss->reg_lock, flags); - snd_ad1848_out(chip_wss, AD1848_MISC_INFO, /* switch on MODE2 */ - chip_wss->image[AD1848_MISC_INFO] |= 0x40); - spin_unlock_irqrestore(&chip_wss->reg_lock, flags); - - if ((err = snd_cmi8330_mixer(card, chip_wss)) < 0) { + if ((err = snd_cmi8330_mixer(card, acard->wss)) < 0) { snd_printk("failed to create mixers\n"); snd_card_free(card); return err; } - spin_lock_irqsave(&chip_wss->reg_lock, flags); + spin_lock_irqsave(&acard->wss->reg_lock, flags); for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) - snd_ad1848_out(chip_wss, i, chip_wss->image[i]); - spin_unlock_irqrestore(&chip_wss->reg_lock, flags); - - /* - * KLUDGE ALERT - * disable AD1848 playback - * disable SB16 capture - */ - pcm = wss_pcm; - pstr = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; - snd_magic_kfree(pstr->substream); - pstr->substream = 0; - pstr->substream_count = 0; - - pcm = sb_pcm; - pstr = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; - snd_magic_kfree(pstr->substream); - pstr->substream = 0; - pstr->substream_count = 0; + snd_ad1848_out(acard->wss, i, acard->wss->image[i]); + spin_unlock_irqrestore(&acard->wss->reg_lock, flags); + + if ((err = snd_cmi8330_pcm(card, acard)) < 0) { + snd_printk("failed to create pcms\n"); + snd_card_free(card); + return err; + } strcpy(card->driver, "CMI8330/C3D"); strcpy(card->shortname, "C-Media CMI8330/C3D"); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", - wss_pcm->name, - chip_wss->port, + card->shortname, + acard->wss->port, wssirq[dev], wssdma[dev]); diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c index 80ceb4581ba3..7aeaf531df97 100644 --- a/sound/isa/cs423x/cs4231_lib.c +++ b/sound/isa/cs423x/cs4231_lib.c @@ -158,7 +158,7 @@ static __CS4231_INLINE__ u8 cs4231_inb(cs4231_t *chip, u8 offset) } else { #endif #ifdef SBUS_SUPPORT - return sbus_writeb(chip->port + (offset << 2)); + return sbus_readb(chip->port + (offset << 2)); #endif #ifdef EBUS_SUPPORT } @@ -338,13 +338,13 @@ void snd_cs4231_mce_up(cs4231_t *chip) unsigned long flags; int timeout; - spin_lock_irqsave(&chip->reg_lock, flags); for (timeout = 250; timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); timeout--) udelay(100); #ifdef CONFIG_SND_DEBUG if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) snd_printk("mce_up - auto calibration time out (0)\n"); #endif + spin_lock_irqsave(&chip->reg_lock, flags); chip->mce_bit |= CS4231_MCE; timeout = cs4231_inb(chip, CS4231P(REGSEL)); if (timeout == 0x80) @@ -360,7 +360,6 @@ void snd_cs4231_mce_down(cs4231_t *chip) int timeout; signed long time; - spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_busy_wait(chip); #if 0 printk("(1) timeout = %i\n", timeout); @@ -369,14 +368,15 @@ void snd_cs4231_mce_down(cs4231_t *chip) if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL)); #endif + spin_lock_irqsave(&chip->reg_lock, flags); chip->mce_bit &= ~CS4231_MCE; timeout = cs4231_inb(chip, CS4231P(REGSEL)); cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); if (timeout == 0x80) snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { - spin_unlock_irqrestore(&chip->reg_lock, flags); return; } snd_cs4231_busy_wait(chip); @@ -387,7 +387,6 @@ void snd_cs4231_mce_down(cs4231_t *chip) udelay(10); if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) { snd_printd("cs4231_mce_down - auto calibration time out (1)\n"); - spin_unlock_irqrestore(&chip->reg_lock, flags); return; } #if 0 @@ -395,30 +394,25 @@ void snd_cs4231_mce_down(cs4231_t *chip) #endif time = HZ / 4; while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { - spin_unlock_irqrestore(&chip->reg_lock, flags); if (time <= 0) { snd_printk("mce_down - auto calibration time out (2)\n"); return; } set_current_state(TASK_INTERRUPTIBLE); time = schedule_timeout(time); - spin_lock_irqsave(&chip->reg_lock, flags); } #if 0 printk("(3) jiffies = %li\n", jiffies); #endif time = HZ / 10; while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) { - spin_unlock_irqrestore(&chip->reg_lock, flags); if (time <= 0) { - snd_printk("mce_down - auto calibration time out (3)\n"); + snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); return; } set_current_state(TASK_INTERRUPTIBLE); time = schedule_timeout(time); - spin_lock_irqsave(&chip->reg_lock, flags); } - spin_unlock_irqrestore(&chip->reg_lock, flags); #if 0 printk("(4) jiffies = %li\n", jiffies); snd_printk("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL))); @@ -1375,20 +1369,19 @@ static void snd_cs4231_resume(cs4231_t *chip) This is the first half of copy of snd_cs4231_mce_down(), but doesn't include rescheduling. -- iwai */ - spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_busy_wait(chip); + spin_lock_irqsave(&chip->reg_lock, flags); chip->mce_bit &= ~CS4231_MCE; timeout = cs4231_inb(chip, CS4231P(REGSEL)); cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); if (timeout == 0x80) snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { - spin_unlock_irqrestore(&chip->reg_lock, flags); return; } snd_cs4231_busy_wait(chip); - spin_unlock_irqrestore(&chip->reg_lock, flags); #endif } diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index f62c466009fe..31442b641067 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -460,6 +460,11 @@ static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream, return 0; } +static int snd_es18xx_pcm_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + static int snd_es18xx_playback1_prepare(es18xx_t *chip, snd_pcm_substream_t *substream) { @@ -1540,6 +1545,7 @@ static snd_pcm_ops_t snd_es18xx_playback_ops = { .close = snd_es18xx_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es18xx_playback_hw_params, + .hw_free = snd_es18xx_pcm_hw_free, .prepare = snd_es18xx_playback_prepare, .trigger = snd_es18xx_playback_trigger, .pointer = snd_es18xx_playback_pointer, @@ -1550,6 +1556,7 @@ static snd_pcm_ops_t snd_es18xx_capture_ops = { .close = snd_es18xx_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es18xx_capture_hw_params, + .hw_free = snd_es18xx_pcm_hw_free, .prepare = snd_es18xx_capture_prepare, .trigger = snd_es18xx_capture_trigger, .pointer = snd_es18xx_capture_pointer, @@ -1606,9 +1613,8 @@ static void snd_es18xx_suspend(es18xx_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); @@ -1619,24 +1625,19 @@ static void snd_es18xx_suspend(es18xx_t *chip) snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void snd_es18xx_resume(es18xx_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* restore PM register, we won't wake till (not 0x07) i/o activity though */ snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } /* callback for control API */ diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c index 55fd6f5f6d6b..3ea4d76ca101 100644 --- a/sound/isa/gus/gus_irq.c +++ b/sound/isa/gus/gus_irq.c @@ -132,26 +132,8 @@ void snd_gus_irq_profile_init(snd_gus_card_t *gus) { snd_info_entry_t *entry; - gus->irq_entry = NULL; - entry = snd_info_create_card_entry(gus->card, "gusirq", gus->card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->c.text.read_size = 512; - entry->c.text.read = snd_gus_irq_info_read; - entry->private_data = gus; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - gus->irq_entry = entry; + if (! snd_card_proc_new(gus->card, "gusirq", &entry)) + snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read); } -void snd_gus_irq_profile_done(snd_gus_card_t *gus) -{ - if (gus->irq_entry) { - snd_info_unregister(gus->irq_entry); - gus->irq_entry = NULL; - } -} #endif diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index 9cedfa7e83da..b20cac1f6e6a 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -264,19 +264,10 @@ int snd_gf1_mem_init(snd_gus_card_t * gus) if (snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; #ifdef CONFIG_SND_DEBUG - alloc->info_entry = NULL; - entry = snd_info_create_card_entry(gus->card, "gusmem", gus->card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; + if (! snd_card_proc_new(gus->card, "gusmem", &entry)) { + snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read); entry->c.text.read_size = 256 * 1024; - entry->c.text.read = snd_gf1_mem_info_read; - entry->private_data = gus; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - alloc->info_entry = entry; #endif return 0; } @@ -293,10 +284,6 @@ int snd_gf1_mem_done(snd_gus_card_t * gus) snd_gf1_mem_xfree(alloc, block); block = nblock; } -#ifdef CONFIG_SND_DEBUG - if (alloc->info_entry) - snd_info_unregister(alloc->info_entry); -#endif return 0; } diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c index 58284186c88c..32f02ad501da 100644 --- a/sound/isa/gus/gus_mem_proc.c +++ b/sound/isa/gus/gus_mem_proc.c @@ -96,73 +96,40 @@ int snd_gf1_mem_proc_init(snd_gus_card_t * gus) gus_proc_private_t *priv; snd_info_entry_t *entry; - memset(&gus->gf1.rom_entries, 0, sizeof(gus->gf1.rom_entries)); - memset(&gus->gf1.ram_entries, 0, sizeof(gus->gf1.ram_entries)); for (idx = 0; idx < 4; idx++) { if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); - if (priv == NULL) { - snd_gf1_mem_proc_done(gus); + if (priv == NULL) return -ENOMEM; - } priv->gus = gus; sprintf(name, "gus-ram-%i", idx); - entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); - if (entry) { + if (! snd_card_proc_new(gus->card, name, &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = priv; entry->private_free = snd_gf1_mem_proc_free; entry->c.ops = &snd_gf1_mem_proc_ops; priv->address = gus->gf1.mem_alloc.banks_8[idx].address; priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - gus->gf1.ram_entries[idx] = entry; } } for (idx = 0; idx < 4; idx++) { if (gus->gf1.rom_present & (1 << idx)) { priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); - if (priv == NULL) { - snd_gf1_mem_proc_done(gus); + if (priv == NULL) return -ENOMEM; - } priv->rom = 1; priv->gus = gus; sprintf(name, "gus-rom-%i", idx); - entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); - if (entry) { + if (! snd_card_proc_new(gus->card, name, &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = priv; entry->private_free = snd_gf1_mem_proc_free; entry->c.ops = &snd_gf1_mem_proc_ops; priv->address = idx * 4096 * 1024; priv->size = entry->size = gus->gf1.rom_memory; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - gus->gf1.rom_entries[idx] = entry; } } return 0; } - -int snd_gf1_mem_proc_done(snd_gus_card_t * gus) -{ - int idx; - - for (idx = 0; idx < 4; idx++) { - if (gus->gf1.ram_entries[idx]) - snd_info_unregister(gus->gf1.ram_entries[idx]); - } - for (idx = 0; idx < 4; idx++) { - if (gus->gf1.rom_entries[idx]) - snd_info_unregister(gus->gf1.rom_entries[idx]); - } - return 0; -} diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c index a6f086781f0b..10182383a7a1 100644 --- a/sound/isa/gus/gus_reset.c +++ b/sound/isa/gus/gus_reset.c @@ -410,10 +410,6 @@ int snd_gf1_stop(snd_gus_card_t * gus) snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */ snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ snd_gf1_timers_done(gus); -#ifdef CONFIG_SND_DEBUG - snd_gus_irq_profile_done(gus); -#endif - snd_gf1_mem_proc_done(gus); snd_gf1_mem_done(gus); #if 0 snd_gf1_lfo_done(gus); diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 0b8edb576638..57c7116a1472 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -107,7 +107,6 @@ static void snd_sb_qsound_destroy(snd_sb_csp_t * p); static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p); static int init_proc_entry(snd_sb_csp_t * p, int device); -static void delete_proc_entry(snd_sb_csp_t * p); static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); /* @@ -170,7 +169,6 @@ static void snd_sb_csp_free(snd_hwdep_t *hwdep) if (p) { if (p->running & SNDRV_SB_CSP_ST_RUNNING) snd_sb_csp_stop(p); - delete_proc_entry(p); snd_magic_kfree(p); } } @@ -1104,28 +1102,11 @@ static int init_proc_entry(snd_sb_csp_t * p, int device) char name[16]; snd_info_entry_t *entry; sprintf(name, "cspD%d", device); - entry = p->proc = snd_info_create_card_entry(p->chip->card, name, p->chip->card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->c.text.read_size = 256; - entry->c.text.read = info_read; - entry->private_data = p; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - p->proc = NULL; - } - } + if (! snd_card_proc_new(p->chip->card, name, &entry)) + snd_info_set_text_ops(entry, p, info_read); return 0; } -static void delete_proc_entry(snd_sb_csp_t * p) -{ - if (p->proc) { - snd_info_unregister(p->proc); - p->proc = NULL; - } -} - static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, entry->private_data, return); diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 30a1a67249ce..4b4ce71593e8 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -887,7 +887,14 @@ int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm) return 0; } +const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction) +{ + return direction == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_sb16_playback_ops : &snd_sb16_capture_ops; +} + EXPORT_SYMBOL(snd_sb16dsp_pcm); +EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops); EXPORT_SYMBOL(snd_sb16dsp_configure); EXPORT_SYMBOL(snd_sb16dsp_interrupt); diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index e63a3052def7..f555de1de90c 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -64,7 +64,8 @@ unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg) { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .info = snd_sbmixer_info_single, \ - .get = snd_sbmixer_get_single, put: snd_sbmixer_put_single, \ + .get = snd_sbmixer_get_single, \ + .put = snd_sbmixer_put_single, \ .private_value = reg | (shift << 16) | (mask << 24) } static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) @@ -123,7 +124,8 @@ static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .info = snd_sbmixer_info_double, \ - .get = snd_sbmixer_get_double, put: snd_sbmixer_put_double, \ + .get = snd_sbmixer_get_double, \ + .put = snd_sbmixer_put_double, \ .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) } static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) @@ -366,7 +368,8 @@ static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .info = snd_sb16mixer_info_input_sw, \ - .get = snd_sb16mixer_get_input_sw, put: snd_sb16mixer_put_input_sw, \ + .get = snd_sb16mixer_get_input_sw, \ + .put = snd_sb16mixer_put_input_sw, \ .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index a02b6e8ef8f5..1b6dbaf42cce 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -51,7 +51,6 @@ MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC); */ static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97); -static void snd_ac97_proc_done(ac97_t * ac97); typedef struct { unsigned int id; @@ -240,6 +239,18 @@ static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg) return 1; } +/** + * snd_ac97_write - write a value on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Writes a value on the given register. This will invoke the write + * callback directly after the register check. + * This function doesn't change the register cache unlike + * #snd_ca97_write_cache(), so use this only when you don't want to + * reflect the change to the suspend/resume state. + */ void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value) { if (!snd_ac97_valid_reg(ac97, reg)) @@ -247,6 +258,17 @@ void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value) ac97->write(ac97, reg, value); } +/** + * snd_ac97_read - read a value from the given register + * + * @ac97: the ac97 instance + * @reg: the register to read + * + * Reads a value from the given register. This will invoke the read + * callback directly after the register check. + * + * Returns the read value. + */ unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) { if (!snd_ac97_valid_reg(ac97, reg)) @@ -254,6 +276,16 @@ unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) return ac97->read(ac97, reg); } +/** + * snd_ac97_write_cache - write a value on the given register and update the cache + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Writes a value on the given register and updates the register + * cache. The cached values are used for the cached-read and the + * suspend/resume. + */ void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value) { if (!snd_ac97_valid_reg(ac97, reg)) @@ -279,6 +311,18 @@ static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned snd_ac97_write_cache(ac97, reg, value); } +/** + * snd_ac97_update - update the value on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Compares the value with the register cache and updates the value + * only when the value is changed. + * + * Retruns 1 if the value is changed, 0 if no change, or a negative + * code on failure. + */ int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) { int change; @@ -295,7 +339,7 @@ int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) return change; } -int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) +static int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) { int change; unsigned short old, new; @@ -312,6 +356,19 @@ int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short return change; } +/** + * snd_ac97_update_bits - update the bits on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @mask: the bit-mask to change + * @value: the value to set + * + * Updates the masked-bits on the given register onle when the value + * is changed. + * + * Returns 1 if the bits are changed, 0 if no change, or a negative + * code on failure. + */ int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) { int change; @@ -321,7 +378,7 @@ int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, return change; } -int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) +static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) { int change; unsigned short old, new; @@ -935,6 +992,11 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0), AC97_SINGLE("Center/LFE Down Mix", AC97_ALC650_MULTICH, 2, 1, 0), AC97_SINGLE("Exchange Center/LFE", AC97_ALC650_MULTICH, 3, 1, 0), + /* 4: Analog Input To Surround */ + /* 5: Analog Input To Center/LFE */ + /* 6: Indepedent Master Volume Right */ + /* 7: Indepedent Master Volume Left */ + /* 8: reserved */ AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0), AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0), AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0), @@ -944,6 +1006,10 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0), AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0), #endif + AC97_SINGLE("Surround DAC Switch", AC97_ALC650_SURR_DAC_VOL, 15, 1, 1), + AC97_DOUBLE("Surround DAC Volume", AC97_ALC650_SURR_DAC_VOL, 8, 0, 31, 1), + AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1), + AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1), }; /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ @@ -1098,7 +1164,6 @@ static const snd_kcontrol_new_t snd_ac97_ymf753_controls_spdif[3] = { static int snd_ac97_free(ac97_t *ac97) { if (ac97) { - snd_ac97_proc_done(ac97); if (ac97->private_free) ac97->private_free(ac97); snd_magic_kfree(ac97); @@ -1686,6 +1751,32 @@ static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name) sprintf(name + strlen(name), " (%x)", id & 0xff); } + +/** + * snd_ac97_mixer - create an AC97 codec component + * @card: the card instance + * @_ac97: the template of ac97, including index, callbacks and + * the private data. + * @rac97: the pointer to store the new ac97 instance. + * + * Creates an AC97 codec component. An ac97_t instance is newly + * allocated and initialized from the template (_ac97). The codec + * is then initialized by the standard procedure. + * + * The template must include the valid callbacks (at least read and + * write), the codec number (num) and address (addr), and the private + * data (private_data). The other callbacks, wait and reset, are not + * mandantory. + * + * The clock is set to 48000. If another clock is needed, reset + * ac97->clock manually afterwards. + * + * The ac97 instance is registered as a low-level device, so you don't + * have to release it manually. + * + * Returns zero if sucessful, or a negative error code on failure. + */ + int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) { int err; @@ -1866,7 +1957,7 @@ int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) } /* - + * proc interface */ static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) @@ -2089,46 +2180,14 @@ static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97) sprintf(name, "ac97#%d-%d", ac97->addr, ac97->num); else sprintf(name, "ac97#%d", ac97->addr); - if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ac97; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; - entry->c.text.read = snd_ac97_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ac97->proc_entry = entry; + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read); if (ac97->num) sprintf(name, "ac97#%d-%dregs", ac97->addr, ac97->num); else sprintf(name, "ac97#%dregs", ac97->addr); - if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ac97; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 1024; - entry->c.text.read = snd_ac97_proc_regs_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ac97->proc_regs_entry = entry; -} - -static void snd_ac97_proc_done(ac97_t * ac97) -{ - if (ac97->proc_regs_entry) { - snd_info_unregister(ac97->proc_regs_entry); - ac97->proc_regs_entry = NULL; - } - if (ac97->proc_entry) { - snd_info_unregister(ac97->proc_entry); - ac97->proc_entry = NULL; - } + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read); } /* @@ -2174,6 +2233,23 @@ static int set_spdif_rate(ac97_t *ac97, unsigned short rate) return 0; } +/** + * snd_ac97_set_rate - change the rate of the given input/output. + * @ac97: the ac97 instance + * @reg: the register to change + * @rate: the sample rate to set + * + * Changes the rate of the given input/output on the codec. + * If the codec doesn't support VAR, the rate must be 48000 (except + * for SPDIF). + * + * The valid registers are AC97_PMC_MIC_ADC_RATE, + * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE and AC97_SPDIF. + * The SPDIF register is a pseudo-register to change the rate of SPDIF + * (only if supported). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) { unsigned short mask; @@ -2213,8 +2289,11 @@ int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) #ifdef CONFIG_PM -/* - * general suspend procedure +/** + * snd_ac97_suspend - General suspend function for AC97 codec + * @ac97: the ac97 instance + * + * Suspends the codec, power down the chip. */ void snd_ac97_suspend(ac97_t *ac97) { @@ -2231,8 +2310,12 @@ void snd_ac97_suspend(ac97_t *ac97) snd_ac97_write(ac97, AC97_POWERDOWN, power); } -/* - * general resume procedure +/** + * snd_ac97_resume - General resume function for AC97 codec + * @ac97: the ac97 instance + * + * Do the standard resume procedure, power up and restoring the + * old register values. */ void snd_ac97_resume(ac97_t *ac97) { diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index 22afe9f7d82b..18e1731d9bcf 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -33,7 +33,6 @@ MODULE_LICENSE("GPL"); #define chip_t ak4531_t static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531); -static void snd_ak4531_proc_done(ak4531_t * ak4531); /* * @@ -314,7 +313,6 @@ AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0) static int snd_ak4531_free(ak4531_t *ak4531) { if (ak4531) { - snd_ak4531_proc_done(ak4531); if (ak4531->private_free) ak4531->private_free(ak4531); snd_magic_kfree(ak4531); @@ -425,26 +423,8 @@ static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(card, "ak4531", card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ak4531; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_ak4531_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ak4531->proc_entry = entry; -} - -static void snd_ak4531_proc_done(ak4531_t * ak4531) -{ - if (ak4531->proc_entry) { - snd_info_unregister(ak4531->proc_entry); - ak4531->proc_entry = NULL; - } + if (! snd_card_proc_new(card, "ak4531", &entry)) + snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read); } EXPORT_SYMBOL(snd_ak4531_mixer); diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index ce0a20f9f7e5..b3a02850d812 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -472,7 +472,6 @@ struct snd_stru_cmipci { snd_rawmidi_t *rmidi; spinlock_t reg_lock; - snd_info_entry_t *proc_entry; }; @@ -2746,26 +2745,8 @@ static void __devinit snd_cmipci_proc_init(cmipci_t *cm) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(cm->card, "cmipci", cm->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = cm; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_cmipci_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - cm->proc_entry = entry; -} - -static void snd_cmipci_proc_done(cmipci_t *cm) -{ - if (cm->proc_entry) { - snd_info_unregister(cm->proc_entry); - cm->proc_entry = NULL; - } + if (! snd_card_proc_new(cm->card, "cmipci", &entry)) + snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read); } @@ -2832,8 +2813,6 @@ static void __devinit query_chip(cmipci_t *cm) static int snd_cmipci_free(cmipci_t *cm) { - snd_cmipci_proc_done(cm); - if (cm->irq >= 0) { snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); @@ -2949,20 +2928,10 @@ static int __devinit snd_cmipci_create(snd_card_t *card, /* Assume TX and compatible chip set (Autodetection required for VX chip sets) */ switch (pci->device) { - struct list_head *pos; - int txvx; case PCI_DEVICE_ID_CMEDIA_CM8738: case PCI_DEVICE_ID_CMEDIA_CM8738B: - txvx = 1; - list_for_each(pos, &(pci->global_list)) { - struct pci_dev * cur = list_entry(pos, struct pci_dev, global_list); - if (cur->vendor != 0x8086) /* PCI_VENDOR_ID_INTEL */ - continue; - if (cur->device != 0x7030) /* PCI_DEVICE_ID_INTEL_82437VX */ - continue; - txvx = 0; - } - if (txvx) + /* PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX */ + if (! pci_find_device(0x8086, 0x7030, NULL)) snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_TXVX); break; default: diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index a9468e9e127c..11207a67b87e 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -499,13 +499,9 @@ struct snd_cs4281 { unsigned int spurious_dhtc_irq; unsigned int spurious_dtc_irq; - void *proc_entry_BA0; - void *proc_entry_BA1; - spinlock_t reg_lock; unsigned int midcr; unsigned int uartm; - snd_info_entry_t *proc_entry; struct snd_cs4281_gameport *gameport; @@ -1251,55 +1247,19 @@ static void __devinit snd_cs4281_proc_init(cs4281_t * chip) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(chip->card, "cs4281", chip->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_cs4281_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - chip->proc_entry = entry; - if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA0", chip->card->proc_root)) != NULL) { + if (! snd_card_proc_new(chip->card, "cs4281", &entry)) + snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read); + if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; entry->c.ops = &snd_cs4281_proc_ops_BA0; entry->size = CS4281_BA0_SIZE; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } } - chip->proc_entry_BA0 = entry; - if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA1", chip->card->proc_root)) != NULL) { + if (! snd_card_proc_new(chip->card, "cs4281_BA1", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; entry->c.ops = &snd_cs4281_proc_ops_BA1; entry->size = CS4281_BA1_SIZE; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } - } - chip->proc_entry_BA1 = entry; -} - -static void snd_cs4281_proc_done(cs4281_t * chip) -{ - if (chip->proc_entry_BA1) { - snd_info_unregister(chip->proc_entry_BA1); - chip->proc_entry_BA1 = NULL; - } - if (chip->proc_entry_BA0) { - snd_info_unregister(chip->proc_entry_BA0); - chip->proc_entry_BA0 = NULL; - } - if (chip->proc_entry) { - snd_info_unregister(chip->proc_entry); - chip->proc_entry = NULL; } } @@ -1414,7 +1374,6 @@ static int snd_cs4281_free(cs4281_t *chip) kfree(chip->gameport); } #endif - snd_cs4281_proc_done(chip); if (chip->irq >= 0) synchronize_irq(chip->irq); @@ -2097,9 +2056,8 @@ static void cs4281_suspend(cs4281_t *chip) u32 ulCLK; int i; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); @@ -2132,8 +2090,6 @@ static void cs4281_suspend(cs4281_t *chip) snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void cs4281_resume(cs4281_t *chip) @@ -2142,9 +2098,8 @@ static void cs4281_resume(cs4281_t *chip) int i; u32 ulCLK; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; pci_enable_device(chip->pci); @@ -2169,8 +2124,6 @@ static void cs4281_resume(cs4281_t *chip) snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index d4a97f58c4dd..f8546a2979f1 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -26,7 +26,7 @@ * at Cirrus for have helping me out with the DSP, however we * still dont have sufficient documentation and technical * references to be able to implement all fancy feutures - * supported by the cs46xx DPS's. + * supported by the cs46xx DSP's. * Benny * * This program is free software; you can redistribute it and/or modify @@ -57,6 +57,8 @@ #include #include #include +#include +#include #include #include @@ -730,10 +732,12 @@ static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream, snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t diff; cs46xx_pcm_t * cpcm; - int buffer_size = runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size; cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); + buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift; + diff = runtime->control->appl_ptr - cpcm->appl_ptr; if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) @@ -774,7 +778,7 @@ static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream, cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->capt.appl_ptr; - int buffer_size = runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size = runtime->period_size * CS46XX_FRAGS << chip->capt.shift; if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; @@ -830,7 +834,7 @@ static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_ size_t ptr; cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); ssize_t bytes; - int buffer_size = substream->runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << cpcm->shift; #ifdef CONFIG_SND_CS46XX_NEW_DSP snd_assert (cpcm->pcm_channel,return -ENXIO); @@ -865,7 +869,7 @@ static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t cs46xx_t *chip = snd_pcm_substream_chip(substream); size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; ssize_t bytes = ptr - chip->capt.hw_io; - int buffer_size = substream->runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << chip->capt.shift; if (bytes < 0) bytes += buffer_size; @@ -1071,7 +1075,7 @@ static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, int err; cs46xx_t *chip = snd_pcm_substream_chip(substream); int sample_rate = params_rate(hw_params); - int period_size = params_period_size(hw_params); + int period_size = params_period_bytes(hw_params); cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -1090,12 +1094,15 @@ static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, return -ENXIO; } - if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size * 4)) { + + if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) { up (&chip->spos_mutex); return -EINVAL; } - snd_printdd ("period_size (%d), periods (%d)\n", - period_size, params_periods(hw_params)); + + snd_printdd ("period_size (%d), periods (%d) buffer_size(%d)\n", + period_size, params_periods(hw_params), + params_buffer_bytes(hw_params)); #endif if (params_periods(hw_params) == CS46XX_FRAGS) { @@ -1255,12 +1262,10 @@ static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream, cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err; - int period_size = params_period_size(hw_params); + int period_size = params_period_bytes(hw_params); #ifdef CONFIG_SND_CS46XX_NEW_DSP - snd_printdd ("capture period size (%d)\n",period_size); - - cs46xx_dsp_pcm_ostream_set_period (chip,period_size * 4); + cs46xx_dsp_pcm_ostream_set_period (chip,period_size); #endif if (runtime->periods == CS46XX_FRAGS) { if (runtime->dma_area != chip->capt.hw_area) @@ -1445,7 +1450,7 @@ static snd_pcm_hardware_t snd_cs46xx_capture = .fifo_size = 0, }; -static unsigned int period_sizes[] = { 8, 16, 32, 64, 128, 256, 512 }; +static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 }; #define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) @@ -1488,8 +1493,11 @@ static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pc cpcm->pcm_channel = NULL; cpcm->pcm_channel_id = pcm_channel_id; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_sizes); + up (&chip->spos_mutex); #else chip->playback_pcm = cpcm; /* HACK */ @@ -1565,7 +1573,8 @@ static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream) chip->amplifier_ctrl(chip, 1); #ifdef CONFIG_SND_CS46XX_NEW_DSP - snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_sizes); #endif return 0; @@ -2874,19 +2883,13 @@ static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip) for (idx = 0; idx < 5; idx++) { snd_cs46xx_region_t *region = &chip->region.idx[idx]; - entry = snd_info_create_card_entry(card, region->name, card->proc_root); - if (entry) { + if (! snd_card_proc_new(card, region->name, &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; entry->c.ops = &snd_cs46xx_proc_io_ops; entry->size = region->size; entry->mode = S_IFREG | S_IRUSR; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } } - region->proc_entry = entry; } #ifdef CONFIG_SND_CS46XX_NEW_DSP cs46xx_dsp_proc_init(card, chip); @@ -2896,15 +2899,6 @@ static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip) static int snd_cs46xx_proc_done(cs46xx_t *chip) { - int idx; - - for (idx = 0; idx < 5; idx++) { - snd_cs46xx_region_t *region = &chip->region.idx[idx]; - if (region->proc_entry) { - snd_info_unregister((snd_info_entry_t *) region->proc_entry); - region->proc_entry = NULL; - } - } #ifdef CONFIG_SND_CS46XX_NEW_DSP cs46xx_dsp_proc_done(chip); #endif @@ -3716,16 +3710,13 @@ void snd_cs46xx_suspend(cs46xx_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); snd_cs46xx_hw_stop(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } void snd_cs46xx_resume(cs46xx_t *chip) @@ -3733,9 +3724,8 @@ void snd_cs46xx_resume(cs46xx_t *chip) snd_card_t *card = chip->card; int amp_saved; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; pci_enable_device(chip->pci); amp_saved = chip->amplifier; @@ -3764,8 +3754,6 @@ void snd_cs46xx_resume(cs46xx_t *chip) chip->active_ctrl(chip, -1); } snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } static int snd_cs46xx_set_power_state(snd_card_t *card, unsigned int power_state) diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index fe5c5d0b3698..0e9a18fb235c 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -80,6 +80,7 @@ MODULE_PARM_SYNTAX(enable_ir, SNDRV_ENABLE_DESC); static struct pci_device_id snd_emu10k1_ids[] __devinitdata = { { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */ + { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Dell OEM version (EMU10K1) */ { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */ { 0, } }; diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 48b558a76194..2575010a28ed 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -494,7 +494,6 @@ static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu) static int snd_emu10k1_free(emu10k1_t *emu) { - snd_emu10k1_proc_done(emu); if (emu->res_port != NULL) { /* avoid access to already used hardware */ snd_emu10k1_fx8010_tram_setup(emu, 0); snd_emu10k1_done(emu); diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 1885bebc908c..1c3d91f84169 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -365,7 +365,7 @@ static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, snd_util_memblk_t *memblk; if (epcm->memblk != NULL) snd_emu10k1_free_pages(emu, epcm->memblk); - memblk = snd_emu10k1_alloc_pages(emu, (struct snd_sg_buf *)substream->dma_private); + memblk = snd_emu10k1_alloc_pages(emu, substream); if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) { epcm->start_addr = 0; return -ENOMEM; diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 1fcade671328..fa0e42af4f6d 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -239,111 +239,43 @@ int __devinit snd_emu10k1_proc_init(emu10k1_t * emu) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(emu->card, "emu10k1", emu->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 4096; - entry->c.text.read = snd_emu10k1_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - emu->proc_entry = entry; - entry = NULL; - if ((entry = snd_info_create_card_entry(emu->card, "fx8010_gpr", emu->card->proc_root)) != NULL) { + if (! snd_card_proc_new(emu->card, "emu10k1", &entry)) + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read); + + if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_GPR; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_gpr = entry; - entry = NULL; - if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_data", emu->card->proc_root)) != NULL) { + if (!emu->audigy && ! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_TANKMEM_DATA; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_tram_data = entry; - entry = NULL; - if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_addr", emu->card->proc_root)) != NULL) { + if (!emu->audigy && ! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_TANKMEM_ADDR; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_tram_addr = entry; - entry = NULL; - if ((entry = snd_info_create_card_entry(emu->card, "fx8010_code", emu->card->proc_root)) != NULL) { + if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_CODE; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_code = entry; - entry = NULL; - if ((entry = snd_info_create_card_entry(emu->card, "fx8010_acode", emu->card->proc_root)) != NULL) { + if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->c.text.read_size = 64*1024; entry->c.text.read = snd_emu10k1_proc_acode_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - emu->proc_entry_fx8010_iblocks = entry; - return 0; -} - -int snd_emu10k1_proc_done(emu10k1_t * emu) -{ - if (emu->proc_entry) { - snd_info_unregister(emu->proc_entry); - emu->proc_entry = NULL; - } - if (emu->proc_entry_fx8010_gpr) { - snd_info_unregister(emu->proc_entry_fx8010_gpr); - emu->proc_entry_fx8010_gpr = NULL; - } - if (emu->proc_entry_fx8010_tram_data) { - snd_info_unregister(emu->proc_entry_fx8010_tram_data); - emu->proc_entry_fx8010_tram_data = NULL; - } - if (emu->proc_entry_fx8010_tram_addr) { - snd_info_unregister(emu->proc_entry_fx8010_tram_addr); - emu->proc_entry_fx8010_tram_addr = NULL; - } - if (emu->proc_entry_fx8010_code) { - snd_info_unregister(emu->proc_entry_fx8010_code); - emu->proc_entry_fx8010_code = NULL; - } - if (emu->proc_entry_fx8010_iblocks) { - snd_info_unregister(emu->proc_entry_fx8010_iblocks); - emu->proc_entry_fx8010_iblocks = NULL; } return 0; } diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index dc1f597eb70c..5037fb5fdea4 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -25,6 +25,7 @@ #include #include #include +#include /* page arguments of these two macros are Emu page (4096 bytes), not like * aligned pages in others @@ -288,8 +289,9 @@ int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk) * page allocation for DMA */ snd_util_memblk_t * -snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf) +snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream) { + struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return NULL); snd_util_memhdr_t *hdr; emu10k1_memblk_t *blk; int page, err, idx; @@ -306,7 +308,7 @@ snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf) return NULL; } /* fill buffer addresses but pointers are not stored so that - * snd_free_pci_pages() is not called in in synth_free() + * snd_free_pci_page() is not called in in synth_free() */ idx = 0; for (page = blk->first_page; page <= blk->last_page; page++, idx++) { @@ -430,11 +432,11 @@ static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) get_single_page_range(emu->memhdr, blk, &first_page, &last_page); /* allocate kernel pages */ for (page = first_page; page <= last_page; page++) { - ptr = snd_malloc_pci_pages(emu->pci, PAGE_SIZE, &addr); + ptr = snd_malloc_pci_page(emu->pci, &addr); if (ptr == NULL) goto __fail; if (! is_valid_page(addr)) { - snd_free_pci_pages(emu->pci, PAGE_SIZE, ptr, addr); + snd_free_pci_page(emu->pci, ptr, addr); goto __fail; } emu->page_addr_table[page] = addr; @@ -446,7 +448,7 @@ __fail: /* release allocated pages */ last_page = page - 1; for (page = first_page; page <= last_page; page++) { - snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } @@ -464,7 +466,7 @@ static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) get_single_page_range(emu->memhdr, blk, &first_page, &last_page); for (page = first_page; page <= last_page; page++) { if (emu->page_ptr_table[page]) - snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 1b3c1f7b3036..d0e112a04a15 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -398,8 +398,6 @@ struct _snd_ensoniq { unsigned int spdif_default; unsigned int spdif_stream; - snd_info_entry_t *proc_entry; - #ifdef CHIP1370 unsigned char *bugbuf; dma_addr_t bugbuf_addr; @@ -1264,9 +1262,10 @@ static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t * Mixer section */ +/* + * ENS1371 mixer (including SPDIF interface) + */ #ifdef CHIP1371 - - static int snd_ens1373_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; @@ -1490,6 +1489,8 @@ static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) #endif /* CHIP1371 */ +/* generic control callbacks for ens1370 and for joystick */ +#if defined(CHIP1370) || defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) #define ENSONIQ_CONTROL(xname, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \ .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \ @@ -1517,7 +1518,6 @@ static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value } #ifdef CHIP1370 - static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); @@ -1535,14 +1535,21 @@ static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value spin_unlock_irqrestore(&ensoniq->reg_lock, flags); return change; } +#endif /* CHIP1370 */ +#endif /* CHIP1370 || GAMEPORT */ -#define ES1370_CONTROLS 2 +/* + * ENS1370 mixer + */ +#ifdef CHIP1370 static snd_kcontrol_new_t snd_es1370_controls[2] __devinitdata = { ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0), ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1) }; +#define ES1370_CONTROLS ARRAY_SIZE(snd_es1370_controls) + static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531) { ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); @@ -1725,26 +1732,8 @@ static void __devinit snd_ensoniq_proc_init(ensoniq_t * ensoniq) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(ensoniq->card, "audiopci", ensoniq->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ensoniq; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_ensoniq_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ensoniq->proc_entry = entry; -} - -static void snd_ensoniq_proc_done(ensoniq_t * ensoniq) -{ - if (ensoniq->proc_entry) { - snd_info_unregister(ensoniq->proc_entry); - ensoniq->proc_entry = NULL; - } + if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry)) + snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read); } /* @@ -1757,7 +1746,6 @@ static int snd_ensoniq_free(ensoniq_t *ensoniq) if (ensoniq->ctrl & ES_JYSTK_EN) snd_ensoniq_joy_disable(ensoniq); #endif - snd_ensoniq_proc_done(ensoniq); if (ensoniq->irq < 0) goto __hw_end; #ifdef CHIP1370 diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 0b91028e2768..4de0e08b524c 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -202,8 +202,6 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); typedef struct _snd_es1938 es1938_t; struct _snd_es1938 { - unsigned long dma1size; - unsigned long dma2size; int irq; unsigned long io_port; @@ -840,6 +838,25 @@ static int snd_es1938_capture_copy(snd_pcm_substream_t *substream, return 0; } +/* + * buffer management + */ +static int snd_es1938_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t * hw_params) + +{ + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es1938_pcm_hw_free(snd_pcm_substream_t *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + /* ---------------------------------------------------------------------- * Audio1 Capture (ADC) * ----------------------------------------------------------------------*/ @@ -890,8 +907,6 @@ static int snd_es1938_capture_open(snd_pcm_substream_t * substream) if (chip->playback2_substream) return -EAGAIN; - if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma2size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) - return -ENOMEM; chip->capture_substream = substream; runtime->hw = snd_es1938_capture; snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, @@ -907,15 +922,11 @@ static int snd_es1938_playback_open(snd_pcm_substream_t * substream) switch (substream->number) { case 0: - if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) - return -ENOMEM; chip->playback1_substream = substream; break; case 1: if (chip->capture_substream) return -EAGAIN; - if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) - return -ENOMEM; chip->playback2_substream = substream; break; default: @@ -963,6 +974,8 @@ static snd_pcm_ops_t snd_es1938_playback_ops = { .open = snd_es1938_playback_open, .close = snd_es1938_playback_close, .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1938_pcm_hw_params, + .hw_free = snd_es1938_pcm_hw_free, .prepare = snd_es1938_playback_prepare, .trigger = snd_es1938_playback_trigger, .pointer = snd_es1938_playback_pointer, @@ -972,6 +985,8 @@ static snd_pcm_ops_t snd_es1938_capture_ops = { .open = snd_es1938_capture_open, .close = snd_es1938_capture_close, .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1938_pcm_hw_params, + .hw_free = snd_es1938_pcm_hw_free, .prepare = snd_es1938_capture_prepare, .trigger = snd_es1938_capture_trigger, .pointer = snd_es1938_capture_pointer, @@ -1364,8 +1379,6 @@ static int snd_es1938_dev_free(snd_device_t *device) static int __devinit snd_es1938_create(snd_card_t * card, struct pci_dev * pci, - unsigned long dma1size, - unsigned long dma2size, es1938_t ** rchip) { es1938_t *chip; @@ -1393,8 +1406,6 @@ static int __devinit snd_es1938_create(snd_card_t * card, spin_lock_init(&chip->mixer_lock); chip->card = card; chip->pci = pci; - chip->dma1size = dma1size; - chip->dma2size = dma2size; chip->io_port = pci_resource_start(pci, 0); if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) { snd_es1938_free(chip); @@ -1608,10 +1619,7 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci, return -ENODEV; } } - if ((err = snd_es1938_create(card, pci, - 64 * 1024, - 64 * 1024, - &chip)) < 0) { + if ((err = snd_es1938_create(card, pci, &chip)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 29c94cbb229c..d27a8d2a73e4 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2420,15 +2420,12 @@ static void es1968_suspend(es1968_t *chip) if (! chip->do_pm) return; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); snd_es1968_bob_stop(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void es1968_resume(es1968_t *chip) @@ -2438,9 +2435,8 @@ static void es1968_resume(es1968_t *chip) if (! chip->do_pm) return; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* restore all our config */ pci_enable_device(chip->pci); @@ -2459,8 +2455,6 @@ static void es1968_resume(es1968_t *chip) if (atomic_read(&chip->bobclient)) snd_es1968_bob_start(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 8133aa9796c3..5c5120904532 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1681,12 +1681,11 @@ static void snd_ice1712_proc_read(snd_info_entry_t *entry, snd_iprintf(buffer, " CCS%02x : 0x%02x\n", idx, inb(ice->port+idx)); for (idx = 0x0; idx < 0x30 ; idx++) snd_iprintf(buffer, " MT%02x : 0x%02x\n", idx, inb(ice->profi_port+idx)); - - } - else { + } else { snd_iprintf(buffer, " PSDOUT03 : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_PSDOUT03))); snd_iprintf(buffer, " CAPTURE : 0x%08x\n", inl(ICEMT(ice, ROUTE_CAPTURE))); snd_iprintf(buffer, " SPDOUT : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT))); + snd_iprintf(buffer, " RATE : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE))); } } @@ -1694,26 +1693,8 @@ static void __devinit snd_ice1712_proc_init(ice1712_t * ice) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ice; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 2048; - entry->c.text.read = snd_ice1712_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ice->proc_entry = entry; -} - -static void snd_ice1712_proc_done(ice1712_t * ice) -{ - if (ice->proc_entry) { - snd_info_unregister(ice->proc_entry); - ice->proc_entry = NULL; - } + if (! snd_card_proc_new(ice->card, "ice1712", &entry)) + snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read); } /* @@ -2689,7 +2670,6 @@ static int snd_ice1712_free(ice1712_t *ice) outb(0xff, ICEREG(ice, IRQMASK)); /* --- */ __hw_end: - snd_ice1712_proc_done(ice); if (ice->irq >= 0) { synchronize_irq(ice->irq); free_irq(ice->irq, (void *) ice); diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 392178381d5e..bbfa3c2435b2 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -322,7 +322,6 @@ typedef struct { unsigned char piv_saved; unsigned short picb_saved; #endif - snd_info_entry_t *proc_entry; } ichdev_t; typedef struct _snd_intel8x0 intel8x0_t; @@ -368,7 +367,6 @@ struct _snd_intel8x0 { spinlock_t reg_lock; spinlock_t ac97_lock; - snd_info_entry_t *proc_entry; u32 bdbars_count; u32 *bdbars; @@ -1854,8 +1852,6 @@ static int snd_intel8x0_chip_init(intel8x0_t *chip) return 0; } -static void snd_intel8x0_proc_done(intel8x0_t * chip); - static int snd_intel8x0_free(intel8x0_t *chip) { int i; @@ -1871,7 +1867,6 @@ static int snd_intel8x0_free(intel8x0_t *chip) /* --- */ synchronize_irq(chip->irq); __hw_end: - snd_intel8x0_proc_done(chip); if (chip->bdbars) snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); if (chip->remap_addr) @@ -1900,17 +1895,15 @@ static void intel8x0_suspend(intel8x0_t *chip) { snd_card_t *card = chip->card; - chip->in_suspend = 1; - snd_power_lock(card); - if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + if (chip->in_suspend || + card->power_state == SNDRV_CTL_POWER_D3hot) + return; + chip->in_suspend = 1; snd_pcm_suspend_all(chip->pcm); if (chip->pcm_mic) snd_pcm_suspend_all(chip->pcm_mic); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void intel8x0_resume(intel8x0_t *chip) @@ -1918,9 +1911,9 @@ static void intel8x0_resume(intel8x0_t *chip) snd_card_t *card = chip->card; int i; - snd_power_lock(card); - if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + if (! chip->in_suspend || + card->power_state == SNDRV_CTL_POWER_D0) + return; pci_enable_device(chip->pci); snd_intel8x0_chip_init(chip); @@ -1930,8 +1923,6 @@ static void intel8x0_resume(intel8x0_t *chip) chip->in_suspend = 0; snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND @@ -2100,26 +2091,8 @@ static void __devinit snd_intel8x0_proc_init(intel8x0_t * chip) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(chip->card, "intel8x0", chip->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 2048; - entry->c.text.read = snd_intel8x0_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - chip->proc_entry = entry; -} - -static void snd_intel8x0_proc_done(intel8x0_t * chip) -{ - if (chip->proc_entry) { - snd_info_unregister(chip->proc_entry); - chip->proc_entry = NULL; - } + if (! snd_card_proc_new(chip->card, "intel8x0", &entry)) + snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read); } static int snd_intel8x0_dev_free(snd_device_t *device) @@ -2493,6 +2466,8 @@ static struct pci_device_id snd_intel8x0_joystick_ids[] __devinitdata = { // { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 440MX */ // { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SI7012 */ { 0x10de, 0x01b2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE */ + { 0x10de, 0x006b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE2 */ + { 0x10de, 0x00db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE3 */ { 0, } }; diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 75f26090ed67..1e6ef1efaf3b 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -368,7 +368,6 @@ struct _snd_korg1212 { snd_pcm_substream_t *playback_substream; snd_pcm_substream_t *capture_substream; - snd_info_entry_t * proc_entry; CardState cardState; int running; @@ -1898,26 +1897,8 @@ static void __devinit snd_korg1212_proc_init(korg1212_t *korg1212) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(korg1212->card, "korg1212", korg1212->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = korg1212; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_korg1212_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - korg1212->proc_entry = entry; -} - -static void snd_korg1212_proc_done(korg1212_t * korg1212) -{ - if (korg1212->proc_entry) { - snd_info_unregister(korg1212->proc_entry); - korg1212->proc_entry = NULL; - } + if (! snd_card_proc_new(korg1212->card, "korg1212", &entry)) + snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read); } static int __devinit snd_korg1212_create(korg1212_t *korg1212) @@ -2174,8 +2155,6 @@ snd_korg1212_free(void *private_data) return; } - snd_korg1212_proc_done(korg1212); - snd_korg1212_TurnOffIdleMonitor(korg1212); snd_korg1212_DisableCardInterrupts(korg1212); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 18749650cb71..003f7de25dff 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2377,9 +2377,8 @@ static void m3_suspend(m3_t *chip) snd_card_t *card = chip->card; int i, index; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); @@ -2400,8 +2399,6 @@ static void m3_suspend(m3_t *chip) snd_m3_outw(chip, 0xffff, 0x54); snd_m3_outw(chip, 0xffff, 0x56); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void m3_resume(m3_t *chip) @@ -2409,9 +2406,8 @@ static void m3_resume(m3_t *chip) snd_card_t *card = chip->card; int i, index; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* first lets just bring everything back. .*/ snd_m3_outw(chip, 0, 0x54); @@ -2442,8 +2438,6 @@ static void m3_resume(m3_t *chip) snd_m3_amp_enable(chip, 1); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 7fedaab022c6..2336a659f89a 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1270,24 +1270,20 @@ static void nm256_suspend(nm256_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); chip->coeffs_current = 0; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void nm256_resume(nm256_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* Perform a full reset on the hardware */ pci_enable_device(chip->pci); @@ -1297,8 +1293,6 @@ static void nm256_resume(nm256_t *chip) snd_ac97_resume(chip->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index b1f061459a3d..bbe85ce76cf4 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -220,7 +220,6 @@ typedef struct snd_rme32 { snd_pcm_t *spdif_pcm; snd_pcm_t *adat_pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; } rme32_t; @@ -561,18 +560,22 @@ static int snd_rme32_setclockmode(rme32_t * rme32, int mode) { switch (mode) { case RME32_CLOCKMODE_SLAVE: + /* AutoSync */ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) & ~RME32_WCR_FREQ_1; break; case RME32_CLOCKMODE_MASTER_32: + /* Internal 32.0kHz */ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) & ~RME32_WCR_FREQ_1; break; case RME32_CLOCKMODE_MASTER_44: + /* Internal 44.1kHz */ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) | RME32_WCR_FREQ_1; break; case RME32_CLOCKMODE_MASTER_48: + /* Internal 48.0kHz */ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) | RME32_WCR_FREQ_1; break; @@ -1240,7 +1243,6 @@ static void snd_rme32_free(void *private_data) if (rme32->irq >= 0) { snd_rme32_playback_stop(rme32); snd_rme32_capture_stop(rme32); - snd_rme32_proc_done(rme32); free_irq(rme32->irq, (void *) rme32); rme32->irq = -1; } @@ -1464,9 +1466,9 @@ snd_rme32_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) snd_rme32_playback_getrate(rme32)); } if (rme32->wcreg & RME32_RCR_KMODE) { - snd_iprintf(buffer, " clock mode: slave\n"); + snd_iprintf(buffer, " sample clock source: AutoSync\n"); } else { - snd_iprintf(buffer, " clock mode: master\n"); + snd_iprintf(buffer, " sample clock source: Internal\n"); } if (rme32->wcreg & RME32_WCR_PRO) { snd_iprintf(buffer, " format: AES/EBU (professional)\n"); @@ -1484,26 +1486,8 @@ static void __devinit snd_rme32_proc_init(rme32_t * rme32) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(rme32->card, "rme32", rme32->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = rme32; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_rme32_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - rme32->proc_entry = entry; -} - -static void snd_rme32_proc_done(rme32_t * rme32) -{ - if (rme32->proc_entry) { - snd_info_unregister(rme32->proc_entry); - rme32->proc_entry = NULL; - } + if (! snd_card_proc_new(rme32->card, "rme32", &entry)) + snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read); } /* @@ -1646,7 +1630,10 @@ static int snd_rme32_info_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) { - static char *texts[4] = { "Slave", "Master (32.0 kHz)", "Master (44.1 kHz)", "Master (48.0 kHz)" }; + static char *texts[4] = { "AutoSync", + "Internal 32.0kHz", + "Internal 44.1kHz", + "Internal 48.0kHz" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 65279749c7bf..5986e8e9e015 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -257,7 +257,6 @@ typedef struct snd_rme96 { snd_pcm_t *spdif_pcm; snd_pcm_t *adat_pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; } rme96_t; @@ -308,9 +307,6 @@ snd_rme96_capture_pointer(snd_pcm_substream_t *substream); static void __devinit snd_rme96_proc_init(rme96_t *rme96); -static void -snd_rme96_proc_done(rme96_t *rme96); - static int snd_rme96_create_switches(snd_card_t *card, rme96_t *rme96); @@ -329,6 +325,20 @@ snd_rme96_capture_ptr(rme96_t *rme96) & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog; } +static int +snd_rme96_ratecode(int rate) +{ + switch (rate) { + case 32000: return SNDRV_PCM_RATE_32000; + case 44100: return SNDRV_PCM_RATE_44100; + case 48000: return SNDRV_PCM_RATE_48000; + case 64000: return SNDRV_PCM_RATE_64000; + case 88200: return SNDRV_PCM_RATE_88200; + case 96000: return SNDRV_PCM_RATE_96000; + } + return 0; +} + static int snd_rme96_playback_silence(snd_pcm_substream_t *substream, int channel, /* not used (interleaved data) */ @@ -671,8 +681,8 @@ snd_rme96_playback_getrate(rme96_t *rme96) if (!(rme96->wcreg & RME96_WCR_MASTER) && (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { - /* slave clock */ - return rate; + /* slave clock */ + return rate; } rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) + (((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1); @@ -974,14 +984,22 @@ static int snd_rme96_playback_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { - unsigned long flags; + unsigned long flags; rme96_t *rme96 = _snd_pcm_substream_chip(substream); - int err; + int err, rate, dummy; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) return err; spin_lock_irqsave(&rme96->lock, flags); - if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + if (params_rate(params) != rate) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { spin_unlock_irqrestore(&rme96->lock, flags); return err; } @@ -1024,7 +1042,8 @@ snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, { unsigned long flags; rme96_t *rme96 = _snd_pcm_substream_chip(substream); - int err, isadat; + snd_pcm_runtime_t *runtime = substream->runtime; + int err, isadat, rate; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) return err; @@ -1040,9 +1059,6 @@ snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, spin_unlock_irqrestore(&rme96->lock, flags); return err; } - } else if (params_rate(params) != snd_rme96_capture_getrate(rme96, &isadat)) { - spin_unlock_irqrestore(&rme96->lock, flags); - return -EBUSY; } snd_rme96_setframelog(rme96, params_channels(params), 0); if (rme96->playback_periodsize != 0) { @@ -1052,7 +1068,18 @@ snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, spin_unlock_irqrestore(&rme96->lock, flags); return -EBUSY; } - } + } else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if (params_rate(params) != rate) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + if ((isadat && runtime->hw.channels_min == 2) || + (!isadat && runtime->hw.channels_min == 8)) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + } rme96->capture_periodsize = params_period_size(params) << rme96->capture_frlog; snd_rme96_set_period_properties(rme96, rme96->capture_periodsize); @@ -1138,7 +1165,7 @@ snd_rme96_interrupt(int irq, if (rme96->rcreg & RME96_RCR_IRQ) { /* playback */ - snd_pcm_period_elapsed(rme96->playback_substream); + snd_pcm_period_elapsed(rme96->playback_substream); writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); } if (rme96->rcreg & RME96_RCR_IRQ_2) { @@ -1162,12 +1189,17 @@ static int snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) { unsigned long flags; + int rate, dummy; rme96_t *rme96 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irqsave(&rme96->lock, flags); + if (rme96->playback_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->wcreg &= ~RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; @@ -1175,7 +1207,15 @@ snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) rme96->playback_ptr = 0; spin_unlock_irqrestore(&rme96->lock, flags); - runtime->hw = snd_rme96_playback_spdif_info; + runtime->hw = snd_rme96_playback_spdif_info; + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); @@ -1190,28 +1230,31 @@ static int snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream) { unsigned long flags; - int isadat; + int isadat, rate; rme96_t *rme96 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { - /* no input */ - return -EIO; - } - if (isadat) { - /* ADAT input */ - return -EBUSY; - } snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme96->lock, flags); + runtime->hw = snd_rme96_capture_spdif_info; + if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if (isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->capture_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->capture_substream = substream; rme96->capture_ptr = 0; spin_unlock_irqrestore(&rme96->lock, flags); - runtime->hw = snd_rme96_capture_spdif_info; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); @@ -1222,12 +1265,17 @@ static int snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) { unsigned long flags; + int rate, dummy; rme96_t *rme96 = _snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irqsave(&rme96->lock, flags); + if (rme96->playback_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->wcreg |= RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; @@ -1236,6 +1284,14 @@ snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) spin_unlock_irqrestore(&rme96->lock, flags); runtime->hw = snd_rme96_playback_adat_info; + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); return 0; @@ -1245,27 +1301,31 @@ static int snd_rme96_capture_adat_open(snd_pcm_substream_t *substream) { unsigned long flags; - int isadat; + int isadat, rate; rme96_t *rme96 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { - /* no input */ - return -EIO; - } - if (!isadat) { - /* S/PDIF input */ - return -EBUSY; - } snd_pcm_set_sync(substream); + runtime->hw = snd_rme96_capture_adat_info; + if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if (!isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->capture_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->capture_substream = substream; rme96->capture_ptr = 0; spin_unlock_irqrestore(&rme96->lock, flags); - runtime->hw = snd_rme96_capture_adat_info; snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); return 0; @@ -1279,6 +1339,9 @@ snd_rme96_playback_close(snd_pcm_substream_t *substream) int spdif = 0; spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } rme96->playback_substream = NULL; rme96->playback_periodsize = 0; spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0; @@ -1298,6 +1361,9 @@ snd_rme96_capture_close(snd_pcm_substream_t *substream) rme96_t *rme96 = _snd_pcm_substream_chip(substream); spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } rme96->capture_substream = NULL; rme96->capture_periodsize = 0; spin_unlock_irqrestore(&rme96->lock, flags); @@ -1558,7 +1624,6 @@ snd_rme96_free(void *private_data) snd_rme96_capture_stop(rme96); rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); - snd_rme96_proc_done(rme96); free_irq(rme96->irq, (void *)rme96); rme96->irq = -1; } @@ -1843,27 +1908,8 @@ snd_rme96_proc_init(rme96_t *rme96) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(rme96->card, "rme96", rme96->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = rme96; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_rme96_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - rme96->proc_entry = entry; -} - -static void -snd_rme96_proc_done(rme96_t * rme96) -{ - if (rme96->proc_entry) { - snd_info_unregister(rme96->proc_entry); - rme96->proc_entry = NULL; - } + if (! snd_card_proc_new(rme96->card, "rme96", &entry)) + snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read); } /* diff --git a/sound/pci/rme9652/hammerfall_mem.c b/sound/pci/rme9652/hammerfall_mem.c index 067ce2c18fb0..6981baaa43c5 100644 --- a/sound/pci/rme9652/hammerfall_mem.c +++ b/sound/pci/rme9652/hammerfall_mem.c @@ -25,7 +25,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - $Id: hammerfall_mem.c,v 1.6 2002/12/19 15:59:18 tiwai Exp $ + $Id: hammerfall_mem.c,v 1.7 2003/01/06 14:21:28 perex Exp $ Tue Oct 17 2000 Jaroslav Kysela @@ -179,7 +179,7 @@ void snd_hammerfall_free_buffer (struct pci_dev *pcidev, void *addr) printk ("Hammerfall memory allocator: unknown buffer address or PCI device ID"); } -static void __exit hammerfall_free_buffers (void) +static void hammerfall_free_buffers (void) { int i; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 320a057bdfe2..8ce7169d419a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -368,7 +368,6 @@ struct _hdsp { snd_card_t *card; snd_pcm_t *pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; unsigned short mixer_matrix[HDSP_MATRIX_MIXER_SIZE]; }; @@ -2076,27 +2075,8 @@ static void __devinit snd_hdsp_proc_init(hdsp_t *hdsp) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(hdsp->card, "hdsp", hdsp->card->proc_root)) != - NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = hdsp; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_hdsp_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - hdsp->proc_entry = entry; -} - -static void snd_hdsp_proc_done(hdsp_t *hdsp) -{ - if (hdsp->proc_entry) { - snd_info_unregister(hdsp->proc_entry); - hdsp->proc_entry = NULL; - } + if (! snd_card_proc_new(hdsp->card, "hdsp", &entry)) + snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read); } static void snd_hdsp_free_buffers(hdsp_t *hdsp) @@ -3078,7 +3058,6 @@ static int snd_hdsp_free(hdsp_t *hdsp) if (hdsp->irq >= 0) free_irq(hdsp->irq, (void *)hdsp); - snd_hdsp_proc_done(hdsp); snd_hdsp_free_buffers(hdsp); if (hdsp->iobase) diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 3c86c0d418f9..646b925100a2 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -263,7 +263,6 @@ typedef struct snd_rme9652 { snd_card_t *card; snd_pcm_t *pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; } rme9652_t; @@ -1803,27 +1802,8 @@ static void __devinit snd_rme9652_proc_init(rme9652_t *rme9652) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(rme9652->card, "rme9652", rme9652->card->proc_root)) != - NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = rme9652; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_rme9652_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - rme9652->proc_entry = entry; -} - -static void snd_rme9652_proc_done(rme9652_t *rme9652) -{ - if (rme9652->proc_entry) { - snd_info_unregister(rme9652->proc_entry); - rme9652->proc_entry = NULL; - } + if (! snd_card_proc_new(rme9652->card, "rme9652", &entry)) + snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read); } static void snd_rme9652_free_buffers(rme9652_t *rme9652) @@ -1855,7 +1835,6 @@ static int snd_rme9652_free(rme9652_t *rme9652) { if (rme9652->irq >= 0) rme9652_stop(rme9652); - snd_rme9652_proc_done(rme9652); snd_rme9652_free_buffers(rme9652); if (rme9652->iobase) diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 5f1767c27cf0..ecc1be3df90f 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -247,7 +247,6 @@ struct _snd_sonicvibes { snd_hwdep_t *fmsynth; /* S3FM */ spinlock_t reg_lock; - snd_info_entry_t *proc_entry; unsigned int p_dma_size; unsigned int c_dma_size; @@ -1175,26 +1174,8 @@ static void __devinit snd_sonicvibes_proc_init(sonicvibes_t * sonic) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(sonic->card, "sonicvibes", sonic->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = sonic; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_sonicvibes_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - sonic->proc_entry = entry; -} - -static void snd_sonicvibes_proc_done(sonicvibes_t * sonic) -{ - if (sonic->proc_entry) { - snd_info_unregister(sonic->proc_entry); - sonic->proc_entry = NULL; - } + if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry)) + snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read); } /* @@ -1210,7 +1191,6 @@ static int snd_sonicvibes_free(sonicvibes_t *sonic) if (sonic->gameport.io) gameport_unregister_port(&sonic->gameport); #endif - snd_sonicvibes_proc_done(sonic); pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port); pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port); if (sonic->res_sb_port) { diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 0e3496c3c7e0..43b2d84b959f 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -756,16 +757,19 @@ int snd_trident_allocate_pcm_mem(snd_pcm_substream_t * substream, snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; int err; - if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) - return err; - if (err > 0 && trident->tlb.entries) { - if (voice->memblk) - snd_trident_free_pages(trident, voice->memblk); - spin_lock_irq(&trident->reg_lock); - voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - spin_unlock_irq(&trident->reg_lock); - if (voice->memblk == NULL) - return -ENOMEM; + if (trident->tlb.entries) { + if ((err = snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = snd_trident_alloc_pages(trident, substream); + if (voice->memblk == NULL) + return -ENOMEM; + } + } else { + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; } return 0; } @@ -792,7 +796,7 @@ int snd_trident_allocate_evoice(snd_pcm_substream_t * substream, /* voice management */ - if (params_buffer_size(hw_params) / 2 != params_buffer_size(hw_params)) { + if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { if (evoice == NULL) { evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (evoice == NULL) @@ -851,11 +855,14 @@ static int snd_trident_hw_free(snd_pcm_substream_t * substream) snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; snd_trident_voice_t *evoice = voice ? voice->extra : NULL; - if (trident->tlb.entries && voice && voice->memblk) { - snd_trident_free_pages(trident, voice->memblk); - voice->memblk = NULL; - } - snd_pcm_lib_free_pages(substream); + if (trident->tlb.entries) { + if (voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_sgbuf_free(substream); + } else + snd_pcm_lib_free_pages(substream); if (evoice != NULL) { snd_trident_free_voice(trident, evoice); voice->extra = NULL; @@ -990,10 +997,12 @@ static int snd_trident_capture_prepare(snd_pcm_substream_t * substream) outb(0x54, TRID_REG(trident, LEGACY_DMAR11)); // Set channel buffer Address - voice->LBA = runtime->dma_addr; - outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); + /* FIXME: LEGACY_DMAR0 correctly set? */ if (voice->memblk) voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); // set ESO ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1; @@ -1343,6 +1352,7 @@ static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream) voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); /* set Loop Back Address */ + /* FIXME: LBAO?? */ LBAO = runtime->dma_addr; if (voice->memblk) voice->LBA = voice->memblk->offset; @@ -1755,17 +1765,27 @@ static snd_pcm_hardware_t snd_trident_spdif_7018 = .fifo_size = 0, }; +static int snd_trident_sgbuf_init(trident_t *trident, snd_pcm_substream_t *substream) +{ + if (trident->tlb.entries) + return snd_pcm_sgbuf_init(substream, trident->pci, 32); + return 0; +} + +static void snd_trident_sgbuf_delete(trident_t *trident, snd_pcm_substream_t *substream) +{ + if (trident->tlb.entries) + snd_pcm_sgbuf_delete(substream); +} + static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime) { - unsigned long flags; snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; trident_t *trident; if (voice) { trident = voice->trident; - spin_lock_irqsave(&trident->reg_lock, flags); snd_trident_free_voice(trident, voice); - spin_unlock_irqrestore(&trident->reg_lock, flags); } } @@ -1774,14 +1794,15 @@ static int snd_trident_playback_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_trident_voice_t *voice; + int err; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } - spin_unlock_irq(&trident->reg_lock); snd_trident_pcm_mixer_build(trident, voice, substream); voice->substream = substream; runtime->private_data = voice; @@ -1808,6 +1829,7 @@ static int snd_trident_playback_close(snd_pcm_substream_t * substream) snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; snd_trident_pcm_mixer_free(trident, voice, substream); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1827,11 +1849,13 @@ static int snd_trident_spdif_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; + int err; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } voice->spdif = 1; @@ -1889,6 +1913,7 @@ static int snd_trident_spdif_close(snd_pcm_substream_t * substream) trident->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1908,15 +1933,16 @@ static int snd_trident_capture_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; + int err; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } voice->capture = 1; - spin_unlock_irq(&trident->reg_lock); voice->substream = substream; runtime->private_data = voice; runtime->private_free = snd_trident_pcm_free_substream; @@ -1937,6 +1963,8 @@ static int snd_trident_capture_open(snd_pcm_substream_t * substream) ---------------------------------------------------------------------------*/ static int snd_trident_capture_close(snd_pcm_substream_t * substream) { + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1954,26 +1982,19 @@ static int snd_trident_capture_close(snd_pcm_substream_t * substream) static int snd_trident_foldback_open(snd_pcm_substream_t * substream) { trident_t *trident = snd_pcm_substream_chip(substream); + int err; snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } - if (trident->tlb.entries) { - voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - if (voice->memblk == NULL) { - snd_trident_free_voice(trident, voice); - spin_unlock_irq(&trident->reg_lock); - return -ENOMEM; - } - } - voice->substream = substream; voice->foldback_chan = substream->number; - spin_unlock_irq(&trident->reg_lock); + voice->substream = substream; runtime->private_data = voice; runtime->private_free = snd_trident_pcm_free_substream; runtime->hw = snd_trident_foldback; @@ -2001,6 +2022,7 @@ static int snd_trident_foldback_close(snd_pcm_substream_t * substream) spin_lock_irq(&trident->reg_lock); outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -2019,6 +2041,20 @@ static snd_pcm_ops_t snd_trident_playback_ops = { .pointer = snd_trident_playback_pointer, }; +static snd_pcm_ops_t snd_trident_nx_playback_ops = { + .open = snd_trident_playback_open, + .close = snd_trident_playback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_playback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, + .copy = snd_pcm_sgbuf_ops_copy_playback, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_capture_ops = { .open = snd_trident_capture_open, .close = snd_trident_capture_close, @@ -2030,6 +2066,20 @@ static snd_pcm_ops_t snd_trident_capture_ops = { .pointer = snd_trident_capture_pointer, }; +static snd_pcm_ops_t snd_trident_nx_capture_ops = { + .open = snd_trident_capture_open, + .close = snd_trident_capture_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_capture_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_capture_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_capture_pointer, + .copy = snd_pcm_sgbuf_ops_copy_capture, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_si7018_capture_ops = { .open = snd_trident_capture_open, .close = snd_trident_capture_close, @@ -2052,6 +2102,20 @@ static snd_pcm_ops_t snd_trident_foldback_ops = { .pointer = snd_trident_playback_pointer, }; +static snd_pcm_ops_t snd_trident_nx_foldback_ops = { + .open = snd_trident_foldback_open, + .close = snd_trident_foldback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_foldback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, + .copy = snd_pcm_sgbuf_ops_copy_capture, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_spdif_ops = { .open = snd_trident_spdif_open, .close = snd_trident_spdif_close, @@ -2063,6 +2127,20 @@ static snd_pcm_ops_t snd_trident_spdif_ops = { .pointer = snd_trident_spdif_pointer, }; +static snd_pcm_ops_t snd_trident_nx_spdif_ops = { + .open = snd_trident_spdif_open, + .close = snd_trident_spdif_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_spdif_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_spdif_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_spdif_pointer, + .copy = snd_pcm_sgbuf_ops_copy_playback, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_spdif_7018_ops = { .open = snd_trident_spdif_open, .close = snd_trident_spdif_close, @@ -2088,21 +2166,24 @@ static void snd_trident_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->pcm = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->foldback = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->spdif = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_free_for_all(pcm); } /*--------------------------------------------------------------------------- @@ -2129,18 +2210,24 @@ int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm pcm->private_data = trident; pcm->private_free = snd_trident_pcm_free; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + if (trident->tlb.entries) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_capture_ops); + } else { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, trident->device != TRIDENT_DEVICE_ID_SI7018 ? - &snd_trident_capture_ops : - &snd_trident_si7018_capture_ops); + &snd_trident_capture_ops : + &snd_trident_si7018_capture_ops); + } pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strcpy(pcm->name, "Trident 4DWave"); trident->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 256*1024); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -2174,7 +2261,10 @@ int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_ foldback->private_data = trident; foldback->private_free = snd_trident_foldback_pcm_free; - snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); + if (trident->tlb.entries) + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops); + else + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); foldback->info_flags = 0; strcpy(foldback->name, "Trident 4DWave"); substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; @@ -2189,7 +2279,8 @@ int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_ } trident->foldback = foldback; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); if (rpcm) *rpcm = foldback; @@ -2219,7 +2310,9 @@ int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t * spdif->private_data = trident; spdif->private_free = snd_trident_spdif_pcm_free; - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + if (trident->tlb.entries) { + snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_spdif_ops); + } else if (trident->device != TRIDENT_DEVICE_ID_SI7018) { snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops); } else { snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); @@ -2228,7 +2321,8 @@ int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t * strcpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); if (rpcm) *rpcm = spdif; @@ -2886,7 +2980,7 @@ static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t *v static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device) { - ac97_t _ac97, *ac97; + ac97_t _ac97; snd_card_t * card = trident->card; snd_kcontrol_t *kctl; snd_ctl_elem_value_t uctl; @@ -2900,7 +2994,7 @@ static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device _ac97.private_data = trident; trident->ac97_detect = 1; __again: - if ((err = snd_ac97_mixer(trident->card, &_ac97, &ac97)) < 0) { + if ((err = snd_ac97_mixer(trident->card, &_ac97, &trident->ac97)) < 0) { if (trident->device == TRIDENT_DEVICE_ID_SI7018) { if ((err = snd_trident_sis_reset(trident)) < 0) return err; @@ -3076,6 +3170,21 @@ void __devinit snd_trident_gameport(trident_t *chip) } #endif /* CONFIG_GAMEPORT */ +/* + * delay for 1 tick + */ +inline static void do_delay(trident_t *chip) +{ +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay((1000 + HZ - 1) / HZ); + return; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); +} + /* * SiS reset routine */ @@ -3086,7 +3195,7 @@ static int snd_trident_sis_reset(trident_t *trident) unsigned int i; int r; - r = 2; /* count of retries */ + r = trident->in_suspend ? 0 : 2; /* count of retries */ __si7018_retry: pci_write_config_byte(trident->pci, 0x46, 0x04); /* SOFTWARE RESET */ udelay(100); @@ -3107,15 +3216,13 @@ static int snd_trident_sis_reset(trident_t *trident) do { if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) goto __si7018_ok; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + do_delay(trident); } while (time_after_eq(end_time, jiffies)); snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL))); if (r-- > 0) { end_time = jiffies + HZ; do { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + do_delay(trident); } while (time_after_eq(end_time, jiffies)); goto __si7018_retry; } @@ -3173,36 +3280,195 @@ static void snd_trident_proc_read(snd_info_entry_t *entry, static void __devinit snd_trident_proc_init(trident_t * trident) { snd_info_entry_t *entry; - char *s = "trident"; + const char *s = "trident"; if (trident->device == TRIDENT_DEVICE_ID_SI7018) s = "sis7018"; - if ((entry = snd_info_create_card_entry(trident->card, s, trident->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = trident; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_trident_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } + if (! snd_card_proc_new(trident->card, s, &entry)) + snd_info_set_text_ops(entry, trident, snd_trident_proc_read); +} + +static int snd_trident_dev_free(snd_device_t *device) +{ + trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); + return snd_trident_free(trident); +} + +/*--------------------------------------------------------------------------- + snd_trident_tlb_alloc + + Description: Allocate and set up the TLB page table on 4D NX. + Each entry has 4 bytes (physical PCI address). + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: 0 or negative error code + + ---------------------------------------------------------------------------*/ + +static int __devinit snd_trident_tlb_alloc(trident_t *trident) +{ + int i; + + /* TLB array must be aligned to 16kB !!! so we allocate + 32kB region and correct offset when necessary */ + + trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); + if (trident->tlb.buffer == NULL) { + snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n"); + return -ENOMEM; + } + trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); + trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); + /* allocate shadow TLB page table (virtual addresses) */ + trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); + if (trident->tlb.shadow_entries == NULL) { + snd_printk(KERN_ERR "trident: unable to allocate shadow TLB entries\n"); + return -ENOMEM; + } + /* allocate and setup silent page and initialise TLB entries */ + trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); + if (trident->tlb.silent_page == 0UL) { + snd_printk(KERN_ERR "trident: unable to allocate silent page\n"); + return -ENOMEM; } - trident->proc_entry = entry; + memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); + for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { + trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); + trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; + } + + /* use emu memory block manager code to manage tlb page allocation */ + trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); + if (trident->tlb.memhdr == NULL) + return -ENOMEM; + + trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t); + return 0; +} + +/* + * initialize 4D DX chip + */ + +static void snd_trident_stop_all_voices(trident_t *trident) +{ + outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); + outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); + outl(0, TRID_REG(trident, T4D_AINTEN_A)); + outl(0, TRID_REG(trident, T4D_AINTEN_B)); +} + +static int snd_trident_4d_dx_init(trident_t *trident) +{ + struct pci_dev *pci = trident->pci; + signed long end_time; + + /* reset the legacy configuration and whole audio/wavetable block */ + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + pci_write_config_byte(pci, 0x46, 4); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* DAC on, disable SB IRQ and try to force ADC valid signal */ + trident->ac97_ctrl = 0x0000004a; + outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) + goto __dx_ok; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "AC'97 codec ready error\n"); + return -EIO; + + __dx_ok: + snd_trident_stop_all_voices(trident); + + return 0; } -static void snd_trident_proc_done(trident_t * trident) +/* + * initialize 4D NX chip + */ +static int snd_trident_4d_nx_init(trident_t *trident) { - if (trident->proc_entry) { - snd_info_unregister(trident->proc_entry); - trident->proc_entry = NULL; + struct pci_dev *pci = trident->pci; + signed long end_time; + + /* reset the legacy configuration and whole audio/wavetable block */ + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + + pci_write_config_byte(pci, 0x46, 1); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) + goto __nx_ok; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); + return -EIO; + + __nx_ok: + /* DAC on */ + trident->ac97_ctrl = 0x00000002; + outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* disable SB IRQ */ + outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); + + snd_trident_stop_all_voices(trident); + + if (trident->tlb.entries != NULL) { + unsigned int i; + /* enable virtual addressing via TLB */ + i = trident->tlb.entries_dmaaddr; + i |= 0x00000001; + outl(i, TRID_REG(trident, NX_TLBC)); + } else { + outl(0, TRID_REG(trident, NX_TLBC)); } + /* initialize S/PDIF */ + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + + return 0; } -static int snd_trident_dev_free(snd_device_t *device) +/* + * initialize sis7018 chip + */ +static int snd_trident_sis_init(trident_t *trident) { - trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); - return snd_trident_free(trident); + int err; + + if ((err = snd_trident_sis_reset(trident)) < 0) + return err; + + snd_trident_stop_all_voices(trident); + + /* initialize S/PDIF */ + outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); + + return 0; } /*--------------------------------------------------------------------------- @@ -3229,9 +3495,7 @@ int __devinit snd_trident_create(snd_card_t * card, trident_t ** rtrident) { trident_t *trident; - unsigned int i; - int err; - signed long end_time; + int i, err; snd_trident_voice_t *voice; snd_trident_pcm_mixer_t *tmix; static snd_device_ops_t ops = { @@ -3290,151 +3554,39 @@ int __devinit snd_trident_create(snd_card_t * card, trident->tlb.entries = NULL; trident->tlb.buffer = NULL; if (trident->device == TRIDENT_DEVICE_ID_NX) { - /* allocate and setup TLB page table */ - /* each entry has 4 bytes (physical PCI address) */ - /* TLB array must be aligned to 16kB !!! so we allocate - 32kB region and correct offset when necessary */ - trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); - if (trident->tlb.buffer == NULL) { - snd_trident_free(trident); - snd_printk("unable to allocate TLB buffer\n"); - return -ENOMEM; - } - trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); - trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); - /* allocate shadow TLB page table (virtual addresses) */ - trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); - if (trident->tlb.shadow_entries == NULL) { + if ((err = snd_trident_tlb_alloc(trident)) < 0) { snd_trident_free(trident); - snd_printk("unable to allocate shadow TLB entries\n"); - return -ENOMEM; - } - /* allocate and setup silent page and initialise TLB entries */ - trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); - if (trident->tlb.silent_page == 0UL) { - snd_trident_free(trident); - snd_printk("unable to allocate silent page\n"); - return -ENOMEM; - } - memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); - for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { - trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); - trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; - } - - /* use emu memory block manager code to manage tlb page allocation */ - trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); - if (trident->tlb.memhdr == NULL) { - snd_trident_free(trident); - return -ENOMEM; + return err; } - trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t); } - /* reset the legacy configuration and whole audio/wavetable block */ - if (trident->device == TRIDENT_DEVICE_ID_DX || - trident->device == TRIDENT_DEVICE_ID_NX) { - pci_write_config_dword(pci, 0x40, 0); /* DDMA */ - pci_write_config_byte(pci, 0x44, 0); /* ports */ - pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ - if (trident->device == TRIDENT_DEVICE_ID_DX) { - pci_write_config_byte(pci, 0x46, 4); /* reset */ - udelay(100); - pci_write_config_byte(pci, 0x46, 0); /* release reset */ - udelay(100); - } else /* NX */ { - pci_write_config_byte(pci, 0x46, 1); /* reset */ - udelay(100); - pci_write_config_byte(pci, 0x46, 0); /* release reset */ - udelay(100); - } - } - - /* initialize chip */ + trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + /* initialize chip */ switch (trident->device) { case TRIDENT_DEVICE_ID_DX: - /* warm reset of the AC'97 codec */ - outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - udelay(100); - outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - /* DAC on, disable SB IRQ and try to force ADC valid signal */ - trident->ac97_ctrl = 0x0000004a; - outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - /* wait, until the codec is ready */ - end_time = (jiffies + (HZ * 3) / 4) + 1; - do { - if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) - goto __dx_ok; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (end_time - (signed long)jiffies >= 0); - snd_printk("AC'97 codec ready error\n"); - snd_trident_free(trident); - return -EIO; - __dx_ok: + err = snd_trident_4d_dx_init(trident); break; case TRIDENT_DEVICE_ID_NX: - /* warm reset of the AC'97 codec */ - outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - udelay(100); - outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - /* wait, until the codec is ready */ - end_time = (jiffies + (HZ * 3) / 4) + 1; - do { - if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) - goto __nx_ok; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (end_time - (signed long)jiffies >= 0); - snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); - snd_trident_free(trident); - return -EIO; - __nx_ok: - /* DAC on */ - trident->ac97_ctrl = 0x00000002; - outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - /* disable SB IRQ */ - outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); + err = snd_trident_4d_nx_init(trident); break; case TRIDENT_DEVICE_ID_SI7018: - if ((err = snd_trident_sis_reset(trident)) < 0) { - snd_trident_free(trident); - return err; - } + err = snd_trident_sis_init(trident); + break; + default: + snd_BUG(); + break; + } + if (err < 0) { + snd_trident_free(trident); + return err; } - - outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); - outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); - outl(0, TRID_REG(trident, T4D_AINTEN_A)); - outl(0, TRID_REG(trident, T4D_AINTEN_B)); if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) { snd_trident_free(trident); return err; } - if (trident->device == TRIDENT_DEVICE_ID_NX) { - if (trident->tlb.entries != NULL) { - /* enable virtual addressing via TLB */ - i = trident->tlb.entries_dmaaddr; - i |= 0x00000001; - outl(i, TRID_REG(trident, NX_TLBC)); - } else { - outl(0, TRID_REG(trident, NX_TLBC)); - } - /* initialize S/PDIF */ - trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; - outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); - outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - } - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - /* initialize S/PDIF */ - trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; - outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); - } - /* initialise synth voices */ for (i = 0; i < 64; i++) { voice = &trident->synth.voices[i]; @@ -3493,7 +3645,6 @@ int snd_trident_free(trident_t *trident) else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } - snd_trident_proc_done(trident); if (trident->tlb.buffer) { outl(0, TRID_REG(trident, NX_TLBC)); if (trident->tlb.memhdr) @@ -3654,6 +3805,7 @@ snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int pvoice->capture = 0; pvoice->spdif = 0; pvoice->memblk = NULL; + pvoice->substream = NULL; spin_unlock_irqrestore(&trident->voice_alloc, flags); return pvoice; } @@ -3732,9 +3884,9 @@ void snd_trident_suspend(trident_t *trident) { snd_card_t *card = trident->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; + trident->in_suspend = 1; snd_pcm_suspend_all(trident->pcm); if (trident->foldback) snd_pcm_suspend_all(trident->foldback); @@ -3748,27 +3900,40 @@ void snd_trident_suspend(trident_t *trident) break; } snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } void snd_trident_resume(trident_t *trident) { snd_card_t *card = trident->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; + + pci_enable_device(trident->pci); + pci_set_dma_mask(trident->pci, 0x3fffffff); /* to be sure */ + pci_set_master(trident->pci); /* to be sure */ + switch (trident->device) { case TRIDENT_DEVICE_ID_DX: + snd_trident_4d_dx_init(trident); + break; case TRIDENT_DEVICE_ID_NX: - break; /* TODO */ + snd_trident_4d_nx_init(trident); + break; case TRIDENT_DEVICE_ID_SI7018: + snd_trident_sis_init(trident); break; } + + snd_ac97_resume(trident->ac97); + + /* restore some registers */ + outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + + snd_trident_enable_eso(trident); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); + trident->in_suspend = 0; } static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state) diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index d7701b999dd9..fb8a44f283a7 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -28,6 +28,7 @@ #include #include #include +#include /* page arguments of these two macros are Trident page (4096 bytes), not like * aligned pages in others @@ -166,9 +167,8 @@ __found_pages: /* * check if the given pointer is valid for pages */ -static int is_valid_page(void *pages) +static int is_valid_page(unsigned long ptr) { - unsigned long ptr = (unsigned long)virt_to_phys(pages); if (ptr & ~0x3fffffffUL) { snd_printk("max memory size is 1GB!!\n"); return 0; @@ -184,33 +184,42 @@ static int is_valid_page(void *pages) * page allocation for DMA */ snd_util_memblk_t * -snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size) +snd_trident_alloc_pages(trident_t *trident, snd_pcm_substream_t *substream) { - unsigned long ptr; snd_util_memhdr_t *hdr; snd_util_memblk_t *blk; - int page; + int idx, page; + struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return NULL); snd_assert(trident != NULL, return NULL); - snd_assert(size > 0 && size < SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); + snd_assert(sgbuf->size > 0 && sgbuf->size < SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); hdr = trident->tlb.memhdr; snd_assert(hdr != NULL, return NULL); - if (! is_valid_page(pages)) - return NULL; - down(&hdr->block_mutex); - blk = search_empty(hdr, size); + blk = search_empty(hdr, sgbuf->size); if (blk == NULL) { up(&hdr->block_mutex); return NULL; } + if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) { + snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk)); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + /* set TLB entries */ - ptr = (unsigned long)pages; - for (page = firstpg(blk); page <= lastpg(blk); page++) { + idx = 0; + for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) { + dma_addr_t addr = sgbuf->table[idx].addr; + unsigned long ptr = (unsigned long)sgbuf->table[idx].buf; + if (! is_valid_page(addr)) { + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } set_tlb_bus(trident, page, ptr, addr); - ptr += ALIGN_PAGE_SIZE; - addr += ALIGN_PAGE_SIZE; } up(&hdr->block_mutex); return blk; @@ -301,7 +310,8 @@ static void clear_tlb(trident_t *trident, int page) void *ptr = page_to_ptr(trident, page); dma_addr_t addr = page_to_addr(trident, page); set_silent_tlb(trident, page); - snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); + if (ptr) + snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); } /* check new allocation range */ @@ -346,7 +356,7 @@ static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk) ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr); if (ptr == NULL) goto __fail; - if (! is_valid_page(ptr)) { + if (! is_valid_page(addr)) { snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr); goto __fail; } diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index b5ce828f1aa2..6f7d49d2fdf0 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1827,26 +1827,8 @@ static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip) { snd_info_entry_t *entry; - entry = snd_info_create_card_entry(card, "ymfpci", card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 4096; - entry->c.text.read = snd_ymfpci_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } - } - chip->proc_entry = entry; - return 0; -} - -static int snd_ymfpci_proc_done(ymfpci_t *chip) -{ - if (chip->proc_entry) - snd_info_unregister((snd_info_entry_t *) chip->proc_entry); + if (! snd_card_proc_new(card, "ymfpci", &entry)) + snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read); return 0; } @@ -2037,7 +2019,6 @@ static int snd_ymfpci_free(ymfpci_t *chip) u16 ctrl; snd_assert(chip != NULL, return -EINVAL); - snd_ymfpci_proc_done(chip); if (chip->res_reg_area) { /* don't touch busy hardware */ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); @@ -2127,9 +2108,8 @@ void snd_ymfpci_suspend(ymfpci_t *chip) snd_card_t *card = chip->card; int i; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); snd_pcm_suspend_all(chip->pcm2); snd_pcm_suspend_all(chip->pcm_spdif); @@ -2140,8 +2120,6 @@ void snd_ymfpci_suspend(ymfpci_t *chip) snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); snd_ymfpci_disable_dsp(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } void snd_ymfpci_resume(ymfpci_t *chip) @@ -2149,10 +2127,8 @@ void snd_ymfpci_resume(ymfpci_t *chip) snd_card_t *card = chip->card; int i; - snd_power_lock(card); - if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; pci_enable_device(chip->pci); pci_set_master(chip->pci); @@ -2174,8 +2150,6 @@ void snd_ymfpci_resume(ymfpci_t *chip) spin_unlock(&chip->reg_lock); } snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } static int snd_ymfpci_set_power_state(snd_card_t *card, unsigned int power_state) diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 190984c2e287..3d443de8f3a5 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -1460,9 +1460,8 @@ static void snd_pmac_suspend(pmac_t *chip) unsigned long flags; snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; if (chip->suspend) chip->suspend(chip); @@ -1476,17 +1475,14 @@ static void snd_pmac_suspend(pmac_t *chip) disable_irq(chip->rx_irq); snd_pmac_sound_feature(chip, 0); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void snd_pmac_resume(pmac_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; snd_pmac_sound_feature(chip, 1); if (chip->resume) @@ -1505,8 +1501,6 @@ static void snd_pmac_resume(pmac_t *chip) enable_irq(chip->rx_irq); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } /* the chip is stored statically by snd_pmac_register_sleep_notifier diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 66e2c95adb32..1564ca401970 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -184,7 +184,6 @@ struct snd_usb_stream { int pcm_index; snd_usb_substream_t substream[2]; struct list_head list; - snd_info_entry_t *proc_entry; }; #define chip_t snd_usb_stream_t @@ -930,6 +929,70 @@ static struct audioformat *find_format(snd_usb_substream_t *subs, snd_pcm_runtim } +/* + * initialize the picth control and sample rate + */ +static int init_usb_pitch(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt) +{ + unsigned int ep; + unsigned char data[1]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { + data[0] = 1; + if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", + dev->devnum, iface, ep); + return err; + } + } + return 0; +} + +static int init_usb_sample_rate(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate) +{ + unsigned int ep; + unsigned char data[3]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) { + int crate; + data[0] = rate; + data[1] = rate >> 8; + data[2] = rate >> 16; + if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n", + dev->devnum, iface, fmt->altsetting, rate, ep); + return err; + } + if ((err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot get freq at ep 0x%x\n", + dev->devnum, iface, fmt->altsetting, ep); + return err; + } + crate = data[0] | (data[1] << 8) | (data[2] << 16); + if (crate != rate) { + snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); + // runtime->rate = crate; + } + } + return 0; +} + /* * find a matching format and set up the interface */ @@ -942,7 +1005,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) struct usb_interface *iface; struct audioformat *fmt; unsigned int ep, attr; - unsigned char data[3]; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; int err; @@ -1015,44 +1077,11 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) subs->syncinterval = get_endpoint(alts, 1)->bRefresh; } - ep = get_endpoint(alts, 0)->bEndpointAddress; - /* if endpoint has pitch control, enable it */ - if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { - data[0] = 1; - if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", - dev->devnum, subs->interface, ep); - return err; - } - } - /* if endpoint has sampling rate control, set it */ - if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) { - int crate; - data[0] = runtime->rate; - data[1] = runtime->rate >> 8; - data[2] = runtime->rate >> 16; - if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n", - dev->devnum, subs->interface, fmt->altsetting, runtime->rate, ep); - return err; - } - if ((err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot get freq at ep 0x%x\n", - dev->devnum, subs->interface, fmt->altsetting, ep); - return err; - } - crate = data[0] | (data[1] << 8) | (data[2] << 16); - if (crate != runtime->rate) { - snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, runtime->rate); - // runtime->rate = crate; - } - } + if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0 || + (err = init_usb_sample_rate(dev, subs->interface, alts, fmt, + runtime->rate)) < 0) + return err; + /* always fill max packet size */ if (fmt->attributes & EP_CS_ATTR_FILL_MAX) subs->fill_max = 1; @@ -1426,18 +1455,8 @@ static void proc_pcm_format_add(snd_usb_stream_t *stream) snd_card_t *card = stream->chip->card; sprintf(name, "stream%d", stream->pcm_index); - if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = stream; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 4096; - entry->c.text.read = proc_pcm_format_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - stream->proc_entry = entry; + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, stream, proc_pcm_format_read); } @@ -1492,10 +1511,6 @@ static void free_substream(snd_usb_substream_t *subs) */ static void snd_usb_audio_stream_free(snd_usb_stream_t *stream) { - if (stream->proc_entry) { - snd_info_unregister(stream->proc_entry); - stream->proc_entry = NULL; - } free_substream(&stream->substream[0]); free_substream(&stream->substream[1]); list_del(&stream->list); @@ -1828,6 +1843,10 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, i kfree(fp); return err; } + /* try to set the interface... */ + usb_set_interface(chip->dev, iface_no, i); + init_usb_pitch(chip->dev, iface_no, alts, fp); + init_usb_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max); } return 0; } @@ -1890,9 +1909,10 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif, /* skip non-supported classes */ continue; } - parse_audio_endpoints(chip, buffer, buflen, j); - usb_set_interface(dev, j, 0); /* reset the current interface */ - usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + if (! parse_audio_endpoints(chip, buffer, buflen, j)) { + usb_set_interface(dev, j, 0); /* reset the current interface */ + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + } } return 0; -- cgit v1.2.3 From 1ae204fde0981efe9e9d0905f0bd8b85349bdb2d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 27 Jan 2003 21:41:56 +0100 Subject: ALSA update - added documentation for OSS emulation - CMI8330 - duplex/mixer cleanups - via82xx - rewritten for 8233+ (multiple playback, S/PDIF, secondary capture) - USB - quirk code update --- .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 21 +- Documentation/sound/alsa/OSS-Emulation.txt | 297 ++++++ include/sound/ad1848.h | 48 +- include/sound/sb.h | 45 + include/sound/version.h | 2 +- sound/core/info.c | 4 +- sound/isa/ad1848/ad1848_lib.c | 73 +- sound/isa/cmi8330.c | 162 ++- sound/isa/sb/sb_common.c | 1 + sound/isa/sb/sb_mixer.c | 279 ++--- sound/isa/sgalaxy.c | 9 +- sound/pci/Kconfig | 4 +- sound/pci/intel8x0.c | 35 +- sound/pci/rme32.c | 7 +- sound/pci/via82xx.c | 1106 +++++++++++++------- sound/pci/ymfpci/ymfpci_main.c | 8 + sound/usb/usbaudio.c | 1 - sound/usb/usbaudio.h | 2 +- sound/usb/usbmidi.c | 83 +- sound/usb/usbquirks.h | 18 - 20 files changed, 1534 insertions(+), 671 deletions(-) create mode 100644 Documentation/sound/alsa/OSS-Emulation.txt (limited to 'include') diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index 8cf02dfa80d2..66ce8db8d10a 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -4539,28 +4539,13 @@ - #include /* get the physical page pointer on the given offset */ - static struct page *mychip_page(snd_pcm_substream_t *subs, + static struct page *mychip_page(snd_pcm_substream_t *substream, unsigned long offset) { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - unsigned long lpage; - void *pageptr = subs->runtime->dma_area + offset; - struct page *page = NOPAGE_SIGBUS; - - lpage = VMALLOC_VMADDR(pageptr); - spin_lock(&init_mm.page_table_lock); - pgd = pgd_offset(&init_mm, lpage); - pmd = pmd_offset(pgd, lpage); - pte = pte_offset(pmd, lpage); - page = pte_page(*pte); - spin_unlock(&init_mm.page_table_lock); - - return page; + void *pageptr = substream->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); } ]]> diff --git a/Documentation/sound/alsa/OSS-Emulation.txt b/Documentation/sound/alsa/OSS-Emulation.txt new file mode 100644 index 000000000000..f2d1c90d2de9 --- /dev/null +++ b/Documentation/sound/alsa/OSS-Emulation.txt @@ -0,0 +1,297 @@ + NOTES ON KERNEL OSS-EMULATION + ============================= + + Jan. 9, 2003 Takashi Iwai + + +Modules +======= + +ALSA provides a powerful OSS emulation on the kernel. +The OSS emulation for PCM, mixer and sequencer devices is implemented +as add-on kernel modules, snd-pcm-oss, snd-mixer-oss and snd-seq-oss. +When you need to access the OSS PCM, mixer or sequencer devices, the +corresponding module has to be loaded. + +For loading these modules automatically, define the aliases in +/etc/modules.conf like below: + + alias sound-service-0-0 snd-mixer-oss + alias sound-service-0-1 snd-seq-oss + alias sound-service-0-3 snd-pcm-oss + alias sound-service-0-8 snd-seq-oss + alias sound-service-0-12 snd-pcm-oss + +Then the access to an OSS device file such as /dev/dsp0 triggers to +load the necessary module via KMOD. + +For auto-loading the secondary card device like /dev/dsp1, the +following aliases are necessary in addition: + + alias sound-service-1-0 snd-mixer-oss + alias sound-service-1-3 snd-pcm-oss + alias sound-service-1-12 snd-pcm-oss + +Here you don't need to define service-1-1 and service-1-8 because +there is only one sequencer device. +Similarly, you can add definitions for the third or later cards as +sound-service-X-Y. + +The OSS-MIDI is emulated directly in the ALSA rawmidi module, +therefore no extra module exists for that purpose. + +The currently available OSS configuration is shown in +/proc/asound/oss/sndstat. This shows in the same syntax of +/dev/sndstat, which is available on the commercial OSS driver. +On ALSA, you can symlink /dev/sndstat to this proc file. + +Please note that the devices listed in this proc file appear only +after the corresponding OSS-emulation module is loaded. Don't worry +even if "NOT ENABLED IN CONFIG" is shown in it. + + +Device Mapping +============== + +ALSA supports the following OSS device files: + + PCM: + /dev/dspX + /dev/adspX + + Mixer: + /dev/mixerX + + MIDI: + /dev/midi0X + /dev/amidi0X + + Sequencer: + /dev/sequencer + /dev/sequencer2 (aka /dev/music) + +where X is the card number from 0 to 7. + +(NOTE: Some distributions have the device files like /dev/midi0 and + /dev/midi1. They are NOT for OSS but for tclmidi, which is + a totally different thing.) + +Unlike the real OSS, ALSA cannot use the device files more than the +assigned ones. For example, the first card cannot use /dev/dsp1 or +/dev/dsp2, but only /dev/dsp0 and /dev/adsp0. + +As seen above, PCM and MIDI may have two devices. Usually, the first +PCM device (hw:0,0 in ALSA) is mapped to /dev/dsp and the secondary +device (hw:0,1) to /dev/adsp (if available). For MIDI, /dev/midi and +/dev/amidi, respectively. + +You can change this device mapping via the module options of +snd-pcm-oss and snd-rawmidi. In the case of PCM, the following +options are available for snd-pcm-oss: + + dsp_map PCM device number assigned to /dev/dspX + (default = 0) + adsp_map PCM device number assigned to /dev/adspX + (default = 1) + +For example, to map the third PCM device (hw:0,2) to /dev/adsp0, +define like this: + + options snd-pcm-oss adsp_map=2 + +The options take arrays. For configuring the second card, specify +two entries separated by comma. For example, to map the third PCM +device on the second card to /dev/adsp1, define like below: + + options snd-pcm-oss adsp_map=0,2 + +To change the mapping of MIDI devices, the following options are +available for snd-rawmidi: + + midi_map MIDI device number assigned to /dev/midi0X + (default = 0) + amidi_map MIDI device number assigned to /dev/amidi0X + (default = 1) + +For example, to assign the third MIDI device on the first card to +/dev/midi00, define as follows: + + options snd-rawmidi midi_map=2 + + +PCM Mode +======== + +As default, ALSA emulates the OSS PCM with so-called plugin layer, +i.e. tries to convert the sample format, rate or channels +automatically when the card doesn't support it natively. +This will lead to some problems for some applications like quake or +wine, especially if they use the card only in the MMAP mode. + +In such a case, you can change the behavior of PCM per application by +writing a command to the proc file. There is a proc file for each PCM +stream, /proc/asound/cardX/pcmY[cp]/oss, where X is the card number +(zero-based), Y the PCM device number (zero-based), and 'p' is for +playback and 'c' for capture, respectively. Note that this proc file +exists only after snd-pcm-oss module is loaded. + +The command sequence has the following syntax: + + app_name fragments fragment_size [options] + +app_name is the name of application with (higher priority) or without +path. +fragments specifies the number of fragments or zero if no specific +number is given. +fragment_size is the size of fragment in bytes or zero if not given. +options is the optional parameters. The following options are +available: + + disable the application tries to open a pcm device for + this channel but does not want to use it. + direct don't use plugins + block force block open mode + non-block force non-block open mode + +The disable option is useful when one stream direction (playback or +capture) is not handled correctly by the application although the +hardware itself does support both directions. +The direct option is used, as mentioned above, to bypass the automatic +conversion and useful for MMAP-applications. +For example, to playback the first PCM device without plugins for +quake, send a command via echo like the following: + + % echo "quake 0 0 direct" > /proc/asound/card0/pcm0p/oss + +The permission of proc files depend on the module options of snd. +As default it's set as root, so you'll likely need to be superuser for +sending the command above. + +The block and non-block options are used to change the behavior of +opening the device file. +As default, ALSA behaves as defined in POSIX, i.e. blocks the file +when it's busy until the device becomes free (unless O_NONBLOCK is +specified). Some applications assume the non-block open behavior, +which are actually implemented in some real OSS drivers. + +This blocking behavior can be changed globally via nonblock_open +module option of snd-pcm-oss. For using the non-block mode as default +for OSS devices, define like the following: + + options snd-pcm-oss nonblock_open=1 + +You can check the currently defined configuration by reading the proc +file. The read image can be sent to the proc file again, hence you +can save the current configuration + + % cat /proc/asound/card0/pcm0p/oss > /somewhere/oss-cfg + +and restore it like + + % cat /somewhere/oss-cfg > /proc/asound/card0/pcm0p/oss + +Also, for clearing all the current configuration, send "erase" command +as below: + + % echo "erase" > /proc/asound/card0/pcm0p/oss + + +Mixer Elements +============== + +Since ALSA has completely different mixer interface, the emulation of +OSS mixer is relatively complicated. ALSA builds up a mixer element +from several different ALSA (mixer) controls based on the name +string. For example, the volume element SOUND_MIXER_PCM is composed +from "PCM Playback Volume" and "PCM Playback Switch" controls for the +playback direction and from "PCM Capture Volume" and "PCM Capture +Switch" for the capture directory (if exists). When the PCM volume of +OSS is changed, all the volume and switch controls above are adjusted +automatically. + +As default, ALSA uses the following control for OSS volumes: + + OSS volume ALSA control Index + ----------------------------------------------------- + SOUND_MIXER_VOLUME Master 0 + SOUND_MIXER_BASS Tone Control - Bass 0 + SOUND_MIXER_TREBLE Tone Control - Treble 0 + SOUND_MIXER_SYNTH Synth 0 + SOUND_MIXER_PCM PCM 0 + SOUND_MIXER_SPEAKER PC Speaker 0 + SOUND_MIXER_LINE Line 0 + SOUND_MIXER_MIC Mic 0 + SOUND_MIXER_CD CD 0 + SOUND_MIXER_IMIX Monitor Mix 0 + SOUND_MIXER_ALTPCM PCM 1 + SOUND_MIXER_RECLEV (not assigned) + SOUND_MIXER_IGAIN Capture 0 + SOUND_MIXER_OGAIN Playback 0 + SOUND_MIXER_LINE1 Aux 0 + SOUND_MIXER_LINE2 Aux 1 + SOUND_MIXER_LINE3 Aux 2 + SOUND_MIXER_DIGITAL1 Digital 0 + SOUND_MIXER_DIGITAL2 Digital 1 + SOUND_MIXER_DIGITAL3 Digital 2 + SOUND_MIXER_PHONEIN Phone 0 + SOUND_MIXER_PHONEOUT Phone 1 + SOUND_MIXER_VIDEO Video 0 + SOUND_MIXER_RADIO Radio 0 + SOUND_MIXER_MONITOR Monitor 0 + +The second column is the base-string of the corresponding ALSA +control. In fact, the controls with "XXX [Playback|Capture] +[Volume|Switch]" will be checked in addition. + +The current assignment of these mixer elements is listed in the proc +file, /proc/asound/cardX/mixer_oss, which will be like the following + + VOLUME "Master" 0 + BASS "" 0 + TREBLE "" 0 + SYNTH "" 0 + PCM "PCM" 0 + ... + +where the first column is the OSS volume element, the second column +the base-string of the corresponding ALSA control, and the third the +control index. When the string is empty, it means that the +corresponding OSS control is not available. + +For changing the assignment, you can write the configuration to this +proc file. For example, to map "Wave Playback" to the PCM volume, +send the command like the following: + + % echo 'VOLUME "Wave Playback" 0' > /proc/asound/card0/mixer_oss + +The command is exactly as same as listed in the proc file. You can +change one or more elements, one volume per line. In the last +example, both "Wave Playback Volume" and "Wave Playback Switch" will +be affected when PCM volume is changed. + +Like the case of PCM proc file, the permission of proc files depend on +the module options of snd. you'll likely need to be superuser for +sending the command above. + +As well as in the case of PCM proc file, you can save and restore the +current mixer configuration by reading and writing the whole file +image. + + +Unsupported Features +==================== + +MMAP on ICE1712 driver +---------------------- +ICE1712 supports only the unconventional format, interleaved +10-channels 24bit (packed in 32bit) format. Therefore you cannot mmap +the buffer as the conventional (mono or 2-channels, 8 or 16bit) format +on OSS. + +USB devices +----------- +Some USB devices support only 24bit format packed in 3bytes. This +format is not supported by OSS and no conversion is provided by kernel +OSS emulation. You can use the user-space OSS emulation via libaoss +instead. + diff --git a/include/sound/ad1848.h b/include/sound/ad1848.h index 18f1fec77e2d..155fa2b6ec0f 100644 --- a/include/sound/ad1848.h +++ b/include/sound/ad1848.h @@ -22,7 +22,6 @@ * */ -#include "control.h" #include "pcm.h" /* IO ports */ @@ -166,25 +165,40 @@ const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction); int snd_ad1848_mixer(ad1848_t * chip); void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs); -#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_ad1848_info_single, \ - .get = snd_ad1848_get_single, .put = snd_ad1848_put_single, \ - .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } +/* exported mixer stuffs */ +enum { AD1848_MIX_SINGLE, AD1848_MIX_DOUBLE, AD1848_MIX_CAPTURE }; + +#define AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) \ + ((reg) | ((shift) << 8) | ((mask) << 16) | ((invert) << 24)) +#define AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) \ + ((left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22)) + +int snd_ad1848_add_ctl(ad1848_t *chip, const char *name, int index, int type, unsigned long value); -int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); -int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); -int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +/* for ease of use */ +struct ad1848_mix_elem { + const char *name; + int index; + int type; + unsigned long private_value; +}; + +#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ .name = xname, \ + .index = xindex, \ + .type = AD1848_MIX_SINGLE, \ + .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) } #define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_ad1848_info_double, \ - .get = snd_ad1848_get_double, .put = snd_ad1848_put_double, \ - .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } - -int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); -int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); -int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +{ .name = xname, \ + .index = xindex, \ + .type = AD1848_MIX_DOUBLE, \ + .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) } + +static inline int snd_ad1848_add_ctl_elem(ad1848_t *chip, const struct ad1848_mix_elem *c) +{ + return snd_ad1848_add_ctl(chip, c->name, c->index, c->type, c->private_value); +} #ifdef CONFIG_SND_DEBUG void snd_ad1848_debug(ad1848_t *chip); diff --git a/include/sound/sb.h b/include/sound/sb.h index 4b9b902e9ea7..7815b9200d35 100644 --- a/include/sound/sb.h +++ b/include/sound/sb.h @@ -314,4 +314,49 @@ int snd_sb16_capture_open(snd_pcm_substream_t *substream); int snd_sb16_playback_close(snd_pcm_substream_t *substream); int snd_sb16_capture_close(snd_pcm_substream_t *substream); +/* exported mixer stuffs */ +enum { + SB_MIX_SINGLE, + SB_MIX_DOUBLE, + SB_MIX_INPUT_SW, + SB_MIX_CAPTURE_PRO, + SB_MIX_CAPTURE_DT019X +}; + +#define SB_MIXVAL_DOUBLE(left_reg, right_reg, left_shift, right_shift, mask) \ + ((left_reg) | ((right_reg) << 8) | ((left_shift) << 16) | ((right_shift) << 19) | ((mask) << 24)) +#define SB_MIXVAL_SINGLE(reg, shift, mask) \ + ((reg) | ((shift) << 16) | ((mask) << 24)) +#define SB_MIXVAL_INPUT_SW(reg1, reg2, left_shift, right_shift) \ + ((reg1) | ((reg2) << 8) | ((left_shift) << 16) | ((right_shift) << 24)) + +int snd_sbmixer_add_ctl(sb_t *chip, const char *name, int index, int type, unsigned long value); + +/* for ease of use */ +struct sbmix_elem { + const char *name; + int type; + unsigned long private_value; +}; + +#define SB_SINGLE(xname, reg, shift, mask) \ +{ .name = xname, \ + .type = SB_MIX_SINGLE, \ + .private_value = SB_MIXVAL_SINGLE(reg, shift, mask) } + +#define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \ +{ .name = xname, \ + .type = SB_MIX_DOUBLE, \ + .private_value = SB_MIXVAL_DOUBLE(left_reg, right_reg, left_shift, right_shift, mask) } + +#define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \ +{ .name = xname, \ + .type = SB_MIX_INPUT_SW, \ + .private_value = SB_MIXVAL_INPUT_SW(reg1, reg2, left_shift, right_shift) } + +static inline int snd_sbmixer_add_ctl_elem(sb_t *chip, const struct sbmix_elem *c) +{ + return snd_sbmixer_add_ctl(chip, c->name, 0, c->type, c->private_value); +} + #endif /* __SOUND_SB_H */ diff --git a/include/sound/version.h b/include/sound/version.h index 323f4e184ed5..70d20d7d3806 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc6" -#define CONFIG_SND_DATE " (Wed Jan 08 17:04:59 2003 UTC)" +#define CONFIG_SND_DATE " (Fri Jan 10 18:18:43 2003 UTC)" diff --git a/sound/core/info.c b/sound/core/info.c index b37093a9df78..816e01bc6780 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -1061,7 +1061,7 @@ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, return NULL; entry->content = SNDRV_INFO_CONTENT_DEVICE; entry->mode = mode; - entry->c.device.major = major; + entry->c.device.major = _major; entry->c.device.minor = minor; down(&info_mutex); p = create_proc_entry(entry->name, entry->mode, snd_proc_dev); @@ -1085,7 +1085,7 @@ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, char dname[32]; sprintf(dname, "snd/%s", name); devfs_register(NULL, dname, DEVFS_FL_DEFAULT, - major, minor, mode, + _major, minor, mode, &snd_fops, NULL); } #endif diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index a46d5a4613ad..0a5a5a58daf2 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -996,7 +997,7 @@ static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * return change; } -int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +static int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { int mask = (kcontrol->private_value >> 16) & 0xff; @@ -1007,7 +1008,7 @@ int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo return 0; } -int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ad1848_t *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; @@ -1024,7 +1025,7 @@ int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucon return 0; } -int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ad1848_t *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; @@ -1047,7 +1048,7 @@ int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucon return change; } -int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +static int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { int mask = (kcontrol->private_value >> 24) & 0xff; @@ -1058,7 +1059,7 @@ int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo return 0; } -int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ad1848_t *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; @@ -1080,7 +1081,7 @@ int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucon return 0; } -int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ad1848_t *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; @@ -1117,9 +1118,47 @@ int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucon return change; } -#define AD1848_CONTROLS (sizeof(snd_ad1848_controls)/sizeof(snd_kcontrol_new_t)) +/* + */ +int snd_ad1848_add_ctl(ad1848_t *chip, const char *name, int index, int type, unsigned long value) +{ + static snd_kcontrol_new_t newctls[] = { + [AD1848_MIX_SINGLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ad1848_info_single, + .get = snd_ad1848_get_single, + .put = snd_ad1848_put_single, + }, + [AD1848_MIX_DOUBLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ad1848_info_double, + .get = snd_ad1848_get_double, + .put = snd_ad1848_put_double, + }, + [AD1848_MIX_CAPTURE] = { + .info = snd_ad1848_info_mux, + .get = snd_ad1848_get_mux, + .put = snd_ad1848_put_mux, + }, + }; + snd_kcontrol_t *ctl; + int err; + + ctl = snd_ctl_new1(&newctls[type], chip); + if (! ctl) + return -ENOMEM; + strncpy(ctl->id.name, name, sizeof(ctl->id.name)-1); + ctl->id.index = index; + ctl->private_value = value; + if ((err = snd_ctl_add(chip->card, ctl)) < 0) { + snd_ctl_free_one(ctl); + return err; + } + return 0; +} -static snd_kcontrol_new_t snd_ad1848_controls[] = { + +static struct ad1848_mix_elem snd_ad1848_controls[] = { AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), @@ -1128,11 +1167,8 @@ AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGH AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", - .info = snd_ad1848_info_mux, - .get = snd_ad1848_get_mux, - .put = snd_ad1848_put_mux, + .type = AD1848_MIX_CAPTURE, }, AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) @@ -1151,10 +1187,10 @@ int snd_ad1848_mixer(ad1848_t *chip) strcpy(card->mixername, pcm->name); - for (idx = 0; idx < AD1848_CONTROLS; idx++) { - if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1848_controls[idx], chip))) < 0) + for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) + if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0) return err; - } + return 0; } @@ -1168,12 +1204,7 @@ EXPORT_SYMBOL(snd_ad1848_create); EXPORT_SYMBOL(snd_ad1848_pcm); EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); EXPORT_SYMBOL(snd_ad1848_mixer); -EXPORT_SYMBOL(snd_ad1848_info_single); -EXPORT_SYMBOL(snd_ad1848_get_single); -EXPORT_SYMBOL(snd_ad1848_put_single); -EXPORT_SYMBOL(snd_ad1848_info_double); -EXPORT_SYMBOL(snd_ad1848_get_double); -EXPORT_SYMBOL(snd_ad1848_put_double); +EXPORT_SYMBOL(snd_ad1848_add_ctl); /* * INIT part diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index cf41048fbf65..2ab41c8b410d 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -57,6 +57,13 @@ #define SNDRV_GET_ID #include +/* + */ +#define ENABLE_SB_MIXER +#define PLAYBACK_ON_SB + +/* + */ MODULE_AUTHOR("George Talusan "); MODULE_DESCRIPTION("C-Media CMI8330"); MODULE_LICENSE("GPL"); @@ -129,8 +136,12 @@ MODULE_PARM_SYNTAX(wssdma, SNDRV_DMA8_DESC ",prefers:{0}"); static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = { - 0x0, /* 16 - recording mux */ - 0x40, /* 17 - mute mux */ + 0x40, /* 16 - recording mux (SB-mixer-enabled) */ +#ifdef ENABLE_SB_MIXER + 0x40, /* 17 - mute mux (Mode2) */ +#else + 0x0, /* 17 - mute mux */ +#endif 0x0, /* 18 - vol */ 0x0, /* 19 - master volume */ 0x0, /* 20 - line-in volume */ @@ -142,6 +153,8 @@ static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = 0x0 /* 26 - cd-in rec gain */ }; +typedef int (*snd_pcm_open_callback_t)(snd_pcm_substream_t *); + struct snd_cmi8330 { #ifdef __ISAPNP__ struct isapnp_dev *cap; @@ -152,10 +165,11 @@ struct snd_cmi8330 { sb_t *sb; snd_pcm_t *pcm; - snd_pcm_ops_t playback_ops; - int (*playback_open)(snd_pcm_substream_t *); - snd_pcm_ops_t capture_ops; - int (*capture_open)(snd_pcm_substream_t *); + struct snd_cmi8330_stream { + snd_pcm_ops_t ops; + snd_pcm_open_callback_t open; + void *private_data; /* sb or wss */ + } streams[2]; }; static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; @@ -182,9 +196,8 @@ ISAPNP_CARD_TABLE(snd_cmi8330_pnpids); #endif -#define CMI8330_CONTROLS (sizeof(snd_cmi8330_controls)/sizeof(snd_kcontrol_new_t)) -static snd_kcontrol_new_t snd_cmi8330_controls[] __devinitdata = { +static struct ad1848_mix_elem snd_cmi8330_controls[] __initdata = { AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), @@ -212,16 +225,79 @@ AD1848_SINGLE("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1), AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 1, 1), }; -static int __init snd_cmi8330_mixer(snd_card_t *card, ad1848_t *chip) +#ifdef ENABLE_SB_MIXER +static struct sbmix_elem cmi8330_sb_mixers[] __initdata = { +SB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), +SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), +SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), +SB_DOUBLE("SB PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31), +SB_DOUBLE("SB Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31), +SB_DOUBLE("SB CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), +SB_DOUBLE("SB CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31), +SB_DOUBLE("SB Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), +SB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31), +SB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1), +SB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), +SB_SINGLE("SB PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3), +SB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3), +SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3), +SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1), +}; + +static unsigned char cmi8330_sb_init_values[][2] __initdata = { + { SB_DSP4_MASTER_DEV + 0, 0 }, + { SB_DSP4_MASTER_DEV + 1, 0 }, + { SB_DSP4_PCM_DEV + 0, 0 }, + { SB_DSP4_PCM_DEV + 1, 0 }, + { SB_DSP4_SYNTH_DEV + 0, 0 }, + { SB_DSP4_SYNTH_DEV + 1, 0 }, + { SB_DSP4_INPUT_LEFT, 0 }, + { SB_DSP4_INPUT_RIGHT, 0 }, + { SB_DSP4_OUTPUT_SW, 0 }, + { SB_DSP4_SPEAKER_DEV, 0 }, +}; + + +static int __init cmi8330_add_sb_mixers(sb_t *chip) +{ + int idx, err; + unsigned long flags; + + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */ + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + /* mute and zero volume channels */ + for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_init_values); idx++) { + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0], + cmi8330_sb_init_values[idx][1]); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + } + + for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_mixers); idx++) { + if ((err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx])) < 0) + return err; + } + return 0; +} +#endif + +static int __init snd_cmi8330_mixer(snd_card_t *card, struct snd_cmi8330 *acard) { int idx, err; strcpy(card->mixername, "CMI8330/C3D"); - for (idx = 0; idx < CMI8330_CONTROLS; idx++) - if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmi8330_controls[idx], chip))) < 0) + for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { + if ((err = snd_ad1848_add_ctl_elem(acard->wss, &snd_cmi8330_controls[idx])) < 0) return err; + } +#ifdef ENABLE_SB_MIXER + if ((err = cmi8330_add_sb_mixers(acard->sb)) < 0) + return err; +#endif return 0; } @@ -316,13 +392,21 @@ static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard) * */ +#ifdef PLAYBACK_ON_SB +#define CMI_SB_STREAM SNDRV_PCM_STREAM_PLAYBACK +#define CMI_AD_STREAM SNDRV_PCM_STREAM_CAPTURE +#else +#define CMI_SB_STREAM SNDRV_PCM_STREAM_CAPTURE +#define CMI_AD_STREAM SNDRV_PCM_STREAM_PLAYBACK +#endif + static int snd_cmi8330_playback_open(snd_pcm_substream_t * substream) { struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); /* replace the private_data and call the original open callback */ - substream->private_data = chip->sb; - return chip->playback_open(substream); + substream->private_data = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].private_data; + return chip->streams[SNDRV_PCM_STREAM_PLAYBACK].open(substream); } static int snd_cmi8330_capture_open(snd_pcm_substream_t * substream) @@ -330,8 +414,8 @@ static int snd_cmi8330_capture_open(snd_pcm_substream_t * substream) struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); /* replace the private_data and call the original open callback */ - substream->private_data = chip->wss; - return chip->capture_open(substream); + substream->private_data = chip->streams[SNDRV_PCM_STREAM_CAPTURE].private_data; + return chip->streams[SNDRV_PCM_STREAM_CAPTURE].open(substream); } static void snd_cmi8330_pcm_free(snd_pcm_t *pcm) @@ -344,27 +428,33 @@ static int __init snd_cmi8330_pcm(snd_card_t *card, struct snd_cmi8330 *chip) snd_pcm_t *pcm; const snd_pcm_ops_t *ops; int err; - + static snd_pcm_open_callback_t cmi_open_callbacks[2] = { + snd_cmi8330_playback_open, + snd_cmi8330_capture_open + }; + if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0) return err; strcpy(pcm->name, "CMI8330"); pcm->private_data = chip; pcm->private_free = snd_cmi8330_pcm_free; - /* playback - SB16 */ - ops = snd_sb16dsp_get_pcm_ops(SNDRV_PCM_STREAM_PLAYBACK); - chip->playback_ops = *ops; - chip->playback_open = ops->open; - chip->playback_ops.open = snd_cmi8330_playback_open; - - /* capture - AD1848 */ - ops = snd_ad1848_get_pcm_ops(SNDRV_PCM_STREAM_CAPTURE); - chip->capture_ops = *ops; - chip->capture_open = ops->open; - chip->capture_ops.open = snd_cmi8330_capture_open; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->capture_ops); + /* SB16 */ + ops = snd_sb16dsp_get_pcm_ops(CMI_SB_STREAM); + chip->streams[CMI_SB_STREAM].ops = *ops; + chip->streams[CMI_SB_STREAM].open = ops->open; + chip->streams[CMI_SB_STREAM].ops.open = cmi_open_callbacks[CMI_SB_STREAM]; + chip->streams[CMI_SB_STREAM].private_data = chip->sb; + + /* AD1848 */ + ops = snd_ad1848_get_pcm_ops(CMI_AD_STREAM); + chip->streams[CMI_AD_STREAM].ops = *ops; + chip->streams[CMI_AD_STREAM].open = ops->open; + chip->streams[CMI_AD_STREAM].ops.open = cmi_open_callbacks[CMI_AD_STREAM]; + chip->streams[CMI_AD_STREAM].private_data = chip->wss; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops); snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); chip->pcm = pcm; @@ -457,22 +547,18 @@ static int __init snd_cmi8330_probe(int dev) snd_card_free(card); return -ENODEV; } - memcpy(&acard->wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image)); spin_lock_irqsave(&acard->wss->reg_lock, flags); - snd_ad1848_out(acard->wss, AD1848_MISC_INFO, /* switch on MODE2 */ - acard->wss->image[AD1848_MISC_INFO] |= 0x40); + snd_ad1848_out(acard->wss, AD1848_MISC_INFO, 0x40); /* switch on MODE2 */ + for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) + snd_ad1848_out(acard->wss, i, snd_cmi8330_image[i - CMI8330_RMUX3D]); spin_unlock_irqrestore(&acard->wss->reg_lock, flags); - if ((err = snd_cmi8330_mixer(card, acard->wss)) < 0) { + if ((err = snd_cmi8330_mixer(card, acard)) < 0) { snd_printk("failed to create mixers\n"); snd_card_free(card); return err; } - spin_lock_irqsave(&acard->wss->reg_lock, flags); - for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) - snd_ad1848_out(acard->wss, i, acard->wss->image[i]); - spin_unlock_irqrestore(&acard->wss->reg_lock, flags); if ((err = snd_cmi8330_pcm(card, acard)) < 0) { snd_printk("failed to create pcms\n"); diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index 08237ab055c8..1b32c915d008 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -297,6 +297,7 @@ EXPORT_SYMBOL(snd_sbdsp_create); EXPORT_SYMBOL(snd_sbmixer_write); EXPORT_SYMBOL(snd_sbmixer_read); EXPORT_SYMBOL(snd_sbmixer_new); +EXPORT_SYMBOL(snd_sbmixer_add_ctl); /* * INIT part diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index f555de1de90c..b5532d9e19da 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -60,14 +60,6 @@ unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg) * Single channel mixer element */ -#define SB_SINGLE(xname, reg, shift, mask) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .info = snd_sbmixer_info_single, \ - .get = snd_sbmixer_get_single, \ - .put = snd_sbmixer_put_single, \ - .private_value = reg | (shift << 16) | (mask << 24) } - static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { int mask = (kcontrol->private_value >> 24) & 0xff; @@ -120,14 +112,6 @@ static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ * Double channel mixer element */ -#define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .info = snd_sbmixer_info_double, \ - .get = snd_sbmixer_get_double, \ - .put = snd_sbmixer_put_double, \ - .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) } - static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { int mask = (kcontrol->private_value >> 24) & 0xff; @@ -364,14 +348,6 @@ static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * SB16 input switch */ -#define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .info = snd_sb16mixer_info_input_sw, \ - .get = snd_sb16mixer_get_input_sw, \ - .put = snd_sb16mixer_put_input_sw, \ - .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } - static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; @@ -431,61 +407,114 @@ static int snd_sb16mixer_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_va return change; } -#define SB20_CONTROLS (sizeof(snd_sb20_controls)/sizeof(snd_kcontrol_new_t *)) -static snd_kcontrol_new_t snd_sb20_ctl_master_play_vol = +/* + */ +/* + */ +int snd_sbmixer_add_ctl(sb_t *chip, const char *name, int index, int type, unsigned long value) +{ + static snd_kcontrol_new_t newctls[] = { + [SB_MIX_SINGLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sbmixer_info_single, + .get = snd_sbmixer_get_single, + .put = snd_sbmixer_put_single, + }, + [SB_MIX_DOUBLE] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sbmixer_info_double, + .get = snd_sbmixer_get_double, + .put = snd_sbmixer_put_double, + }, + [SB_MIX_INPUT_SW] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sb16mixer_info_input_sw, + .get = snd_sb16mixer_get_input_sw, + .put = snd_sb16mixer_put_input_sw, + }, + [SB_MIX_CAPTURE_PRO] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_sb8mixer_info_mux, + .get = snd_sb8mixer_get_mux, + .put = snd_sb8mixer_put_mux, + }, + [SB_MIX_CAPTURE_DT019X] = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_dt019x_input_sw_info, + .get = snd_dt019x_input_sw_get, + .put = snd_dt019x_input_sw_put, + }, + }; + snd_kcontrol_t *ctl; + int err; + + ctl = snd_ctl_new1(&newctls[type], chip); + if (! ctl) + return -ENOMEM; + strncpy(ctl->id.name, name, sizeof(ctl->id.name)-1); + ctl->id.index = index; + ctl->private_value = value; + if ((err = snd_ctl_add(chip->card, ctl)) < 0) { + snd_ctl_free_one(ctl); + return err; + } + return 0; +} + +/* + * SB 2.0 specific mixer elements + */ + +static struct sbmix_elem snd_sb20_ctl_master_play_vol = SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7); -static snd_kcontrol_new_t snd_sb20_ctl_pcm_play_vol = +static struct sbmix_elem snd_sb20_ctl_pcm_play_vol = SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3); -static snd_kcontrol_new_t snd_sb20_ctl_synth_play_vol = +static struct sbmix_elem snd_sb20_ctl_synth_play_vol = SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7); -static snd_kcontrol_new_t snd_sb20_ctl_cd_play_vol = +static struct sbmix_elem snd_sb20_ctl_cd_play_vol = SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7); -static snd_kcontrol_new_t *snd_sb20_controls[] = { +static struct sbmix_elem *snd_sb20_controls[] = { &snd_sb20_ctl_master_play_vol, &snd_sb20_ctl_pcm_play_vol, &snd_sb20_ctl_synth_play_vol, &snd_sb20_ctl_cd_play_vol }; -#define SB20_INIT_VALUES (sizeof(snd_sb20_init_values)/sizeof(unsigned char)/2) - static unsigned char snd_sb20_init_values[][2] = { { SB_DSP20_MASTER_DEV, 0 }, { SB_DSP20_FM_DEV, 0 }, }; -#define SBPRO_CONTROLS (sizeof(snd_sbpro_controls)/sizeof(snd_kcontrol_new_t *)) - -static snd_kcontrol_new_t snd_sbpro_ctl_master_play_vol = +/* + * SB Pro specific mixer elements + */ +static struct sbmix_elem snd_sbpro_ctl_master_play_vol = SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7); -static snd_kcontrol_new_t snd_sbpro_ctl_pcm_play_vol = +static struct sbmix_elem snd_sbpro_ctl_pcm_play_vol = SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7); -static snd_kcontrol_new_t snd_sbpro_ctl_pcm_play_filter = +static struct sbmix_elem snd_sbpro_ctl_pcm_play_filter = SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1); -static snd_kcontrol_new_t snd_sbpro_ctl_synth_play_vol = +static struct sbmix_elem snd_sbpro_ctl_synth_play_vol = SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7); -static snd_kcontrol_new_t snd_sbpro_ctl_cd_play_vol = +static struct sbmix_elem snd_sbpro_ctl_cd_play_vol = SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7); -static snd_kcontrol_new_t snd_sbpro_ctl_line_play_vol = +static struct sbmix_elem snd_sbpro_ctl_line_play_vol = SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7); -static snd_kcontrol_new_t snd_sbpro_ctl_mic_play_vol = +static struct sbmix_elem snd_sbpro_ctl_mic_play_vol = SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3); -static snd_kcontrol_new_t snd_sbpro_ctl_capture_source = +static struct sbmix_elem snd_sbpro_ctl_capture_source = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", - .info = snd_sb8mixer_info_mux, - .get = snd_sb8mixer_get_mux, - .put = snd_sb8mixer_put_mux, + .type = SB_MIX_CAPTURE_PRO }; -static snd_kcontrol_new_t snd_sbpro_ctl_capture_filter = +static struct sbmix_elem snd_sbpro_ctl_capture_filter = SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1); -static snd_kcontrol_new_t snd_sbpro_ctl_capture_low_filter = +static struct sbmix_elem snd_sbpro_ctl_capture_low_filter = SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1); -static snd_kcontrol_new_t *snd_sbpro_controls[] = { +static struct sbmix_elem *snd_sbpro_controls[] = { &snd_sbpro_ctl_master_play_vol, &snd_sbpro_ctl_pcm_play_vol, &snd_sbpro_ctl_pcm_play_filter, @@ -498,58 +527,57 @@ static snd_kcontrol_new_t *snd_sbpro_controls[] = { &snd_sbpro_ctl_capture_low_filter }; -#define SBPRO_INIT_VALUES (sizeof(snd_sbpro_init_values)/sizeof(unsigned char)/2) - static unsigned char snd_sbpro_init_values[][2] = { { SB_DSP_MASTER_DEV, 0 }, { SB_DSP_PCM_DEV, 0 }, { SB_DSP_FM_DEV, 0 }, }; -#define SB16_CONTROLS (sizeof(snd_sb16_controls)/sizeof(snd_kcontrol_new_t *)) - -static snd_kcontrol_new_t snd_sb16_ctl_master_play_vol = +/* + * SB16 specific mixer elements + */ +static struct sbmix_elem snd_sb16_ctl_master_play_vol = SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31); -static snd_kcontrol_new_t snd_sb16_ctl_3d_enhance_switch = +static struct sbmix_elem snd_sb16_ctl_3d_enhance_switch = SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1); -static snd_kcontrol_new_t snd_sb16_ctl_tone_bass = +static struct sbmix_elem snd_sb16_ctl_tone_bass = SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15); -static snd_kcontrol_new_t snd_sb16_ctl_tone_treble = +static struct sbmix_elem snd_sb16_ctl_tone_treble = SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15); -static snd_kcontrol_new_t snd_sb16_ctl_pcm_play_vol = +static struct sbmix_elem snd_sb16_ctl_pcm_play_vol = SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31); -static snd_kcontrol_new_t snd_sb16_ctl_synth_capture_route = +static struct sbmix_elem snd_sb16_ctl_synth_capture_route = SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5); -static snd_kcontrol_new_t snd_sb16_ctl_synth_play_vol = +static struct sbmix_elem snd_sb16_ctl_synth_play_vol = SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31); -static snd_kcontrol_new_t snd_sb16_ctl_cd_capture_route = +static struct sbmix_elem snd_sb16_ctl_cd_capture_route = SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1); -static snd_kcontrol_new_t snd_sb16_ctl_cd_play_switch = +static struct sbmix_elem snd_sb16_ctl_cd_play_switch = SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1); -static snd_kcontrol_new_t snd_sb16_ctl_cd_play_vol = +static struct sbmix_elem snd_sb16_ctl_cd_play_vol = SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31); -static snd_kcontrol_new_t snd_sb16_ctl_line_capture_route = +static struct sbmix_elem snd_sb16_ctl_line_capture_route = SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3); -static snd_kcontrol_new_t snd_sb16_ctl_line_play_switch = +static struct sbmix_elem snd_sb16_ctl_line_play_switch = SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1); -static snd_kcontrol_new_t snd_sb16_ctl_line_play_vol = +static struct sbmix_elem snd_sb16_ctl_line_play_vol = SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31); -static snd_kcontrol_new_t snd_sb16_ctl_mic_capture_route = +static struct sbmix_elem snd_sb16_ctl_mic_capture_route = SB16_INPUT_SW("Mic Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0); -static snd_kcontrol_new_t snd_sb16_ctl_mic_play_switch = +static struct sbmix_elem snd_sb16_ctl_mic_play_switch = SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1); -static snd_kcontrol_new_t snd_sb16_ctl_mic_play_vol = +static struct sbmix_elem snd_sb16_ctl_mic_play_vol = SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31); -static snd_kcontrol_new_t snd_sb16_ctl_pc_speaker_vol = +static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol = SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3); -static snd_kcontrol_new_t snd_sb16_ctl_capture_vol = +static struct sbmix_elem snd_sb16_ctl_capture_vol = SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3); -static snd_kcontrol_new_t snd_sb16_ctl_play_vol = +static struct sbmix_elem snd_sb16_ctl_play_vol = SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3); -static snd_kcontrol_new_t snd_sb16_ctl_auto_mic_gain = +static struct sbmix_elem snd_sb16_ctl_auto_mic_gain = SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1); -static snd_kcontrol_new_t *snd_sb16_controls[] = { +static struct sbmix_elem *snd_sb16_controls[] = { &snd_sb16_ctl_master_play_vol, &snd_sb16_ctl_3d_enhance_switch, &snd_sb16_ctl_tone_bass, @@ -572,8 +600,6 @@ static snd_kcontrol_new_t *snd_sb16_controls[] = { &snd_sb16_ctl_auto_mic_gain }; -#define SB16_INIT_VALUES (sizeof(snd_sb16_init_values)/sizeof(unsigned char)/2) - static unsigned char snd_sb16_init_values[][2] = { { SB_DSP4_MASTER_DEV + 0, 0 }, { SB_DSP4_MASTER_DEV + 1, 0 }, @@ -587,37 +613,34 @@ static unsigned char snd_sb16_init_values[][2] = { { SB_DSP4_SPEAKER_DEV, 0 }, }; -#define DT019X_CONTROLS (sizeof(snd_dt019x_controls)/sizeof(snd_kcontrol_new_t *)) - - -static snd_kcontrol_new_t snd_dt019x_ctl_master_play_vol = +/* + * DT019x specific mixer elements + */ +static struct sbmix_elem snd_dt019x_ctl_master_play_vol = SB_DOUBLE("Master Playback Volume", SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4,0, 15); -static snd_kcontrol_new_t snd_dt019x_ctl_pcm_play_vol = +static struct sbmix_elem snd_dt019x_ctl_pcm_play_vol = SB_DOUBLE("PCM Playback Volume", SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4,0, 15); -static snd_kcontrol_new_t snd_dt019x_ctl_synth_play_vol = +static struct sbmix_elem snd_dt019x_ctl_synth_play_vol = SB_DOUBLE("Synth Playback Volume", SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4,0, 15); -static snd_kcontrol_new_t snd_dt019x_ctl_cd_play_vol = +static struct sbmix_elem snd_dt019x_ctl_cd_play_vol = SB_DOUBLE("CD Playback Volume", SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4,0, 15); -static snd_kcontrol_new_t snd_dt019x_ctl_mic_play_vol = +static struct sbmix_elem snd_dt019x_ctl_mic_play_vol = SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7); -static snd_kcontrol_new_t snd_dt019x_ctl_pc_speaker_vol = +static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol = SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0, 7); -static snd_kcontrol_new_t snd_dt019x_ctl_line_play_vol = +static struct sbmix_elem snd_dt019x_ctl_line_play_vol = SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15); -static snd_kcontrol_new_t snd_dt019x_ctl_pcm_play_switch = +static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch = SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1); -static snd_kcontrol_new_t snd_dt019x_ctl_synth_play_switch = +static struct sbmix_elem snd_dt019x_ctl_synth_play_switch = SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1); -static snd_kcontrol_new_t snd_dt019x_ctl_capture_source = +static struct sbmix_elem snd_dt019x_ctl_capture_source = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", - .info = snd_dt019x_input_sw_info, - .get = snd_dt019x_input_sw_get, - .put = snd_dt019x_input_sw_put, + .type = SB_MIX_CAPTURE_DT019X }; -static snd_kcontrol_new_t *snd_dt019x_controls[] = { +static struct sbmix_elem *snd_dt019x_controls[] = { &snd_dt019x_ctl_master_play_vol, &snd_dt019x_ctl_pcm_play_vol, &snd_dt019x_ctl_synth_play_vol, @@ -633,8 +656,6 @@ static snd_kcontrol_new_t *snd_dt019x_controls[] = { &snd_dt019x_ctl_capture_source }; -#define DT019X_INIT_VALUES (sizeof(snd_dt019x_init_values)/sizeof(unsigned char)/2) - static unsigned char snd_dt019x_init_values[][2] = { { SB_DT019X_MASTER_DEV, 0 }, { SB_DT019X_PCM_DEV, 0 }, @@ -647,35 +668,36 @@ static unsigned char snd_dt019x_init_values[][2] = { { SB_DT019X_CAPTURE_SW, 0x06 }, }; +/* + * ALS4000 specific mixer elements + */ /* FIXME: SB_ALS4000_MONO_IO_CTRL needs output select ctrl ! */ -static snd_kcontrol_new_t snd_als4000_ctl_mono_output_switch = +static struct sbmix_elem snd_als4000_ctl_mono_output_switch = SB_SINGLE("Mono Output Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1); /* FIXME: mono input switch also available on DT019X ? */ -static snd_kcontrol_new_t snd_als4000_ctl_mono_input_switch = +static struct sbmix_elem snd_als4000_ctl_mono_input_switch = SB_SINGLE("Mono Input Switch", SB_DT019X_OUTPUT_SW2, 0, 1); -static snd_kcontrol_new_t snd_als4000_ctl_mic_20db_boost = +static struct sbmix_elem snd_als4000_ctl_mic_20db_boost = SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03); -static snd_kcontrol_new_t snd_als4000_ctl_mixer_out_to_in = +static struct sbmix_elem snd_als4000_ctl_mixer_out_to_in = SB_SINGLE("Mixer Out To In", SB_ALS4000_MIC_IN_GAIN, 7, 0x01); /* FIXME: 3D needs much more sophisticated controls, many more features ! */ -static snd_kcontrol_new_t snd_als4000_ctl_3d_output_switch = +static struct sbmix_elem snd_als4000_ctl_3d_output_switch = SB_SINGLE("3D Output Switch", SB_ALS4000_3D_SND_FX, 6, 0x01); -static snd_kcontrol_new_t snd_als4000_ctl_3d_output_ratio = +static struct sbmix_elem snd_als4000_ctl_3d_output_ratio = SB_SINGLE("3D Output Ratio", SB_ALS4000_3D_SND_FX, 0, 0x07); -static snd_kcontrol_new_t snd_als4000_ctl_3d_poweroff_switch = +static struct sbmix_elem snd_als4000_ctl_3d_poweroff_switch = SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01); -static snd_kcontrol_new_t snd_als4000_ctl_3d_delay = +static struct sbmix_elem snd_als4000_ctl_3d_delay = SB_SINGLE("3D Delay", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f); #if NOT_AVAILABLE -static snd_kcontrol_new_t snd_als4000_ctl_fmdac = +static struct sbmix_elem snd_als4000_ctl_fmdac = SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01); -static snd_kcontrol_new_t snd_als4000_ctl_qsound = +static struct sbmix_elem snd_als4000_ctl_qsound = SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f); #endif -#define ALS4000_CONTROLS (sizeof(snd_als4000_controls)/sizeof(snd_kcontrol_new_t *)) - -static snd_kcontrol_new_t *snd_als4000_controls[] = { +static struct sbmix_elem *snd_als4000_controls[] = { &snd_sb16_ctl_master_play_vol, &snd_dt019x_ctl_pcm_play_switch, &snd_sb16_ctl_pcm_play_vol, @@ -709,8 +731,6 @@ static snd_kcontrol_new_t *snd_als4000_controls[] = { #endif }; -#define ALS4000_INIT_VALUES (sizeof(snd_als4000_init_values)/sizeof(unsigned char)/2) - static unsigned char snd_als4000_init_values[][2] = { { SB_DSP4_MASTER_DEV + 0, 0 }, { SB_DSP4_MASTER_DEV + 1, 0 }, @@ -726,8 +746,11 @@ static unsigned char snd_als4000_init_values[][2] = { { SB_ALS4000_MIC_IN_GAIN, 0 }, }; + +/* + */ static int snd_sbmixer_init(sb_t *chip, - snd_kcontrol_new_t **controls, + struct sbmix_elem **controls, int controls_count, unsigned char map[][2], int map_count, @@ -750,7 +773,7 @@ static int snd_sbmixer_init(sb_t *chip, } for (idx = 0; idx < controls_count; idx++) { - if ((err = snd_ctl_add(card, snd_ctl_new1(controls[idx], chip))) < 0) + if ((err = snd_sbmixer_add_ctl_elem(chip, controls[idx])) < 0) return err; } snd_component_add(card, name); @@ -773,37 +796,47 @@ int snd_sbmixer_new(sb_t *chip) case SB_HW_20: case SB_HW_201: if ((err = snd_sbmixer_init(chip, - snd_sb20_controls, SB20_CONTROLS, - snd_sb20_init_values, SB20_INIT_VALUES, + snd_sb20_controls, + ARRAY_SIZE(snd_sb20_controls), + snd_sb20_init_values, + ARRAY_SIZE(snd_sb20_init_values), "CTL1335")) < 0) return err; break; case SB_HW_PRO: if ((err = snd_sbmixer_init(chip, - snd_sbpro_controls, SBPRO_CONTROLS, - snd_sbpro_init_values, SBPRO_INIT_VALUES, + snd_sbpro_controls, + ARRAY_SIZE(snd_sbpro_controls), + snd_sbpro_init_values, + ARRAY_SIZE(snd_sbpro_init_values), "CTL1345")) < 0) return err; break; case SB_HW_16: case SB_HW_ALS100: if ((err = snd_sbmixer_init(chip, - snd_sb16_controls, SB16_CONTROLS, - snd_sb16_init_values, SB16_INIT_VALUES, + snd_sb16_controls, + ARRAY_SIZE(snd_sb16_controls), + snd_sb16_init_values, + ARRAY_SIZE(snd_sb16_init_values), "CTL1745")) < 0) return err; break; case SB_HW_ALS4000: if ((err = snd_sbmixer_init(chip, - snd_als4000_controls, ALS4000_CONTROLS, - snd_als4000_init_values, ALS4000_INIT_VALUES, + snd_als4000_controls, + ARRAY_SIZE(snd_als4000_controls), + snd_als4000_init_values, + ARRAY_SIZE(snd_als4000_init_values), "ALS4000")) < 0) return err; break; case SB_HW_DT019X: if ((err = snd_sbmixer_init(chip, - snd_dt019x_controls, DT019X_CONTROLS, - snd_dt019x_init_values, DT019X_INIT_VALUES, + snd_dt019x_controls, + ARRAY_SIZE(snd_dt019x_controls), + snd_dt019x_init_values, + ARRAY_SIZE(snd_dt019x_init_values), "DT019X")) < 0) break; default: diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c index 6645c9be5d7b..a99bb045fb1f 100644 --- a/sound/isa/sgalaxy.c +++ b/sound/isa/sgalaxy.c @@ -30,6 +30,7 @@ #include #include #include +#include #define SNDRV_LEGACY_FIND_FREE_IRQ #define SNDRV_LEGACY_FIND_FREE_DMA #define SNDRV_GET_ID @@ -176,9 +177,7 @@ static int __init snd_sgalaxy_detect(int dev, int irq, int dma) return snd_sgalaxy_setup_wss(wssport[dev], irq, dma); } -#define SGALAXY_CONTROLS 2 - -static snd_kcontrol_new_t snd_sgalaxy_controls[2] = { +static struct ad1848_mix_elem snd_sgalaxy_controls[] = { AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) }; @@ -211,8 +210,8 @@ static int __init snd_sgalaxy_mixer(ad1848_t *chip) if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) return err; /* build AUX2 input */ - for (idx = 0; idx < SGALAXY_CONTROLS; idx++) { - if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sgalaxy_controls[idx], chip))) < 0) + for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { + if ((err = snd_ad1848_add_ctl_elem(chip, &snd_sgalaxy_controls[idx])) < 0) return err; } return 0; diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index d5bca86c637e..f733d7ca6431 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -116,10 +116,10 @@ config SND_ENS1371 Sound Blaster PCI 64 or 128 soundcards. config SND_ES1938 - tristate "ESS ES1938/1946 (Solo-1)" + tristate "ESS ES1938/1946/1969 (Solo-1)" depends on SND && SOUND_GAMEPORT help - Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946) + Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard. config SND_ES1968 diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index bbfa3c2435b2..fbc584055520 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1278,6 +1278,9 @@ static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_ sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname); chip->pcm_mic = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024); + if (rpcm) *rpcm = pcm; return 0; @@ -1312,6 +1315,9 @@ static int __devinit snd_intel8x0_pcm_mic2(intel8x0_t *chip, int device, snd_pcm sprintf(pcm->name, "%s - MIC2 ADC", chip->card->shortname); chip->pcm_mic2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024); + if (rpcm) *rpcm = pcm; return 0; @@ -1346,6 +1352,9 @@ static int __devinit snd_intel8x0_pcm_capture2(intel8x0_t *chip, int device, snd sprintf(pcm->name, "%s - ADC2", chip->card->shortname); chip->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024); + if (rpcm) *rpcm = pcm; return 0; @@ -1380,6 +1389,9 @@ static int __devinit snd_intel8x0_pcm_spdif(intel8x0_t *chip, int device, snd_pc sprintf(pcm->name, "%s - IEC958", chip->card->shortname); chip->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + if (rpcm) *rpcm = pcm; return 0; @@ -1415,6 +1427,9 @@ static int __devinit snd_intel8x0_ali_spdif(intel8x0_t *chip, int device, snd_pc sprintf(pcm->name, "%s - IEC958", chip->card->shortname); chip->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + if (rpcm) *rpcm = pcm; return 0; @@ -1449,6 +1464,9 @@ static int __devinit snd_intel8x0_ali_ac97spdif(intel8x0_t *chip, int device, sn sprintf(pcm->name, "%s - AC97 IEC958", chip->card->shortname); chip->pcm_ac97spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + if (rpcm) *rpcm = pcm; return 0; @@ -1790,6 +1808,10 @@ static int snd_intel8x0_ich_chip_init(intel8x0_t *chip) } while (time_after_eq(end_time, jiffies)); __ok3: + if (chip->device_type == DEVICE_SIS) { + /* unmute the output on SIS7012 */ + iputword(chip, 0x4c, igetword(chip, 0x4c) | 1); + } return 0; } @@ -2476,6 +2498,8 @@ static struct pci_driver joystick_driver = { .id_table = snd_intel8x0_joystick_ids, .probe = snd_intel8x0_joystick_probe, }; + +static int have_joystick; #endif static int __init alsa_card_intel8x0_init(void) @@ -2489,7 +2513,13 @@ static int __init alsa_card_intel8x0_init(void) return err; } #if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) - pci_module_init(&joystick_driver); + if (pci_module_init(&joystick_driver) < 0) { + snd_printdd(KERN_INFO "no joystick found\n"); + have_joystick = 0; + } else { + snd_printdd(KERN_INFO "joystick(s) found\n"); + have_joystick = 1; + } #endif return 0; @@ -2499,7 +2529,8 @@ static void __exit alsa_card_intel8x0_exit(void) { pci_unregister_driver(&driver); #if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) - pci_unregister_driver(&joystick_driver); + if (have_joystick) + pci_unregister_driver(&joystick_driver); #endif } diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index bbe85ce76cf4..bf4577f49fe9 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1,7 +1,7 @@ /* * ALSA driver for RME Digi32, Digi32/8 and Digi32 PRO audio interfaces * - * Copyright (c) 2002 Martin Langer + * Copyright (c) 2002, 2003 Martin Langer * * Thanks to : Anders Torger , * Henk Hesselink @@ -70,6 +70,7 @@ #include #define SNDRV_GET_ID #include +#include #include @@ -256,8 +257,6 @@ snd_rme32_capture_pointer(snd_pcm_substream_t * substream); static void snd_rme32_proc_init(rme32_t * rme32); -static void snd_rme32_proc_done(rme32_t * rme32); - static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32); static inline unsigned int snd_rme32_playback_ptr(rme32_t * rme32) @@ -1830,7 +1829,7 @@ static snd_kcontrol_new_t snd_rme32_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "Clock Mode", + .name = "Sample Clock Source", .info = snd_rme32_info_clockmode_control, .get = snd_rme32_get_clockmode_control, .put = snd_rme32_put_clockmode_control diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8a0f92d265fd..9c01b8cc9c6c 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -23,6 +23,26 @@ * */ +/* + * Changes: + * + * Dec. 19, 2002 Takashi Iwai + * - use the DSX channels for the first pcm playback. + * (on VIA8233, 8233C and 8235 only) + * this will allow you play simultaneously up to 4 streams. + * multi-channel playback is assigned to the second device + * on these chips. + * - support the secondary capture (on VIA8233/C,8235) + * - SPDIF support + * the DSX3 channel can be used for SPDIF output. + * on VIA8233A, this channel is assigned to the second pcm + * playback. + * the card config of alsa-lib will assign the correct + * device for applications. + * - clean up the code, separate low-level initialization + * routines for each chipset. + */ + #include #include #include @@ -48,7 +68,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("VIA VT82xx audio"); MODULE_LICENSE("GPL"); MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/B/C}}"); +MODULE_DEVICES("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -183,6 +203,13 @@ DEFINE_VIA_REGSET(CAPTURE_8233, 0x60); #define VIA_TBL_BIT_FLAG 0x40000000 #define VIA_TBL_BIT_EOL 0x80000000 +/* + */ + +typedef struct _snd_via82xx via82xx_t; +typedef struct via_dev viadev_t; +#define chip_t via82xx_t + /* * pcm stream */ @@ -194,18 +221,20 @@ struct snd_via_sg_table { #define VIA_TABLE_SIZE 255 -typedef struct { - unsigned long reg_offset; +struct via_dev { + unsigned int reg_offset; + int direction; /* playback = 0, capture = 1 */ snd_pcm_substream_t *substream; int running; unsigned int tbl_entries; /* # descriptors */ u32 *table; /* physical address + flag */ dma_addr_t table_addr; struct snd_via_sg_table *idx_table; + /* for recovery from the unexpected pointer */ unsigned int lastpos; unsigned int bufsize; unsigned int bufsize2; -} viadev_t; +}; /* @@ -218,7 +247,7 @@ static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, unsigned int periods, unsigned int fragsize) { unsigned int i, idx, ofs, rest; - struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); if (! dev->table) { /* the start of each lists must be aligned to 8 bytes, @@ -297,8 +326,13 @@ static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, enum { TYPE_VIA686 = 1, TYPE_VIA8233 }; -typedef struct _snd_via82xx via82xx_t; -#define chip_t via82xx_t +#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */ + +struct via_rate_lock { + spinlock_t lock; + int rate; + int used; +}; struct _snd_via82xx { int irq; @@ -314,9 +348,10 @@ struct _snd_via82xx { struct pci_dev *pci; snd_card_t *card; - snd_pcm_t *pcm; - viadev_t playback; - viadev_t capture; + int num_devs; + int playback_devno, multi_devno, capture_devno; + viadev_t devs[VIA_MAX_DEVS]; + struct via_rate_lock rates[2]; /* playback and capture */ snd_rawmidi_t *rmidi; @@ -361,7 +396,7 @@ static int snd_via82xx_codec_ready(via82xx_t *chip, int secondary) if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)) return val & 0xffff; } - snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); + snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); return -EIO; } @@ -377,7 +412,7 @@ static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary) if ((val = snd_via82xx_codec_xread(chip)) & stat) return val & 0xffff; } - snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); + snd_printk(KERN_ERR "codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); return -EIO; } @@ -452,11 +487,57 @@ static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev) viadev->lastpos = 0; } -static int snd_via82xx_trigger(via82xx_t *chip, viadev_t *viadev, int cmd) + +/* + * Interrupt handler + */ + +static void snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + via82xx_t *chip = snd_magic_cast(via82xx_t, dev_id, return); + unsigned int status; + int i; + + spin_lock(&chip->reg_lock); + if (chip->chip_type == TYPE_VIA686) { + /* check mpu401 interrupt */ + status = inl(VIAREG(chip, SGD_SHADOW)); + if ((status & 0x00000077) == 0) { + spin_unlock(&chip->reg_lock); + if (chip->rmidi != NULL) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + return; + } + } + /* check status for each stream */ + for (i = 0; i < chip->num_devs; i++) { + viadev_t *viadev = &chip->devs[i]; + if (inb(chip->port + viadev->reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) { + outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); + if (viadev->substream && viadev->running) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(viadev->substream); + spin_lock(&chip->reg_lock); + } + } + } + spin_unlock(&chip->reg_lock); +} + +/* + * PCM callbacks + */ + +/* + * trigger callback + */ +static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; unsigned char val; unsigned long port = chip->port + viadev->reg_offset; - + if (chip->chip_type == TYPE_VIA8233) val = VIA_REG_CTRL_INT; else @@ -487,114 +568,104 @@ static int snd_via82xx_trigger(via82xx_t *chip, viadev_t *viadev, int cmd) } -static int snd_via82xx_set_format(via82xx_t *chip, viadev_t *viadev, - snd_pcm_substream_t *substream) +/* + * pointer callbacks + */ + +/* + * calculate the linear position at the given sg-buffer index and the rest count + */ +static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, unsigned int count) { - snd_pcm_runtime_t *runtime = substream->runtime; - unsigned long port = chip->port + viadev->reg_offset; + unsigned int size, res; - snd_via82xx_channel_reset(chip, viadev); + size = viadev->idx_table[idx].size; + res = viadev->idx_table[idx].offset + size - count; - outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); - switch (chip->chip_type) { - case TYPE_VIA686: - outb(VIA_REG_TYPE_AUTOSTART | - (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | - (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | - ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) | - VIA_REG_TYPE_INT_EOL | - VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE); - break; - case TYPE_VIA8233: - if (viadev->reg_offset == VIA_REG_MULTPLAY_STATUS) { - unsigned int slots; - int fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT; - fmt |= runtime->channels << 4; - outb(fmt, port + VIA_REG_OFFSET_TYPE); - /* set sample number to slot 3, 4, 7, 8, 6, 9 */ - switch (runtime->channels) { - case 1: slots = (1<<0) | (1<<4); break; - case 2: slots = (1<<0) | (2<<4); break; - case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break; - case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break; - default: slots = 0; break; - } - /* STOP index is never reached */ - outl(0xff000000 | slots, port + VIA_REG_OFFSET_STOP_IDX); + /* check the validity of the calculated position */ + if (size < count || (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))) { +#ifdef POINTER_DEBUG + printk("fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count); +#endif + /* count register returns full size when end of buffer is reached */ + if (size != count) { + snd_printd(KERN_ERR "invalid via82xx_cur_ptr, using last valid pointer\n"); + res = viadev->lastpos; } else { - outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | - (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | - 0xff000000, /* STOP index is never reached */ - port + VIA_REG_OFFSET_STOP_IDX); + res = viadev->idx_table[idx].offset + size; + if (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2)) { + snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), using last valid pointer\n"); + res = viadev->lastpos; + } } - break; } - return 0; + viadev->lastpos = res; /* remember the last positiion */ + if (res >= viadev->bufsize) + res -= viadev->bufsize; + return res; } /* - * Interrupt handler + * get the current pointer on via686 */ - -static inline void snd_via82xx_update(via82xx_t *chip, viadev_t *viadev) +static snd_pcm_uframes_t snd_via686_pcm_pointer(snd_pcm_substream_t *substream) { - outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); - if (viadev->substream && viadev->running) { - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(viadev->substream); - spin_lock(&chip->reg_lock); - } -} + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned int idx, ptr, count, res; -static void snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - via82xx_t *chip = snd_magic_cast(via82xx_t, dev_id, return); - unsigned int status; + snd_assert(viadev->tbl_entries, return 0); + if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) + return 0; spin_lock(&chip->reg_lock); - if (chip->chip_type == TYPE_VIA686) { - /* check mpu401 interrupt */ - status = inl(VIAREG(chip, SGD_SHADOW)); - if ((status & 0x00000077) == 0) { - spin_unlock(&chip->reg_lock); - if (chip->rmidi != NULL) - snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); - return; - } - } - /* check status for each stream */ - if (inb(chip->port + chip->playback.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) - snd_via82xx_update(chip, &chip->playback); - if (inb(chip->port + chip->capture.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) - snd_via82xx_update(chip, &chip->capture); + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; + /* The via686a does not have the current index register, + * so we need to calculate the index from CURR_PTR. + */ + ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset); + if (ptr <= (unsigned int)viadev->table_addr) + idx = 0; + else /* CURR_PTR holds the address + 8 */ + idx = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries; + res = calc_linear_pos(viadev, idx, count); spin_unlock(&chip->reg_lock); + + return bytes_to_frames(substream->runtime, res); } /* - * PCM part + * get the current pointer on via823x */ - -static int snd_via82xx_playback_trigger(snd_pcm_substream_t * substream, - int cmd) +static snd_pcm_uframes_t snd_via8233_pcm_pointer(snd_pcm_substream_t *substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned int idx, count, res; + + snd_assert(viadev->tbl_entries, return 0); + if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) + return 0; + spin_lock(&chip->reg_lock); + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset); + idx = count >> 24; + count &= 0xffffff; + res = calc_linear_pos(viadev, idx, count); + spin_unlock(&chip->reg_lock); - return snd_via82xx_trigger(chip, &chip->playback, cmd); + return bytes_to_frames(substream->runtime, res); } -static int snd_via82xx_capture_trigger(snd_pcm_substream_t * substream, - int cmd) -{ - via82xx_t *chip = snd_pcm_substream_chip(substream); - - return snd_via82xx_trigger(chip, &chip->capture, cmd); -} +/* + * hw_params callback: + * allocate the buffer and build up the buffer description table + */ static int snd_via82xx_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) { via82xx_t *chip = snd_pcm_substream_chip(substream); - viadev_t *viadev = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? &chip->playback : &chip->capture; + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; int err; err = snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params)); @@ -605,131 +676,184 @@ static int snd_via82xx_hw_params(snd_pcm_substream_t * substream, params_period_bytes(hw_params)); if (err < 0) return err; - return err; + + return 0; } +/* + * hw_free callback: + * clean up the buffer description table and release the buffer + */ static int snd_via82xx_hw_free(snd_pcm_substream_t * substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); - viadev_t *viadev = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? &chip->playback : &chip->capture; + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; clean_via_table(viadev, substream, chip->pci); snd_pcm_sgbuf_free(substream); return 0; } -static int snd_via82xx_playback_prepare(snd_pcm_substream_t * substream) + +/* + * prepare callback for playback and capture on via686 + */ +static void via686_setup_format(via82xx_t *chip, viadev_t *viadev, snd_pcm_runtime_t *runtime) +{ + unsigned long port = chip->port + viadev->reg_offset; + + snd_via82xx_channel_reset(chip, viadev); + /* this must be set after channel_reset */ + outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + outb(VIA_REG_TYPE_AUTOSTART | + (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | + ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) | + VIA_REG_TYPE_INT_EOL | + VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE); +} + +static int snd_via686_playback_prepare(snd_pcm_substream_t *substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; snd_pcm_runtime_t *runtime = substream->runtime; snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); - snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate); - snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate); - snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); - if (chip->chip_type == TYPE_VIA8233 && - chip->playback.reg_offset != VIA_REG_MULTPLAY_STATUS) { - unsigned int tmp; - /* I don't understand this stuff but its from the documentation and this way it works */ - outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L)); - outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R)); - tmp = inl(VIAREG(chip, PLAYBACK_STOP_IDX)) & ~0xfffff; - outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_STOP_IDX)); - } - return snd_via82xx_set_format(chip, &chip->playback, substream); + via686_setup_format(chip, viadev, runtime); + return 0; } -static int snd_via82xx_capture_prepare(snd_pcm_substream_t * substream) +static int snd_via686_capture_prepare(snd_pcm_substream_t *substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; snd_pcm_runtime_t *runtime = substream->runtime; snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); - if (chip->chip_type == TYPE_VIA8233) - outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO)); - return snd_via82xx_set_format(chip, &chip->capture, substream); + via686_setup_format(chip, viadev, runtime); + return 0; } -static inline unsigned int snd_via82xx_cur_ptr(via82xx_t *chip, viadev_t *viadev) +/* + * lock the current rate + */ +static int via_lock_rate(struct via_rate_lock *rec, int rate) { - unsigned int val, ptr, count, res; - - snd_assert(viadev->tbl_entries, return 0); - if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) - return 0; - - spin_lock(&chip->reg_lock); - - switch (chip->chip_type) { - case TYPE_VIA686: - count &= 0xffffff; - /* The via686a does not have the current index register, - * so we need to calculate the index from CURR_PTR. - */ - ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset); - count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; - if (ptr <= (unsigned int)viadev->table_addr) - val = 0; - else /* CURR_PTR holds the address + 8 */ - val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries; - break; - - case TYPE_VIA8233: - default: - count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset); - val = count >> 24; - count &= 0xffffff; - break; - } - - /* convert to the linear position */ - ptr = viadev->idx_table[val].size; - res = viadev->idx_table[val].offset + ptr - count; - - if (ptr < count || (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))) { -#ifdef POINTER_DEBUG - printk("fail: val = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", val, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[val].offset, viadev->idx_table[val].size, count); -#endif - /* VIA8233 count register returns full size when end of buffer is reached */ - if (ptr != count) { - snd_printk("invalid via82xx_cur_ptr, using last valid pointer\n"); - res = viadev->lastpos; - } else { - res = viadev->idx_table[val].offset + ptr; - if (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2)) { - snd_printk("invalid via82xx_cur_ptr (2), using last valid pointer\n"); - res = viadev->lastpos; - } + spin_lock(&rec->lock); + if (rec->rate) { + if (rec->rate != rate && rec->used > 1) { + spin_unlock(&rec->lock); + return -EINVAL; } - } + } else + rec->rate = rate; + spin_unlock(&rec->lock); + return 0; +} - viadev->lastpos = res; - spin_unlock(&chip->reg_lock); +/* + * prepare callback for DSX playback on via823x + */ +static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned long port = chip->port + viadev->reg_offset; + snd_pcm_runtime_t *runtime = substream->runtime; - return res; + if (via_lock_rate(&chip->rates[0], runtime->rate) < 0) + return -EINVAL; + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + if (viadev->reg_offset == 0x30) /* DSX3 */ + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + snd_via82xx_channel_reset(chip, viadev); + outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L)); + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R)); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | /* format */ + (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | /* stereo */ + (0xffff * runtime->rate)/(48000/16) | /* rate */ + 0xff000000, /* STOP index is never reached */ + port + VIA_REG_OFFSET_STOP_IDX); + return 0; } -static snd_pcm_uframes_t snd_via82xx_playback_pointer(snd_pcm_substream_t * substream) +/* + * prepare callback for multi-channel playback on via823x + */ +static int snd_via8233_multi_prepare(snd_pcm_substream_t *substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); - return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->playback)); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned long port = chip->port + viadev->reg_offset; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int slots; + int fmt; + + if (via_lock_rate(&chip->rates[0], runtime->rate) < 0) + return -EINVAL; + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate); + snd_via82xx_channel_reset(chip, viadev); + outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + + fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT; + fmt |= runtime->channels << 4; + outb(fmt, port + VIA_REG_OFFSET_TYPE); + /* set sample number to slot 3, 4, 7, 8, 6, 9 */ + /* corresponding to FL, FR, RL, RR, C, LFE ?? */ + switch (runtime->channels) { + case 1: slots = (1<<0) | (1<<4); break; + case 2: slots = (1<<0) | (2<<4); break; + case 3: slots = (1<<0) | (2<<4) | (5<<8); break; + case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break; + case 5: slots = (1<<0) | (2<<4) | (5<<8) | (3<<12) | (4<<16); break; + case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break; + default: slots = 0; break; + } + /* STOP index is never reached */ + outl(0xff000000 | slots, port + VIA_REG_OFFSET_STOP_IDX); + return 0; } -static snd_pcm_uframes_t snd_via82xx_capture_pointer(snd_pcm_substream_t * substream) +/* + * prepare callback for capture on via823x + */ +static int snd_via8233_capture_prepare(snd_pcm_substream_t *substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); - return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->capture)); + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned long port = chip->port + viadev->reg_offset; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (via_lock_rate(&chip->rates[1], runtime->rate) < 0) + return -EINVAL; + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_via82xx_channel_reset(chip, viadev); + outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO)); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | + 0xff000000, /* STOP index is never reached */ + port + VIA_REG_OFFSET_STOP_IDX); + return 0; } -static snd_pcm_hardware_t snd_via82xx_playback = + +/* + * pcm hardware definition, identical for both playback and capture + */ +static snd_pcm_hardware_t snd_via82xx_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = 0, - .rate_min = 8000, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, .rate_max = 48000, .channels_min = 1, .channels_max = 2, @@ -741,151 +865,310 @@ static snd_pcm_hardware_t snd_via82xx_playback = .fifo_size = 0, }; -static snd_pcm_hardware_t snd_via82xx_capture = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = 0, - .rate_min = 8000, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 128 * 1024, - .periods_min = 2, - .periods_max = VIA_TABLE_SIZE / 2, - .fifo_size = 0, -}; - -static unsigned int channels[] = { - 1, 2, 4, 6 -}; - -#define CHANNELS sizeof(channels) / sizeof(channels[0]) -static snd_pcm_hw_constraint_list_t hw_constraints_channels = { - .count = CHANNELS, - .list = channels, - .mask = 0, -}; - -static int snd_via82xx_playback_open(snd_pcm_substream_t * substream) +/* + * open callback skeleton + */ +static int snd_via82xx_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_substream_t * substream) { - via82xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err; + unsigned long flags; + struct via_rate_lock *ratep; + + runtime->hw = snd_via82xx_hw; + + /* set the hw rate condition */ + ratep = &chip->rates[viadev->direction]; + spin_lock_irqsave(&ratep->lock, flags); + ratep->used++; + if (! ratep->rate) { + int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC; + runtime->hw.rates = chip->ac97->rates[idx]; + if (runtime->hw.rates & SNDRV_PCM_RATE_8000) + runtime->hw.rate_min = 8000; + } else { + /* a fixed rate */ + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate; + } + spin_unlock_irqrestore(&ratep->lock, flags); - chip->playback.substream = substream; - runtime->hw = snd_via82xx_playback; - runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC]; - if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) - runtime->hw.rate_min = 48000; if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) return err; /* we may remove following constaint when we modify table entries in interrupt */ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; - if (chip->chip_type == TYPE_VIA8233) { - runtime->hw.channels_max = 6; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); - } + + runtime->private_data = viadev; + viadev->substream = substream; + return 0; } -static int snd_via82xx_capture_open(snd_pcm_substream_t * substream) + +/* + * open callback for playback on via686 and via823x DSX + */ +static int snd_via82xx_playback_open(snd_pcm_substream_t * substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; + viadev_t *viadev = &chip->devs[chip->playback_devno + substream->number]; + + return snd_via82xx_pcm_open(chip, viadev, substream); +} + +/* + * open callback for playback on via823x multi-channel + */ +static int snd_via8233_multi_open(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + viadev_t *viadev = &chip->devs[chip->multi_devno]; int err; + /* channels constraint for VIA8233A + * 3 and 5 channels are not supported + */ + static unsigned int channels[] = { + 1, 2, 4, 6 + }; + static snd_pcm_hw_constraint_list_t hw_constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, + }; - chip->capture.substream = substream; - runtime->hw = snd_via82xx_capture; - runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC]; - if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) - runtime->hw.rate_min = 48000; - if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) - return err; - if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0) return err; + substream->runtime->hw.channels_max = 6; + if (chip->revision == VIA_REV_8233A) + snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); return 0; } -static int snd_via82xx_playback_close(snd_pcm_substream_t * substream) +/* + * open callback for capture on via686 and via823x + */ +static int snd_via82xx_capture_open(snd_pcm_substream_t * substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); - chip->playback.substream = NULL; - snd_pcm_sgbuf_delete(substream); - return 0; + viadev_t *viadev = &chip->devs[chip->capture_devno + substream->pcm->device]; + + return snd_via82xx_pcm_open(chip, viadev, substream); } -static int snd_via82xx_capture_close(snd_pcm_substream_t * substream) +/* + * close callback + */ +static int snd_via82xx_pcm_close(snd_pcm_substream_t * substream) { via82xx_t *chip = snd_pcm_substream_chip(substream); - chip->capture.substream = NULL; + viadev_t *viadev = (viadev_t *)substream->runtime->private_data; + unsigned long flags; + struct via_rate_lock *ratep; + + /* release the rate lock */ + ratep = &chip->rates[viadev->direction]; + spin_lock_irqsave(&ratep->lock, flags); + ratep->used--; + if (! ratep->used) + ratep->rate = 0; + spin_unlock_irqrestore(&ratep->lock, flags); + + viadev->substream = NULL; snd_pcm_sgbuf_delete(substream); return 0; } -static snd_pcm_ops_t snd_via82xx_playback_ops = { + +/* via686 playback callbacks */ +static snd_pcm_ops_t snd_via686_playback_ops = { .open = snd_via82xx_playback_open, - .close = snd_via82xx_playback_close, + .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, - .prepare = snd_via82xx_playback_prepare, - .trigger = snd_via82xx_playback_trigger, - .pointer = snd_via82xx_playback_pointer, + .prepare = snd_via686_playback_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via686_pcm_pointer, .copy = snd_pcm_sgbuf_ops_copy_playback, .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; -static snd_pcm_ops_t snd_via82xx_capture_ops = { +/* via686 capture callbacks */ +static snd_pcm_ops_t snd_via686_capture_ops = { .open = snd_via82xx_capture_open, - .close = snd_via82xx_capture_close, + .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, - .prepare = snd_via82xx_capture_prepare, - .trigger = snd_via82xx_capture_trigger, - .pointer = snd_via82xx_capture_pointer, + .prepare = snd_via686_capture_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via686_pcm_pointer, .copy = snd_pcm_sgbuf_ops_copy_capture, .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; -static void snd_via82xx_pcm_free(snd_pcm_t *pcm) +/* via823x DSX playback callbacks */ +static snd_pcm_ops_t snd_via8233_playback_ops = { + .open = snd_via82xx_playback_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_playback_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .copy = snd_pcm_sgbuf_ops_copy_playback, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x multi-channel playback callbacks */ +static snd_pcm_ops_t snd_via8233_multi_ops = { + .open = snd_via8233_multi_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_multi_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .copy = snd_pcm_sgbuf_ops_copy_playback, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x capture callbacks */ +static snd_pcm_ops_t snd_via8233_capture_ops = { + .open = snd_via82xx_capture_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_capture_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .copy = snd_pcm_sgbuf_ops_copy_capture, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + + +/* + * create pcm instances for VIA8233, 8233C and 8235 (not 8233A) + */ +static int __devinit snd_via8233_pcm_new(via82xx_t *chip) { - via82xx_t *chip = snd_magic_cast(via82xx_t, pcm->private_data, return); - chip->pcm = NULL; + snd_pcm_t *pcm; + int i, err; + + chip->playback_devno = 0; /* x 4 */ + chip->multi_devno = 4; /* x 1 */ + chip->capture_devno = 5; /* x 2 */ + chip->num_devs = 7; + + /* PCM #0: 4 DSX playbacks and 1 capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 4, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playbacks */ + for (i = 0; i < 4; i++) { + chip->devs[i].reg_offset = 0x10 * i; + chip->devs[i].direction = 0; + } + /* capture */ + chip->devs[chip->capture_devno].reg_offset = VIA_REG_CAPTURE_8233_STATUS; + chip->devs[chip->capture_devno].direction = 1; + + /* PCM #1: multi-channel playback and 2nd capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playback */ + chip->devs[chip->multi_devno].reg_offset = VIA_REG_MULTPLAY_STATUS; + chip->devs[chip->multi_devno].direction = 0; + /* set up capture */ + chip->devs[chip->capture_devno + 1].reg_offset = VIA_REG_CAPTURE_8233_STATUS + 0x10; + chip->devs[chip->capture_devno + 1].direction = 1; + return 0; } -static int __devinit snd_via82xx_pcm(via82xx_t *chip, int device, snd_pcm_t ** rpcm) +/* + * create pcm instances for VIA8233A + */ +static int __devinit snd_via8233a_pcm_new(via82xx_t *chip) { snd_pcm_t *pcm; int err; - if (rpcm) - *rpcm = NULL; - err = snd_pcm_new(chip->card, chip->card->shortname, device, 1, 1, &pcm); + chip->playback_devno = 0; + chip->multi_devno = 1; + chip->capture_devno = 2; + chip->num_devs = 3; + + /* PCM #0: multi-channel playback and capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); if (err < 0) return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playback */ + chip->devs[chip->multi_devno].reg_offset = VIA_REG_MULTPLAY_STATUS; + chip->devs[chip->multi_devno].direction = 0; + /* capture */ + chip->devs[chip->capture_devno].reg_offset = VIA_REG_CAPTURE_8233_STATUS; + chip->devs[chip->capture_devno].direction = 1; + + /* PCM #1: DXS3 playback (for spdif) */ + err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 0, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + /* set up playback */ + chip->devs[chip->playback_devno].reg_offset = 0x30; + chip->devs[chip->playback_devno].direction = 0; + return 0; +} - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via82xx_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via82xx_capture_ops); +/* + * create a pcm instance for via686a/b + */ +static int __devinit snd_via686_pcm_new(via82xx_t *chip) +{ + snd_pcm_t *pcm; + int err; + chip->playback_devno = 0; + chip->capture_devno = 1; + chip->num_devs = 2; + + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); pcm->private_data = chip; - pcm->private_free = snd_via82xx_pcm_free; - pcm->info_flags = 0; strcpy(pcm->name, chip->card->shortname); - chip->pcm = pcm; - - if (rpcm) - *rpcm = NULL; + chip->devs[0].reg_offset = VIA_REG_PLAYBACK_STATUS; + chip->devs[0].direction = 0; + chip->devs[1].reg_offset = VIA_REG_CAPTURE_STATUS; + chip->devs[1].direction = 1; return 0; } @@ -911,23 +1194,25 @@ static int snd_via8233_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_ele static int snd_via8233_capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { via82xx_t *chip = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = inb(VIAREG(chip, CAPTURE_CHANNEL)) & VIA_REG_CAPTURE_CHANNEL_MIC ? 1 : 0; + unsigned long port = chip->port + kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL; + ucontrol->value.enumerated.item[0] = inb(port) & VIA_REG_CAPTURE_CHANNEL_MIC ? 1 : 0; return 0; } static int snd_via8233_capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { via82xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long port = chip->port + kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL; unsigned long flags; u8 val, oval; spin_lock_irqsave(&chip->reg_lock, flags); - oval = inb(VIAREG(chip, CAPTURE_CHANNEL)); + oval = inb(port); val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC; if (ucontrol->value.enumerated.item[0]) val |= VIA_REG_CAPTURE_CHANNEL_MIC; if (val != oval) - outb(val, VIAREG(chip, CAPTURE_CHANNEL)); + outb(val, port); spin_unlock_irqrestore(&chip->reg_lock, flags); return val != oval; } @@ -940,13 +1225,59 @@ static snd_kcontrol_new_t snd_via8233_capture_source __devinitdata = { .put = snd_via8233_capture_source_put, }; +static int snd_via8233_dxs3_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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 snd_via8233_dxs3_spdif_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u8 val; + + pci_read_config_byte(chip->pci, 0x49, &val); + ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0; + return 0; +} + +static int snd_via8233_dxs3_spdif_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u8 val, oval; + + pci_read_config_byte(chip->pci, 0x49, &oval); + val = oval & ~0x08; + if (ucontrol->value.integer.value[0]) + val |= 0x08; + if (val != oval) { + pci_write_config_byte(chip->pci, 0x49, val); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_via8233_dxs3_spdif_control __devinitdata = { + .name = "IEC958 Output Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_via8233_dxs3_spdif_info, + .get = snd_via8233_dxs3_spdif_get, + .put = snd_via8233_dxs3_spdif_put, +}; + +/* + */ + static void snd_via82xx_mixer_free_ac97(ac97_t *ac97) { via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); chip->ac97 = NULL; } -static int __devinit snd_via82xx_mixer(via82xx_t *chip) +static int __devinit snd_via82xx_mixer_new(via82xx_t *chip) { ac97_t ac97; int err; @@ -1010,6 +1341,100 @@ static snd_kcontrol_new_t snd_via82xx_joystick_control __devinitdata = { .put = snd_via82xx_joystick_put, }; +/* + * + */ + +static int snd_via8233_init_misc(via82xx_t *chip, int dev) +{ + int i, err, caps; + + caps = chip->revision == VIA_REV_8233A ? 1 : 2; + for (i = 0; i < caps; i++) { + snd_via8233_capture_source.index = i; + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_capture_source, chip)); + if (err < 0) + return err; + } + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip)); + if (err < 0) + return err; + return 0; +} + +static int snd_via686_init_misc(via82xx_t *chip, int dev) +{ + unsigned char legacy, legacy_cfg; + int rev_h = 0; + + legacy = chip->old_legacy; + legacy_cfg = chip->old_legacy_cfg; + legacy |= 0x40; /* disable MIDI */ + legacy &= ~0x08; /* disable joystick */ + if (chip->revision >= 0x20) { + if (check_region(pci_resource_start(chip->pci, 2), 4)) { + rev_h = 0; + legacy &= ~0x80; /* disable PCI I/O 2 */ + } else { + rev_h = 1; + legacy |= 0x80; /* enable PCI I/O 2 */ + } + } + pci_write_config_byte(chip->pci, 0x42, legacy); + pci_write_config_byte(chip->pci, 0x43, legacy_cfg); + if (rev_h && mpu_port[dev] >= 0x200) { /* force MIDI */ + legacy |= 0x02; /* enable MPU */ + pci_write_config_dword(chip->pci, 0x18, (mpu_port[dev] & 0xfffc) | 0x01); + } else { + if (rev_h && (legacy & 0x02)) { + mpu_port[dev] = pci_resource_start(chip->pci, 2); + if (mpu_port[dev] < 0x200) /* bad value */ + legacy &= ~0x02; /* disable MIDI */ + } else { + switch (mpu_port[dev]) { /* force MIDI */ + case 0x300: + case 0x310: + case 0x320: + case 0x330: + legacy_cfg &= ~(3 << 2); + legacy_cfg |= (mpu_port[dev] & 0x0030) >> 2; + legacy |= 0x02; + break; + default: /* no, use BIOS settings */ + if (legacy & 0x02) + mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2); + } + } + } + pci_write_config_byte(chip->pci, 0x42, legacy); + pci_write_config_byte(chip->pci, 0x43, legacy_cfg); + if (legacy & 0x02) { + if (check_region(mpu_port[dev], 2)) { + printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(chip->pci, 0x42, legacy); + goto __skip_mpu; + } + if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A, + mpu_port[dev], 0, + chip->irq, 0, + &chip->rmidi) < 0) { + printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(chip->pci, 0x42, legacy); + goto __skip_mpu; + } + legacy &= ~0x40; /* enable MIDI interrupt */ + pci_write_config_byte(chip->pci, 0x42, legacy); + __skip_mpu: + ; + } + + /* card switches */ + return snd_ctl_add(chip->card, snd_ctl_new1(&snd_via82xx_joystick_control, chip)); +} + + /* * */ @@ -1102,20 +1527,18 @@ static int __devinit snd_via82xx_chip_init(via82xx_t *chip) outl(0, chip->port + 0x8c); } - /* disable interrupts */ - snd_via82xx_channel_reset(chip, &chip->playback); - snd_via82xx_channel_reset(chip, &chip->capture); return 0; } static int snd_via82xx_free(via82xx_t *chip) { + int i; + if (chip->irq < 0) goto __end_hw; /* disable interrupts */ - snd_via82xx_channel_reset(chip, &chip->playback); - snd_via82xx_channel_reset(chip, &chip->capture); - /* --- */ + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); synchronize_irq(chip->irq); __end_hw: if (chip->res_port) { @@ -1160,6 +1583,7 @@ static int __devinit snd_via82xx_create(snd_card_t * card, spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->ac97_lock); + spin_lock_init(&chip->rate_lock); chip->card = card; chip->pci = pci; chip->irq = -1; @@ -1185,21 +1609,6 @@ static int __devinit snd_via82xx_create(snd_card_t * card, pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); synchronize_irq(chip->irq); - /* initialize offsets */ - switch (chip->chip_type) { - case TYPE_VIA686: - chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; - chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; - break; - case TYPE_VIA8233: - /* we use multi-channel playback mode, since this mode is supported - * by all VIA8233 models (and obviously suitable for our purpose). - */ - chip->playback.reg_offset = VIA_REG_MULTPLAY_STATUS; - chip->capture.reg_offset = VIA_REG_CAPTURE_8233_STATUS; - break; - } - if ((err = snd_via82xx_chip_init(chip)) < 0) { snd_via82xx_free(chip); return err; @@ -1225,9 +1634,9 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci, static int dev; snd_card_t *card; via82xx_t *chip; - int pcm_dev = 0; + unsigned char revision; int chip_type; - int err; + int i, err; if (dev >= SNDRV_CARDS) return -ENODEV; @@ -1241,122 +1650,51 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci, return -ENOMEM; chip_type = pci_id->driver_data; + pci_read_config_byte(pci, PCI_REVISION_ID, &revision); switch (chip_type) { case TYPE_VIA686: strcpy(card->driver, "VIA686A"); strcpy(card->shortname, "VIA 82C686A/B"); break; case TYPE_VIA8233: - strcpy(card->driver, "VIA8233"); - strcpy(card->shortname, "VIA 8233A/C"); + if (revision == VIA_REV_8233A) { + strcpy(card->driver, "VIA8233A"); + strcpy(card->shortname, "VIA 8233A"); + } else { + strcpy(card->driver, "VIA8233"); + strcpy(card->shortname, "VIA 8233/C"); + } break; default: snd_printk(KERN_ERR "invalid chip type %d\n", chip_type); - snd_card_free(card); - return -EINVAL; + err = -EINVAL; + goto __error; } - if ((err = snd_via82xx_create(card, pci, chip_type, ac97_clock[dev], &chip)) < 0) { - snd_card_free(card); - return err; - } - - if (snd_via82xx_mixer(chip) < 0) { - snd_card_free(card); - return err; - } - if (snd_via82xx_pcm(chip, pcm_dev++, NULL) < 0) { - snd_card_free(card); - return err; - } -#if 0 - if (snd_via82xx_pcm_fm(chip, pcm_dev++, NULL) < 0) { - snd_card_free(card); - return err; - } -#endif + if ((err = snd_via82xx_create(card, pci, chip_type, ac97_clock[dev], &chip)) < 0) + goto __error; - if (chip->chip_type == TYPE_VIA686) { - unsigned char legacy, legacy_cfg; - int rev_h = 0; - legacy = chip->old_legacy; - legacy_cfg = chip->old_legacy_cfg; - legacy |= 0x40; /* disable MIDI */ - legacy &= ~0x08; /* disable joystick */ - if (chip->revision >= 0x20) { - if (check_region(pci_resource_start(pci, 2), 4)) { - rev_h = 0; - legacy &= ~0x80; /* disable PCI I/O 2 */ - } else { - rev_h = 1; - legacy |= 0x80; /* enable PCI I/O 2 */ - } - } - pci_write_config_byte(pci, 0x42, legacy); - pci_write_config_byte(pci, 0x43, legacy_cfg); - if (rev_h && mpu_port[dev] >= 0x200) { /* force MIDI */ - legacy |= 0x02; /* enable MPU */ - pci_write_config_dword(pci, 0x18, (mpu_port[dev] & 0xfffc) | 0x01); - } else { - if (rev_h && (legacy & 0x02)) { - mpu_port[dev] = pci_resource_start(pci, 2); - if (mpu_port[dev] < 0x200) /* bad value */ - legacy &= ~0x02; /* disable MIDI */ - } else { - switch (mpu_port[dev]) { /* force MIDI */ - case 0x300: - case 0x310: - case 0x320: - case 0x330: - legacy_cfg &= ~(3 << 2); - legacy_cfg |= (mpu_port[dev] & 0x0030) >> 2; - legacy |= 0x02; - break; - default: /* no, use BIOS settings */ - if (legacy & 0x02) - mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2); - } - } - } - pci_write_config_byte(pci, 0x42, legacy); - pci_write_config_byte(pci, 0x43, legacy_cfg); - if (legacy & 0x02) { - if (check_region(mpu_port[dev], 2)) { - printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", mpu_port[dev]); - legacy &= ~0x02; - pci_write_config_byte(pci, 0x42, legacy); - goto __skip_mpu; - } - if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A, - mpu_port[dev], 0, - pci->irq, 0, - &chip->rmidi) < 0) { - printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", mpu_port[dev]); - legacy &= ~0x02; - pci_write_config_byte(pci, 0x42, legacy); - goto __skip_mpu; - } - legacy &= ~0x40; /* enable MIDI interrupt */ - pci_write_config_byte(pci, 0x42, legacy); - __skip_mpu: - ; - } - - /* card switches */ - err = snd_ctl_add(card, snd_ctl_new1(&snd_via82xx_joystick_control, chip)); - if (err < 0) { - snd_card_free(card); - return err; - } + if ((err = snd_via82xx_mixer_new(chip)) < 0) + goto __error; + if (chip_type == TYPE_VIA686) { + if ((err = snd_via686_pcm_new(chip)) < 0 || + (err = snd_via686_init_misc(chip, dev)) < 0) + goto __error; } else { - /* VIA8233 */ - err = snd_ctl_add(card, snd_ctl_new1(&snd_via8233_capture_source, chip)); - if (err < 0) { - snd_card_free(card); - return err; + if (revision == VIA_REV_8233A) { + if ((err = snd_via8233a_pcm_new(chip)) < 0) + goto __error; + } else { + if ((err = snd_via8233_pcm_new(chip)) < 0) + goto __error; } + if ((err = snd_via8233_init_misc(chip, dev)) < 0) + goto __error; } + /* disable interrupts */ + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, chip->port, chip->irq); @@ -1368,6 +1706,10 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci, pci_set_drvdata(pci, card); dev++; return 0; + + __error: + snd_card_free(card); + return err; } static void __devexit snd_via82xx_remove(struct pci_dev *pci) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 6f7d49d2fdf0..37cf830733ab 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1170,6 +1170,7 @@ static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm) { ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); chip->pcm_spdif = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); } int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) @@ -1190,6 +1191,9 @@ int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm pcm->info_flags = 0; strcpy(pcm->name, "YMFPCI - IEC958"); chip->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + if (rpcm) *rpcm = pcm; return 0; @@ -1210,6 +1214,7 @@ static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm) { ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); chip->pcm_4ch = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); } int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) @@ -1230,6 +1235,9 @@ int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) pcm->info_flags = 0; strcpy(pcm->name, "YMFPCI - Rear PCM"); chip->pcm_4ch = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + if (rpcm) *rpcm = pcm; return 0; diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 1564ca401970..95e903ee6a00 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -1953,7 +1953,6 @@ static int snd_usb_roland_ua100_hack_intf(snd_usb_audio_t *chip, int ifnum) static int snd_usb_roland_ua100_hack(snd_usb_audio_t *chip) { static const snd_usb_midi_endpoint_info_t ep_quirk = { - .epnum = -1, .out_cables = 0x0007, .in_cables = 0x0007 }; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 2160937c9036..69f3dee665b8 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -165,7 +165,7 @@ struct snd_usb_audio_quirk { /* data for QUIRK_MIDI_FIXED_ENDPOINT */ struct snd_usb_midi_endpoint_info { - int16_t epnum; /* ep number, -1 autodetect */ + int8_t out_ep, in_ep; /* ep number, 0 autodetect */ uint16_t out_cables; /* bitmask */ uint16_t in_cables; /* bitmask */ }; diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 10e71356d5de..427f61c64d5f 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -579,9 +579,9 @@ static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi, return -ENOMEM; } if (int_epd) - pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->epnum); + pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep); else - pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->epnum); + pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep); length = usb_maxpacket(umidi->chip->dev, pipe, 0); buffer = kmalloc(length, GFP_KERNEL); if (!buffer) { @@ -652,7 +652,7 @@ static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi, snd_usbmidi_out_endpoint_delete(ep); return -ENOMEM; } - pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->epnum); + pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep); ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1) & ~3; buffer = kmalloc(ep->max_transfer, GFP_KERNEL); if (!buffer) { @@ -737,8 +737,6 @@ static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi, int out_ports = 0, in_ports = 0; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - if (!endpoints[i].epnum) - continue; if (endpoints[i].out_cables) { err = snd_usbmidi_out_endpoint_create(umidi, &endpoints[i], &umidi->endpoints[i]); @@ -812,50 +810,64 @@ static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi, ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT || ms_ep->bDescriptorSubtype != MS_GENERAL) continue; - if (endpoints[epidx].epnum != 0 && - endpoints[epidx].epnum != (ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) { - ++epidx; - if (epidx >= MIDI_MAX_ENDPOINTS) { - printk(KERN_WARNING "snd-usb-midi: too many endpoints\n"); - break; + if ((ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) == USB_DIR_OUT) { + if (endpoints[epidx].out_ep) { + if (++epidx >= MIDI_MAX_ENDPOINTS) { + printk(KERN_WARNING "snd-usb-midi: too many endpoints\n"); + break; + } } - } - endpoints[epidx].epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - if (ep->bEndpointAddress & USB_DIR_IN) { - endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; - } else { + endpoints[epidx].out_ep = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; + printk(KERN_INFO "snd-usb-midi: EP %02X: %d jack(s)\n", + ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); + } else { + if (endpoints[epidx].in_ep) { + if (++epidx >= MIDI_MAX_ENDPOINTS) { + printk(KERN_WARNING "snd-usb-midi: too many endpoints\n"); + break; + } + } + endpoints[epidx].in_ep = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; + printk(KERN_INFO "snd-usb-midi: EP %02X: %d jack(s)\n", + ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); } - printk(KERN_INFO "snd-usb-midi: detected %d %s jack(s) on endpoint %d\n", - ms_ep->bNumEmbMIDIJack, - ep->bEndpointAddress & USB_DIR_IN ? "input" : "output", - endpoints[epidx].epnum); } return 0; } /* - * If the first endpoint isn't specified, use the first endpoint in the + * If the endpoints aren't specified, use the first bulk endpoints in the * first alternate setting of the interface. */ static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* endpoint) + snd_usb_midi_endpoint_info_t* endpoint) { struct usb_interface* intf; struct usb_host_interface *hostif; struct usb_interface_descriptor* intfd; struct usb_endpoint_descriptor* epd; + int i; - if (endpoint->epnum == -1) { - intf = umidi->iface; - if (!intf || intf->num_altsetting < 1) - return -ENOENT; - hostif = intf->altsetting; - intfd = get_iface_desc(hostif); - if (intfd->bNumEndpoints < 1) - return -ENOENT; - epd = get_endpoint(hostif, 0); - endpoint->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + intf = umidi->iface; + if (!intf || intf->num_altsetting < 1) + return -ENOENT; + hostif = intf->altsetting; + intfd = get_iface_desc(hostif); + if (intfd->bNumEndpoints < 1) + return -ENOENT; + + for (i = 0; i < intfd->bNumEndpoints; ++i) { + epd = get_endpoint(hostif, i); + if ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) + continue; + if (!endpoint->out_ep && endpoint->out_cables && + (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) + endpoint->out_ep = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + if (!endpoint->in_ep && endpoint->in_cables && + (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + endpoint->in_ep = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; } return 0; } @@ -892,7 +904,6 @@ static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, if (!endpoint->in_cables && !endpoint->out_cables) return -ENOENT; - endpoint->epnum = -1; return snd_usbmidi_detect_endpoint(umidi, endpoint); } @@ -940,13 +951,13 @@ static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, } } - ep_info.epnum = get_endpoint(hostif, 2)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.out_ep = get_endpoint(hostif, 2)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; ep_info.out_cables = endpoint->out_cables & 0x5555; err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); if (err < 0) return err; - ep_info.epnum = get_endpoint(hostif, 0)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.in_ep = get_endpoint(hostif, 0)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; ep_info.in_cables = endpoint->in_cables; err = snd_usbmidi_in_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); if (err < 0) @@ -954,7 +965,7 @@ static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, umidi->endpoints[0].in->urb->complete = snd_usb_complete_callback(snd_usbmidi_in_midiman_complete); if (endpoint->out_cables > 0x0001) { - ep_info.epnum = get_endpoint(hostif, 4)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.out_ep = get_endpoint(hostif, 4)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; ep_info.out_cables = endpoint->out_cables & 0xaaaa; err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[1]); if (err < 0) diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index a64f0409a081..477f580b24b1 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -245,7 +245,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x000f, .in_cables = 0x000f } @@ -259,7 +258,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x003f, .in_cables = 0x003f } @@ -273,7 +271,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0003, .in_cables = 0x0003 } @@ -287,7 +284,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0003, .in_cables = 0x0003 } @@ -301,7 +297,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0013, .in_cables = 0x0013 } @@ -315,7 +310,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0001, .in_cables = 0x0001 } @@ -329,7 +323,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0001, .in_cables = 0x0001 } @@ -343,7 +336,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0013, .in_cables = 0x0013 } @@ -357,7 +349,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0007, .in_cables = 0x0007 } @@ -371,7 +362,6 @@ .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0001, .in_cables = 0x0001 } @@ -385,7 +375,6 @@ .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x01ff, .in_cables = 0x01ff } @@ -399,7 +388,6 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x000f, .in_cables = 0x000f } @@ -413,7 +401,6 @@ .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x003f, .in_cables = 0x003f } @@ -427,7 +414,6 @@ .ifnum = 3, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0001, .in_cables = 0x0001 } @@ -441,7 +427,6 @@ .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0003, .in_cables = 0x0007 } @@ -455,7 +440,6 @@ .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x000f, .in_cables = 0x000f } @@ -469,7 +453,6 @@ .ifnum = 3, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0003, .in_cables = 0x0003 } @@ -483,7 +466,6 @@ .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .epnum = -1, .out_cables = 0x0003, .in_cables = 0x0007 } -- cgit v1.2.3 From bfc3f9b2c11607559cee38098c33fb8b2e3cb884 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 27 Jan 2003 22:00:27 +0100 Subject: ALSA update - updated programmer's documentation - recoded PCM scatter-gather memory management - MPU401 - cleanups - CMI8330 - cleanups - EMU10K1 - Audigy2 update - ENS1371 - added surround support - USB - added more quirks and improved PCM constraint definitions --- .../sound/alsa/DocBook/alsa-driver-api.tmpl | 3 + .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 77 ++--- Documentation/sound/alsa/OSS-Emulation.txt | 5 + include/sound/core.h | 47 ++- include/sound/emu10k1.h | 2 +- include/sound/info.h | 3 +- include/sound/pcm.h | 5 + include/sound/pcm_sgbuf.h | 16 +- include/sound/version.h | 2 +- sound/core/control.c | 3 +- sound/core/init.c | 12 +- sound/core/oss/pcm_oss.c | 39 ++- sound/core/pcm_lib.c | 1 + sound/core/pcm_memory.c | 15 + sound/core/pcm_native.c | 8 + sound/core/pcm_sgbuf.c | 378 +++++++-------------- sound/core/sound.c | 2 +- sound/drivers/mpu401/mpu401.c | 9 +- sound/drivers/mpu401/mpu401_uart.c | 102 +++--- sound/isa/cmi8330.c | 4 +- sound/isa/sb/sb16.c | 3 + sound/pci/ac97/ac97_codec.c | 90 +++-- sound/pci/ac97/ac97_patch.c | 9 + sound/pci/cmipci.c | 57 +++- sound/pci/cs46xx/cs46xx_lib.c | 93 ++--- sound/pci/emu10k1/emu10k1.c | 2 +- sound/pci/emu10k1/emu10k1_main.c | 44 ++- sound/pci/emu10k1/emupcm.c | 16 +- sound/pci/ens1370.c | 67 +++- sound/pci/ice1712/amp.c | 7 + sound/pci/rme32.c | 130 +++++-- sound/pci/rme9652/hdsp.c | 13 +- sound/pci/trident/trident_main.c | 87 ++--- sound/pci/via82xx.c | 69 ++-- sound/pci/ymfpci/ymfpci_main.c | 10 +- sound/usb/usbaudio.c | 372 ++++++++++++++++---- sound/usb/usbaudio.h | 10 +- sound/usb/usbmidi.c | 41 ++- sound/usb/usbquirks.h | 114 ++++++- 39 files changed, 1225 insertions(+), 742 deletions(-) (limited to 'include') diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl index 6421246ddac0..7d797f57355a 100644 --- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl +++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl @@ -94,6 +94,9 @@ ISA DMA Helpers !Esound/core/isadma.c + Other Helper Macros +!Iinclude/sound/core.h + diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index 66ce8db8d10a..41edc20350ed 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -3659,13 +3659,8 @@ - Note that these callbacks are atomic unlike the callbacks of - control API since the ac97 middle-layer routine takes a - spinlock before calling these callbacks. Therefore you cannot - use mutex or schedule in them. However, these are never called - from interrupts unless you do by yourself. - Thus it's not necessarily protected with - spin_lock_irqsave() usually. + These callbacks are non-atomic like the callbacks of control API + unless they are called during suspend/resume phase. @@ -3679,22 +3674,22 @@ The reset callback is used to reset the codec. If the chip requires a special way of reset, you can define this callback. - This callback is atomic, since it can be called in the suspend - phase, too. + This callback must be atomic when it's called in the suspend + mode. The wait callback is used for a certain wait at the standard initialization of the codec. If the chip requires the extra wait-time, define this callback. - This callback is non-atomic. + This callback is always non-atomic, because it's never called + in the resume mode. The init callback is used for additional initialization of the codec. This callback is called - after the reset phase, and should be atomic, since it's called - from the resume handler, too. + after the reset, and should be atomic in the resume mode. @@ -4462,13 +4457,12 @@ For creating the SG-buffer handler, call - snd_pcm_sgbuf_init() in the - open callback - of a pcm substream (or in the constructor of the pcm). + snd_pcm_lib_preallocate_sg_pages() or + snd_pcm_lib_preallocate_sg_pages_for_all() + in the PCM constructor like other PCI pre-allocator. You need to pass the - pci_dev struct pointer of the chip - and the default table size (which can be changed - dynamically). The snd_sg_buf_t instance is created as + pci_dev struct pointer of the chip. + The snd_sg_buf_t instance is created as substream->dma_private. You can cast the pointer like: @@ -4483,41 +4477,29 @@ - Then call snd_pcm_sgbuf_alloc() instead - of normal snd_pcm_lib_malloc_pages() in - hw_params callback. The SG-handler - will allocate single pages - and build the table on sgbuf->table. The pointer and the - physical address of each page is stored in this table. You can - get the physical address at a certain offset via + Then call snd_pcm_lib_malloc_pages() + in hw_params callback + as well as in the case of normal PCI buffer. + The SG-buffer handler will allocate the non-contiguous kernel + pages of the given size and map them onto the virtually contiguous + memory. The virtual pointer is addressed in runtime->dma_area. + The physical address (runtime->dma_addr) is set to zero, + because the buffer is physically non-contigous. + The physical address table is set up in sgbuf->table. + You can get the physical address at a certain offset via snd_pcm_sgbuf_get_addr(). When a SG-handler is used, you need to set - snd_pcm_sgbuf_ops_copy_playback and - snd_pcm_ops_silence as the - copy and the - silence callbacks, respectively. - All the jobs described in the previous section will be done - by these helper functions. - Also, the page callback must be set as - snd_pcm_sgbuf_ops_page. + snd_pcm_sgbuf_ops_page as + the page callback. For releasing the data, call - snd_pcm_sgbuf_free() in the - hw_free callback, and call - snd_pcm_sgbuf_delete() in the - close callback (or in the destructor, - if snd_pcm_sgbuf_init() was called in the - constructor). - - - - Note that you must not do pre-allocation if a SG-handler is used. - They conflict with each other. + snd_pcm_lib_free_pages() in the + hw_free callback as usual. @@ -4551,13 +4533,6 @@ - - - You don't need copy and - silence callbacks in this case, - because the copy/set operations are available on the virtual - address. - diff --git a/Documentation/sound/alsa/OSS-Emulation.txt b/Documentation/sound/alsa/OSS-Emulation.txt index f2d1c90d2de9..3cba232d43fe 100644 --- a/Documentation/sound/alsa/OSS-Emulation.txt +++ b/Documentation/sound/alsa/OSS-Emulation.txt @@ -163,6 +163,11 @@ quake, send a command via echo like the following: % echo "quake 0 0 direct" > /proc/asound/card0/pcm0p/oss +While quake wants only playback, you may append the second command +to notify driver that only this direction is about to be allocated: + + % echo "quake 0 0 disable" > /proc/asound/card0/pcm0c/oss + The permission of proc files depend on the module options of snd. As default it's set as root, so you'll likely need to be superuser for sending the command above. diff --git a/include/sound/core.h b/include/sound/core.h index 68b3a692eff7..7a3e4676d2c3 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -357,13 +357,17 @@ void snd_verbose_printk(const char *file, int line, const char *format, ...); #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) void snd_verbose_printd(const char *file, int line, const char *format, ...); #endif -#if defined(CONFIG_SND_DEBUG) && !defined(CONFIG_SND_VERBOSE_PRINTK) -void snd_printd(const char *format, ...); -#endif /* --- */ #ifdef CONFIG_SND_VERBOSE_PRINTK +/** + * snd_printk - printk wrapper + * @fmt: format string + * + * Works like print() but prints the file and the line of the caller + * when configured with CONFIG_SND_VERBOSE_PRINTK. + */ #define snd_printk(fmt, args...) \ snd_verbose_printk(__FILE__, __LINE__, fmt ,##args) #else @@ -376,15 +380,45 @@ void snd_printd(const char *format, ...); #define __ASTRING__(x) #x #ifdef CONFIG_SND_VERBOSE_PRINTK +/** + * snd_printd - debug printk + * @format: format string + * + * Compiled only when Works like snd_printk() for debugging purpose. + * Ignored when CONFIG_SND_DEBUG is not set. + */ #define snd_printd(fmt, args...) \ snd_verbose_printd(__FILE__, __LINE__, fmt ,##args) +#else +#define snd_printd(fmt, args...) \ + printk(fmt ,##args) #endif +/** + * snd_assert - run-time assersion macro + * @expr: expression + * @args...: the action + * + * This macro checks the expression in run-time and invokes the commands + * given in the rest arguments if the assertion is failed. + * When CONFIG_SND_DEBUG is not set, the expression is executed but + * not checked. + */ #define snd_assert(expr, args...) do {\ if (!(expr)) {\ snd_printk("BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ args;\ }\ } while (0) +/** + * snd_runtime_check - run-time assersion macro + * @expr: expression + * @args...: the action + * + * This macro checks the expression in run-time and invokes the commands + * given in the rest arguments if the assertion is failed. + * Unlike snd_assert(), the action commands are executed even if + * CONFIG_SND_DEBUG is not set but without any error messages. + */ #define snd_runtime_check(expr, args...) do {\ if (!(expr)) {\ snd_printk("ERROR (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ @@ -401,6 +435,13 @@ void snd_printd(const char *format, ...); #endif /* CONFIG_SND_DEBUG */ #ifdef CONFIG_SND_DEBUG_DETECT +/** + * snd_printdd - debug printk + * @format: format string + * + * Compiled only when Works like snd_printk() for debugging purpose. + * Ignored when CONFIG_SND_DEBUG_DETECT is not set. + */ #define snd_printdd(format, args...) snd_printk(format, ##args) #else #define snd_printdd(format, args...) /* nothing */ diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index afdb277148c0..d5b57b2ccbdb 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -231,7 +231,7 @@ #define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ #define A_GPINPUT_MASK 0xff00 #define A_GPOUTPUT_MASK 0x00ff -#define A_IOCFG_GPOUT0 0x0040 /* analog/digital? */ +#define A_IOCFG_GPOUT0 0x0044 /* analog/digital? */ #define TIMER 0x1a /* Timer terminal count register */ /* NOTE: After the rate is changed, a maximum */ diff --git a/include/sound/info.h b/include/sound/info.h index c9bb51aedb04..34126e7985e6 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -173,8 +173,9 @@ static inline snd_info_entry_t *snd_info_create_device(const char *name, unsigned int mode) { return NULL; } static inline void snd_info_free_device(snd_info_entry_t * entry) { ; } +static inline int snd_info_card_create(snd_card_t * card) { return 0; } static inline int snd_info_card_register(snd_card_t * card) { return 0; } -static inline int snd_info_card_unregister(snd_card_t * card) { return 0; } +static inline int snd_info_card_free(snd_card_t * card) { return 0; } static inline int snd_info_register(snd_info_entry_t * entry) { return 0; } static inline int snd_info_unregister(snd_info_entry_t * entry) { return 0; } diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0bc5fef9456e..2f81279b633f 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -125,6 +125,7 @@ typedef struct _snd_pcm_ops { #define SNDRV_PCM_DMA_TYPE_ISA 2 /* ISA continuous */ #define SNDRV_PCM_DMA_TYPE_PCI 3 /* PCI continuous */ #define SNDRV_PCM_DMA_TYPE_SBUS 4 /* SBUS continuous */ +#define SNDRV_PCM_DMA_TYPE_PCI_SG 5 /* PCI SG-buffer */ /* If you change this don't forget to change rates[] table in pcm_native.c */ #define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ @@ -743,6 +744,10 @@ int snd_pcm_hw_param_near(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int *dir); +int snd_pcm_hw_param_set(snd_pcm_substream_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int dir); int snd_pcm_hw_params_choose(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); int snd_pcm_hw_refine(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); diff --git a/include/sound/pcm_sgbuf.h b/include/sound/pcm_sgbuf.h index e5dda1d053af..c05bd90606a9 100644 --- a/include/sound/pcm_sgbuf.h +++ b/include/sound/pcm_sgbuf.h @@ -32,6 +32,7 @@ struct snd_sg_buf { int pages; /* allocated pages */ int tblsize; /* allocated table size */ struct snd_sg_page *table; + struct page **page_table; struct pci_dev *pci; }; @@ -53,18 +54,17 @@ static inline dma_addr_t snd_pcm_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE; } -int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize); -int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream); -int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size); -int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream); +struct snd_sg_buf *snd_pcm_sgbuf_init(struct pci_dev *pci); +void snd_pcm_sgbuf_delete(struct snd_sg_buf *sgbuf); +void *snd_pcm_sgbuf_alloc_pages(struct snd_sg_buf *sgbuf, size_t size); +int snd_pcm_sgbuf_free_pages(struct snd_sg_buf *sgbuf, void *vmaddr); -int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count); -int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count); -int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count); -struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); +int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, snd_pcm_substream_t *substream); +int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, snd_pcm_t *pcm); #define _snd_pcm_substream_sgbuf(substream) ((substream)->dma_private) #define snd_pcm_substream_sgbuf(substream) snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return -ENXIO) +struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); #endif /* __SOUND_PCM_SGBUF_H */ diff --git a/include/sound/version.h b/include/sound/version.h index 70d20d7d3806..a9e126352a2c 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc6" -#define CONFIG_SND_DATE " (Fri Jan 10 18:18:43 2003 UTC)" +#define CONFIG_SND_DATE " (Mon Jan 27 17:35:55 2003 UTC)" diff --git a/sound/core/control.c b/sound/core/control.c index da7471dca9a4..6ae150e048af 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -211,7 +211,8 @@ snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) kctl.id.iface = ncontrol->iface; kctl.id.device = ncontrol->device; kctl.id.subdevice = ncontrol->subdevice; - strncpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)-1); + if (ncontrol->name) + strncpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)-1); kctl.id.index = ncontrol->index; kctl.access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); diff --git a/sound/core/init.c b/sound/core/init.c index 30e217be051d..f1b40b70d9e4 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -86,11 +86,19 @@ snd_card_t *snd_card_new(int idx, const char *xid, idx = idx2; break; } + if (idx < 0 && snd_ecards_limit < SNDRV_CARDS) + /* for dynamically additional devices like hotplug: + * increment the limit if still free slot exists. + */ + idx = snd_ecards_limit++; } else if (idx < snd_ecards_limit) { if (snd_cards_lock & (1 << idx)) idx = -1; /* invalid */ - } - if (idx < 0 || idx >= snd_ecards_limit) { + } else if (idx < SNDRV_CARDS) + snd_ecards_limit = idx + 1; /* increase the limit */ + else + idx = -1; + if (idx < 0) { write_unlock(&snd_card_rwlock); if (idx >= snd_ecards_limit) snd_printk(KERN_ERR "card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index ac284d748d3d..292af9f829f1 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -251,6 +251,43 @@ static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream, return 0; } +static int choose_rate(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, int best_rate) +{ + snd_interval_t *it; + snd_pcm_hw_params_t *save; + int rate; + + save = kmalloc(sizeof(*save), GFP_KERNEL); + if (save == NULL) + return -ENOMEM; + *save = *params; + it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE); + + /* try multiples of the best rate */ + rate = best_rate; + for (;;) { + if (it->max < rate || (it->max == rate && it->openmax)) + break; + if (it->min < rate || (it->min == rate && !it->openmin)) { + int ret; + ret = snd_pcm_hw_param_set(substream, params, + SNDRV_PCM_HW_PARAM_RATE, + rate, 0); + if (ret == rate) { + kfree(save); + return rate; + } + *params = *save; + } + rate += best_rate; + } + + /* not found, use the nearest rate */ + kfree(save); + return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, 0); +} + static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; @@ -287,7 +324,7 @@ static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream) snd_printd("No usable accesses\n"); return -EINVAL; } - snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_RATE, runtime->oss.rate, 0); + choose_rate(substream, &sparams, runtime->oss.rate); snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); format = snd_pcm_oss_format_from(runtime->oss.format); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1c3a6ec1dc50..efabbb4efabc 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2578,6 +2578,7 @@ EXPORT_SYMBOL(snd_pcm_hw_param_mask); EXPORT_SYMBOL(snd_pcm_hw_param_first); EXPORT_SYMBOL(snd_pcm_hw_param_last); EXPORT_SYMBOL(snd_pcm_hw_param_near); +EXPORT_SYMBOL(snd_pcm_hw_param_set); EXPORT_SYMBOL(snd_pcm_hw_refine); EXPORT_SYMBOL(snd_pcm_hw_constraints_init); EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index b2da702f0d7e..b923bfffe51e 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -26,6 +26,9 @@ #include #include #include +#ifdef CONFIG_PCI +#include +#endif static int preallocate_dma = 1; MODULE_PARM(preallocate_dma, "i"); @@ -60,6 +63,10 @@ static int alloc_pcm_pages(snd_pcm_substream_t *substream, size_t size, case SNDRV_PCM_DMA_TYPE_PCI: *dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, dma_addr); break; + case SNDRV_PCM_DMA_TYPE_PCI_SG: + *dma_area = snd_pcm_sgbuf_alloc_pages((struct snd_sg_buf *)substream->dma_private, size); + *dma_addr = 0; + break; #endif #ifdef CONFIG_SBUS case SNDRV_PCM_DMA_TYPE_SBUS: @@ -121,6 +128,9 @@ static void free_pcm_pages(snd_pcm_substream_t *substream, size_t size, snd_free_pci_pages((struct pci_dev *)substream->dma_private, size, dma_area, dma_addr); break; + case SNDRV_PCM_DMA_TYPE_PCI_SG: + snd_pcm_sgbuf_free_pages((struct snd_sg_buf *)substream->dma_private, dma_area); + break; #endif #ifdef CONFIG_SBUS case SNDRV_PCM_DMA_TYPE_SBUS: @@ -158,6 +168,11 @@ int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) snd_info_unregister(substream->proc_prealloc_entry); substream->proc_prealloc_entry = NULL; } +#ifdef CONFIG_PCI + if (substream->dma_type == SNDRV_PCM_DMA_TYPE_PCI_SG) + snd_pcm_sgbuf_delete((struct snd_sg_buf *)substream->dma_private); +#endif + substream->dma_type = SNDRV_PCM_DMA_TYPE_UNKNOWN; return 0; } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index c12bad5d92b4..9b220abfcc96 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1098,6 +1098,8 @@ static int snd_pcm_playback_drain(snd_pcm_substream_t * substream) /* Fall through */ case SNDRV_PCM_STATE_SETUP: goto _end; + default: + break; } if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { @@ -1200,6 +1202,8 @@ static int snd_pcm_playback_drop(snd_pcm_substream_t *substream) spin_lock_irq(&runtime->lock); } goto _xrun_recovery; + default: + break; } runtime->control->appl_ptr = runtime->status->hw_ptr; _end: @@ -1253,6 +1257,8 @@ static int snd_pcm_capture_drain(snd_pcm_substream_t * substream) spin_lock_irq(&runtime->lock); } goto _xrun_recovery; + default: + break; } _end: spin_unlock_irq(&runtime->lock); @@ -1295,6 +1301,8 @@ static int snd_pcm_capture_drop(snd_pcm_substream_t * substream) case SNDRV_PCM_STATE_XRUN: snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); break; + default: + break; } runtime->control->appl_ptr = runtime->status->hw_ptr; _end: diff --git a/sound/core/pcm_sgbuf.c b/sound/core/pcm_sgbuf.c index 914373a2a7e9..3125e109033d 100644 --- a/sound/core/pcm_sgbuf.c +++ b/sound/core/pcm_sgbuf.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -30,176 +31,128 @@ #define SGBUF_TBL_ALIGN 32 #define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN) -/* - * shrink to the given pages. - * free the unused pages - */ -static void sgbuf_shrink(struct snd_sg_buf *sgbuf, int pages) -{ - snd_assert(sgbuf, return); - if (! sgbuf->table) - return; - while (sgbuf->pages > pages) { - sgbuf->pages--; - snd_free_pci_page(sgbuf->pci, sgbuf->table[sgbuf->pages].buf, - sgbuf->table[sgbuf->pages].addr); - } -} -/** - * snd_pcm_sgbuf_init - initialize the sg buffer - * @substream: the pcm substream instance +/* + * snd_pcm_sgbuf_new - constructor of the sgbuf instance * @pci: pci device pointer - * @tblsize: the default table size * - * Initializes the SG-buffer instance and assigns it to - * substream->dma_private. The SG-table is initialized with the - * given size. + * Initializes the SG-buffer instance to be assigned to + * substream->dma_private. * - * Call this function in the open callback. - * - * Returns zero if successful, or a negative error code on failure. + * Returns the pointer of the instance, or NULL at error. */ -int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize) +struct snd_sg_buf *snd_pcm_sgbuf_new(struct pci_dev *pci) { struct snd_sg_buf *sgbuf; - tblsize = sgbuf_align_table(tblsize); sgbuf = snd_magic_kcalloc(snd_pcm_sgbuf_t, 0, GFP_KERNEL); if (! sgbuf) - return -ENOMEM; - substream->dma_private = sgbuf; + return NULL; sgbuf->pci = pci; sgbuf->pages = 0; - sgbuf->tblsize = tblsize; - sgbuf->table = kmalloc(sizeof(struct snd_sg_page) * tblsize, GFP_KERNEL); - if (! sgbuf->table) { - snd_pcm_sgbuf_delete(substream); - return -ENOMEM; - } - memset(sgbuf->table, 0, sizeof(struct snd_sg_page) * tblsize); - return 0; + sgbuf->tblsize = 0; + + return sgbuf; } -/** - * snd_pcm_sgbuf_delete - release all pages and free the sgbuf instance - * @substream: the pcm substream instance - * - * Releaes all pages and free the sgbuf instance. - * - * Call this function in the close callback. +/* + * snd_pcm_sgbuf_delete - destructor of sgbuf instance + * @sgbuf: the SG-buffer instance * - * Returns zero if successful, or a negative error code on failure. + * Destructor Releaes all pages and free the sgbuf instance. */ -int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream) +void snd_pcm_sgbuf_delete(struct snd_sg_buf *sgbuf) { - struct snd_sg_buf *sgbuf; - - /* return in case, when sgbuf is not initialized */ - if (substream->dma_private == NULL) - return -EINVAL; - sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); - sgbuf_shrink(sgbuf, 0); - if (sgbuf->table) - kfree(sgbuf->table); + snd_pcm_sgbuf_free_pages(sgbuf, NULL); snd_magic_kfree(sgbuf); - substream->dma_private = NULL; - return 0; } /** - * snd_pcm_sgbuf_alloc - allocate the pages for the SG buffer - * @substream: the pcm substream instance + * snd_pcm_sgbuf_alloc_pages - allocate the pages for the SG buffer + * @sgbuf: the sgbuf instance * @size: the requested buffer size in bytes * * Allocates the buffer pages for the given size and updates the - * sg buffer table. If the buffer table already exists, try to resize - * it. + * sg buffer table. The pages are mapped to the virtually continuous + * memory. * - * Call this function from hw_params callback. + * This function is usually called from snd_pcm_lib_malloc_pages(). * - * Returns 1 if the buffer is changed, 0 if not changed, or a negative - * code on failure. + * Returns the mapped virtual address of the buffer if allocation was + * successful, or NULL at error. */ -int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size) +void *snd_pcm_sgbuf_alloc_pages(struct snd_sg_buf *sgbuf, size_t size) { - struct snd_sg_buf *sgbuf; - unsigned int pages; - unsigned int tblsize; - int changed = 0; + unsigned int i, pages; + void *vmaddr; - sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); pages = snd_pcm_sgbuf_pages(size); - tblsize = sgbuf_align_table(pages); - if (pages < sgbuf->pages) { - /* release unsed pages */ - sgbuf_shrink(sgbuf, pages); - if (substream->runtime) - substream->runtime->dma_bytes = size; - return 1; /* changed */ - } else if (pages > sgbuf->tblsize) { - /* bigger than existing one. reallocate the table. */ - struct snd_sg_page *table; - table = kmalloc(sizeof(*table) * tblsize, GFP_KERNEL); - if (! table) - return -ENOMEM; - memcpy(table, sgbuf->table, sizeof(*table) * sgbuf->tblsize); - kfree(sgbuf->table); - sgbuf->table = table; - sgbuf->tblsize = tblsize; - } + sgbuf->tblsize = sgbuf_align_table(pages); + sgbuf->table = snd_kcalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL); + if (! sgbuf->table) + goto _failed; + sgbuf->page_table = snd_kcalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL); + if (! sgbuf->page_table) + goto _failed; + /* allocate each page */ - while (sgbuf->pages < pages) { + for (i = 0; i < pages; i++) { void *ptr; dma_addr_t addr; ptr = snd_malloc_pci_page(sgbuf->pci, &addr); if (! ptr) - return -ENOMEM; - sgbuf->table[sgbuf->pages].buf = ptr; - sgbuf->table[sgbuf->pages].addr = addr; + goto _failed; + sgbuf->table[i].buf = ptr; + sgbuf->table[i].addr = addr; + sgbuf->page_table[i] = virt_to_page(ptr); sgbuf->pages++; - changed = 1; } + sgbuf->size = size; - if (substream->runtime) - substream->runtime->dma_bytes = size; - return changed; + vmaddr = vmap(sgbuf->page_table, sgbuf->pages); + if (! vmaddr) + goto _failed; + return vmaddr; + + _failed: + snd_pcm_sgbuf_free_pages(sgbuf, NULL); /* free the table */ + return NULL; } /** - * snd_pcm_sgbuf_free - free the sg buffer - * @substream: the pcm substream instance + * snd_pcm_sgbuf_free_pages - free the sg buffer + * @sgbuf: the sgbuf instance + * @vmaddr: the mapped virtual address * - * Releases the pages. The SG-table itself is still kept. + * Releases the pages and the mapped tables. * - * Call this function from hw_free callback. + * This function is called usually from snd_pcm_lib_free_pages(). * * Returns zero if successful, or a negative error code on failure. */ -int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream) +int snd_pcm_sgbuf_free_pages(struct snd_sg_buf *sgbuf, void *vmaddr) { - struct snd_sg_buf *sgbuf; + if (vmaddr) + vunmap(vmaddr); - sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); - sgbuf_shrink(sgbuf, 0); + while (sgbuf->pages > 0) { + sgbuf->pages--; + snd_free_pci_page(sgbuf->pci, sgbuf->table[sgbuf->pages].buf, + sgbuf->table[sgbuf->pages].addr); + } + if (sgbuf->table) + kfree(sgbuf->table); + sgbuf->table = NULL; + if (sgbuf->page_table) + kfree(sgbuf->page_table); + sgbuf->page_table = NULL; + sgbuf->tblsize = 0; + sgbuf->pages = 0; + sgbuf->size = 0; + return 0; } -/* - * get the page pointer on the given offset - */ -static void *sgbuf_get_addr(snd_pcm_substream_t *substream, unsigned long offset) -{ - struct snd_sg_buf *sgbuf; - unsigned int idx; - - sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return NULL); - idx = offset >> PAGE_SHIFT; - if (idx >= sgbuf->pages) - return 0; - return sgbuf->table[idx].buf; -} - /** * snd_pcm_sgbuf_ops_page - get the page struct at the given offset * @substream: the pcm substream instance @@ -210,167 +163,68 @@ static void *sgbuf_get_addr(snd_pcm_substream_t *substream, unsigned long offset */ struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) { - void *addr = sgbuf_get_addr(substream, offset); - if (addr) - return virt_to_page(addr); - else - return 0; -} + struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return NULL); -/* - * do copy_from_user to the sg buffer - */ -static int copy_from_user_sg_buf(snd_pcm_substream_t *substream, - char *buf, size_t hwoff, ssize_t bytes) -{ - int len; - char *addr; - size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT; - hwoff -= p; - len = PAGE_SIZE - hwoff; - for (;;) { - addr = sgbuf_get_addr(substream, p); - if (! addr) - return -EFAULT; - if (len > bytes) - len = bytes; - if (copy_from_user(addr + hwoff, buf, len)) - return -EFAULT; - bytes -= len; - if (bytes <= 0) - break; - buf += len; - p += PAGE_SIZE; - len = PAGE_SIZE; - hwoff = 0; - } - return 0; -} - -/* - * do copy_to_user from the sg buffer - */ -static int copy_to_user_sg_buf(snd_pcm_substream_t *substream, - char *buf, size_t hwoff, ssize_t bytes) -{ - int len; - char *addr; - size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT; - hwoff -= p; - len = PAGE_SIZE - hwoff; - for (;;) { - addr = sgbuf_get_addr(substream, p); - if (! addr) - return -EFAULT; - if (len > bytes) - len = bytes; - if (copy_to_user(buf, addr + hwoff, len)) - return -EFAULT; - bytes -= len; - if (bytes <= 0) - break; - buf += len; - p += PAGE_SIZE; - len = PAGE_SIZE; - hwoff = 0; - } - return 0; + unsigned int idx = offset >> PAGE_SHIFT; + if (idx >= sgbuf->pages) + return NULL; + return sgbuf->page_table[idx]; } -/* - * set silence on the sg buffer - */ -static int set_silence_sg_buf(snd_pcm_substream_t *substream, - size_t hwoff, ssize_t samples) -{ - snd_pcm_runtime_t *runtime = substream->runtime; - int len, page_len; - char *addr; - size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT; - hwoff -= p; - len = bytes_to_samples(substream->runtime, PAGE_SIZE - hwoff); - page_len = bytes_to_samples(substream->runtime, PAGE_SIZE); - for (;;) { - addr = sgbuf_get_addr(substream, p); - if (! addr) - return -EFAULT; - if (len > samples) - len = samples; - snd_pcm_format_set_silence(runtime->format, addr + hwoff, len); - samples -= len; - if (samples <= 0) - break; - p += PAGE_SIZE; - len = page_len; - hwoff = 0; - } - return 0; -} /** - * snd_pcm_sgbuf_ops_copy_playback - copy callback for playback pcm ops + * snd_pcm_lib_preallocate_sg_pages - initialize SG-buffer for the PCI bus + * + * @pci: pci device + * @substream: substream to assign the buffer + * + * Initializes SG-buffer for the PCI bus. * - * copy callback for playback pcm ops + * Returns zero if successful, or a negative error code on failure. */ -int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, - snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count) +int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream) { - snd_pcm_runtime_t *runtime = substream->runtime; - if (channel < 0) { - return copy_from_user_sg_buf(substream, buf, frames_to_bytes(runtime, hwoff), frames_to_bytes(runtime, count)); - } else { - size_t dma_csize = runtime->dma_bytes / runtime->channels; - size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff); - return copy_from_user_sg_buf(substream, buf, c_ofs, samples_to_bytes(runtime, count)); - } + if ((substream->dma_private = snd_pcm_sgbuf_new(pci)) == NULL) + return -ENOMEM; + substream->dma_type = SNDRV_PCM_DMA_TYPE_PCI_SG; + substream->dma_area = 0; + substream->dma_addr = 0; + substream->dma_bytes = 0; + substream->buffer_bytes_max = UINT_MAX; + substream->dma_max = 0; + return 0; } -/** - * snd_pcm_sgbuf_ops_copy_capture - copy callback for capture pcm ops +/* + * FIXME: the function name is too long for docbook! * - * copy callback for capture pcm ops + * snd_pcm_lib_preallocate_sg_pages_for_all - initialize SG-buffer for the PCI bus (all substreams) + * @pci: pci device + * @pcm: pcm to assign the buffer + * + * Initialize the SG-buffer to all substreams of the given pcm for the + * PCI bus. + * + * Returns zero if successful, or a negative error code on failure. */ -int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, - snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count) +int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm) { - snd_pcm_runtime_t *runtime = substream->runtime; - if (channel < 0) { - return copy_to_user_sg_buf(substream, buf, frames_to_bytes(runtime, hwoff), frames_to_bytes(runtime, count)); - } else { - size_t dma_csize = runtime->dma_bytes / runtime->channels; - size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff); - return copy_to_user_sg_buf(substream, buf, c_ofs, samples_to_bytes(runtime, count)); - } -} + snd_pcm_substream_t *substream; + int stream, err; -/** - * snd_pcm_sgbuf_ops_silence - silence callback for pcm ops - * - * silence callback for pcm ops - */ -int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, - snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count) -{ - snd_pcm_runtime_t *runtime = substream->runtime; - if (channel < 0) { - return set_silence_sg_buf(substream, frames_to_bytes(runtime, hwoff), - frames_to_bytes(runtime, count)); - } else { - size_t dma_csize = runtime->dma_bytes / runtime->channels; - size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff); - return set_silence_sg_buf(substream, c_ofs, samples_to_bytes(runtime, count)); - } + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_sg_pages(pci, substream)) < 0) + return err; + return 0; } /* * Exported symbols */ -EXPORT_SYMBOL(snd_pcm_sgbuf_init); -EXPORT_SYMBOL(snd_pcm_sgbuf_delete); -EXPORT_SYMBOL(snd_pcm_sgbuf_alloc); -EXPORT_SYMBOL(snd_pcm_sgbuf_free); -EXPORT_SYMBOL(snd_pcm_sgbuf_ops_copy_playback); -EXPORT_SYMBOL(snd_pcm_sgbuf_ops_copy_capture); -EXPORT_SYMBOL(snd_pcm_sgbuf_ops_silence); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all); EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); diff --git a/sound/core/sound.c b/sound/core/sound.c index 3f6a4b68cce4..51da3f2e7ef9 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -90,7 +90,7 @@ void snd_request_card(int card) read_unlock(&snd_card_rwlock); if (locked) return; - if (card < 0 || card >= snd_ecards_limit) + if (card < 0 || card >= cards_limit) return; sprintf(str, "snd-card-%i", card); request_module(str); diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index 7d649fd991f7..557c39f3120c 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -40,7 +40,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ -#ifdef CONFIG_PC9800 +#ifdef CONFIG_X86_PC9800 static int pc98ii[SNDRV_CARDS]; /* PC98-II dauther board */ #endif @@ -59,7 +59,7 @@ MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); -#ifdef CONFIG_PC9800 +#ifdef CONFIG_X86_PC9800 MODULE_PARM(pc98ii, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(pc98ii, "Roland MPU-PC98II support."); MODULE_PARM_SYNTAX(pc98ii, SNDRV_BOOLEAN_FALSE_DESC); @@ -85,7 +85,7 @@ static int __init snd_card_mpu401_probe(int dev) if (card == NULL) return -ENOMEM; if (snd_mpu401_uart_new(card, 0, -#ifdef CONFIG_PC9800 +#ifdef CONFIG_X86_PC9800 pc98ii[dev] ? MPU401_HW_PC98II : #endif MPU401_HW_MPU401, @@ -154,6 +154,9 @@ static int __init alsa_card_mpu401_setup(char *str) (void)(get_option(&str,&enable[nr_dev]) == 2 && get_option(&str,&index[nr_dev]) == 2 && get_id(&str,&id[nr_dev]) == 2 && +#ifdef CONFIG_X86_PC9800 + get_option(&str,&pc98ii[nr_dev]) == 2 && +#endif get_option(&str,(int *)&port[nr_dev]) == 2 && get_option(&str,&irq[nr_dev]) == 2); nr_dev++; diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 33aab157bf99..f9d4692dcb32 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -65,13 +65,22 @@ static void snd_mpu401_uart_clear_rx(mpu401_t *mpu) static void _snd_mpu401_uart_interrupt(mpu401_t *mpu) { - if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) - snd_mpu401_uart_input_read(mpu); - else + if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { + if (! test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) { + spin_lock(&mpu->input_lock); + snd_mpu401_uart_input_read(mpu); + spin_unlock(&mpu->input_lock); + clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode); + } + } else snd_mpu401_uart_clear_rx(mpu); - /* ok. for better Tx performance try do some output when input is done */ - if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + /* ok. for better Tx performance try do some output when input is done */ + if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && + test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { + spin_lock(&mpu->output_lock); snd_mpu401_uart_output_write(mpu); + spin_unlock(&mpu->output_lock); + } } /** @@ -91,20 +100,26 @@ void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) _snd_mpu401_uart_interrupt(mpu); } +/* + * timer callback + * reprogram the timer and call the interrupt job + */ static void snd_mpu401_uart_timer(unsigned long data) { - unsigned long flags; mpu401_t *mpu = snd_magic_cast(mpu401_t, (void *)data, return); - spin_lock_irqsave(&mpu->timer_lock, flags); + spin_lock(&mpu->timer_lock); /*mpu->mode |= MPU401_MODE_TIMER;*/ mpu->timer.expires = 1 + jiffies; add_timer(&mpu->timer); - spin_unlock_irqrestore(&mpu->timer_lock, flags); + spin_unlock(&mpu->timer_lock); if (mpu->rmidi) _snd_mpu401_uart_interrupt(mpu); } +/* + * initialize the timer callback if not programmed yet + */ static void snd_mpu401_uart_add_timer (mpu401_t *mpu, int input) { unsigned long flags; @@ -121,6 +136,9 @@ static void snd_mpu401_uart_add_timer (mpu401_t *mpu, int input) spin_unlock_irqrestore (&mpu->timer_lock, flags); } +/* + * remove the timer callback if still active + */ static void snd_mpu401_uart_remove_timer (mpu401_t *mpu, int input) { unsigned long flags; @@ -244,7 +262,7 @@ static int snd_mpu401_uart_output_close(snd_rawmidi_substream_t * substream) } /* - * trigger input + * trigger input callback */ static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) { @@ -253,48 +271,48 @@ static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, i int max = 64; mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); - spin_lock_irqsave(&mpu->input_lock, flags); if (up) { if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { - /* flush FIFO */ + /* first time - flush FIFO */ while (max-- > 0) inb(MPU401D(mpu)); + if (mpu->irq < 0) + snd_mpu401_uart_add_timer(mpu, 1); + } + + /* read data in advance */ + /* prevent double enter via rawmidi->event callback */ + if (! test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) { + spin_lock_irqsave(&mpu->input_lock, flags); + snd_mpu401_uart_input_read(mpu); + spin_unlock_irqrestore(&mpu->input_lock, flags); + clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode); } - if (mpu->irq < 0) - snd_mpu401_uart_add_timer(mpu, 1); } else { if (mpu->irq < 0) snd_mpu401_uart_remove_timer(mpu, 1); clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); } - spin_unlock_irqrestore(&mpu->input_lock, flags); - if (up) - snd_mpu401_uart_input_read(mpu); } +/* + * transfer input pending data + * call with input_lock spinlock held + */ static void snd_mpu401_uart_input_read(mpu401_t * mpu) { int max = 128; unsigned char byte; - /* prevent double enter via event callback */ - if (test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) - return; - spin_lock(&mpu->input_lock); while (max-- > 0) { if (snd_mpu401_input_avail(mpu)) { byte = inb(MPU401D(mpu)); - if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { - spin_unlock(&mpu->input_lock); + if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) snd_rawmidi_receive(mpu->substream_input, &byte, 1); - spin_lock(&mpu->input_lock); - } } else { break; /* input not available */ } } - spin_unlock(&mpu->input_lock); - clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode); } /* @@ -305,18 +323,16 @@ static void snd_mpu401_uart_input_read(mpu401_t * mpu) * SoundBlaster AWE 64 - 2 bytes (ugly hardware) */ +/* + * write output pending bytes + * call with output_lock spinlock held + */ static void snd_mpu401_uart_output_write(mpu401_t * mpu) { unsigned char byte; int max = 256, timeout; - if (!test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) - return; - /* prevent double enter */ - if (test_and_set_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode)) - return; do { - spin_lock(&mpu->output_lock); if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { for (timeout = 100; timeout > 0; timeout--) { if (snd_mpu401_output_ready(mpu)) { @@ -329,28 +345,38 @@ static void snd_mpu401_uart_output_write(mpu401_t * mpu) snd_mpu401_uart_remove_timer (mpu, 0); max = 1; /* no other data - leave the tx loop */ } - spin_unlock(&mpu->output_lock); } while (--max > 0); - clear_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode); } +/* + * output trigger callback + */ static void snd_mpu401_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; mpu401_t *mpu; mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); - spin_lock_irqsave(&mpu->output_lock, flags); if (up) { set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + /* try to add the timer at each output trigger, + * since the output timer might have been removed in + * snd_mpu401_uart_output_write(). + */ snd_mpu401_uart_add_timer(mpu, 0); + + /* output pending data */ + /* prevent double enter via rawmidi->event callback */ + if (! test_and_set_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode)) { + spin_lock_irqsave(&mpu->output_lock, flags); + snd_mpu401_uart_output_write(mpu); + spin_unlock_irqrestore(&mpu->output_lock, flags); + clear_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode); + } } else { snd_mpu401_uart_remove_timer(mpu, 0); clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); } - spin_unlock_irqrestore(&mpu->output_lock, flags); - if (up) - snd_mpu401_uart_output_write(mpu); } /* diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 2ab41c8b410d..890434a42622 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -59,7 +59,7 @@ /* */ -#define ENABLE_SB_MIXER +/* #define ENABLE_SB_MIXER */ #define PLAYBACK_ON_SB /* @@ -209,7 +209,7 @@ AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), -AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), +AD1848_DOUBLE("CD Capture Volume", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0), AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0), AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0), diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index 41d6d1094c3c..83d631eef133 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c @@ -204,6 +204,9 @@ static struct isapnp_card_id snd_sb16_pnpids[] __devinitdata = { ISAPNP_SB16('C','T','L',0x0070,0x0001), /* Sound Blaster Vibra16CL - added by ctm@ardi.com */ ISAPNP_SB16('C','T','L',0x0080,0x0041), + /* Sound Blaster 16 'value' PnP. It says model ct4130 on the pcb, */ + /* but ct4131 on a sticker on the board.. */ + ISAPNP_SB16('C','T','L',0x0086,0x0041), /* Sound Blaster Vibra16X */ ISAPNP_SB16('C','T','L',0x00f0,0x0043), #else /* SNDRV_SBAWE defined */ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 1b6dbaf42cce..b262a07ede5e 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -141,7 +141,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x57454301, 0xffffffff, "W83971D", NULL }, { 0x574d4c00, 0xffffffff, "WM9701A", patch_wolfson00 }, { 0x574d4c03, 0xffffffff, "WM9703/9707", patch_wolfson03 }, -{ 0x574d4c04, 0xffffffff, "WM9704 (quad)", patch_wolfson04 }, +{ 0x574d4c04, 0xffffffff, "WM9704/quad", patch_wolfson04 }, { 0x574d4c05, 0xffffffff, "WM9705", NULL }, // patch? { 0x594d4800, 0xffffffff, "YMF743", NULL }, { 0x594d4802, 0xffffffff, "YMF752", NULL }, @@ -291,8 +291,9 @@ void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value if (!snd_ac97_valid_reg(ac97, reg)) return; spin_lock(&ac97->reg_lock); - ac97->write(ac97, reg, ac97->regs[reg] = value); + ac97->regs[reg] = value; spin_unlock(&ac97->reg_lock); + ac97->write(ac97, reg, value); set_bit(reg, ac97->reg_accessed); } @@ -301,12 +302,12 @@ static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned #if 0 if (!snd_ac97_valid_reg(ac97, reg)) return; - spin_lock(&ac97->reg_lock); + //spin_lock(&ac97->reg_lock); ac97->write(ac97, reg, value); ac97->regs[reg] = ac97->read(ac97, reg); if (value != ac97->regs[reg]) snd_printk("AC97 reg=%02x val=%04x real=%04x\n", reg, value, ac97->regs[reg]); - spin_unlock(&ac97->reg_lock); + //spin_unlock(&ac97->reg_lock); #endif snd_ac97_write_cache(ac97, reg, value); } @@ -332,27 +333,11 @@ int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) spin_lock(&ac97->reg_lock); change = ac97->regs[reg] != value; if (change) { - ac97->write(ac97, reg, value); ac97->regs[reg] = value; - } - spin_unlock(&ac97->reg_lock); - return change; -} - -static int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) -{ - int change; - unsigned short old, new; - - if (!snd_ac97_valid_reg(ac97, reg)) - return -EINVAL; - old = ac97->regs[reg]; - new = (old & ~mask) | value; - change = old != new; - if (change) { - ac97->write(ac97, reg, new); - ac97->regs[reg] = new; - } + spin_unlock(&ac97->reg_lock); + ac97->write(ac97, reg, value); + } else + spin_unlock(&ac97->reg_lock); return change; } @@ -372,9 +357,20 @@ static int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigne int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) { int change; + unsigned short old, new; + + if (!snd_ac97_valid_reg(ac97, reg)) + return -EINVAL; spin_lock(&ac97->reg_lock); - change = snd_ac97_update_bits_nolock(ac97, reg, mask, value); - spin_unlock(&ac97->reg_lock); + old = ac97->regs[reg]; + new = (old & ~mask) | value; + change = old != new; + if (change) { + ac97->regs[reg] = new; + spin_unlock(&ac97->reg_lock); + ac97->write(ac97, reg, new); + } else + spin_unlock(&ac97->reg_lock); return change; } @@ -389,15 +385,16 @@ static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned sho new = (old & ~mask) | value; change = old != new; if (change) { + ac97->spec.ad18xx.pcmreg[codec] = new; + spin_unlock(&ac97->reg_lock); /* select single codec */ ac97->write(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); /* update PCM bits */ ac97->write(ac97, AC97_PCM, new); /* select all codecs */ ac97->write(ac97, AC97_AD_SERIAL_CFG, 0x7000); - ac97->spec.ad18xx.pcmreg[codec] = new; - } - spin_unlock(&ac97->reg_lock); + } else + spin_unlock(&ac97->reg_lock); up(&ac97->spec.ad18xx.mutex); return change; } @@ -795,7 +792,7 @@ static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_val ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned int new = 0; unsigned short val = 0; - int change = 0; + int change; spin_lock(&ac97->reg_lock); new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO); @@ -826,7 +823,9 @@ static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_val } } - + change = ac97->spdif_status != new; + ac97->spdif_status = new; + spin_unlock(&ac97->reg_lock); if (ac97->flags & AC97_CS_SPDIF) { int x = (val >> 12) & 0x03; @@ -835,21 +834,18 @@ static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_val case 2: x = 0; break; // 48.0 default: x = 0; break; // illegal. } - change = snd_ac97_update_bits_nolock(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12))); + change |= snd_ac97_update_bits(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12))); } else if (ac97->flags & AC97_CX_SPDIF) { int v; - v = ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT; - v |= ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM; - change = snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC, - AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT, - v); + v = new & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT; + v |= new & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM; + change |= snd_ac97_update_bits(ac97, AC97_CXR_AUDIO_MISC, + AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT, + v); } else { - change = snd_ac97_update_bits_nolock(ac97, AC97_SPDIF, 0x3fff, val); + change |= snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val); } - change |= ac97->spdif_status != new; - ac97->spdif_status = new; - spin_unlock(&ac97->reg_lock); return change; } @@ -1005,11 +1001,11 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { #if 0 /* always set in patch_alc650 */ AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0), AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0), -#endif AC97_SINGLE("Surround DAC Switch", AC97_ALC650_SURR_DAC_VOL, 15, 1, 1), AC97_DOUBLE("Surround DAC Volume", AC97_ALC650_SURR_DAC_VOL, 8, 0, 31, 1), AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1), AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1), +#endif }; /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ @@ -1748,7 +1744,7 @@ static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name) pid->patch(ac97); return; } - sprintf(name + strlen(name), " (%x)", id & 0xff); + sprintf(name + strlen(name), " id %x", id & 0xff); } @@ -2224,12 +2220,12 @@ static int set_spdif_rate(ac97_t *ac97, unsigned short rate) spin_lock(&ac97->reg_lock); old = ac97->regs[reg] & ~AC97_SC_SPSR_MASK; + spin_unlock(&ac97->reg_lock); if (old != bits) { - snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); - snd_ac97_update_bits_nolock(ac97, reg, AC97_SC_SPSR_MASK, bits); + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); + snd_ac97_update_bits(ac97, reg, AC97_SC_SPSR_MASK, bits); } - snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); - spin_unlock(&ac97->reg_lock); + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); return 0; } diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index ef1c768a9128..61940d00d5f9 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -353,8 +353,17 @@ int patch_ad1980(ac97_t * ac97) int patch_alc650(ac97_t * ac97) { + unsigned short val; + /* enable spdif in */ snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x03); + val = snd_ac97_read(ac97, AC97_ALC650_MULTICH); + val &= ~0xc000; /* slot: 3,4,7,8,6,9 */ + snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, + val | 0x03); + /* full DAC volume */ + snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); + snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808); return 0; } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index b3a02850d812..0b20f34f11b1 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -20,9 +20,7 @@ /* Does not work. Warning may block system in capture mode */ /* #define USE_VAR48KRATE */ -/* Define this if you want soft ac3 encoding - it's still buggy.. */ -/* #define DO_SOFT_AC3 */ -/* #define USE_AES_IEC958 */ +/* Define this if you want soft ac3 encoding */ #define DO_SOFT_AC3 #define USE_AES_IEC958 @@ -59,6 +57,9 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ static long mpu_port[SNDRV_CARDS] = {0x330, [1 ... (SNDRV_CARDS-1)]=-1}; static long fm_port[SNDRV_CARDS] = {0x388, [1 ... (SNDRV_CARDS-1)]=-1}; +#ifdef DO_SOFT_AC3 +static int soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1}; +#endif MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(index, "Index value for C-Media PCI soundcard."); @@ -75,6 +76,11 @@ MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{-1},{0x330},{0x320},{0x310 MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); MODULE_PARM_DESC(fm_port, "FM port."); MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{-1},{0x388},{0x3c8},{0x3e0},{0x3e8}},dialog:list"); +#ifdef DO_SOFT_AC3 +MODULE_PARM(soft_ac3, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(soft_ac3, "Sofware-conversion of raw SPDIF packets (model 033 only)."); +MODULE_PARM_SYNTAX(soft_ac3, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +#endif #ifndef PCI_DEVICE_ID_CMEDIA_CM8738 #define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 @@ -442,6 +448,7 @@ struct snd_stru_cmipci { unsigned int can_ac3_sw: 1; unsigned int can_ac3_hw: 1; unsigned int can_multi_ch: 1; + unsigned int do_soft_ac3: 1; unsigned int spdif_playback_avail: 1; /* spdif ready? */ unsigned int spdif_playback_enabled: 1; /* spdif switch enabled? */ @@ -1960,11 +1967,22 @@ static snd_pcm_ops_t snd_cmipci_playback_spdif_ops = { .prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */ .trigger = snd_cmipci_playback_trigger, .pointer = snd_cmipci_playback_pointer, +}; + #ifdef DO_SOFT_AC3 +static snd_pcm_ops_t snd_cmipci_playback_spdif_soft_ops = { + .open = snd_cmipci_playback_spdif_open, + .close = snd_cmipci_playback_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cmipci_hw_params, + .hw_free = snd_cmipci_playback_hw_free, + .prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */ + .trigger = snd_cmipci_playback_trigger, + .pointer = snd_cmipci_playback_pointer, .copy = snd_cmipci_ac3_copy, .silence = snd_cmipci_ac3_silence, -#endif }; +#endif static snd_pcm_ops_t snd_cmipci_capture_spdif_ops = { .open = snd_cmipci_capture_spdif_open, @@ -2040,7 +2058,14 @@ static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device) if (err < 0) return err; +#ifdef DO_SOFT_AC3 + if (cm->can_ac3_hw) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); + else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_soft_ops); +#else snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); +#endif snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops); pcm->private_data = cm; @@ -2776,7 +2801,10 @@ static void __devinit query_chip(cmipci_t *cm) if (! detect) { cm->chip_version = 33; cm->max_channels = 2; - cm->can_ac3_sw = 1; + if (cm->do_soft_ac3) + cm->can_ac3_sw = 1; + else + cm->can_ac3_hw = 1; cm->has_dual_dac = 1; } else { cm->chip_version = 37; @@ -2843,11 +2871,8 @@ static int snd_cmipci_dev_free(snd_device_t *device) return snd_cmipci_free(cm); } -static int __devinit snd_cmipci_create(snd_card_t *card, - struct pci_dev *pci, - unsigned long iomidi, - unsigned long iosynth, - cmipci_t **rcmipci) +static int __devinit snd_cmipci_create(snd_card_t *card, struct pci_dev *pci, + int dev, cmipci_t **rcmipci) { cmipci_t *cm; int err; @@ -2855,8 +2880,10 @@ static int __devinit snd_cmipci_create(snd_card_t *card, .dev_free = snd_cmipci_dev_free, }; unsigned int val = 0; + unsigned long iomidi = mpu_port[dev]; + unsigned long iosynth = fm_port[dev]; int pcm_index, pcm_spdif_index; - + *rcmipci = NULL; if ((err = pci_enable_device(pci)) < 0) @@ -2897,6 +2924,9 @@ static int __devinit snd_cmipci_create(snd_card_t *card, cm->chip_version = 0; cm->max_channels = 2; +#ifdef DO_SOFT_AC3 + cm->do_soft_ac3 = soft_ac3[dev]; +#endif query_chip(cm); @@ -3078,10 +3108,7 @@ static int __devinit snd_cmipci_probe(struct pci_dev *pci, break; } - if ((err = snd_cmipci_create(card, pci, - mpu_port[dev], - fm_port[dev], - &cm)) < 0) { + if ((err = snd_cmipci_create(card, pci, dev, &cm)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index f8546a2979f1..4d42fef9d162 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -273,9 +273,6 @@ static void snd_cs46xx_ac97_write(ac97_t *ac97, unsigned short val) { cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); -#ifndef CONFIG_SND_CS46XX_NEW_DSP - int val2 = 0; -#endif int codec_index = -1; /* UGGLY: nr_ac97_codecs == 0 primery codec detection is in progress */ @@ -287,59 +284,9 @@ static void snd_cs46xx_ac97_write(ac97_t *ac97, else snd_assert(0,return); -#ifndef CONFIG_SND_CS46XX_NEW_DSP - if (reg == AC97_CD) - val2 = snd_cs46xx_codec_read(chip, AC97_CD, codec_index); -#endif - + chip->active_ctrl(chip, 1); snd_cs46xx_codec_write(chip, reg, val, codec_index); - -#ifndef CONFIG_SND_CS46XX_NEW_DSP - /* Benny: I've not found *one* soundcard where - this code below could do any sense, and - with the HW mixering it's anyway broken, with - more then 1 PCM stream the amplifier will not - be turned off by unmuting CD channel. So just - lets skip it. - */ - - /* - * Adjust power if the mixer is selected/deselected according - * to the CD. - * - * IF the CD is a valid input source (mixer or direct) AND - * the CD is not muted THEN power is needed - * - * We do two things. When record select changes the input to - * add/remove the CD we adjust the power count if the CD is - * unmuted. - * - * When the CD mute changes we adjust the power level if the - * CD was a valid input. - * - * We also check for CD volume != 0, as the CD mute isn't - * normally tweaked from userspace. - */ - - /* CD mute change ? */ - - /* Benny: this hack dont seems to make any sense to me, at least on the Game Theater XP, - Turning of the amplifier just make the PCM sound very distorcionated. - is this really needed ???????????????? - */ - if (reg == AC97_CD) { - /* Mute bit change ? */ - if ((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) { - /* Mute on */ - if(val&0x8000 || val == 0x1f1f) - chip->amplifier_ctrl(chip, -1); - else /* Mute off power on */ - chip->amplifier_ctrl(chip, 1); - } - } - chip->active_ctrl(chip, -1); -#endif } @@ -1506,7 +1453,6 @@ static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pc if (chip->accept_valid) substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID; chip->active_ctrl(chip, 1); - chip->amplifier_ctrl(chip, 1); return 0; } @@ -1570,7 +1516,6 @@ static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream) substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID; chip->active_ctrl(chip, 1); - chip->amplifier_ctrl(chip, 1); #ifdef CONFIG_SND_CS46XX_NEW_DSP snd_pcm_hw_constraint_list(substream->runtime, 0, @@ -1605,7 +1550,6 @@ static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream) cpcm->substream = NULL; snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr); chip->active_ctrl(chip, -1); - chip->amplifier_ctrl(chip, -1); return 0; } @@ -1617,7 +1561,6 @@ static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream) chip->capt.substream = NULL; snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr); chip->active_ctrl(chip, -1); - chip->amplifier_ctrl(chip, -1); return 0; } @@ -3545,9 +3488,6 @@ static void voyetra_mixer_init (cs46xx_t *chip) { snd_printdd ("initializing Voyetra mixer\n"); - /* turnon Amplifier and leave it on */ - chip->amplifier_ctrl(chip, 1); - /* Enable SPDIF out */ snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0); snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0); @@ -3566,9 +3506,6 @@ static void hercules_mixer_init (cs46xx_t *chip) snd_printdd ("initializing Hercules mixer\n"); #ifdef CONFIG_SND_CS46XX_NEW_DSP - /* turnon Amplifier and leave it on */ - chip->amplifier_ctrl(chip, 1); - for (idx = 0 ; idx < sizeof(snd_hercules_controls) / sizeof(snd_hercules_controls[0]) ; idx++) { snd_kcontrol_t *kctl; @@ -3708,6 +3645,8 @@ static struct cs_card_type __devinitdata cards[] = { #ifdef CONFIG_PM void snd_cs46xx_suspend(cs46xx_t *chip) { + int amp_saved; + snd_card_t *card = chip->card; if (card->power_state == SNDRV_CTL_POWER_D3hot) @@ -3715,7 +3654,13 @@ void snd_cs46xx_suspend(cs46xx_t *chip) snd_pcm_suspend_all(chip->pcm); // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); + amp_saved = chip->amplifier; + /* turn off amp */ + chip->amplifier_ctrl(chip, -chip->amplifier); snd_cs46xx_hw_stop(chip); + /* disable CLKRUN */ + chip->active_ctrl(chip, -chip->amplifier); + chip->amplifier = amp_saved; /* restore the status */ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); } @@ -3748,11 +3693,10 @@ void snd_cs46xx_resume(cs46xx_t *chip) snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); if (amp_saved) - chip->amplifier_ctrl(chip, 1); /* try to turn on */ - if (! amp_saved) { - chip->amplifier = 1; - chip->active_ctrl(chip, -1); - } + chip->amplifier_ctrl(chip, 1); /* turn amp on */ + else + chip->active_ctrl(chip, -1); /* disable CLKRUN */ + chip->amplifier = amp_saved; snd_power_change_state(card, SNDRV_CTL_POWER_D0); } @@ -3853,11 +3797,11 @@ int __devinit snd_cs46xx_create(snd_card_t * card, for (cp = &cards[0]; cp->name; cp++) { if (cp->vendor == ss_vendor && cp->id == ss_card) { snd_printd ("hack for %s enabled\n", cp->name); - if (cp->init) - cp->init(chip); chip->amplifier_ctrl = cp->amp; chip->active_ctrl = cp->active; chip->mixer_init = cp->mixer_init; + if (cp->init) + cp->init(chip); break; } } @@ -3878,7 +3822,7 @@ int __devinit snd_cs46xx_create(snd_card_t * card, if (chip->active_ctrl == NULL) chip->active_ctrl = amp_none; - chip->active_ctrl(chip, 1); + chip->active_ctrl(chip, 1); /* enable CLKRUN */ pci_set_master(pci); @@ -3929,7 +3873,10 @@ int __devinit snd_cs46xx_create(snd_card_t * card, return err; } - chip->active_ctrl(chip, -1); + /* turn on amplifier */ + chip->amplifier_ctrl(chip, 1); + + chip->active_ctrl(chip, -1); /* disable CLKRUN */ *rchip = chip; return 0; diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 0e9a18fb235c..3eb231cbcd4b 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -182,7 +182,7 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, strcpy(card->driver, "EMU10K1"); strcpy(card->shortname, "Sound Blaster Live!"); } - sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, emu->port, emu->irq); + sprintf(card->longname, "%s (rev.%d) at 0x%lx, irq %i", card->shortname, emu->revision, emu->port, emu->irq); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 2575010a28ed..95a786c57958 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -161,6 +161,26 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + + if (emu->audigy && emu->revision == 4) { /* audigy2 */ + /* Hacks for Alice3 to work independent of haP16V driver */ + u32 tmp; + + //Setup SRCMulti_I2S SamplingRate + tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); + tmp &= 0xfffff1ff; + tmp |= (0x2<<9); + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); + + /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ + outl(0x600000, emu->port + 0x20); + outl(0x14, emu->port + 0x24); + + /* Setup SRCMulti Input Audio Enable */ + outl(0x6E0000, emu->port + 0x20); + outl(0xFF00FF00, emu->port + 0x24); + } + /* * Clear page with silence & setup all pointers to this page */ @@ -185,9 +205,15 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) * Lock Sound Memory = 0 * Auto Mute = 1 */ - if (emu->audigy) - outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); - else if (emu->model == 0x20 || + if (emu->audigy) { + if (emu->revision == 4) /* audigy2 */ + outl(HCFG_AUDIOENABLE | + HCFG_AC3ENABLE_CDSPDIF | + HCFG_AC3ENABLE_GPSPDIF | + HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + else + outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + } else if (emu->model == 0x20 || emu->model == 0xc400 || (emu->model == 0x21 && emu->revision < 6)) outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG); @@ -221,9 +247,17 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); /* Enable analog/digital outs on audigy */ - if (emu->audigy) + if (emu->audigy) { outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); - + + if (emu->revision == 4) { /* audigy2 */ + /* Unmute Analog now. Set GPO6 to 1 for Apollo. + * This has to be done after init ALice3 I2SOut beyond 48KHz. + * So, sequence is important. */ + outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); + } + } + #if 0 { unsigned int tmp; diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 1c3d91f84169..419dba8ae917 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -359,7 +359,7 @@ static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) return err; - if ((err = snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params))) < 0) + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) return err; if (err > 0) { /* change */ snd_util_memblk_t *memblk; @@ -401,7 +401,7 @@ static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream) epcm->memblk = NULL; epcm->start_addr = 0; } - snd_pcm_sgbuf_free(substream); + snd_pcm_lib_free_pages(substream); return 0; } @@ -784,10 +784,6 @@ static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream) runtime->private_data = epcm; runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_playback; - if ((err = snd_pcm_sgbuf_init(substream, emu->pci, 32)) < 0) { - snd_magic_kfree(epcm); - return err; - } if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { snd_magic_kfree(epcm); return err; @@ -814,7 +810,6 @@ static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream) emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; mix->epcm = NULL; - snd_pcm_sgbuf_delete(substream); snd_emu10k1_pcm_mixer_notify(emu->card, mix, 0); return 0; } @@ -953,8 +948,6 @@ static snd_pcm_ops_t snd_emu10k1_playback_ops = { .prepare = snd_emu10k1_playback_prepare, .trigger = snd_emu10k1_playback_trigger, .pointer = snd_emu10k1_playback_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -999,9 +992,12 @@ int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) strcpy(pcm->name, "EMU10K1"); emu->pcm = pcm; + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_sg_pages(emu->pci, substream)) < 0) + return err; + for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) snd_pcm_lib_preallocate_pci_pages(emu->pci, substream, 64*1024, 64*1024); - return err; if (rpcm) *rpcm = pcm; diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index d0e112a04a15..7f368f594731 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -150,6 +150,9 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #define ES_REG_STATUS 0x04 /* R/O: Interrupt/Chip select status register */ #define ES_INTR (1<<31) /* Interrupt is pending */ #define ES_1371_ST_AC97_RST (1<<29) /* CT5880 AC'97 Reset bit */ +#define ES_1373_REAR_BIT27 (1<<27) /* rear bits: 000 - front, 010 - mirror, 101 - separate */ +#define ES_1373_REAR_BIT26 (1<<26) +#define ES_1373_REAR_BIT24 (1<<24) #define ES_1373_GPIO_INT_EN(o)(((o)&0x0f)<<20)/* GPIO [3:0] pins - interrupt enable */ #define ES_1373_SPDIF_EN (1<<18) /* SPDIF enable */ #define ES_1373_SPDIF_TEST (1<<17) /* SPDIF test */ @@ -1197,7 +1200,11 @@ static int __devinit snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t if (err < 0) return err; +#ifdef CHIP1370 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); +#else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); +#endif snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops); pcm->private_data = ensoniq; @@ -1239,8 +1246,11 @@ static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t if (err < 0) return err; +#ifdef CHIP1370 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); - +#else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); +#endif pcm->private_data = ensoniq; pcm->private_free = snd_ensoniq_pcm_free2; pcm->info_flags = 0; @@ -1422,6 +1432,53 @@ static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t static snd_kcontrol_new_t snd_es1371_mixer_spdif __devinitdata = ES1371_SPDIF("IEC958 Playback Switch"); +static int snd_es1373_rear_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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 snd_es1373_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + int val = 0; + + spin_lock_irq(&ensoniq->reg_lock); + if ((ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) == ES_1373_REAR_BIT26) + val = 1; + ucontrol->value.integer.value[0] = val; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_es1373_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int nval1; + int change; + + nval1 = ucontrol->value.integer.value[0] ? ES_1373_REAR_BIT26 : (ES_1373_REAR_BIT27|ES_1373_REAR_BIT24); + spin_lock_irq(&ensoniq->reg_lock); + change = (ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) != nval1; + ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24); + ensoniq->cssr |= nval1; + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +static snd_kcontrol_new_t snd_ens1373_rear __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "AC97 2ch->4ch Copy Switch", + .info = snd_es1373_rear_info, + .get = snd_es1373_rear_get, + .put = snd_es1373_rear_put, +}; + static void snd_ensoniq_mixer_free_ac97(ac97_t *ac97) { ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); @@ -1432,7 +1489,7 @@ static struct { unsigned short vid; /* vendor ID */ unsigned short did; /* device ID */ unsigned char rev; /* revision */ -} __devinitdata es1371_spdif_present[] = { +} es1371_spdif_present[] __devinitdata = { { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C }, { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D }, { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E }, @@ -1484,6 +1541,12 @@ static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) snd_ctl_add(card, kctl); break; } + if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SDAC) { + /* mirror rear to front speakers */ + ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT24); + ensoniq->cssr |= ES_1373_REAR_BIT26; + snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_rear, ensoniq)); + } return 0; } diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 74ecc8150d33..95c088c78b71 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -42,6 +42,12 @@ static int __devinit snd_vt1724_amp_init(ice1712_t *ice) return 0; } +static int __devinit snd_vt1724_amp_add_controls(ice1712_t *ice) +{ + /* we use pins 39 and 41 of the VT1616 for left and right read outputs */ + snd_ac97_write_cache(ice->ac97, 0x5a, snd_ac97_read(ice->ac97, 0x5a) & ~0x8000); +} + /* entry point */ struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = { @@ -49,6 +55,7 @@ struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = { VT1724_SUBDEVICE_AUDIO2000, "AMP Ltd AUDIO2000", snd_vt1724_amp_init, + snd_vt1724_amp_add_controls, }, { } /* terminator */ }; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index bf4577f49fe9..cdea96b7a73e 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -70,7 +70,6 @@ #include #define SNDRV_GET_ID #include -#include #include @@ -272,6 +271,19 @@ static inline unsigned int snd_rme32_capture_ptr(rme32_t * rme32) & RME32_RCR_AUDIO_ADDR_MASK) >> rme32->capture_frlog; } +static int snd_rme32_ratecode(int rate) +{ + switch (rate) { + case 32000: return SNDRV_PCM_RATE_32000; + case 44100: return SNDRV_PCM_RATE_44100; + case 48000: return SNDRV_PCM_RATE_48000; + case 64000: return SNDRV_PCM_RATE_64000; + case 88200: return SNDRV_PCM_RATE_88200; + case 96000: return SNDRV_PCM_RATE_96000; + } + return 0; +} + static int snd_rme32_playback_silence(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, snd_pcm_uframes_t count) @@ -663,14 +675,21 @@ static int snd_rme32_playback_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * params) { + int err, rate, dummy; rme32_t *rme32 = _snd_pcm_substream_chip(substream); - int err; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) return err; spin_lock_irq(&rme32->lock); - if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) { + if ((rme32->rcreg & RME32_RCR_KMODE) && + (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { + /* AutoSync */ + if (params_rate(params) != rate) { + spin_unlock_irq(&rme32->lock); + return -EIO; + } + } else if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) { spin_unlock_irq(&rme32->lock); return err; } @@ -710,8 +729,9 @@ snd_rme32_capture_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * params) { unsigned long flags; + int err, isadat, rate; rme32_t *rme32 = _snd_pcm_substream_chip(substream); - int err, isadat; + snd_pcm_runtime_t *runtime = substream->runtime; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) @@ -729,9 +749,16 @@ snd_rme32_capture_hw_params(snd_pcm_substream_t * substream, spin_unlock_irqrestore(&rme32->lock, flags); return err; } - if (params_rate(params) != snd_rme32_capture_getrate(rme32, &isadat)) { - spin_unlock_irqrestore(&rme32->lock, flags); - return -EBUSY; + if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { + if (params_rate(params) != rate) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EIO; + } + if ((isadat && runtime->hw.channels_min == 2) || + (!isadat && runtime->hw.channels_min == 8)) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EIO; + } } /* AutoSync off for recording */ rme32->wcreg &= ~RME32_WCR_AUTOSYNC; @@ -791,7 +818,8 @@ static void snd_rme32_playback_stop(rme32_t * rme32) writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ); } rme32->wcreg &= ~RME32_WCR_START; - rme32->wcreg |= RME32_WCR_MUTE; + if (rme32->wcreg & RME32_WCR_SEL) + rme32->wcreg |= RME32_WCR_MUTE; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); } @@ -838,12 +866,17 @@ static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream) { unsigned long flags; + int rate, dummy; rme32_t *rme32 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irqsave(&rme32->lock, flags); + if (rme32->playback_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } rme32->wcreg &= ~RME32_WCR_ADAT; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); rme32->playback_substream = substream; @@ -856,7 +889,13 @@ static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream) runtime->hw.rates |= SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; } - + if ((rme32->rcreg & RME32_RCR_KMODE) && + (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { + /* AutoSync */ + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); @@ -874,23 +913,17 @@ static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream) static int snd_rme32_capture_spdif_open(snd_pcm_substream_t * substream) { unsigned long flags; - int isadat; + int isadat, rate; rme32_t *rme32 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); - if (snd_rme32_capture_getrate(rme32, &isadat) < 0) { - /* no input */ - return -EIO; - } - if (isadat) { - /* ADAT input */ - return -EBUSY; - } snd_pcm_set_sync(substream); spin_lock_irqsave(&rme32->lock, flags); - + if (rme32->capture_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } rme32->capture_substream = substream; rme32->capture_ptr = 0; spin_unlock_irqrestore(&rme32->lock, flags); @@ -900,6 +933,14 @@ static int snd_rme32_capture_spdif_open(snd_pcm_substream_t * substream) runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; } + if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { + if (isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, @@ -915,12 +956,17 @@ static int snd_rme32_playback_adat_open(snd_pcm_substream_t *substream) { unsigned long flags; + int rate, dummy; rme32_t *rme32 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irqsave(&rme32->lock, flags); + if (rme32->playback_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } rme32->wcreg |= RME32_WCR_ADAT; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); rme32->playback_substream = substream; @@ -929,6 +975,13 @@ snd_rme32_playback_adat_open(snd_pcm_substream_t *substream) spin_unlock_irqrestore(&rme32->lock, flags); runtime->hw = snd_rme32_playback_adat_info; + if ((rme32->rcreg & RME32_RCR_KMODE) && + (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { + /* AutoSync */ + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, @@ -940,27 +993,31 @@ static int snd_rme32_capture_adat_open(snd_pcm_substream_t *substream) { unsigned long flags; - int isadat; + int isadat, rate; rme32_t *rme32 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); - if (snd_rme32_capture_getrate(rme32, &isadat) < 0) { - /* no input */ - return -EIO; - } - if (!isadat) { - /* S/PDIF input */ - return -EBUSY; - } - snd_pcm_set_sync(substream); + runtime->hw = snd_rme32_capture_adat_info; + if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { + if (!isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + snd_pcm_set_sync(substream); + spin_lock_irqsave(&rme32->lock, flags); + if (rme32->capture_substream != NULL) { + spin_unlock_irqrestore(&rme32->lock, flags); + return -EBUSY; + } rme32->capture_substream = substream; rme32->capture_ptr = 0; spin_unlock_irqrestore(&rme32->lock, flags); - runtime->hw = snd_rme32_capture_adat_info; snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, @@ -1010,7 +1067,8 @@ static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream) snd_rme32_playback_stop(rme32); } writel(0, rme32->iobase + RME32_IO_RESET_POS); - rme32->wcreg &= ~RME32_WCR_MUTE; + if (rme32->wcreg & RME32_WCR_SEL) + rme32->wcreg &= ~RME32_WCR_MUTE; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); spin_unlock_irqrestore(&rme32->lock, flags); return 0; @@ -1464,7 +1522,7 @@ snd_rme32_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) snd_iprintf(buffer, " sample rate: %d Hz\n", snd_rme32_playback_getrate(rme32)); } - if (rme32->wcreg & RME32_RCR_KMODE) { + if (rme32->rcreg & RME32_RCR_KMODE) { snd_iprintf(buffer, " sample clock source: AutoSync\n"); } else { snd_iprintf(buffer, " sample clock source: Internal\n"); @@ -1529,6 +1587,10 @@ snd_rme32_put_loopback_control(snd_kcontrol_t * kcontrol, spin_lock_irqsave(&rme32->lock, flags); val = (rme32->wcreg & ~RME32_WCR_SEL) | val; change = val != rme32->wcreg; + if (ucontrol->value.integer.value[0]) + val &= ~RME32_WCR_MUTE; + else + val |= RME32_WCR_MUTE; writel(rme32->wcreg = val, rme32->iobase + RME32_IO_CONTROL_REGISTER); spin_unlock_irqrestore(&rme32->lock, flags); diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 8ce7169d419a..30859a768ea5 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -817,10 +818,18 @@ static inline int snd_hdsp_midi_input_available (hdsp_t *hdsp, int id) static inline int snd_hdsp_midi_output_possible (hdsp_t *hdsp, int id) { + int fifo_bytes_used; + if (id) { - return (hdsp_read(hdsp, HDSP_midiStatusOut1) & 0xff) < 128; + fifo_bytes_used = hdsp_read(hdsp, HDSP_midiStatusOut1) & 0xff; } else { - return (hdsp_read(hdsp, HDSP_midiStatusOut0) & 0xff)< 128; + fifo_bytes_used = hdsp_read(hdsp, HDSP_midiStatusOut0) & 0xff; + } + + if (fifo_bytes_used < 128) { + return 128 - fifo_bytes_used; + } else { + return 0; } } diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 43b2d84b959f..5c604e727e54 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -757,9 +757,9 @@ int snd_trident_allocate_pcm_mem(snd_pcm_substream_t * substream, snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; int err; + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; if (trident->tlb.entries) { - if ((err = snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params))) < 0) - return err; if (err > 0) { /* change */ if (voice->memblk) snd_trident_free_pages(trident, voice->memblk); @@ -767,9 +767,6 @@ int snd_trident_allocate_pcm_mem(snd_pcm_substream_t * substream, if (voice->memblk == NULL) return -ENOMEM; } - } else { - if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) - return err; } return 0; } @@ -860,9 +857,8 @@ static int snd_trident_hw_free(snd_pcm_substream_t * substream) snd_trident_free_pages(trident, voice->memblk); voice->memblk = NULL; } - snd_pcm_sgbuf_free(substream); - } else - snd_pcm_lib_free_pages(substream); + } + snd_pcm_lib_free_pages(substream); if (evoice != NULL) { snd_trident_free_voice(trident, evoice); voice->extra = NULL; @@ -1765,19 +1761,6 @@ static snd_pcm_hardware_t snd_trident_spdif_7018 = .fifo_size = 0, }; -static int snd_trident_sgbuf_init(trident_t *trident, snd_pcm_substream_t *substream) -{ - if (trident->tlb.entries) - return snd_pcm_sgbuf_init(substream, trident->pci, 32); - return 0; -} - -static void snd_trident_sgbuf_delete(trident_t *trident, snd_pcm_substream_t *substream) -{ - if (trident->tlb.entries) - snd_pcm_sgbuf_delete(substream); -} - static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime) { snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; @@ -1794,15 +1777,10 @@ static int snd_trident_playback_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_trident_voice_t *voice; - int err; - if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) - return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) { - snd_trident_sgbuf_delete(trident, substream); + if (voice == NULL) return -EAGAIN; - } snd_trident_pcm_mixer_build(trident, voice, substream); voice->substream = substream; runtime->private_data = voice; @@ -1829,7 +1807,6 @@ static int snd_trident_playback_close(snd_pcm_substream_t * substream) snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; snd_trident_pcm_mixer_free(trident, voice, substream); - snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1849,15 +1826,10 @@ static int snd_trident_spdif_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; - int err; - if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) - return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) { - snd_trident_sgbuf_delete(trident, substream); + if (voice == NULL) return -EAGAIN; - } voice->spdif = 1; voice->substream = substream; trident->spdif_pcm_bits = trident->spdif_bits; @@ -1913,7 +1885,6 @@ static int snd_trident_spdif_close(snd_pcm_substream_t * substream) trident->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); - snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1933,15 +1904,10 @@ static int snd_trident_capture_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; - int err; - if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) - return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) { - snd_trident_sgbuf_delete(trident, substream); + if (voice == NULL) return -EAGAIN; - } voice->capture = 1; voice->substream = substream; runtime->private_data = voice; @@ -1963,8 +1929,6 @@ static int snd_trident_capture_open(snd_pcm_substream_t * substream) ---------------------------------------------------------------------------*/ static int snd_trident_capture_close(snd_pcm_substream_t * substream) { - trident_t *trident = snd_pcm_substream_chip(substream); - snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1982,17 +1946,12 @@ static int snd_trident_capture_close(snd_pcm_substream_t * substream) static int snd_trident_foldback_open(snd_pcm_substream_t * substream) { trident_t *trident = snd_pcm_substream_chip(substream); - int err; snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; - if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) - return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) { - snd_trident_sgbuf_delete(trident, substream); + if (voice == NULL) return -EAGAIN; - } voice->foldback_chan = substream->number; voice->substream = substream; runtime->private_data = voice; @@ -2022,7 +1981,6 @@ static int snd_trident_foldback_close(snd_pcm_substream_t * substream) spin_lock_irq(&trident->reg_lock); outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); spin_unlock_irq(&trident->reg_lock); - snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -2050,8 +2008,6 @@ static snd_pcm_ops_t snd_trident_nx_playback_ops = { .prepare = snd_trident_playback_prepare, .trigger = snd_trident_trigger, .pointer = snd_trident_playback_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -2075,8 +2031,6 @@ static snd_pcm_ops_t snd_trident_nx_capture_ops = { .prepare = snd_trident_capture_prepare, .trigger = snd_trident_trigger, .pointer = snd_trident_capture_pointer, - .copy = snd_pcm_sgbuf_ops_copy_capture, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -2111,8 +2065,6 @@ static snd_pcm_ops_t snd_trident_nx_foldback_ops = { .prepare = snd_trident_foldback_prepare, .trigger = snd_trident_trigger, .pointer = snd_trident_playback_pointer, - .copy = snd_pcm_sgbuf_ops_copy_capture, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -2136,8 +2088,6 @@ static snd_pcm_ops_t snd_trident_nx_spdif_ops = { .prepare = snd_trident_spdif_prepare, .trigger = snd_trident_trigger, .pointer = snd_trident_spdif_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -2166,24 +2116,21 @@ static void snd_trident_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->pcm = NULL; - if (! trident->tlb.entries) - snd_pcm_lib_preallocate_free_for_all(pcm); + snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->foldback = NULL; - if (! trident->tlb.entries) - snd_pcm_lib_preallocate_free_for_all(pcm); + snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->spdif = NULL; - if (! trident->tlb.entries) - snd_pcm_lib_preallocate_free_for_all(pcm); + snd_pcm_lib_preallocate_free_for_all(pcm); } /*--------------------------------------------------------------------------- @@ -2226,7 +2173,9 @@ int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm strcpy(pcm->name, "Trident 4DWave"); trident->pcm = pcm; - if (! trident->tlb.entries) + if (trident->tlb.entries) + snd_pcm_lib_preallocate_sg_pages_for_all(trident->pci, pcm); + else snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 128*1024); if (rpcm) @@ -2279,7 +2228,9 @@ int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_ } trident->foldback = foldback; - if (! trident->tlb.entries) + if (trident->tlb.entries) + snd_pcm_lib_preallocate_sg_pages_for_all(trident->pci, foldback); + else snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); if (rpcm) @@ -2321,7 +2272,9 @@ int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t * strcpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; - if (! trident->tlb.entries) + if (trident->tlb.entries) + snd_pcm_lib_preallocate_sg_pages_for_all(trident->pci, spdif); + else snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); if (rpcm) diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 9c01b8cc9c6c..c4827f02330d 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -668,7 +668,7 @@ static int snd_via82xx_hw_params(snd_pcm_substream_t * substream, viadev_t *viadev = (viadev_t *)substream->runtime->private_data; int err; - err = snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params)); + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; err = build_via_table(viadev, substream, chip->pci, @@ -690,7 +690,7 @@ static int snd_via82xx_hw_free(snd_pcm_substream_t * substream) viadev_t *viadev = (viadev_t *)substream->runtime->private_data; clean_via_table(viadev, substream, chip->pci); - snd_pcm_sgbuf_free(substream); + snd_pcm_lib_free_pages(substream); return 0; } @@ -740,16 +740,20 @@ static int snd_via686_capture_prepare(snd_pcm_substream_t *substream) */ static int via_lock_rate(struct via_rate_lock *rec, int rate) { + int changed = 0; + spin_lock(&rec->lock); if (rec->rate) { if (rec->rate != rate && rec->used > 1) { spin_unlock(&rec->lock); return -EINVAL; } - } else + } else { rec->rate = rate; + changed = 1; + } spin_unlock(&rec->lock); - return 0; + return changed; } /* @@ -761,19 +765,24 @@ static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream) viadev_t *viadev = (viadev_t *)substream->runtime->private_data; unsigned long port = chip->port + viadev->reg_offset; snd_pcm_runtime_t *runtime = substream->runtime; + int rate_changed; + u32 rbits; - if (via_lock_rate(&chip->rates[0], runtime->rate) < 0) - return -EINVAL; - snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); - if (viadev->reg_offset == 0x30) /* DSX3 */ + if ((rate_changed = via_lock_rate(&chip->rates[0], runtime->rate)) < 0) + return rate_changed; + if (rate_changed) { + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + } + rbits = (0xfffff / 48000) * runtime->rate + ((0xfffff % 48000) * runtime->rate) / 48000; + snd_assert((rbits & ~0xfffff) == 0, return -EINVAL); snd_via82xx_channel_reset(chip, viadev); outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L)); outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R)); outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | /* format */ (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | /* stereo */ - (0xffff * runtime->rate)/(48000/16) | /* rate */ + rbits | /* rate */ 0xff000000, /* STOP index is never reached */ port + VIA_REG_OFFSET_STOP_IDX); return 0; @@ -796,6 +805,7 @@ static int snd_via8233_multi_prepare(snd_pcm_substream_t *substream) snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate); snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); snd_via82xx_channel_reset(chip, viadev); outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); @@ -894,8 +904,6 @@ static int snd_via82xx_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_subst } spin_unlock_irqrestore(&ratep->lock, flags); - if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) - return err; /* we may remove following constaint when we modify table entries in interrupt */ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) @@ -977,7 +985,6 @@ static int snd_via82xx_pcm_close(snd_pcm_substream_t * substream) spin_unlock_irqrestore(&ratep->lock, flags); viadev->substream = NULL; - snd_pcm_sgbuf_delete(substream); return 0; } @@ -992,8 +999,6 @@ static snd_pcm_ops_t snd_via686_playback_ops = { .prepare = snd_via686_playback_prepare, .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via686_pcm_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -1007,8 +1012,6 @@ static snd_pcm_ops_t snd_via686_capture_ops = { .prepare = snd_via686_capture_prepare, .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via686_pcm_pointer, - .copy = snd_pcm_sgbuf_ops_copy_capture, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -1022,8 +1025,6 @@ static snd_pcm_ops_t snd_via8233_playback_ops = { .prepare = snd_via8233_playback_prepare, .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via8233_pcm_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -1037,8 +1038,6 @@ static snd_pcm_ops_t snd_via8233_multi_ops = { .prepare = snd_via8233_multi_prepare, .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via8233_pcm_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -1052,8 +1051,6 @@ static snd_pcm_ops_t snd_via8233_capture_ops = { .prepare = snd_via8233_capture_prepare, .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via8233_pcm_pointer, - .copy = snd_pcm_sgbuf_ops_copy_capture, - .silence = snd_pcm_sgbuf_ops_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -1088,6 +1085,9 @@ static int __devinit snd_via8233_pcm_new(via82xx_t *chip) chip->devs[chip->capture_devno].reg_offset = VIA_REG_CAPTURE_8233_STATUS; chip->devs[chip->capture_devno].direction = 1; + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm)) < 0) + return err; + /* PCM #1: multi-channel playback and 2nd capture */ err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm); if (err < 0) @@ -1102,6 +1102,10 @@ static int __devinit snd_via8233_pcm_new(via82xx_t *chip) /* set up capture */ chip->devs[chip->capture_devno + 1].reg_offset = VIA_REG_CAPTURE_8233_STATUS + 0x10; chip->devs[chip->capture_devno + 1].direction = 1; + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm)) < 0) + return err; + return 0; } @@ -1133,6 +1137,9 @@ static int __devinit snd_via8233a_pcm_new(via82xx_t *chip) chip->devs[chip->capture_devno].reg_offset = VIA_REG_CAPTURE_8233_STATUS; chip->devs[chip->capture_devno].direction = 1; + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm)) < 0) + return err; + /* PCM #1: DXS3 playback (for spdif) */ err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 0, &pcm); if (err < 0) @@ -1143,6 +1150,10 @@ static int __devinit snd_via8233a_pcm_new(via82xx_t *chip) /* set up playback */ chip->devs[chip->playback_devno].reg_offset = 0x30; chip->devs[chip->playback_devno].direction = 0; + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm)) < 0) + return err; + return 0; } @@ -1169,6 +1180,10 @@ static int __devinit snd_via686_pcm_new(via82xx_t *chip) chip->devs[0].direction = 0; chip->devs[1].reg_offset = VIA_REG_CAPTURE_STATUS; chip->devs[1].direction = 1; + + if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm)) < 0) + return err; + return 0; } @@ -1348,6 +1363,7 @@ static snd_kcontrol_new_t snd_via82xx_joystick_control __devinitdata = { static int snd_via8233_init_misc(via82xx_t *chip, int dev) { int i, err, caps; + unsigned char val; caps = chip->revision == VIA_REV_8233A ? 1 : 2; for (i = 0; i < caps; i++) { @@ -1359,6 +1375,12 @@ static int snd_via8233_init_misc(via82xx_t *chip, int dev) err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip)); if (err < 0) return err; + + /* select spdif data slot 10/11 */ + pci_read_config_byte(chip->pci, 0x49, &val); + val &= ~0x03; + pci_write_config_byte(chip->pci, 0x49, val); + return 0; } @@ -1583,7 +1605,8 @@ static int __devinit snd_via82xx_create(snd_card_t * card, spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->ac97_lock); - spin_lock_init(&chip->rate_lock); + spin_lock_init(&chip->rates[0].lock); + spin_lock_init(&chip->rates[1].lock); chip->card = card; chip->pci = pci; chip->irq = -1; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 37cf830733ab..d42113fc202c 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -926,7 +926,7 @@ static int snd_ymfpci_playback_spdif_open(snd_pcm_substream_t * substream) snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2); ymfpci_open_extension(chip); chip->spdif_pcm_bits = chip->spdif_bits; - snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); chip->spdif_opened++; spin_unlock_irqrestore(&chip->reg_lock, flags); @@ -1314,8 +1314,8 @@ static int snd_ymfpci_spdif_mask_get(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_ymfpci_spdif_mask __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_ymfpci_spdif_mask_info, .get = snd_ymfpci_spdif_mask_get, }; @@ -1362,8 +1362,8 @@ static int snd_ymfpci_spdif_stream_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_ymfpci_spdif_stream __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), .info = snd_ymfpci_spdif_stream_info, .get = snd_ymfpci_spdif_stream_get, .put = snd_ymfpci_spdif_stream_put diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 95e903ee6a00..1f0303df05bf 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #define SNDRV_GET_ID #include @@ -88,6 +88,12 @@ MODULE_PARM_SYNTAX(pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16"); #endif +/* + * debug the h/w constraints + */ +/* #define HW_CONST_DEBUG */ + + /* * */ @@ -1160,13 +1166,238 @@ static snd_pcm_hardware_t snd_usb_capture = .periods_max = 1024, }; +/* + * h/w constraints + */ + +#ifdef HW_CONST_DEBUG +#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args) +#else +#define hwc_debug(fmt, args...) /**/ +#endif + +static int hw_check_valid_format(snd_pcm_hw_params_t *params, struct audioformat *fp) +{ + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + snd_interval_t *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_mask_t *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* check the format */ + if (! snd_mask_test(fmts, fp->format)) { + printk(KERN_DEBUG " > check: no supported format %d\n", fp->format); + return 0; + } + /* check the channels */ + if (fp->channels < ct->min || fp->channels > ct->max) { + hwc_debug(" > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max); + return 0; + } + /* check the rate is within the range */ + if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) { + hwc_debug(" > check: rate_min %d > max %d\n", fp->rate_min, it->max); + return 0; + } + if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) { + hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min); + return 0; + } + return 1; +} + +static int hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + int rmin, rmax, changed; + + hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + if (changed++) { + if (rmin > fp->rate_min) + rmin = fp->rate_min; + if (rmax < fp->rate_max) + rmax = fp->rate_max; + } else { + rmin = fp->rate_min; + rmax = fp->rate_max; + } + } + + if (! changed) { + hwc_debug(" --> get empty\n"); + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + + +static int hw_rule_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int rmin, rmax, changed; + + hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max); + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + if (changed++) { + if (rmin > fp->channels) + rmin = fp->channels; + if (rmax < fp->channels) + rmax = fp->channels; + } else { + rmin = fp->channels; + rmax = fp->channels; + } + } + + if (! changed) { + hwc_debug(" --> get empty\n"); + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + +static int hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_mask_t *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + u64 fbits; + u32 oldbits[2]; + int changed; + + hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]); + fbits = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + fbits |= (1UL << fp->format); + } + + oldbits[0] = fmt->bits[0]; + oldbits[1] = fmt->bits[1]; + fmt->bits[0] &= (u32)fbits; + fmt->bits[1] &= (u32)(fbits >> 32); + if (! fmt->bits[0] && ! fmt->bits[1]) { + hwc_debug(" --> get empty\n"); + return -EINVAL; + } + changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]); + hwc_debug(" --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed); + return changed; +} + +/* + * check whether the registered audio formats need special hw-constraints + */ +static int check_hw_params_convention(snd_usb_substream_t *subs) +{ + int i; + u32 channels[64]; + u32 rates[64]; + u32 cmaster, rmaster; + struct list_head *p; + + memset(channels, 0, sizeof(channels)); + memset(rates, 0, sizeof(rates)); + + list_for_each(p, &subs->fmt_list) { + struct audioformat *f; + f = list_entry(p, struct audioformat, list); + /* unconventional channels? */ + if (f->channels > 32) + return 1; + /* combination of continuous rates and fixed rates? */ + if (rates[f->format] & SNDRV_PCM_RATE_CONTINUOUS) { + if (f->rates != rates[f->format]) + return 1; + } + if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) { + if (rates[f->format] && rates[f->format] != f->rates) + return 1; + } + channels[f->format] |= (1 << f->channels); + rates[f->format] |= f->rates; + } + /* check whether channels and rates match for all formats */ + cmaster = rmaster = 0; + for (i = 0; i < 64; i++) { + if (cmaster != channels[i] && cmaster && channels[i]) + return 1; + if (rmaster != rates[i] && rmaster && rates[i]) + return 1; + if (channels[i]) + cmaster = channels[i]; + if (rates[i]) + rmaster = rates[i]; + } + return 0; +} + + /* * set up the runtime hardware information. */ -static void setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs) +static int setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs) { struct list_head *p; + int err; runtime->hw.formats = subs->formats; @@ -1195,9 +1426,28 @@ static void setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs) 1000 * MIN_PACKS_URB, /*(NRPACKS * MAX_URBS) * 1000*/ UINT_MAX); - /* FIXME: we need more constraints to restrict the format type, - * channels and rates according to the audioformat list! - */ + if (check_hw_params_convention(subs)) { + 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, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format, subs, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + } + return 0; } static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction, @@ -1212,8 +1462,7 @@ static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction, runtime->hw = *hw; runtime->private_data = subs; subs->pcm_substream = substream; - setup_hw_info(runtime, subs); - return 0; + return setup_hw_info(runtime, subs); } static int snd_usb_pcm_close(snd_pcm_substream_t *substream, int direction) @@ -1823,7 +2072,7 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, i } #if 0 // FIXME - we need to define constraint if (c >= 13) - fp->rates |= SNDRV_PCM_KNOT; /* unconventional rate */ + fp->rates |= SNDRV_PCM_RATE_KNOT; /* unconventional rate */ #endif } @@ -1918,75 +2167,64 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif, return 0; } -static int snd_usb_roland_ua100_hack_intf(snd_usb_audio_t *chip, int ifnum) +/* + * create a stream for an endpoint/altsetting without proper descriptors + */ +static int create_fixed_stream_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) { struct audioformat *fp; - int err; + struct usb_host_interface *alts; + int stream, err; fp = kmalloc(sizeof(*fp), GFP_KERNEL); if (! fp) { snd_printk(KERN_ERR "cannot malloc\n"); return -ENOMEM; } - memset(fp, 0, sizeof(*fp)); - fp->format = SNDRV_PCM_FORMAT_S16_LE; - fp->channels = ifnum == 0 ? 4 : 2; - fp->iface = ifnum; - fp->altsetting = 1; - fp->altset_idx = 1; - fp->attributes = ifnum == 0 ? 0 : EP_CS_ATTR_FILL_MAX; - fp->endpoint = ifnum == 0 ? 0x01 : 0x81; - fp->ep_attr = ifnum == 0 ? 0x09 : 0x05; - fp->rates = SNDRV_PCM_RATE_CONTINUOUS; - fp->rate_min = fp->rate_max = 44100; - - err = add_audio_endpoint(chip, ifnum == 0 ? SNDRV_PCM_STREAM_PLAYBACK - : SNDRV_PCM_STREAM_CAPTURE, fp); + memcpy(fp, quirk->data, sizeof(*fp)); + stream = (fp->endpoint & USB_DIR_IN) + ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + err = add_audio_endpoint(chip, stream, fp); if (err < 0) { kfree(fp); return err; } - usb_set_interface(chip->dev, ifnum, 0); + alts = &iface->altsetting[fp->altset_idx]; + usb_set_interface(chip->dev, fp->iface, 0); + init_usb_pitch(chip->dev, fp->iface, alts, fp); + init_usb_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max); return 0; } -static int snd_usb_roland_ua100_hack(snd_usb_audio_t *chip) +static int snd_usb_create_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk); + +/* + * handle the quirks for the contained interfaces + */ +static int create_composite_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) { - static const snd_usb_midi_endpoint_info_t ep_quirk = { - .out_cables = 0x0007, - .in_cables = 0x0007 - }; - static const snd_usb_audio_quirk_t midi_quirk = { - .vendor_name = "Roland", - .product_name = "UA-100", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &ep_quirk - }; - struct usb_host_config *cfg = chip->dev->actconfig; - struct usb_interface *iface; + struct usb_config_descriptor *config = chip->dev->actconfig; + int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; int err; - if (get_cfg_desc(cfg)->bNumInterfaces != 3) { - snd_printdd(KERN_ERR "invalid UA-100 descriptor\n"); - return -ENXIO; - } - /* if 0: output */ - if ((err = snd_usb_roland_ua100_hack_intf(chip, 0)) < 0) - return err; - /* if 1: input */ - iface = &cfg->interface[1]; - if (! usb_interface_claimed(iface)) { - if ((err = snd_usb_roland_ua100_hack_intf(chip, 1)) < 0) - return err; - usb_driver_claim_interface(&usb_audio_driver, iface, (void*)-1); - } - /* if 2: MIDI */ - iface = &cfg->interface[2]; - if (! usb_interface_claimed(iface)) { - if ((err = snd_usb_create_midi_interface(chip, iface, &midi_quirk)) < 0) + for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + if (quirk->ifnum >= config->bNumInterfaces) + continue; + iface = &config->interface[quirk->ifnum]; + if (quirk->ifnum != probed_ifnum && + usb_interface_claimed(iface)) + continue; + err = snd_usb_create_quirk(chip, iface, quirk); + if (err < 0) return err; - usb_driver_claim_interface(&usb_audio_driver, iface, (void*)-1); + if (quirk->ifnum != probed_ifnum) + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); } return 0; } @@ -2000,8 +2238,10 @@ static int snd_usb_create_quirk(snd_usb_audio_t *chip, case QUIRK_MIDI_YAMAHA: case QUIRK_MIDI_MIDIMAN: return snd_usb_create_midi_interface(chip, iface, quirk); - case QUIRK_ROLAND_UA100: - return snd_usb_roland_ua100_hack(chip); + case QUIRK_COMPOSITE: + return create_composite_quirk(chip, iface, quirk); + case QUIRK_AUDIO_FIXED_ENDPOINT: + return create_fixed_stream_quirk(chip, iface, quirk); default: snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); return -ENXIO; @@ -2053,6 +2293,7 @@ static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev, chip->dev = dev; chip->card = card; INIT_LIST_HEAD(&chip->pcm_list); + INIT_LIST_HEAD(&chip->midi_list); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_usb_audio_free(chip); @@ -2104,6 +2345,13 @@ static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev, strncpy(card->longname + len, quirk->product_name, sizeof(card->longname) - len - 1); card->longname[sizeof(card->longname) - 1] = '\0'; } + /* add device path to longname */ + len = strlen(card->longname); + if (sizeof(card->longname) - len > 10) { + strcpy(card->longname + len, " at "); + len += 4; + usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); + } *rchip = chip; return 0; @@ -2295,6 +2543,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) release_substream_urbs(subs); } } + /* release the midi resources */ + list_for_each(p, &chip->midi_list) { + snd_usbmidi_disconnect(p); + } up(®ister_mutex); snd_card_free_in_thread(card); } else { diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 69f3dee665b8..fbb1267ae779 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -139,6 +139,7 @@ struct snd_usb_audio { struct list_head pcm_list; /* list of pcm streams */ int pcm_devs; + struct list_head midi_list; /* list of midi interfaces */ int next_midi_device; }; @@ -150,7 +151,8 @@ struct snd_usb_audio { #define QUIRK_MIDI_FIXED_ENDPOINT 0 #define QUIRK_MIDI_YAMAHA 1 #define QUIRK_MIDI_MIDIMAN 2 -#define QUIRK_ROLAND_UA100 3 +#define QUIRK_COMPOSITE 3 +#define QUIRK_AUDIO_FIXED_ENDPOINT 4 typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t; typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t; @@ -175,7 +177,10 @@ struct snd_usb_midi_endpoint_info { /* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info * structure (out_cables and in_cables only) */ -/* for QUIRK_ROLAND_UA100, data is NULL */ +/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk + * structures, terminated with .ifnum = -1 */ + +/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */ /* */ @@ -192,6 +197,7 @@ void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsub int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffer, int buflen); int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *quirk); +void snd_usbmidi_disconnect(struct list_head *p); /* * retrieve usb_interface descriptor from the host interface diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 427f61c64d5f..4061ca6171aa 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -77,6 +77,7 @@ struct snd_usb_midi { struct usb_interface *iface; const snd_usb_audio_quirk_t *quirk; snd_rawmidi_t* rmidi; + struct list_head list; struct snd_usb_midi_endpoint { snd_usb_midi_out_endpoint_t *out; @@ -394,7 +395,7 @@ static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep) unsigned long flags; spin_lock_irqsave(&ep->buffer_lock, flags); - if (urb->status == -EINPROGRESS) { + if (urb->status == -EINPROGRESS || ep->umidi->chip->shutdown) { spin_unlock_irqrestore(&ep->buffer_lock, flags); return; } @@ -451,10 +452,8 @@ static void snd_usbmidi_output_trigger(snd_rawmidi_substream_t* substream, int u usbmidi_out_port_t* port = (usbmidi_out_port_t*)substream->runtime->private_data; port->active = up; - if (up) { - if (! port->ep->umidi->chip->shutdown) /* to be sure... */ - tasklet_hi_schedule(&port->ep->tasklet); - } + if (up) + tasklet_hi_schedule(&port->ep->tasklet); } static int snd_usbmidi_input_open(snd_rawmidi_substream_t* substream) @@ -490,11 +489,8 @@ static snd_rawmidi_ops_t snd_usbmidi_input_ops = { static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep) { if (ep->urb) { - if (ep->urb->transfer_buffer) { - if (! ep->umidi->chip->shutdown) /* to be sure */ - usb_unlink_urb(ep->urb); + if (ep->urb->transfer_buffer) kfree(ep->urb->transfer_buffer); - } usb_free_urb(ep->urb); } snd_magic_kfree(ep); @@ -619,11 +615,8 @@ static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep) if (ep->tasklet.func) tasklet_kill(&ep->tasklet); if (ep->urb) { - if (ep->urb->transfer_buffer) { - if (! ep->umidi->chip->shutdown) /* to be sure */ - usb_unlink_urb(ep->urb); + if (ep->urb->transfer_buffer) kfree(ep->urb->transfer_buffer); - } usb_free_urb(ep->urb); } snd_magic_kfree(ep); @@ -693,6 +686,24 @@ static void snd_usbmidi_free(snd_usb_midi_t* umidi) snd_magic_kfree(umidi); } +/* + * Unlinks all URBs (must be done before the usb_device is deleted). + */ +void snd_usbmidi_disconnect(struct list_head* p) +{ + snd_usb_midi_t* umidi; + int i; + + umidi = list_entry(p, snd_usb_midi_t, list); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; + if (ep->out && ep->out->urb) + usb_unlink_urb(ep->out->urb); + if (ep->in && ep->in->urb) + usb_unlink_urb(ep->in->urb); + } +} + static void snd_usbmidi_rawmidi_free(snd_rawmidi_t* rmidi) { snd_usb_midi_t* umidi = snd_magic_cast(snd_usb_midi_t, rmidi->private_data, return); @@ -994,7 +1005,7 @@ static int snd_usbmidi_create_rawmidi(snd_usb_midi_t* umidi, out_ports, in_ports, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, umidi->chip->card->longname); + strcpy(rmidi->name, umidi->chip->card->shortname); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -1079,6 +1090,8 @@ int snd_usb_create_midi_interface(snd_usb_audio_t* chip, return err; } + list_add(&umidi->list, &umidi->chip->midi_list); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) if (umidi->endpoints[i].in) snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb, diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 477f580b24b1..ffd9347b9eb7 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -161,6 +161,15 @@ .type = QUIRK_MIDI_YAMAHA } }, +{ + USB_DEVICE(0x0499, 0x1011), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Yamaha", + .product_name = "P-250", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_YAMAHA + } +}, { USB_DEVICE(0x0499, 0x1012), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { @@ -233,8 +242,55 @@ .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "UA-100", - .ifnum = 0, - .type = QUIRK_ROLAND_UA100 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = 0x09, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = EP_CS_ATTR_FILL_MAX, + .endpoint = 0x81, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + }, + { + .ifnum = -1 + } + } } }, { @@ -342,15 +398,59 @@ } }, { + /* thanks to Emiliano Grilli for helping researching this data */ USB_DEVICE(0x0582, 0x000c), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SC-D70", - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0007, - .in_cables = 0x0007 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .format = SNDRV_PCM_FORMAT_S24_3LE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x81, + .ep_attr = 0x01, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, + .rate_max = 44100, + } + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + }, + { + .ifnum = -1 + } } } }, -- cgit v1.2.3 From 9dd8f18b00f3568575366f576239771dcfff11d4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 27 Jan 2003 18:58:38 -0800 Subject: [SPARC64]: Kill references to hugepage syscalls. --- arch/sparc64/kernel/systbls.S | 3 --- include/asm-sparc64/unistd.h | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 12069b2b810d..fe902be4fd24 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -65,9 +65,6 @@ sys_call_table32: .word sys32_ipc, sys32_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex /*220*/ .word sys32_sigprocmask, sys_ni_syscall, sys32_delete_module, sys_ni_syscall, sys_getpgid .word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys32_setfsuid16, sys32_setfsgid16 - - /* 234 and 235 were for the hugetlb syscalls. They can be reused */ - /*230*/ .word sys32_select, sys_time, sys_nis_syscall, sys_stime, sys_ni_syscall .word sys_ni_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall /*240*/ .word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index aae9dc3d7d25..33808908dfa9 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -252,8 +252,8 @@ #endif /* #define __NR_oldstat 232 Linux Specific */ #define __NR_stime 233 /* Linux Specific */ -#define __NR_alloc_hugepages 234 /* Linux Specific */ -#define __NR_free_hugepages 235 /* Linux Specific */ +/* #define __NR_UNUSED 234 */ +/* #define __NR_UNUSED 235 */ #define __NR__llseek 236 /* Linux Specific */ #define __NR_mlock 237 #define __NR_munlock 238 -- cgit v1.2.3 From 671ffdc81786daac7fa1834bfbbf1261c93ba22e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 28 Jan 2003 19:37:33 +0100 Subject: ALSA update - fixed makefiles for sequencer modules: when CONFIG_SND_SEQUENCER is m, then synth modules should be m, too --- include/sound/version.h | 2 +- sound/core/ioctl32/ioctl32.c | 3 -- sound/core/seq/Makefile | 105 +++++++++++++++++++++++------------------- sound/core/seq/instr/Makefile | 68 +++++++++++++++------------ sound/core/seq/oss/Makefile | 4 +- sound/isa/gus/Makefile | 20 +++++--- sound/isa/sb/Makefile | 12 +++-- sound/pci/emu10k1/Makefile | 12 +++-- sound/pci/trident/Makefile | 12 +++-- sound/synth/emux/Makefile | 19 ++++++-- 10 files changed, 151 insertions(+), 106 deletions(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index a9e126352a2c..77c9885943eb 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc6" -#define CONFIG_SND_DATE " (Mon Jan 27 17:35:55 2003 UTC)" +#define CONFIG_SND_DATE " (Tue Jan 28 12:17:23 2003 UTC)" diff --git a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c index 4c2e37a01ee4..ba03e6620a25 100644 --- a/sound/core/ioctl32/ioctl32.c +++ b/sound/core/ioctl32/ioctl32.c @@ -249,17 +249,14 @@ static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id) ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); - read_lock(&ctl->card->control_rwlock); kctl = snd_ctl_find_id(ctl->card, id); if (! kctl) { - read_unlock(&ctl->card->control_rwlock); return -ENXIO; } info.id = *id; err = kctl->info(kctl, &info); if (err >= 0) err = info.type; - read_unlock(&ctl->card->control_rwlock); return err; } diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 1a8b6335f5cf..b178b9a5063a 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -22,6 +22,17 @@ snd-seq-instr-objs := seq_instr.o snd-seq-dummy-objs := seq_dummy.o snd-seq-virmidi-objs := seq_virmidi.o +RAWMIDI_OBJS = snd-seq-midi.o snd-seq-midi-event.o +OPL3_OBJS = snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o + +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer := $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),m,$(if $(CONFIG_SND_SEQUENCER),$(1))) + obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o @@ -29,54 +40,52 @@ endif obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o # Toplevel Module Dependency -RAWMIDI_OBJS = snd-seq-midi.o snd-seq-midi-event.o -OPL3_OBJS = snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o -obj-$(CONFIG_SND_SERIAL_U16550) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_MTPAV) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_MPU401) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_ALS100) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_AZT2320) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_DT019X) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_ES18XX) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_OPL3SA2) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_AD1816A) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4231) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_CS4232) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4236) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_PC98_CS4232) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_ES1688) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_GUSCLASSIC) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_GUSMAX) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_GUSEXTREME) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_INTERWAVE) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_INTERWAVE_STB) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI92X_AD1848) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI92X_CS4231) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI93X) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_SB8) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_SB16) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_SBAWE) += $(RAWMIDI_OBJS) $(OPL3_OBJS) snd-seq-virmidi.o -obj-$(CONFIG_SND_ES968) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_WAVEFRONT) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_ALS4000) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_CMIPCI) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4281) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_ENS1370) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_ENS1371) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_ES1938) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_ES1968) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_FM801) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_ICE1712) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_INTEL8X0) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_SONICVIBES) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_VIA82XX) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_ALI5451) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_CS46XX) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_EMU10K1) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-virmidi.o -obj-$(CONFIG_SND_TRIDENT) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-instr.o -obj-$(CONFIG_SND_YMFPCI) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(CONFIG_SND_USB_AUDIO) += $(RAWMIDI_OBJS) -obj-$(CONFIG_SND_HDSP) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SERIAL_U16550)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_MTPAV)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_MPU401)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ALS100)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_AZT2320)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_DT019X)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ES18XX)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPL3SA2)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_AD1816A)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS4231)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS4232)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS4236)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_PC98_CS4232)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ES1688)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_GUSCLASSIC)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_GUSMAX)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_INTERWAVE)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_INTERWAVE_STB)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPTI92X_CS4231)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPTI93X)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SB8)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SB16)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) snd-seq-virmidi.o +obj-$(call sequencer,$(CONFIG_SND_ES968)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_WAVEFRONT)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ALS4000)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CMIPCI)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS4281)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ENS1370)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ENS1371)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ES1938)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ES1968)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_FM801)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ICE1712)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_INTEL8X0)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_VIA82XX)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ALI5451)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS46XX)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-instr.o +obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_USB_AUDIO)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_HDSP)) += $(RAWMIDI_OBJS) obj-m := $(sort $(obj-m)) diff --git a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile index ad002b076f38..16ca46cc6b6b 100644 --- a/sound/core/seq/instr/Makefile +++ b/sound/core/seq/instr/Makefile @@ -10,36 +10,44 @@ snd-ainstr-simple-objs := ainstr_simple.o snd-ainstr-gf1-objs := ainstr_gf1.o snd-ainstr-iw-objs := ainstr_iw.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer := $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),m,$(if $(CONFIG_SND_SEQUENCER),$(1))) + # Toplevel Module Dependency -obj-$(CONFIG_SND_ALS100) += snd-ainstr-fm.o -obj-$(CONFIG_SND_AZT2320) += snd-ainstr-fm.o -obj-$(CONFIG_SND_DT019X) += snd-ainstr-fm.o -obj-$(CONFIG_SND_ES18XX) += snd-ainstr-fm.o -obj-$(CONFIG_SND_OPL3SA2) += snd-ainstr-fm.o -obj-$(CONFIG_SND_AD1816A) += snd-ainstr-fm.o -obj-$(CONFIG_SND_CS4232) += snd-ainstr-fm.o -obj-$(CONFIG_SND_CS4236) += snd-ainstr-fm.o -obj-$(CONFIG_SND_PC98_CS4232) += snd-ainstr-fm.o -obj-$(CONFIG_SND_ES1688) += snd-ainstr-fm.o -obj-$(CONFIG_SND_GUSCLASSIC) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(CONFIG_SND_GUSMAX) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(CONFIG_SND_GUSEXTREME) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-fm.o -obj-$(CONFIG_SND_INTERWAVE) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(CONFIG_SND_INTERWAVE_STB) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ainstr-fm.o -obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-ainstr-fm.o -obj-$(CONFIG_SND_OPTI93X) += snd-ainstr-fm.o -obj-$(CONFIG_SND_SB8) += snd-ainstr-fm.o -obj-$(CONFIG_SND_SB16) += snd-ainstr-fm.o -obj-$(CONFIG_SND_SBAWE) += snd-ainstr-fm.o -obj-$(CONFIG_SND_WAVEFRONT) += snd-ainstr-fm.o -obj-$(CONFIG_SND_ALS4000) += snd-ainstr-fm.o -obj-$(CONFIG_SND_CMIPCI) += snd-ainstr-fm.o -obj-$(CONFIG_SND_CS4281) += snd-ainstr-fm.o -obj-$(CONFIG_SND_ES1938) += snd-ainstr-fm.o -obj-$(CONFIG_SND_FM801) += snd-ainstr-fm.o -obj-$(CONFIG_SND_SONICVIBES) += snd-ainstr-fm.o -obj-$(CONFIG_SND_TRIDENT) += snd-ainstr-simple.o -obj-$(CONFIG_SND_YMFPCI) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_ALS100)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_AZT2320)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_DT019X)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_ES18XX)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_OPL3SA2)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_AD1816A)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_CS4232)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_CS4236)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_PC98_CS4232)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_ES1688)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_GUSCLASSIC)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(call sequencer,$(CONFIG_SND_GUSMAX)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_INTERWAVE)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(call sequencer,$(CONFIG_SND_INTERWAVE_STB)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_OPTI92X_CS4231)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_OPTI93X)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_SB8)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_SB16)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_WAVEFRONT)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_ALS4000)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_CMIPCI)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_CS4281)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_ES1938)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_FM801)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o +obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += snd-ainstr-fm.o obj-m := $(sort $(obj-m)) diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile index 3acc9eda8f00..a37ddedf7107 100644 --- a/sound/core/seq/oss/Makefile +++ b/sound/core/seq/oss/Makefile @@ -7,6 +7,4 @@ snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \ seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \ seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o -ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) - obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o -endif +obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile index 865bf666ed46..a765730bc2fe 100644 --- a/sound/isa/gus/Makefile +++ b/sound/isa/gus/Makefile @@ -19,18 +19,24 @@ snd-gusmax-objs := gusmax.o snd-interwave-objs := interwave.o snd-interwave-stb-objs := interwave-stb.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer := $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),m,$(if $(CONFIG_SND_SEQUENCER),$(1))) + # Toplevel Module Dependency obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o +obj-$(call sequencer,$(CONFIG_SND_GUSCLASSIC)) += snd-gus-synth.o obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o +obj-$(call sequencer,$(CONFIG_SND_GUSMAX)) += snd-gus-synth.o obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o +obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += snd-gus-synth.o obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o +obj-$(call sequencer,$(CONFIG_SND_INTERWAVE)) += snd-gus-synth.o obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) - obj-$(CONFIG_SND_GUSCLASSIC) += snd-gus-synth.o - obj-$(CONFIG_SND_GUSMAX) += snd-gus-synth.o - obj-$(CONFIG_SND_GUSEXTREME) += snd-gus-synth.o - obj-$(CONFIG_SND_INTERWAVE) += snd-gus-synth.o - obj-$(CONFIG_SND_INTERWAVE_STB) += snd-gus-synth.o -endif +obj-$(call sequencer,$(CONFIG_SND_INTERWAVE_STB)) += snd-gus-synth.o obj-m := $(sort $(obj-m)) diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile index dfd8e9160e14..6a212d188e65 100644 --- a/sound/isa/sb/Makefile +++ b/sound/isa/sb/Makefile @@ -15,6 +15,14 @@ snd-sbawe-objs := sbawe.o emu8000.o snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o snd-es968-objs := es968.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer := $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),m,$(if $(CONFIG_SND_SEQUENCER),$(1))) + # Toplevel Module Dependency obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o @@ -28,8 +36,6 @@ ifeq ($(CONFIG_SND_SB16_CSP),y) obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o endif -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) - obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o -endif +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o obj-m := $(sort $(obj-m)) diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile index 54d7455bd857..707243aee54f 100644 --- a/sound/pci/emu10k1/Makefile +++ b/sound/pci/emu10k1/Makefile @@ -10,8 +10,14 @@ snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ emuproc.o emumixer.o emufx.o snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer := $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),m,$(if $(CONFIG_SND_SEQUENCER),$(1))) + # Toplevel Module Dependency obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) - obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1-synth.o -endif +obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emu10k1-synth.o diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile index 05976e52ab57..8defc90462c2 100644 --- a/sound/pci/trident/Makefile +++ b/sound/pci/trident/Makefile @@ -8,8 +8,14 @@ export-objs := trident_main.o snd-trident-objs := trident.o trident_main.o trident_memory.o snd-trident-synth-objs := trident_synth.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer := $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),m,$(if $(CONFIG_SND_SEQUENCER),$(1))) + # Toplevel Module Dependency obj-$(CONFIG_SND_TRIDENT) += snd-trident.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) - obj-$(CONFIG_SND_TRIDENT) += snd-trident-synth.o -endif +obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile index 8667a1badabc..099d1d6eaa9e 100644 --- a/sound/synth/emux/Makefile +++ b/sound/synth/emux/Makefile @@ -5,9 +5,18 @@ export-objs := emux.o -snd-emux-synth-y := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ - emux_effect.o emux_proc.o soundfont.o -snd-emux-synth-$(CONFIG_SND_SEQUENCER_OSS) += emux_oss.o +snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ + emux_effect.o emux_proc.o soundfont.o \ + $(if $(CONFIG_SND_SEQUENCER_OSS),emux_oss.o) -obj-$(CONFIG_SND_SBAWE) += snd-emux-synth.o -obj-$(CONFIG_SND_EMU10K1) += snd-emux-synth.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer := $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),m,$(if $(CONFIG_SND_SEQUENCER),$(1))) + +# Toplevel Module Dependencies +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emux-synth.o +obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emux-synth.o -- cgit v1.2.3 From 115df75c03af47526bf3c7ac51b7b62d623e1a71 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 1 Feb 2003 03:56:54 -0500 Subject: [TG3]: Add device IDs for 5704S/5702a3/5703a3. --- drivers/net/tg3.c | 6 ++++++ include/linux/pci_ids.h | 3 +++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index dc94efd4c6f0..985033813268 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -141,6 +141,12 @@ static struct pci_device_id tg3_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703X, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702A3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703A3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_SYSKONNECT, 0x4400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 013ccd61fb30..94f9d488c2f7 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1661,6 +1661,9 @@ #define PCI_DEVICE_ID_TIGON3_5702FE 0x164d #define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 #define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 +#define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 +#define PCI_DEVICE_ID_TIGON3_5702A3 0x16c6 +#define PCI_DEVICE_ID_TIGON3_5703A3 0x16c7 #define PCI_DEVICE_ID_BCM4401 0x4401 #define PCI_VENDOR_ID_SYBA 0x1592 -- cgit v1.2.3 From f29dc87230a91de8e970f38b5ef091112f3425b5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 1 Feb 2003 16:33:05 +0000 Subject: [ARM PATCH] 1097/3: trizeps IDE support Patch from Guennadi Liakhovetski The enclosed patch includes trizeps-specific IDE code. It adds a Trizeps-specific section to asm/arch/ide.h. The patch is built against 2.5.44-rmk1. --- include/asm-arm/arch-sa1100/ide.h | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-arm/arch-sa1100/ide.h b/include/asm-arm/arch-sa1100/ide.h index a5f392d1dd8d..9c8e1c7644aa 100644 --- a/include/asm-arm/arch-sa1100/ide.h +++ b/include/asm-arm/arch-sa1100/ide.h @@ -49,8 +49,9 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) *irq = 0; } - - +#ifdef CONFIG_SA1100_TRIZEPS +#include +#endif /* * This registers the standard ports for this architecture with the IDE @@ -124,6 +125,23 @@ ide_init_default_hwifs(void) ide_register_hw(&hw); #endif } -} + else if( machine_is_trizeps() ){ +#ifdef CONFIG_SA1100_TRIZEPS + hw_regs_t hw; + /* Enable appropriate GPIOs as interrupt lines */ + GPDR &= ~GPIO_GPIO(TRIZEPS_IRQ_IDE); + set_irq_type( TRIZEPS_IRQ_IDE, IRQT_RISING ); + /* set the pcmcia interface timing */ + //MECR = 0x00060006; // Done on trizeps init + + /* Take hard drives out of reset */ + GPSR = GPIO_GPIO(TRIZEPS_IRQ_IDE); + + ide_init_hwif_ports(&hw, TRIZEPS_IDE_CS0 + 0, TRIZEPS_IDE_CS1 + 6, NULL); + hw.irq = TRIZEPS_IRQ_IDE; + ide_register_hw(&hw, NULL); +#endif + } +} -- cgit v1.2.3 From 2e8c54e8dc1e7166f8e6a4dffc8e035a42a92d96 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 1 Feb 2003 16:59:33 +0000 Subject: [ARM PATCH] 1091/3: support for trizeps board (SA1110-based) Patch from Guennadi Liakhovetski The enclosed patch includes support for the trizeps board, based on the StrongARM-1110 CPU, machine number 74. The patch is built against 2.5.44-rmk1. Only the core files - from arch/arm and include/asm-arm directories. --- arch/arm/def-configs/trizeps | 852 ++++++++++++++++++++++++++++++++++ arch/arm/mach-sa1100/Kconfig | 12 + arch/arm/mach-sa1100/Makefile | 3 + arch/arm/mach-sa1100/trizeps.c | 232 +++++++++ include/asm-arm/arch-sa1100/mftb2.h | 210 +++++++++ include/asm-arm/arch-sa1100/trizeps.h | 20 + 6 files changed, 1329 insertions(+) create mode 100644 arch/arm/def-configs/trizeps create mode 100644 arch/arm/mach-sa1100/trizeps.c create mode 100644 include/asm-arm/arch-sa1100/mftb2.h create mode 100644 include/asm-arm/arch-sa1100/trizeps.h (limited to 'include') diff --git a/arch/arm/def-configs/trizeps b/arch/arm/def-configs/trizeps new file mode 100644 index 000000000000..ce1a1fc9d108 --- /dev/null +++ b/arch/arm/def-configs/trizeps @@ -0,0 +1,852 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_ARM=y +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_GENERIC_BUST_SPINLOCK is not set +# CONFIG_GENERIC_ISA_DMA is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_ANAKIN is not set +# CONFIG_ARCH_ARCA5K is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +CONFIG_ARCH_SA1100=y +# CONFIG_ARCH_SHARK is not set + +# +# Archimedes/A5000 Implementations +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set + +# +# CLPS711X/EP721X Implementations +# +# CONFIG_ARCH_AUTCPU12 is not set +# CONFIG_ARCH_CDB89712 is not set +# CONFIG_ARCH_CEIVA is not set +# CONFIG_ARCH_CLEP7312 is not set +# CONFIG_ARCH_EDB7211 is not set +# CONFIG_ARCH_P720T is not set +# CONFIG_ARCH_FORTUNET is not set +# CONFIG_ARCH_EP7211 is not set +# CONFIG_ARCH_EP7212 is not set + +# +# Epxa10db +# + +# +# Footbridge Implementations +# +# CONFIG_ARCH_CATS is not set +# CONFIG_ARCH_PERSONAL_SERVER is not set +# CONFIG_ARCH_EBSA285_ADDIN is not set +# CONFIG_ARCH_EBSA285_HOST is not set +# CONFIG_ARCH_NETWINDER is not set + +# +# IOP310 Implementation Options +# +# CONFIG_ARCH_IQ80310 is not set +# CONFIG_IOP310_AAU is not set +# CONFIG_IOP310_DMA is not set +# CONFIG_IOP310_MU is not set +# CONFIG_IOP310_PMON is not set + +# +# Intel PXA250/210 Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_ARCH_PXA_IDP is not set + +# +# SA11x0 Implementations +# +# CONFIG_SA1100_ASSABET is not set +# CONFIG_ASSABET_NEPONSET is not set +# CONFIG_SA1100_ADSBITSY is not set +# CONFIG_SA1100_BRUTUS is not set +# CONFIG_SA1100_CERF is not set +# CONFIG_SA1100_H3100 is not set +# CONFIG_SA1100_H3600 is not set +# CONFIG_SA1100_H3800 is not set +# CONFIG_SA1100_H3XXX is not set +# CONFIG_SA1100_EXTENEX1 is not set +# CONFIG_SA1100_FLEXANET is not set +# CONFIG_SA1100_FREEBIRD is not set +# CONFIG_SA1100_GRAPHICSCLIENT is not set +# CONFIG_SA1100_GRAPHICSMASTER is not set +# CONFIG_SA1100_BADGE4 is not set +# CONFIG_SA1100_JORNADA720 is not set +# CONFIG_SA1100_HUW_WEBPANEL is not set +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_LART is not set +# CONFIG_SA1100_NANOENGINE is not set +# CONFIG_SA1100_OMNIMETER is not set +# CONFIG_SA1100_PANGOLIN is not set +# CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_PT_SYSTEM3 is not set +# CONFIG_SA1100_SHANNON is not set +# CONFIG_SA1100_SHERMAN is not set +# CONFIG_SA1100_SIMPAD is not set +CONFIG_SA1100_TRIZEPS=y +CONFIG_TRIZEPS_MFTB2=y +# CONFIG_SA1100_PFS168 is not set +# CONFIG_SA1100_VICTOR is not set +# CONFIG_SA1100_XP860 is not set +# CONFIG_SA1100_YOPY is not set +# CONFIG_SA1100_STORK is not set +# CONFIG_SA1100_USB is not set +# CONFIG_SA1100_USB_NETLINK is not set +# CONFIG_SA1100_USB_CHAR is not set +# CONFIG_H3600_SLEEVE is not set +# CONFIG_ARCH_ACORN is not set +# CONFIG_FOOTBRIDGE is not set +# CONFIG_FOOTBRIDGE_HOST is not set +# CONFIG_FOOTBRIDGE_ADDIN is not set +# CONFIG_SA1111 is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +# CONFIG_CPU_ARM610 is not set +# CONFIG_CPU_ARM710 is not set +# CONFIG_CPU_ARM720T is not set +# CONFIG_CPU_ARM920T is not set +# CONFIG_CPU_ARM922T is not set +# CONFIG_CPU_ARM926T is not set +# CONFIG_CPU_ARM1020 is not set +# CONFIG_CPU_SA110 is not set +CONFIG_CPU_SA1100=y +# CONFIG_CPU_XSCALE is not set +# CONFIG_CPU_32v3 is not set +CONFIG_CPU_32v4=y +# CONFIG_CPU_32v5 is not set +# CONFIG_ARM_THUMB is not set + +# +# General setup +# +CONFIG_DISCONTIGMEM=y +# CONFIG_PCI is not set +CONFIG_ISA=y +# CONFIG_ISA_DMA is not set +# CONFIG_FIQ is not set +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_FREQ_24_API is not set +# CONFIG_CPU_FREQ_26_API is not set +CONFIG_HOTPLUG=y + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=m +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set +# CONFIG_TCIC is not set +# CONFIG_PCMCIA_CLPS6700 is not set +CONFIG_PCMCIA_SA1100=m +# CONFIG_PCMCIA_SA1111 is not set +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_FASTFPE is not set +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PM is not set +# CONFIG_PREEMPT is not set +# CONFIG_APM is not set +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="keepinitrd mem=16M root=/dev/hda2 1" +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=m +CONFIG_MTD_CONCAT=m +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +CONFIG_MTD_AFS_PARTS=m +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set +# CONFIG_MTD_AMDSTD is not set +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_JEDEC is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_CDB89712 is not set +CONFIG_MTD_SA1100=m +# CONFIG_MTD_2PARTS_IPAQ is not set +# CONFIG_MTD_DC21285 is not set +# CONFIG_MTD_IQ80310 is not set +# CONFIG_MTD_EPXA10DB is not set +# CONFIG_MTD_FORTUNET is not set +# CONFIG_MTD_AUTCPU12 is not set +# CONFIG_MTD_EDB7312 is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_CEIVA is not set +# CONFIG_MTD_PCI is not set +# CONFIG_MTD_PCMCIA is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOCPROBE is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_PNP_NAMES is not set +# CONFIG_PNP_DEBUG is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IPV6_SCTP__=y +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_LLC is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DEV_APPLETALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_E1000_NAPI is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=m +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y +# CONFIG_STRIP is not set +# CONFIG_ARLAN is not set +# CONFIG_AIRONET4500 is not set +# CONFIG_AIRONET4500_NONCS is not set +# CONFIG_AIRONET4500_PROC is not set +# CONFIG_WAVELAN is not set +# CONFIG_AIRO is not set +# CONFIG_HERMES is not set +# CONFIG_PCMCIA_NETWAVE is not set +# CONFIG_PCMCIA_WAVELAN is not set +# CONFIG_PCMCIA_HERMES is not set +CONFIG_AIRO_CS=m +CONFIG_NET_WIRELESS=y + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_XIRC2PS is not set +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_ARCNET_COM20020_CS is not set +# CONFIG_PCMCIA_IBMTR is not set +CONFIG_NET_PCMCIA_RADIO=y +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_AIRONET4500_CS is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDISK_STROKE is not set +CONFIG_BLK_DEV_IDECS=m +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_IDE_ICSIDE is not set +# CONFIG_BLK_DEV_IDEDMA_ICS is not set +# CONFIG_IDEDMA_ICS_AUTO is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_BLK_DEV_IDE_RAPIDE is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +CONFIG_BLK_DEV_IDE_MODES=y + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN_BOOL is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_TSLIBDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +# CONFIG_GAMEPORT_NS558 is not set +# CONFIG_GAMEPORT_L4 is not set +# CONFIG_GAMEPORT_EMU10K1 is not set +# CONFIG_GAMEPORT_VORTEX is not set +# CONFIG_GAMEPORT_FM801 is not set +# CONFIG_GAMEPORT_CS461x is not set +# CONFIG_SERIO is not set +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_RPCKBD is not set +# CONFIG_SERIO_AMBAKMI is not set +# CONFIG_SERIO_SA1111 is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_MOUSE_PS2 is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDDLER is not set +# CONFIG_JOYSTICK_DB9 is not set +# CONFIG_JOYSTICK_GAMECON is not set +# CONFIG_JOYSTICK_TURBOGRAFX is not set +# CONFIG_INPUT_JOYDUMP is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_INPUT_MISC is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_UINPUT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_CS is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_SHARE_IRQ is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_RSA is not set +# CONFIG_SERIAL_ACORN is not set +# CONFIG_SERIAL_ANAKIN is not set +# CONFIG_SERIAL_ANAKIN_CONSOLE is not set +# CONFIG_SERIAL_AMBA is not set +# CONFIG_SERIAL_AMBA_CONSOLE is not set +# CONFIG_SERIAL_CLPS711X is not set +# CONFIG_SERIAL_CLPS711X_CONSOLE is not set +# CONFIG_SERIAL_CLPS711X_OLD_NAME is not set +# CONFIG_SERIAL_21285 is not set +# CONFIG_SERIAL_21285_OLD is not set +# CONFIG_SERIAL_21285_CONSOLE is not set +# CONFIG_SERIAL_UART00 is not set +# CONFIG_SERIAL_UART00_CONSOLE is not set +CONFIG_SERIAL_SA1100=y +CONFIG_SERIAL_SA1100_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +CONFIG_I2C=m +CONFIG_I2C_ALGOBIT=m +# CONFIG_I2C_PHILIPSPAR is not set +# CONFIG_I2C_ELV is not set +# CONFIG_I2C_VELLEMAN is not set +# CONFIG_SCx200_I2C is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_BIT_SA1100_GPIO is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_PROC=m + +# +# L3 serial bus support +# +# CONFIG_L3 is not set +# CONFIG_L3_ALGOBIT is not set +# CONFIG_L3_BIT_SA1100_GPIO is not set +# CONFIG_BIT_SA1100_GPIO is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_SA1100_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_SCx200_GPIO is not set +# CONFIG_RAW_DRIVER is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_QFMT_V1 is not set +# CONFIG_QFMT_V2 is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_QUOTA is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_V4 is not set +# CONFIG_NFSD_TCP is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_EXPORTFS is not set +# CONFIG_CIFS is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_AFS_FS is not set +# CONFIG_ZISOFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Multimedia Capabilities Port drivers +# +# CONFIG_MCP is not set +# CONFIG_MCP_SA1100 is not set +# CONFIG_MCP_UCB1200 is not set +# CONFIG_MCP_UCB1200_AUDIO is not set +# CONFIG_MCP_UCB1200_TS is not set + +# +# Console Switches +# +# CONFIG_SWITCHES is not set +# CONFIG_SWITCHES_SA1100 is not set +# CONFIG_SWITCHES_UCB1X00 is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Bluetooth support +# +# CONFIG_BT is not set + +# +# Kernel hacking +# +# CONFIG_NO_FRAME_POINTER is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_INFO is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_DC21285_PORT is not set +# CONFIG_DEBUG_CLPS711X_UART2 is not set + +# +# Security options +# +CONFIG_SECURITY_CAPABILITIES=y + +# +# Library routines +# +# CONFIG_CRC32 is not set +CONFIG_ZLIB_INFLATE=m +CONFIG_ZLIB_DEFLATE=m diff --git a/arch/arm/mach-sa1100/Kconfig b/arch/arm/mach-sa1100/Kconfig index 3ce853d36c0d..ca8c7d80423c 100644 --- a/arch/arm/mach-sa1100/Kconfig +++ b/arch/arm/mach-sa1100/Kconfig @@ -314,6 +314,18 @@ config SA1100_STORK Say Y here if you intend to run this kernel on the Stork handheld computer. +#config SA1100_TRIZEPS +# bool "Trizeps" +# depends on ARCH_SA1100 +# help +# :: write me :: + +#config TRIZEPS_MFTB2 +# bool "MFTB2" +# depends on SA1100_TRIZEPS +# help +# :: write me :: + config SA1100_USB tristate "SA1100 USB function support" depends on ARCH_SA1100 diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile index c3a2adedeeeb..1426ab7ab335 100644 --- a/arch/arm/mach-sa1100/Makefile +++ b/arch/arm/mach-sa1100/Makefile @@ -98,6 +98,9 @@ led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o obj-$(CONFIG_SA1100_STORK) += stork.o export-objs += stork.o +obj-$(CONFIG_SA1100_TRIZEPS) += trizeps.o +export-objs += trizeps.o + obj-$(CONFIG_SA1100_XP860) += xp860.o obj-$(CONFIG_SA1100_YOPY) += yopy.o diff --git a/arch/arm/mach-sa1100/trizeps.c b/arch/arm/mach-sa1100/trizeps.c new file mode 100644 index 000000000000..5546ed1e5d21 --- /dev/null +++ b/arch/arm/mach-sa1100/trizeps.c @@ -0,0 +1,232 @@ +/* + * linux/arch/arm/mach-sa1100/trizeps.c + * + * Authors: + * Andreas Hofer , + * Peter Lueg , + * Guennadi Liakhovetski + * + * This file contains all Trizeps-specific tweaks. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "generic.h" + +#undef DEBUG_TRIZEPS +#ifdef DEBUG_TRIZEPS +#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK( x... ) +#endif + +static struct tri_uart_cts_data_t tri_uart_cts_data[] = { + { TRIZEPS_GPIO_UART1_CTS, 0, NULL, NULL,"int. UART1 cts" }, + { TRIZEPS_GPIO_UART2_CTS, 0, NULL, NULL,"int. UART2 cts" }, + { TRIZEPS_GPIO_UART3_CTS, 0, NULL, NULL,"int. UART3 cts" } +}; + +static void trizeps_cts_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct tri_uart_cts_data_t * uart_data = (struct tri_uart_cts_data_t *)dev_id; + int cts = (!(GPLR & uart_data->cts_gpio)); + + /* NOTE: I suppose that we will not get any interrupts + if the GPIO is not changed, so maybe + the cts_prev_state can be removed ... */ + if (cts != uart_data->cts_prev_state) { + + uart_data->cts_prev_state = cts; + uart_handle_cts_change(uart_data->port, cts); + DPRINTK("(IRQ %d) changed (cts=%d) stop=%d\n", + irq, cts, uart_data->info->tty->hw_stopped); + } +} + +static int +trizeps_register_cts_intr(int gpio, + int irq, + struct tri_uart_cts_data_t *uart_data) +{ + int ret = 0; + + if(irq != NO_IRQ) + { + set_irq_type(irq, IRQT_BOTHEDGE); + + ret = request_irq(irq, trizeps_cts_intr, + SA_INTERRUPT, uart_data->name, uart_data); + if (ret) + printk(KERN_ERR "uart_open: failed to register CTS irq (%d)\n", ret); + } + return ret; +} + +static void trizeps_set_mctrl(struct uart_port *port, u_int mctrl) +{ + if (port->mapbase == _Ser1UTCR0) + { + /**** ttySA1 ****/ + if (mctrl & TIOCM_RTS) + GPCR |= TRIZEPS_GPIO_UART1_RTS; + else + GPSR |= TRIZEPS_GPIO_UART1_RTS; + + DPRINTK("2 ttySA%d Set RTS %s\n",port->line, + mctrl & TIOCM_RTS ? "low" : "high"); + + } + else if (port->mapbase == _Ser3UTCR0) + { + /**** ttySA0 ****/ + } + else + { + /**** ttySA2 ****/ + } +} + +static u_int trizeps_get_mctrl(struct uart_port *port) +{ + int result = TIOCM_CD | TIOCM_DSR; + + if (port->mapbase == _Ser1UTCR0) + { + if (!(GPLR & TRIZEPS_GPIO_UART1_CTS)) + result |= TIOCM_CTS; + } + else if (port->mapbase == _Ser2UTCR0) + { + result |= TIOCM_CTS; + } + else if (port->mapbase == _Ser3UTCR0) + { + result |= TIOCM_CTS; + } + else + { + result = TIOCM_CTS; + } + + DPRINTK(" ttySA%d %s%s%s\n",port->line, + result & TIOCM_CD ? "CD " : "", + result & TIOCM_CTS ? "CTS " : "", + result & TIOCM_DSR ? "DSR " : ""); + + return result; +} + +static struct sa1100_port_fns trizeps_port_fns __initdata = { + .set_mctrl = trizeps_set_mctrl, + .get_mctrl = trizeps_get_mctrl, +}; + +static void trizeps_power_off(void) +{ + printk("trizeps power off\n"); + mdelay(100); + cli(); + /* disable internal oscillator, float CS lines */ + PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS); + /* enable wake-up on GPIO0 (Assabet...) */ + PWER = GFER = GRER = 1; + /* + * set scratchpad to zero, just in case it is used as a + * restart address by the bootloader. + */ + PSPR = 0; + + /* + * Power off + * -> disconnect AKku + */ + TRIZEPS_BCR_set(TRIZEPS_BCR0, TRIZEPS_MFT_OFF); + + /* + * if power supply no Akku + * -> enter sleep mode + */ + PMCR = PMCR_SF; +} + +static int __init trizeps_init(void) +{ + if (!machine_is_trizeps()) + return -EINVAL; + + DPRINTK(" \n"); + pm_power_off = trizeps_power_off; + + // Init UART2 for IrDA +// PPDR |= PPC_TXD2; // Set TXD2 as output + Ser2UTCR4 = UTCR4_HSE; // enable HSE + Ser2HSCR0 = 0; + Ser2HSSR0 = HSSR0_EIF | HSSR0_TUR | HSSR0_RAB | HSSR0_FRE; + + /* Init MECR */ + MECR = 0x00060006; + + /* Set up external serial IRQs */ + GAFR &= ~(GPIO_GPIO16 | GPIO_GPIO17); // no alternate function + GPDR &= ~(GPIO_GPIO16 | GPIO_GPIO17); // Set to Input + set_irq_type(IRQ_GPIO16, IRQT_RISING); + set_irq_type(IRQ_GPIO17, IRQT_RISING); + + return 0; +} + +__initcall(trizeps_init); + +static struct map_desc trizeps_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xF0000000l, 0x30000000l, 0x00800000l, MT_DEVICE }, + { 0xF2000000l, 0x38000000l, 0x00800000l, MT_DEVICE }, +}; + +void __init trizeps_map_io(void) +{ + sa1100_map_io(); + iotable_init(trizeps_io_desc, ARRAY_SIZE(trizeps_io_desc)); + + sa1100_register_uart_fns(&trizeps_port_fns); + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); + sa1100_register_uart(2, 2); +} + +MACHINE_START(TRIZEPS, "TRIZEPS") + MAINTAINER("DSA") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + MAPIO(trizeps_map_io) + INITIRQ(sa1100_init_irq) +MACHINE_END diff --git a/include/asm-arm/arch-sa1100/mftb2.h b/include/asm-arm/arch-sa1100/mftb2.h new file mode 100644 index 000000000000..1d4c9f7dcdf0 --- /dev/null +++ b/include/asm-arm/arch-sa1100/mftb2.h @@ -0,0 +1,210 @@ +#ifndef _ARCH_ARM_MFTB2_h_ +#define _ARCH_ARM_MFTB2_h_ + +// Defines for arch/arm/mm/mm-sa1100.h +#define TRIZEPS_PHYS_VIRT_MAP_SIZE 0x00800000l + +// physical address (only for mm-sa1100.h) +#define TRIZEPS_PHYS_IO_BASE 0x30000000l +#define TRIZEPS_PHYS_MEM_BASE 0x38000000l + +// virtual +#define TRIZEPS_IO_BASE 0xF0000000l +#define TRIZEPS_MEM_BASE 0xF2000000l + +// Offsets for phys and virtual +#define TRIZEPS_OFFSET_REG0 0x00300000l +#define TRIZEPS_OFFSET_REG1 0x00380000l +#define TRIZEPS_OFFSET_IDE_CS0 0x00000000l +#define TRIZEPS_OFFSET_IDE_CS1 0x00080000l +#define TRIZEPS_OFFSET_UART5 0x00100000l +#define TRIZEPS_OFFSET_UART6 0x00180000l +#define TRIZEPS_PHYS_REG0 (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_REG0) +#define TRIZEPS_PHYS_REG1 (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_REG1) +#define TRIZEPS_PHYS_IDE_CS0 (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_IDE_CS0) +#define TRIZEPS_PHYS_IDE_CS1 (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_IDE_CS1) +#define TRIZEPS_PHYS_UART5 (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_UART5) +#define TRIZEPS_PHYS_UART6 (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_UART6) + +// Use follow defines in devices +// virtual address +#define TRIZEPS_REG0 (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_REG0) +#define TRIZEPS_REG1 (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_REG1) +#define TRIZEPS_IDE_CS0 (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_IDE_CS0) +#define TRIZEPS_IDE_CS1 (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_IDE_CS1) +#define TRIZEPS_UART5 (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_UART5) +#define TRIZEPS_UART6 (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_UART6) + +#define TRIZEPS_BAUD_BASE 1500000 + +//#if 0 //temporarily disabled +#ifndef __ASSEMBLY__ +struct tri_uart_cts_data_t { + int cts_gpio; + int cts_prev_state; + struct uart_info *info; + struct uart_port *port; + const char *name; +}; +#endif /* __ASSEMBLY__ */ + +/* Defines for MFTB2 serial_sa1100.c hardware handshaking lines */ +#define SERIAL_FULL +#define NOT_CONNECTED 0 +#ifdef SERIAL_FULL +#define TRIZEPS_GPIO_UART1_RTS GPIO_GPIO14 +#define TRIZEPS_GPIO_UART1_DTR NOT_CONNECTED //GPIO_GPIO9 +#define TRIZEPS_GPIO_UART1_CTS GPIO_GPIO15 +#define TRIZEPS_GPIO_UART1_DCD NOT_CONNECTED //GPIO_GPIO2 +#define TRIZEPS_GPIO_UART1_DSR NOT_CONNECTED //GPIO_GPIO3 +#define TRIZEPS_GPIO_UART3_RTS NOT_CONNECTED //GPIO_GPIO7 +#define TRIZEPS_GPIO_UART3_DTR NOT_CONNECTED //GPIO_GPIO8 +#define TRIZEPS_GPIO_UART3_CTS NOT_CONNECTED //GPIO_GPIO4 +#define TRIZEPS_GPIO_UART3_DCD NOT_CONNECTED //GPIO_GPIO5 +#define TRIZEPS_GPIO_UART3_DSR NOT_CONNECTED //GPIO_GPIO6 + +#define TRIZEPS_GPIO_UART2_RTS NOT_CONNECTED //GPIO_GPIO7 +#define TRIZEPS_GPIO_UART2_DTR NOT_CONNECTED //GPIO_GPIO8 +#define TRIZEPS_GPIO_UART2_CTS NOT_CONNECTED //GPIO_GPIO4 +#define TRIZEPS_GPIO_UART2_DCD NOT_CONNECTED //GPIO_GPIO5 +#define TRIZEPS_GPIO_UART2_DSR NOT_CONNECTED //GPIO_GPIO6 + +#define TRIZEPS_IRQ_UART1_CTS IRQ_GPIO15 +#define TRIZEPS_IRQ_UART1_DCD NO_IRQ //IRQ_GPIO2 +#define TRIZEPS_IRQ_UART1_DSR NO_IRQ //IRQ_GPIO3 +#define TRIZEPS_IRQ_UART3_CTS NO_IRQ //IRQ_GPIO4 +#define TRIZEPS_IRQ_UART3_DCD NO_IRQ //IRQ_GPIO5 +#define TRIZEPS_IRQ_UART3_DSR NO_IRQ //IRQ_GPIO6 + +#define TRIZEPS_IRQ_UART2_CTS NO_IRQ //IRQ_GPIO4 +#define TRIZEPS_IRQ_UART2_DCD NO_IRQ //IRQ_GPIO5 +#define TRIZEPS_IRQ_UART2_DSR NO_IRQ //IRQ_GPIO6 + +#endif /* SERIAL_FULL */ +//#endif //0 + +/* + * This section contains the defines for the MFTB2 implementation + * of drivers/ide/hd.c. HD_IOBASE_0 and HD_IOBASE_1 have to be + * adjusted if hardware changes. + */ +#define TRIZEPS_IRQ_IDE 10 /* MFTB2 specific */ + +/*--- ROOT ---*/ +#define TRIZEPS_GPIO_ROOT_NFS 0 +#define TRIZEPS_GPIO_ROOT_HD 21 +/*--- PCMCIA ---*/ +#define TRIZEPS_GPIO_PCMCIA_IRQ0 1 +#define TRIZEPS_GPIO_PCMCIA_CD0 24 +#define TRIZEPS_IRQ_PCMCIA_IRQ0 TRIZEPS_GPIO_PCMCIA_IRQ0 +#define TRIZEPS_IRQ_PCMCIA_CD0 TRIZEPS_GPIO_PCMCIA_CD0 + 32 - 11 + +// REGISTER 0 -> 0x0XXXX (16bit access) +// read only +#define TRIZEPS_A_STAT 0x8000l +#define TRIZEPS_F_STAT 0x4000l +#define TRIZEPS_BATT_FAULT_EN 0x2000l +#define TRIZEPS_nDQ 0x1000l +#define TRIZEPS_MFT_OFF 0x0800l +#define TRIZEPS_D_APWOFF 0x0400l +#define TRIZEPS_F_CTRL 0x0200l +#define TRIZEPS_F_STOP 0x0100l + +// read / write +#define TRIZEPS_KP_IR_EN 0x0080l +#define TRIZEPS_FIR 0x0040l +#define TRIZEPS_BAR_ON 0x0020l +#define TRIZEPS_VCI_ON 0x0010l +#define TRIZEPS_LED4 0x0008l +#define TRIZEPS_LED3 0x0004l +#define TRIZEPS_LED2 0x0002l +#define TRIZEPS_LED1 0x0001l + +// REGISTER 1 -> 0x1XXXX (16bit access) +// read only +#define TRIZEPS_nVCI2 0x8000l +#define TRIZEPS_nAB_LOW 0x4000l +#define TRIZEPS_nMB_DEAD 0x2000l +#define TRIZEPS_nMB_LOW 0x1000l +#define TRIZEPS_nPCM_VS2 0x0800l +#define TRIZEPS_nPCM_VS1 0x0400l +#define TRIZEPS_PCM_BVD2 0x0200l +#define TRIZEPS_PCM_BVD1 0x0100l + +// read / write +#define TRIZEPS_nROOT_NFS 0x0080l +#define TRIZEPS_nROOT_HD 0x0040l +#define TRIZEPS_nPCM_ENA_REG 0x0020l +#define TRIZEPS_nPCM_RESET_DISABLE 0x0010l +#define TRIZEPS_PCM_EN0_REG 0x0008l +#define TRIZEPS_PCM_EN1_REG 0x0004l +#define TRIZEPS_PCM_V3_EN_REG 0x0002l +#define TRIZEPS_PCM_V5_EN_REG 0x0001l + +/* Access to Board Control Register */ +#define TRIZEPS_BCR0 (*(volatile unsigned short *)(TRIZEPS_REG0)) +#define TRIZEPS_BCR1 (*(volatile unsigned short *)(TRIZEPS_REG1)) + +#define TRIZEPS_BCR_set( reg, x ) do { \ + unsigned long flags; \ + local_irq_save(flags); \ + (reg) |= (x); \ + local_irq_restore(flags); \ +} while (0) + +#define TRIZEPS_BCR_clear( reg, x ) do { \ + unsigned long flags; \ + local_irq_save(flags); \ + (reg) &= ~(x); \ + local_irq_restore(flags); \ +} while (0) + +#define TRIZEPS_OFFSET_KP_REG 0x00200000l +#define TRIZEPS_OFFSET_VCI2 0x00280000l +#define TRIZEPS_OFFSET_VCI4 0x00400000l + +#define TRIZEPS_OFFSET_VCI2_1_DPR (TRIZEPS_OFFSET_VCI2 + 0x00010000l) +#define TRIZEPS_OFFSET_VCI2_2_DPR (TRIZEPS_OFFSET_VCI2 + 0x00018000l) +#define TRIZEPS_OFFSET_VCI2_1_SEMA (TRIZEPS_OFFSET_VCI2 + 0x00020000l) +#define TRIZEPS_OFFSET_VCI2_2_SEMA (TRIZEPS_OFFSET_VCI2 + 0x00028000l) + +#define TRIZEPS_OFFSET_VCI4_1_DPR (TRIZEPS_OFFSET_VCI4 + 0x00000000l) +#define TRIZEPS_OFFSET_VCI4_2_DPR (TRIZEPS_OFFSET_VCI4 + 0x00008000l) +#define TRIZEPS_OFFSET_VCI4_1_SEMA (TRIZEPS_OFFSET_VCI4 + 0x00000380l) +#define TRIZEPS_OFFSET_VCI4_2_SEMA (TRIZEPS_OFFSET_VCI4 + 0x00000388l) +#define TRIZEPS_OFFSET_VCI4_1_CNTR (TRIZEPS_OFFSET_VCI4 + 0x00000390l) +#define TRIZEPS_OFFSET_VCI4_2_CNTR (TRIZEPS_OFFSET_VCI4 + 0x00000392l) + +#define TRIZEPS_PHYS_KP_REG (PHYS_TRIZEPS_IO_BASE + TRIZEPS_OFFSET_KP_REG) + +// VCI address +#define TRIZEPS_PHYS_VCI2_1_DPR (TRIZEPS_PHYS_MEM_BASE + TRIZEPS_OFFSET_VCI2_1_DPR) +#define TRIZEPS_PHYS_VCI2_2_DPR (TRIZEPS_PHYS_MEM_BASE + TRIZEPS_OFFSET_VCI2_2_DPR) +#define TRIZEPS_PHYS_VCI2_1_SEMA (TRIZEPS_PHYS_MEM_BASE + TRIZEPS_OFFSET_VCI2_1_SEMA) +#define TRIZEPS_PHYS_VCI2_2_SEMA (TRIZEPS_PHYS_MEM_BASE + TRIZEPS_OFFSET_VCI2_2_SEMA) + +// VCI4 address +#define TRIZEPS_PHYS_VCI4_1_DPR (TRIZEPS_PHYS_MEM_BASE + TRIZEPS_OFFSET_VCI4_1_DPR) +#define TRIZEPS_PHYS_VCI4_2_DPR (TRIZEPS_PHYS_MEM_BASE + TRIZEPS_OFFSET_VCI4_2_DPR) +#define TRIZEPS_PHYS_VCI4_1_SEMA (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_VCI4_1_SEMA) +#define TRIZEPS_PHYS_VCI4_2_SEMA (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_VCI4_2_SEMA) +#define TRIZEPS_PHYS_VCI4_1_CNTR (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_VCI4_1_CNTR) +#define TRIZEPS_PHYS_VCI4_2_CNTR (TRIZEPS_PHYS_IO_BASE + TRIZEPS_OFFSET_VCI4_2_CNTR) + +#define TRIZEPS_KP_REG (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_KP_REG) + +// VCI address +#define TRIZEPS_VCI2_1_DPR (TRIZEPS_MEM_BASE + TRIZEPS_OFFSET_VCI2_1_DPR) +#define TRIZEPS_VCI2_2_DPR (TRIZEPS_MEM_BASE + TRIZEPS_OFFSET_VCI2_2_DPR) +#define TRIZEPS_VCI2_1_SEMA (TRIZEPS_MEM_BASE + TRIZEPS_OFFSET_VCI2_1_SEMA) +#define TRIZEPS_VCI2_2_SEMA (TRIZEPS_MEM_BASE + TRIZEPS_OFFSET_VCI2_2_SEMA) + +// VCI4 address +#define TRIZEPS_VCI4_1_DPR (TRIZEPS_MEM_BASE + TRIZEPS_OFFSET_VCI4_1_DPR) +#define TRIZEPS_VCI4_2_DPR (TRIZEPS_MEM_BASE + TRIZEPS_OFFSET_VCI4_2_DPR) +#define TRIZEPS_VCI4_1_SEMA (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_VCI4_1_SEMA) +#define TRIZEPS_VCI4_2_SEMA (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_VCI4_2_SEMA) +#define TRIZEPS_VCI4_1_CNTR (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_VCI4_1_CNTR) +#define TRIZEPS_VCI4_2_CNTR (TRIZEPS_IO_BASE + TRIZEPS_OFFSET_VCI4_2_CNTR) + +#endif diff --git a/include/asm-arm/arch-sa1100/trizeps.h b/include/asm-arm/arch-sa1100/trizeps.h new file mode 100644 index 000000000000..b9cc02bac5d9 --- /dev/null +++ b/include/asm-arm/arch-sa1100/trizeps.h @@ -0,0 +1,20 @@ +/* + * linux/include/asm-arm/arch-sa1100/trizeps.h + * + * This file contains the hardware specific definitions for Trizeps + * + * Authors: + * Andreas Hofer , + * Peter Lueg , + * Guennadi Liakhovetski + * + */ + +#ifndef _ASM_ARCH_TRIZEPS_H_ +#define _ASM_ARCH_TRIZEPS_H_ + +#ifdef CONFIG_TRIZEPS_MFTB2 +#include "mftb2.h" +#endif + +#endif // _INCLUDE_TRIZEPS_ -- cgit v1.2.3 From a04d2bdfed842189078523eb59ffd4bba44a62a6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 1 Feb 2003 17:17:52 +0000 Subject: [ARM] Add missing #endif --- include/asm-arm/bug.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/asm-arm/bug.h b/include/asm-arm/bug.h index c9b6e7f6b317..326400b2b83f 100644 --- a/include/asm-arm/bug.h +++ b/include/asm-arm/bug.h @@ -18,3 +18,4 @@ extern volatile void __bug(const char *file, int line, void *data); #endif +#endif -- cgit v1.2.3 From 78a93c96accee297251f9aed06f3c936c508cdcc Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 1 Feb 2003 17:51:52 +0000 Subject: [ARM] Remove IRQ desc->enabled in favour of testing disable_depth --- arch/arm/kernel/irq.c | 45 ++++++++++++++++++++++----------------------- include/asm-arm/mach/irq.h | 5 ++--- 2 files changed, 24 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 26d191131390..054be68a77eb 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -78,7 +78,8 @@ static struct irqdesc bad_irq_desc = { * disable_irq - disable an irq and wait for completion * @irq: Interrupt to disable * - * Disable the selected interrupt line. We do this lazily. + * Disable the selected interrupt line. Enables and disables + * are nested. We do this lazily. * * This function may be called from IRQ context. */ @@ -88,8 +89,7 @@ void disable_irq(unsigned int irq) unsigned long flags; spin_lock_irqsave(&irq_controller_lock, flags); - if (!desc->depth++) - desc->enabled = 0; + desc->disable_depth++; spin_unlock_irqrestore(&irq_controller_lock, flags); } @@ -107,24 +107,25 @@ void enable_irq(unsigned int irq) { struct irqdesc *desc = irq_desc + irq; unsigned long flags; - int pending = 0; spin_lock_irqsave(&irq_controller_lock, flags); - if (unlikely(!desc->depth)) { + if (unlikely(!desc->disable_depth)) { printk("enable_irq(%u) unbalanced from %p\n", irq, __builtin_return_address(0)); - } else if (!--desc->depth) { + } else if (!--desc->disable_depth) { desc->probing = 0; - desc->enabled = 1; desc->chip->unmask(irq); - pending = desc->pending; - desc->pending = 0; + /* - * If the interrupt was waiting to be processed, - * retrigger it. + * If the interrupt is waiting to be processed, + * try to re-run it. We can't directly run it + * from here since the caller might be in an + * interrupt-protected region. */ - if (pending) + if (desc->pending) { + desc->pending = 0; desc->chip->rerun(irq); + } } spin_unlock_irqrestore(&irq_controller_lock, flags); } @@ -264,7 +265,7 @@ do_edge_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) * we shouldn't process the IRQ. Instead, turn on the * hardware masks. */ - if (unlikely(desc->running || !desc->enabled)) + if (unlikely(desc->running || desc->disable_depth)) goto running; /* @@ -286,13 +287,13 @@ do_edge_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) if (!action) break; - if (desc->pending && desc->enabled) { + if (desc->pending && !desc->disable_depth) { desc->pending = 0; desc->chip->unmask(irq); } __do_irq(irq, action, regs); - } while (desc->pending && desc->enabled); + } while (desc->pending && !desc->disable_depth); desc->running = 0; @@ -328,7 +329,7 @@ do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) */ desc->chip->ack(irq); - if (likely(desc->enabled)) { + if (likely(!desc->disable_depth)) { kstat_cpu(cpu).irqs[irq]++; /* @@ -338,7 +339,7 @@ do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) if (action) { __do_irq(irq, desc->action, regs); - if (likely(desc->enabled && + if (likely(!desc->disable_depth && !check_irq_lock(desc, irq, regs))) desc->chip->unmask(irq); } @@ -390,14 +391,13 @@ void __set_irq_handler(unsigned int irq, irq_handler_t handle, int is_chained) if (handle == do_bad_IRQ) { desc->chip->mask(irq); desc->chip->ack(irq); - desc->depth = 1; - desc->enabled = 0; + desc->disable_depth = 1; } desc->handle = handle; if (handle != do_bad_IRQ && is_chained) { desc->valid = 0; desc->probe_ok = 0; - desc->depth = 0; + desc->disable_depth = 0; desc->chip->unmask(irq); } spin_unlock_irqrestore(&irq_controller_lock, flags); @@ -512,10 +512,9 @@ int setup_irq(unsigned int irq, struct irqaction *new) desc->probing = 0; desc->running = 0; desc->pending = 0; - desc->depth = 1; + desc->disable_depth = 1; if (!desc->noautoenable) { - desc->depth = 0; - desc->enabled = 1; + desc->disable_depth = 0; desc->chip->unmask(irq); } } diff --git a/include/asm-arm/mach/irq.h b/include/asm-arm/mach/irq.h index 60ce4643c027..5a3007b97324 100644 --- a/include/asm-arm/mach/irq.h +++ b/include/asm-arm/mach/irq.h @@ -50,8 +50,8 @@ struct irqdesc { irq_handler_t handle; struct irqchip *chip; struct irqaction *action; + unsigned int disable_depth; - unsigned int enabled : 1; /* IRQ is currently enabled */ unsigned int triggered: 1; /* IRQ has occurred */ unsigned int running : 1; /* IRQ is running */ unsigned int pending : 1; /* IRQ is pending */ @@ -59,8 +59,7 @@ struct irqdesc { unsigned int probe_ok : 1; /* IRQ can be used for probe */ unsigned int valid : 1; /* IRQ claimable */ unsigned int noautoenable : 1; /* don't automatically enable IRQ */ - unsigned int unused :23; - unsigned int depth; /* disable depth */ + unsigned int unused :25; /* * IRQ lock detection -- cgit v1.2.3 From 7619fd2bb706279edf839d8caa8a80e63bb93b7a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:06:42 -0800 Subject: [PATCH] Fix inode size accounting race Since Jan removed the lock_kernel()s in inode_add_bytes() and inode_sub_bytes(), these functions have been racy. One problematic workload has been discovered in which concurrent writepage and truncate on SMP quickly causes i_blocks to go negative. writepage() does not take i_sem, and it seems that for ext2, there are no other locks in force when inode_add_bytes() is called. Putting the BKL back in there is not acceptable. To fix this race I have added a new spinlock "i_lock" to the inode. That lock is presently used to protect i_bytes and i_blocks. We could use it to protect i_size as well. The splitting of the used disk space into i_blocks and i_bytes is silly - we should nuke all that and just have a bare loff_t i_usedbytes. Later. --- fs/inode.c | 1 + fs/stat.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 42 +++++++----------------------------------- kernel/ksyms.c | 4 ++++ 4 files changed, 54 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/fs/inode.c b/fs/inode.c index f8cdf05b65a9..257f33f98e05 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -176,6 +176,7 @@ void inode_init_once(struct inode *inode) spin_lock_init(&inode->i_data.private_lock); INIT_LIST_HEAD(&inode->i_data.i_mmap); INIT_LIST_HEAD(&inode->i_data.i_mmap_shared); + spin_lock_init(&inode->i_lock); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) diff --git a/fs/stat.c b/fs/stat.c index 375661315ecd..113f7d0c2bc0 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -316,3 +316,45 @@ asmlinkage long sys_fstat64(unsigned long fd, struct stat64 * statbuf, long flag } #endif /* LFS-64 */ + +void inode_add_bytes(struct inode *inode, loff_t bytes) +{ + spin_lock(&inode->i_lock); + inode->i_blocks += bytes >> 9; + bytes &= 511; + inode->i_bytes += bytes; + if (inode->i_bytes >= 512) { + inode->i_blocks++; + inode->i_bytes -= 512; + } + spin_unlock(&inode->i_lock); +} + +void inode_sub_bytes(struct inode *inode, loff_t bytes) +{ + spin_lock(&inode->i_lock); + inode->i_blocks -= bytes >> 9; + bytes &= 511; + if (inode->i_bytes < bytes) { + inode->i_blocks--; + inode->i_bytes += 512; + } + inode->i_bytes -= bytes; + spin_unlock(&inode->i_lock); +} + +loff_t inode_get_bytes(struct inode *inode) +{ + loff_t ret; + + spin_lock(&inode->i_lock); + ret = (((loff_t)inode->i_blocks) << 9) + inode->i_bytes; + spin_unlock(&inode->i_lock); + return ret; +} + +void inode_set_bytes(struct inode *inode, loff_t bytes) +{ + inode->i_blocks = bytes >> 9; + inode->i_bytes = bytes & 511; +} diff --git a/include/linux/fs.h b/include/linux/fs.h index 595ea1af33fd..76b32526394f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -371,9 +371,10 @@ struct inode { struct timespec i_ctime; unsigned int i_blkbits; unsigned long i_blksize; - unsigned long i_blocks; unsigned long i_version; + unsigned long i_blocks; unsigned short i_bytes; + spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ struct semaphore i_sem; struct inode_operations *i_op; struct file_operations *i_fop; /* former ->i_op->default_file_ops */ @@ -400,7 +401,7 @@ struct inode { void *i_security; __u32 i_generation; union { - void *generic_ip; + void *generic_ip; } u; }; @@ -412,39 +413,6 @@ struct fown_struct { void *security; }; -static inline void inode_add_bytes(struct inode *inode, loff_t bytes) -{ - inode->i_blocks += bytes >> 9; - bytes &= 511; - inode->i_bytes += bytes; - if (inode->i_bytes >= 512) { - inode->i_blocks++; - inode->i_bytes -= 512; - } -} - -static inline void inode_sub_bytes(struct inode *inode, loff_t bytes) -{ - inode->i_blocks -= bytes >> 9; - bytes &= 511; - if (inode->i_bytes < bytes) { - inode->i_blocks--; - inode->i_bytes += 512; - } - inode->i_bytes -= bytes; -} - -static inline loff_t inode_get_bytes(struct inode *inode) -{ - return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes; -} - -static inline void inode_set_bytes(struct inode *inode, loff_t bytes) -{ - inode->i_blocks = bytes >> 9; - inode->i_bytes = bytes & 511; -} - /* * Track a single file's readahead state */ @@ -1277,6 +1245,10 @@ extern int page_symlink(struct inode *inode, const char *symname, int len); extern struct inode_operations page_symlink_inode_operations; extern void generic_fillattr(struct inode *, struct kstat *); extern int vfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); +void inode_add_bytes(struct inode *inode, loff_t bytes); +void inode_sub_bytes(struct inode *inode, loff_t bytes); +loff_t inode_get_bytes(struct inode *inode); +void inode_set_bytes(struct inode *inode, loff_t bytes); extern int vfs_readdir(struct file *, filldir_t, void *); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 574addf5af63..d6f67455a7fe 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -272,6 +272,10 @@ EXPORT_SYMBOL(vfs_fstat); EXPORT_SYMBOL(vfs_stat); EXPORT_SYMBOL(vfs_lstat); EXPORT_SYMBOL(vfs_getattr); +EXPORT_SYMBOL(inode_add_bytes); +EXPORT_SYMBOL(inode_sub_bytes); +EXPORT_SYMBOL(inode_get_bytes); +EXPORT_SYMBOL(inode_set_bytes); EXPORT_SYMBOL(lock_rename); EXPORT_SYMBOL(unlock_rename); EXPORT_SYMBOL(generic_read_dir); -- cgit v1.2.3 From 9c08eeff376642bf89b9e563958d61bc8993afeb Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:06:47 -0800 Subject: [PATCH] vmlinux fix Patch from: "H. J. Lu" Fixes a commonly-reported insmod oops. Move the ksymtab labels definitions inside the liker section, so they get the right addresses. --- include/asm-generic/vmlinux.lds.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 3d97aae1c3e6..7563b5730aaa 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -13,18 +13,18 @@ } \ \ /* Kernel symbol table: Normal symbols */ \ - __start___ksymtab = .; \ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ + __start___ksymtab = .; \ *(__ksymtab) \ + __stop___ksymtab = .; \ } \ - __stop___ksymtab = .; \ \ /* Kernel symbol table: GPL-only symbols */ \ - __start___gpl_ksymtab = .; \ __gpl_ksymtab : AT(ADDR(__gpl_ksymtab) - LOAD_OFFSET) { \ + __start___gpl_ksymtab = .; \ *(__gpl_ksymtab) \ + __stop___gpl_ksymtab = .; \ } \ - __stop___gpl_ksymtab = .; \ \ /* Kernel symbol table: strings */ \ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ -- cgit v1.2.3 From cd9ab8c2b002ecb84a868ba7d36eccd346952cbc Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:07:22 -0800 Subject: [PATCH] ext3: fix scheduling storm and lockups There have been sporadic sightings of ext3 causing little blips of 100,000 context switches per second when under load. At the start of do_get_write_access() we have this logic: repeat: lock_buffer(jh->bh); ... unlock_buffer(jh->bh); ... if (jh->j_list == BJ_Shadow) { sleep_on_buffer(jh->bh); goto repeat; } The problem is that the unlock_buffer() will wake up anyone who is sleeping in the sleep_on_buffer(). So if task A is asleep in sleep_on_buffer() and task B now runs do_get_write_access(), task B will wake task A by accident. Task B will then sleep on the buffer and task A will loop, will run unlock_buffer() and then wake task B. This state will continue until I/O completes against the buffer and kjournal changes jh->j_list. Unless task A and task B happen to both have realtime scheduling policy - if they do then kjournald will never run. The state is never cleared and your box locks up. The fix is to not do the `goto repeat;' until the buffer has been taken of the shadow list. So we don't go and wake up the other waiter(s) until they can actually proceed to use the buffer. The patch removes the exported sleep_on_buffer() function and simply exports an existing function which provides access to a buffer_head's waitqueue pointer. Which is a better interface anyway, because it permits the use of wait_event(). This bug was introduced introduced into 2.4.20-pre5 and was faithfully ported up. --- fs/buffer.c | 14 ++------------ fs/jbd/transaction.c | 5 ++++- include/linux/buffer_head.h | 3 ++- 3 files changed, 8 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/buffer.c b/fs/buffer.c index 148753e121eb..012af654e755 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -81,21 +81,11 @@ init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private) * Return the address of the waitqueue_head to be used for this * buffer_head */ -static wait_queue_head_t *bh_waitq_head(struct buffer_head *bh) +wait_queue_head_t *bh_waitq_head(struct buffer_head *bh) { return &bh_wait_queue_heads[hash_ptr(bh, BH_WAIT_TABLE_ORDER)].wqh; } - -/* - * Wait on a buffer until someone does a wakeup on it. Needs - * lots of external locking. ext3 uses this. Fix it. - */ -void sleep_on_buffer(struct buffer_head *bh) -{ - wait_queue_head_t *wq = bh_waitq_head(bh); - sleep_on(wq); -} -EXPORT_SYMBOL(sleep_on_buffer); +EXPORT_SYMBOL(bh_waitq_head); void wake_up_buffer(struct buffer_head *bh) { diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index e62a542396a6..597562cf47fe 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -689,11 +689,14 @@ repeat: * disk then we cannot do copy-out here. */ if (jh->b_jlist == BJ_Shadow) { + wait_queue_head_t *wqh; + JBUFFER_TRACE(jh, "on shadow: sleep"); spin_unlock(&journal_datalist_lock); unlock_journal(journal); /* commit wakes up all shadow buffers after IO */ - sleep_on_buffer(jh2bh(jh)); + wqh = bh_waitq_head(jh2bh(jh)); + wait_event(*wqh, (jh->b_jlist != BJ_Shadow)); lock_journal(journal); goto repeat; } diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 7fc917c13f32..aaefe4e964b7 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -10,6 +10,7 @@ #include #include #include +#include #include enum bh_state_bits { @@ -154,7 +155,7 @@ void invalidate_bdev(struct block_device *, int); void __invalidate_buffers(kdev_t dev, int); int sync_blockdev(struct block_device *bdev); void __wait_on_buffer(struct buffer_head *); -void sleep_on_buffer(struct buffer_head *bh); +wait_queue_head_t *bh_waitq_head(struct buffer_head *bh); void wake_up_buffer(struct buffer_head *bh); int fsync_bdev(struct block_device *); int fsync_super(struct super_block *); -- cgit v1.2.3 From df38988ca7f60177ffa147e009e7d80bdeb9ad48 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:07:39 -0800 Subject: [PATCH] quota semaphore fix The second quota locking fix. Sorry, I seem to have misplaced the changelog. --- fs/dquot.c | 89 +++++++++++++++++++++++++++------------------------ fs/ext2/inode.c | 6 ++++ fs/quota.c | 6 ++-- fs/super.c | 3 +- include/linux/quota.h | 3 +- 5 files changed, 60 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/fs/dquot.c b/fs/dquot.c index 5722875bf24e..b92f9a26c529 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -159,7 +159,7 @@ static void put_quota_format(struct quota_format_type *fmt) * Note that any operation which operates on dquot data (ie. dq_dqb) must * hold dq_data_lock. * - * Any operation working with dquots must hold dqoff_sem. If operation is + * Any operation working with dquots must hold dqptr_sem. If operation is * just reading pointers from inodes than read lock is enough. If pointers * are altered function must hold write lock. * @@ -270,7 +270,7 @@ static int commit_dqblk(struct dquot *dquot) } /* Invalidate all dquots on the list. Note that this function is called after - * quota is disabled so no new quota might be created. Because we hold dqoff_sem + * quota is disabled so no new quota might be created. Because we hold dqptr_sem * for writing and pointers were already removed from inodes we actually know that * no quota for this sb+type should be held. */ static void invalidate_dquots(struct super_block *sb, int type) @@ -287,7 +287,7 @@ static void invalidate_dquots(struct super_block *sb, int type) if (dquot->dq_type != type) continue; #ifdef __DQUOT_PARANOIA - /* There should be no users of quota - we hold dqoff_sem for writing */ + /* There should be no users of quota - we hold dqptr_sem for writing */ if (atomic_read(&dquot->dq_count)) BUG(); #endif @@ -307,7 +307,7 @@ static int vfs_quota_sync(struct super_block *sb, int type) struct quota_info *dqopt = sb_dqopt(sb); int cnt; - down_read(&dqopt->dqoff_sem); + down_read(&dqopt->dqptr_sem); restart: /* At this point any dirty dquot will definitely be written so we can clear dirty flag from info */ @@ -340,7 +340,7 @@ restart: spin_lock(&dq_list_lock); dqstats.syncs++; spin_unlock(&dq_list_lock); - up_read(&dqopt->dqoff_sem); + up_read(&dqopt->dqptr_sem); return 0; } @@ -427,7 +427,7 @@ static int shrink_dqcache_memory(int nr, unsigned int gfp_mask) /* * Put reference to dquot * NOTE: If you change this function please check whether dqput_blocks() works right... - * MUST be called with dqoff_sem held + * MUST be called with dqptr_sem held */ static void dqput(struct dquot *dquot) { @@ -492,7 +492,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type) /* * Get reference to dquot - * MUST be called with dqoff_sem held + * MUST be called with dqptr_sem held */ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type) { @@ -553,7 +553,7 @@ static int dqinit_needed(struct inode *inode, int type) return 0; } -/* This routine is guarded by dqoff_sem semaphore */ +/* This routine is guarded by dqptr_sem semaphore */ static void add_dquot_ref(struct super_block *sb, int type) { struct list_head *p; @@ -586,7 +586,7 @@ static inline int dqput_blocks(struct dquot *dquot) } /* Remove references to dquots from inode - add dquot to list for freeing if needed */ -/* We can't race with anybody because we hold dqoff_sem for writing... */ +/* We can't race with anybody because we hold dqptr_sem for writing... */ int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head) { struct dquot *dquot = inode->i_dquot[type]; @@ -829,10 +829,10 @@ void dquot_initialize(struct inode *inode, int type) unsigned int id = 0; int cnt; - down_write(&sb_dqopt(inode->i_sb)->dqoff_sem); - /* Having dqoff lock we know NOQUOTA flags can't be altered... */ + down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + /* Having dqptr_sem we know NOQUOTA flags can't be altered... */ if (IS_NOQUOTA(inode)) { - up_write(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); return; } /* Build list of quotas to initialize... */ @@ -853,7 +853,7 @@ void dquot_initialize(struct inode *inode, int type) inode->i_flags |= S_QUOTA; } } - up_write(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); } /* @@ -876,9 +876,9 @@ static void dquot_drop_nolock(struct inode *inode) void dquot_drop(struct inode *inode) { - down_write(&sb_dqopt(inode->i_sb)->dqoff_sem); + down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); dquot_drop_nolock(inode); - up_write(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); } /* @@ -892,7 +892,7 @@ int dquot_alloc_space(struct inode *inode, qsize_t number, int warn) for (cnt = 0; cnt < MAXQUOTAS; cnt++) warntype[cnt] = NOWARN; - down_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) @@ -910,7 +910,7 @@ int dquot_alloc_space(struct inode *inode, qsize_t number, int warn) warn_put_all: spin_unlock(&dq_data_lock); flush_warnings(inode->i_dquot, warntype); - up_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return ret; } @@ -924,7 +924,7 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number) for (cnt = 0; cnt < MAXQUOTAS; cnt++) warntype[cnt] = NOWARN; - down_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) @@ -942,7 +942,7 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number) warn_put_all: spin_unlock(&dq_data_lock); flush_warnings((struct dquot **)inode->i_dquot, warntype); - up_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return ret; } @@ -953,7 +953,7 @@ void dquot_free_space(struct inode *inode, qsize_t number) { unsigned int cnt; - down_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) @@ -962,7 +962,7 @@ void dquot_free_space(struct inode *inode, qsize_t number) } inode_sub_bytes(inode, number); spin_unlock(&dq_data_lock); - up_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); } /* @@ -972,7 +972,7 @@ void dquot_free_inode(const struct inode *inode, unsigned long number) { unsigned int cnt; - down_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) @@ -980,7 +980,7 @@ void dquot_free_inode(const struct inode *inode, unsigned long number) dquot_decr_inodes(inode->i_dquot[cnt], number); } spin_unlock(&dq_data_lock); - up_read(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); } /* @@ -1002,7 +1002,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) transfer_to[cnt] = transfer_from[cnt] = NODQUOT; warntype[cnt] = NOWARN; } - down_write(&sb_dqopt(inode->i_sb)->dqoff_sem); + down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); if (IS_NOQUOTA(inode)) /* File without quota accounting? */ goto warn_put_all; /* First build the transfer_to list - here we can block on reading of dquots... */ @@ -1058,7 +1058,7 @@ warn_put_all: for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (transfer_from[cnt] != NODQUOT) dqput(transfer_from[cnt]); - up_write(&sb_dqopt(inode->i_sb)->dqoff_sem); + up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); return ret; } @@ -1114,7 +1114,8 @@ int vfs_quota_off(struct super_block *sb, int type) goto out; /* We need to serialize quota_off() for device */ - down_write(&dqopt->dqoff_sem); + down(&dqopt->dqonoff_sem); + down_write(&dqopt->dqptr_sem); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; @@ -1145,7 +1146,8 @@ int vfs_quota_off(struct super_block *sb, int type) dqopt->info[cnt].dqi_bgrace = 0; dqopt->ops[cnt] = NULL; } - up_write(&dqopt->dqoff_sem); + up_write(&dqopt->dqptr_sem); + up(&dqopt->dqonoff_sem); out: return 0; } @@ -1177,7 +1179,8 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path) if (!S_ISREG(inode->i_mode)) goto out_f; - down_write(&dqopt->dqoff_sem); + down(&dqopt->dqonoff_sem); + down_write(&dqopt->dqptr_sem); if (sb_has_quota_enabled(sb, type)) { error = -EBUSY; goto out_lock; @@ -1200,17 +1203,19 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path) } up(&dqopt->dqio_sem); set_enable_flags(dqopt, type); + up_write(&dqopt->dqptr_sem); add_dquot_ref(sb, type); + up(&dqopt->dqonoff_sem); - up_write(&dqopt->dqoff_sem); return 0; out_file_init: inode->i_flags = oldflags; dqopt->files[type] = NULL; out_lock: - up_write(&dqopt->dqoff_sem); + up_write(&dqopt->dqptr_sem); + up(&dqopt->dqonoff_sem); out_f: filp_close(f, NULL); out_fmt: @@ -1241,14 +1246,14 @@ int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d { struct dquot *dquot; - down_read(&sb_dqopt(sb)->dqoff_sem); + down_read(&sb_dqopt(sb)->dqptr_sem); if (!(dquot = dqget(sb, id, type))) { - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return -ESRCH; } do_get_dqblk(dquot, di); dqput(dquot); - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return 0; } @@ -1310,14 +1315,14 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d { struct dquot *dquot; - down_read(&sb_dqopt(sb)->dqoff_sem); + down_read(&sb_dqopt(sb)->dqptr_sem); if (!(dquot = dqget(sb, id, type))) { - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return -ESRCH; } do_set_dqblk(dquot, di); dqput(dquot); - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return 0; } @@ -1326,9 +1331,9 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) { struct mem_dqinfo *mi; - down_read(&sb_dqopt(sb)->dqoff_sem); + down_read(&sb_dqopt(sb)->dqptr_sem); if (!sb_has_quota_enabled(sb, type)) { - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return -ESRCH; } mi = sb_dqopt(sb)->info + type; @@ -1338,7 +1343,7 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) ii->dqi_flags = mi->dqi_flags & DQF_MASK; ii->dqi_valid = IIF_ALL; spin_unlock(&dq_data_lock); - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return 0; } @@ -1347,9 +1352,9 @@ int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) { struct mem_dqinfo *mi; - down_read(&sb_dqopt(sb)->dqoff_sem); + down_read(&sb_dqopt(sb)->dqptr_sem); if (!sb_has_quota_enabled(sb, type)) { - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return -ESRCH; } mi = sb_dqopt(sb)->info + type; @@ -1362,7 +1367,7 @@ int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | (ii->dqi_flags & DQF_MASK); mark_info_dirty(mi); spin_unlock(&dq_data_lock); - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return 0; } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index c2132b72ea24..65e99034fcb6 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1239,6 +1239,12 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr) error = inode_change_ok(inode, iattr); if (error) return error; + if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || + (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) { + error = DQUOT_TRANSFER(inode, iattr) ? -EDQUOT : 0; + if (error) + return error; + } inode_setattr(inode, iattr); if (iattr->ia_valid & ATTR_MODE) error = ext2_acl_chmod(inode); diff --git a/fs/quota.c b/fs/quota.c index 2094b133157e..8548c3708989 100644 --- a/fs/quota.c +++ b/fs/quota.c @@ -123,13 +123,13 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, cadd case Q_GETFMT: { __u32 fmt; - down_read(&sb_dqopt(sb)->dqoff_sem); + down_read(&sb_dqopt(sb)->dqptr_sem); if (!sb_has_quota_enabled(sb, type)) { - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); return -ESRCH; } fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; - up_read(&sb_dqopt(sb)->dqoff_sem); + up_read(&sb_dqopt(sb)->dqptr_sem); if (copy_to_user(addr, &fmt, sizeof(fmt))) return -EFAULT; return 0; diff --git a/fs/super.c b/fs/super.c index 09e0fa12b126..a96d8ed78d43 100644 --- a/fs/super.c +++ b/fs/super.c @@ -71,7 +71,8 @@ static struct super_block *alloc_super(void) atomic_set(&s->s_active, 1); sema_init(&s->s_vfs_rename_sem,1); sema_init(&s->s_dquot.dqio_sem, 1); - init_rwsem(&s->s_dquot.dqoff_sem); + sema_init(&s->s_dquot.dqonoff_sem, 1); + init_rwsem(&s->s_dquot.dqptr_sem); s->s_maxbytes = MAX_NON_LFS; s->dq_op = sb_dquot_ops; s->s_qcop = sb_quotactl_ops; diff --git a/include/linux/quota.h b/include/linux/quota.h index e5b1e2187156..e1c097e338d9 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -280,7 +280,8 @@ struct quota_format_type { struct quota_info { unsigned int flags; /* Flags for diskquotas on this device */ struct semaphore dqio_sem; /* lock device while I/O in progress */ - struct rw_semaphore dqoff_sem; /* serialize quota_off() and quota_on() on device and ops using quota_info struct, pointers from inode to dquots */ + struct semaphore dqonoff_sem; /* Serialize quotaon & quotaoff */ + struct rw_semaphore dqptr_sem; /* serialize ops using quota_info struct, pointers from inode to dquots */ struct file *files[MAXQUOTAS]; /* fp's to quotafiles */ struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */ struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */ -- cgit v1.2.3 From 33cd6bac4c8829e7daa5b58cbdc412ee3c0aaeaa Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:08:08 -0800 Subject: [PATCH] blkdev.h fixes Patch from William Lee Irwin III BLK_BOUNCE_HIGH and BLK_BOUNCE_ANY are compared against 64-bit quantities. Cast these unsigned long quantities to avoid overflow. --- include/linux/blkdev.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 6730edd230e0..90171e65e989 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -287,8 +287,8 @@ extern unsigned long blk_max_low_pfn, blk_max_pfn; * BLK_BOUNCE_ANY : don't bounce anything * BLK_BOUNCE_ISA : bounce pages above ISA DMA boundary */ -#define BLK_BOUNCE_HIGH (blk_max_low_pfn << PAGE_SHIFT) -#define BLK_BOUNCE_ANY (blk_max_pfn << PAGE_SHIFT) +#define BLK_BOUNCE_HIGH ((u64)blk_max_low_pfn << PAGE_SHIFT) +#define BLK_BOUNCE_ANY ((u64)blk_max_pfn << PAGE_SHIFT) #define BLK_BOUNCE_ISA (ISA_DMA_THRESHOLD) extern int init_emergency_isa_pool(void); -- cgit v1.2.3 From e7700cb5a5090d9f468e365d3e9abd5abf80e22f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:08:15 -0800 Subject: [PATCH] symbol_get linkage fix Patch from Rusty Russell Make symbol_get() use undefined weak symbols if !CONFIG_MODULE. Many thanks to RTH for introducing undef weak symbols to me. --- include/linux/module.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index 6dad1479105f..5b2fb9d19be3 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -344,7 +344,7 @@ static inline int module_text_address(unsigned long addr) } /* Get/put a kernel symbol (calls should be symmetric) */ -#define symbol_get(x) (&(x)) +#define symbol_get(x) ({ extern typeof(x) x __attribute__((weak)); &(x); }) #define symbol_put(x) do { } while(0) #define symbol_put_addr(x) do { } while(0) -- cgit v1.2.3 From 70494a782be102bc5b6103352218d8779a94353c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:08:22 -0800 Subject: [PATCH] i386 pgd_index() doesn't parenthesize its arg Patch from William Lee Irwin III pgd_index() doesn't parenthesize its argument. This is a bad idea for macros, since it's legitimate to pass expressions to them that will get misinterpreted given operator precedence and the shift. --- include/asm-i386/pgtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index ed061b5fe6af..0213fa88c792 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -242,7 +242,7 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) ((pmd_val(pmd) & (_PAGE_PSE|_PAGE_PRESENT)) == (_PAGE_PSE|_PAGE_PRESENT)) /* to find an entry in a page-table-directory. */ -#define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) +#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) #define __pgd_offset(address) pgd_index(address) -- cgit v1.2.3 From 80e06f8f60d8b4f206263e7c7ea5d4caf25dfed0 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 2 Feb 2003 06:08:33 -0800 Subject: [PATCH] i386 pgd_index() doesn't parenthesize its arg Patch from William Lee Irwin III PAE's pte_none() and pte_pfn() evaluate their arguments twice; analogous fixes have been made to other things; c.f. pgtable.h's long list of one-line inlines with parentheses still around their args. --- include/asm-i386/pgtable-3level.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-i386/pgtable-3level.h b/include/asm-i386/pgtable-3level.h index a69e4be257fc..0751ebf5d340 100644 --- a/include/asm-i386/pgtable-3level.h +++ b/include/asm-i386/pgtable-3level.h @@ -89,8 +89,17 @@ static inline int pte_same(pte_t a, pte_t b) } #define pte_page(x) pfn_to_page(pte_pfn(x)) -#define pte_none(x) (!(x).pte_low && !(x).pte_high) -#define pte_pfn(x) (((x).pte_low >> PAGE_SHIFT) | ((x).pte_high << (32 - PAGE_SHIFT))) + +static inline int pte_none(pte_t pte) +{ + return !pte.pte_low && !pte.pte_high; +} + +static inline unsigned long pte_pfn(pte_t pte) +{ + return (pte.pte_low >> PAGE_SHIFT) | + (pte.pte_high << (32 - PAGE_SHIFT)); +} static inline pte_t pfn_pte(unsigned long page_nr, pgprot_t pgprot) { -- cgit v1.2.3 From d5ea3bb59fe2d4a5216e1daa1e5631f47825665e Mon Sep 17 00:00:00 2001 From: Kai Germaschewski Date: Mon, 3 Feb 2003 02:19:44 -0600 Subject: kbuild: Rename CONFIG_MODVERSIONING -> CONFIG_MODVERSIONS CONFIG_MODVERSIONING was a temporary name introduced to distinguish between the old and new module version implementation. Since the traces of the old implementation are now gone from the build system, we rename the config option back in order to not confuse users more than necessary in 2.6. Also, remove some historic modversions cruft throughout the tree. --- Makefile | 6 +++--- drivers/char/ftape/compressor/zftape-compress.c | 6 +----- drivers/char/ftape/lowlevel/ftape-init.c | 6 +----- drivers/char/ftape/zftape/zftape-init.c | 6 +----- drivers/scsi/aic7xxx/aic79xx_osm.c | 6 ------ drivers/scsi/aic7xxx/aic79xx_osm.h | 3 --- drivers/scsi/aic7xxx/aic7xxx_osm.c | 6 ------ drivers/scsi/aic7xxx/aic7xxx_osm.h | 3 --- include/linux/module.h | 2 +- init/Kconfig | 3 +-- kernel/ksyms.c | 7 ------- kernel/module.c | 4 ++-- scripts/Makefile.build | 12 ++++++------ 13 files changed, 16 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index f2904aa745d7..11b0d705a782 100644 --- a/Makefile +++ b/Makefile @@ -266,7 +266,7 @@ endif # the built-in objects during the descend as well, in order to # make sure the checksums are uptodate before we record them. -ifdef CONFIG_MODVERSIONING +ifdef CONFIG_MODVERSIONS ifeq ($(KBUILD_MODULES),1) ifneq ($(KBUILD_BUILTIN),1) KBUILD_BUILTIN := 1 @@ -402,7 +402,7 @@ $(SUBDIRS): prepare .PHONY: prepare prepare: include/linux/version.h include/asm include/config/MARKER -ifdef CONFIG_MODVERSIONING +ifdef CONFIG_MODVERSIONS ifdef KBUILD_MODULES ifeq ($(origin SUBDIRS),file) $(Q)rm -rf $(MODVERDIR) @@ -511,7 +511,7 @@ ifdef CONFIG_MODULES .PHONY: modules __modversions modules: $(SUBDIRS) __modversions -ifdef CONFIG_MODVERSIONING +ifdef CONFIG_MODVERSIONS __modversions: vmlinux $(SUBDIRS) @echo ' Recording module symbol versions.'; diff --git a/drivers/char/ftape/compressor/zftape-compress.c b/drivers/char/ftape/compressor/zftape-compress.c index 6f58ed077944..37c8fbaf247b 100644 --- a/drivers/char/ftape/compressor/zftape-compress.c +++ b/drivers/char/ftape/compressor/zftape-compress.c @@ -1196,11 +1196,7 @@ int zft_compressor_init(void) printk( KERN_INFO "(c) 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" KERN_INFO "Compressor for zftape (lzrw3 algorithm)\n" -KERN_INFO "Compiled for kernel version %s" -#ifdef MODVERSIONS - " with versioned symbols" -#endif - "\n", UTS_RELEASE); +KERN_INFO "Compiled for kernel version %s\n", UTS_RELEASE); } #else /* !MODULE */ /* print a short no-nonsense boot message */ diff --git a/drivers/char/ftape/lowlevel/ftape-init.c b/drivers/char/ftape/lowlevel/ftape-init.c index 70530a2bebd0..9ffe3de655c0 100644 --- a/drivers/char/ftape/lowlevel/ftape-init.c +++ b/drivers/char/ftape/lowlevel/ftape-init.c @@ -70,11 +70,7 @@ KERN_INFO "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl)\n" KERN_INFO "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n" KERN_INFO "(c) 1996-1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" KERN_INFO "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives\n" -KERN_INFO "Compiled for Linux version %s" -#ifdef MODVERSIONS - " with versioned symbols" -#endif - "\n", UTS_RELEASE); +KERN_INFO "Compiled for Linux version %s\n", UTS_RELEASE); } #else /* !MODULE */ /* print a short no-nonsense boot message */ diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c index 4e56739b6a32..b1c015254c6f 100644 --- a/drivers/char/ftape/zftape/zftape-init.c +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -331,11 +331,7 @@ KERN_INFO KERN_INFO "and builtin compression (lzrw3 algorithm).\n" KERN_INFO -"Compiled for Linux version %s" -#ifdef MODVERSIONS - " with versioned symbols" -#endif - "\n", UTS_RELEASE); +"Compiled for Linux version %s\n", UTS_RELEASE); } #else /* !MODULE */ /* print a short no-nonsense boot message */ diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 05edbed59319..0d15557c8383 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -42,12 +42,6 @@ * POSSIBILITY OF SUCH DAMAGES. */ -/* - * This is the only file where module.h should - * embed module global version info. - */ -#define AHD_MODVERSION_FILE - #include "aic79xx_osm.h" #include "aic79xx_inline.h" #include diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h index ecf6fdf9ef01..38da2b580d2d 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.h +++ b/drivers/scsi/aic7xxx/aic79xx_osm.h @@ -50,9 +50,6 @@ #include #include #include -#ifndef AHD_MODVERSION_FILE -#define __NO_VERSION__ -#endif #include #include diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index 07087d348cd6..123ec0b80606 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -119,12 +119,6 @@ * */ -/* - * This is the only file where module.h should - * embed module global version info. - */ -#define AHC_MODVERSION_FILE - #include "aic7xxx_osm.h" #include "aic7xxx_inline.h" #include diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h index a76fa110ffdc..2c309f53022c 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.h +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h @@ -67,9 +67,6 @@ #include #include #include -#ifndef AHC_MODVERSION_FILE -#define __NO_VERSION__ -#endif #include #include #include diff --git a/include/linux/module.h b/include/linux/module.h index 1be44624ec3a..1265b060475d 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -155,7 +155,7 @@ void *__symbol_get_gpl(const char *symbol); #else -#ifdef CONFIG_MODVERSIONING +#ifdef CONFIG_MODVERSIONS /* Mark the CRC weak since genksyms apparently decides not to * generate a checksums for some symbols */ #define __CRC_SYMBOL(sym, sec) \ diff --git a/init/Kconfig b/init/Kconfig index 5541908be8c0..887abc316ee2 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -144,11 +144,10 @@ config OBSOLETE_MODPARM have not been converted to the new module parameter system yet. If unsure, say Y. -config MODVERSIONING +config MODVERSIONS bool "Module versioning support (EXPERIMENTAL)" depends on MODULES && EXPERIMENTAL help - ---help--- Usually, you have to use modules compiled with your kernel. Saying Y here makes it sometimes possible to use modules compiled for different kernels, by adding enough information diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 574addf5af63..b65124149fc3 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -68,13 +68,6 @@ extern struct timezone sys_tz; extern int panic_timeout; -#ifdef CONFIG_MODVERSIONS -const struct module_symbol __export_Using_Versions -__attribute__((section("__ksymtab"))) = { - 1 /* Version version */, "Using_Versions" -}; -#endif - /* process memory management */ EXPORT_SYMBOL(do_mmap_pgoff); EXPORT_SYMBOL(do_munmap); diff --git a/kernel/module.c b/kernel/module.c index 64fb7e574927..72b3569404bf 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -725,7 +725,7 @@ static int obsolete_params(const char *name, } #endif /* CONFIG_OBSOLETE_MODPARM */ -#ifdef CONFIG_MODVERSIONING +#ifdef CONFIG_MODVERSIONS static int check_version(Elf_Shdr *sechdrs, unsigned int versindex, const char *symname, @@ -779,7 +779,7 @@ static inline int check_version(Elf_Shdr *sechdrs, { return 1; } -#endif /* CONFIG_MODVERSIONING */ +#endif /* CONFIG_MODVERSIONS */ /* Resolve a symbol for this module. I.e. if we find one, record usage. Must be holding module_mutex. */ diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 1c2a3d057b40..554368dc80c8 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -45,7 +45,7 @@ O_TARGET := $(obj)/built-in.o endif endif -ifdef CONFIG_MODVERSIONING +ifdef CONFIG_MODVERSIONS modules := $(obj-m) touch-module = @mkdir -p $(MODVERDIR)/$(@D); touch $(MODVERDIR)/$(@:.o=.ko) else @@ -60,7 +60,7 @@ __build: $(if $(KBUILD_BUILTIN),$(O_TARGET) $(L_TARGET) $(EXTRA_TARGETS)) \ # Module versioning # --------------------------------------------------------------------------- -ifdef CONFIG_MODVERSIONING +ifdef CONFIG_MODVERSIONS # $(call if_changed_rule,vcc_o_c) does essentially the same as the # normal $(call if_changed_dep,cc_o_c), i.e. compile an object file @@ -153,7 +153,7 @@ quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< %.o: %.c FORCE -ifdef CONFIG_MODVERSIONING +ifdef CONFIG_MODVERSIONS $(call if_changed_rule,vcc_o_c) else $(call if_changed_dep,cc_o_c) @@ -162,7 +162,7 @@ endif # For modversioning, we need to special case single-part modules # to mark them in $(MODVERDIR) -ifdef CONFIG_MODVERSIONING +ifdef CONFIG_MODVERSIONS $(single-used-m): %.o: %.c FORCE $(touch-module) $(call if_changed_rule,vcc_o_c) @@ -259,9 +259,9 @@ targets += $(multi-used-y) $(multi-used-m) # Rule to link modules ( .o -> .ko ) # -# With CONFIG_MODVERSIONING, generation of the final .ko is handled +# With CONFIG_MODVERSIONS, generation of the final .ko is handled # by scripts/Makefile.modver -ifndef CONFIG_MODVERSIONING +ifndef CONFIG_MODVERSIONS quiet_cmd_link_module = LD [M] $@ cmd_link_module = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_MODULE) -o $@ $< init/vermagic.o -- cgit v1.2.3